From 93ac71616348cf0c377c881acae07ea7f647b687 Mon Sep 17 00:00:00 2001 From: Pravin B Shelar Date: Thu, 4 Mar 2021 00:01:57 -0800 Subject: [PATCH 01/91] AGW: MME: GTP-APP: Add support to add S8 tunnels Add support to match on ingress and egress inbound packets. New API is added to added to gtp task to add these flows: openflow_add_s8_tunnel() openflow_del_s8_tunnel() Signed-off-by: Pravin B Shelar --- .../openflow/controller/ControllerEvents.cpp | 8 +- .../openflow/controller/ControllerMain.cpp | 36 ++++ .../lib/openflow/controller/ControllerMain.h | 10 ++ .../openflow/controller/GTPApplication.cpp | 154 ++++++++++++------ .../lib/openflow/controller/GTPApplication.h | 24 ++- .../c/oai/tasks/gtpv1-u/gtp_tunnel_openflow.c | 26 +++ lte/gateway/c/oai/tasks/gtpv1-u/gtpv1u.h | 8 + lte/gateway/c/oai/tasks/gtpv1-u/gtpv1u_task.c | 27 +++ .../c/oai/test/openflow/test_gtp_app.cpp | 114 +++++++++++++ 9 files changed, 347 insertions(+), 60 deletions(-) diff --git a/lte/gateway/c/oai/lib/openflow/controller/ControllerEvents.cpp b/lte/gateway/c/oai/lib/openflow/controller/ControllerEvents.cpp index c7b469f814d7..50e12df9f74c 100644 --- a/lte/gateway/c/oai/lib/openflow/controller/ControllerEvents.cpp +++ b/lte/gateway/c/oai/lib/openflow/controller/ControllerEvents.cpp @@ -185,7 +185,7 @@ AddGTPTunnelEvent::AddGTPTunnelEvent( dl_flow_valid_(false), dl_flow_(), dl_flow_precedence_(DEFAULT_PRECEDENCE), - ExternalEvent(EVENT_ADD_GTP_TUNNEL), + ExternalEvent(EVENT_ADD_GTP_S8_TUNNEL), enb_gtp_port_(enb_gtp_port), pgw_gtp_port_(pgw_gtp_port) {} @@ -203,7 +203,7 @@ AddGTPTunnelEvent::AddGTPTunnelEvent( dl_flow_valid_(false), dl_flow_(), dl_flow_precedence_(DEFAULT_PRECEDENCE), - ExternalEvent(EVENT_ADD_GTP_TUNNEL), + ExternalEvent(EVENT_ADD_GTP_S8_TUNNEL), enb_gtp_port_(enb_gtp_port), pgw_gtp_port_(pgw_gtp_port) {} @@ -285,7 +285,7 @@ DeleteGTPTunnelEvent::DeleteGTPTunnelEvent( in_tei_(in_tei), dl_flow_valid_(true), dl_flow_(*dl_flow), - ExternalEvent(EVENT_DELETE_GTP_TUNNEL), + ExternalEvent(EVENT_DELETE_GTP_S8_TUNNEL), enb_gtp_port_(enb_gtp_port), pgw_gtp_port_(pgw_gtp_port) {} @@ -296,7 +296,7 @@ DeleteGTPTunnelEvent::DeleteGTPTunnelEvent( in_tei_(in_tei), dl_flow_valid_(false), dl_flow_(), - ExternalEvent(EVENT_DELETE_GTP_TUNNEL), + ExternalEvent(EVENT_DELETE_GTP_S8_TUNNEL), enb_gtp_port_(enb_gtp_port), pgw_gtp_port_(pgw_gtp_port) {} diff --git a/lte/gateway/c/oai/lib/openflow/controller/ControllerMain.cpp b/lte/gateway/c/oai/lib/openflow/controller/ControllerMain.cpp index 6417eff3b2c0..bf501ef8d4fd 100644 --- a/lte/gateway/c/oai/lib/openflow/controller/ControllerMain.cpp +++ b/lte/gateway/c/oai/lib/openflow/controller/ControllerMain.cpp @@ -64,6 +64,8 @@ int start_of_controller(bool persist_state) { ctrl.register_for_event(>p_app, openflow::EVENT_SWITCH_UP); ctrl.register_for_event(>p_app, openflow::EVENT_ADD_GTP_TUNNEL); ctrl.register_for_event(>p_app, openflow::EVENT_DELETE_GTP_TUNNEL); + ctrl.register_for_event(>p_app, openflow::EVENT_ADD_GTP_S8_TUNNEL); + ctrl.register_for_event(>p_app, openflow::EVENT_DELETE_GTP_S8_TUNNEL); ctrl.register_for_event(>p_app, openflow::EVENT_DISCARD_DATA_ON_GTP_TUNNEL); ctrl.register_for_event(>p_app, openflow::EVENT_FORWARD_DATA_ON_GTP_TUNNEL); ctrl.start(); @@ -122,6 +124,40 @@ int openflow_controller_del_gtp_tunnel( OAILOG_FUNC_RETURN(LOG_GTPV1U, RETURNok); } +int openflow_controller_add_gtp_s8_tunnel( + struct in_addr ue, struct in6_addr* ue_ipv6, int vlan, struct in_addr enb, + struct in_addr pgw, uint32_t i_tei, uint32_t o_tei, const char* imsi, + struct ip_flow_dl* flow_dl, uint32_t flow_precedence_dl, + uint32_t enb_gtp_port, uint32_t pgw_gtp_port) { + if (flow_dl) { + auto add_tunnel = std::make_shared( + ue, ue_ipv6, vlan, enb, pgw, i_tei, o_tei, imsi, flow_dl, + flow_precedence_dl, enb_gtp_port, pgw_gtp_port); + ctrl.inject_external_event(add_tunnel, external_event_callback); + } else { + auto add_tunnel = std::make_shared( + ue, ue_ipv6, vlan, enb, pgw, i_tei, o_tei, imsi, enb_gtp_port, + pgw_gtp_port); + ctrl.inject_external_event(add_tunnel, external_event_callback); + } + OAILOG_FUNC_RETURN(LOG_GTPV1U, RETURNok); +} + +int openflow_controller_del_gtp_s8_tunnel( + struct in_addr ue, struct in6_addr* ue_ipv6, uint32_t i_tei, + struct ip_flow_dl* flow_dl, uint32_t enb_gtp_port, uint32_t pgw_gtp_port) { + if (flow_dl) { + auto del_tunnel = std::make_shared( + ue, ue_ipv6, i_tei, flow_dl, enb_gtp_port, pgw_gtp_port); + ctrl.inject_external_event(del_tunnel, external_event_callback); + } else { + auto del_tunnel = std::make_shared( + ue, ue_ipv6, i_tei, enb_gtp_port, pgw_gtp_port); + ctrl.inject_external_event(del_tunnel, external_event_callback); + } + OAILOG_FUNC_RETURN(LOG_GTPV1U, RETURNok); +} + int openflow_controller_discard_data_on_tunnel( struct in_addr ue, struct in6_addr* ue_ipv6, uint32_t i_tei, struct ip_flow_dl* flow_dl) { diff --git a/lte/gateway/c/oai/lib/openflow/controller/ControllerMain.h b/lte/gateway/c/oai/lib/openflow/controller/ControllerMain.h index acb40a8e844b..c859f31d558d 100644 --- a/lte/gateway/c/oai/lib/openflow/controller/ControllerMain.h +++ b/lte/gateway/c/oai/lib/openflow/controller/ControllerMain.h @@ -54,6 +54,16 @@ int openflow_controller_add_paging_rule(struct in_addr ue_ip); int openflow_controller_delete_paging_rule(struct in_addr ue_ip); +int openflow_controller_add_gtp_s8_tunnel( + struct in_addr ue, struct in6_addr* ue_ipv6, int vlan, struct in_addr enb, + struct in_addr pgw, uint32_t i_tei, uint32_t o_tei, const char* imsi, + struct ip_flow_dl* flow_dl, uint32_t flow_precedence_dl, + uint32_t enb_gtp_port, uint32_t pgw_gtp_port); + +int openflow_controller_del_gtp_s8_tunnel( + struct in_addr ue, struct in6_addr* ue_ipv6, uint32_t i_tei, + struct ip_flow_dl* flow_dl, uint32_t enb_gtp_port, uint32_t pgw_gtp_port); + #ifdef __cplusplus } #endif diff --git a/lte/gateway/c/oai/lib/openflow/controller/GTPApplication.cpp b/lte/gateway/c/oai/lib/openflow/controller/GTPApplication.cpp index 9de562e7bc16..516be336b4b2 100644 --- a/lte/gateway/c/oai/lib/openflow/controller/GTPApplication.cpp +++ b/lte/gateway/c/oai/lib/openflow/controller/GTPApplication.cpp @@ -49,10 +49,12 @@ GTPApplication::GTPApplication( void GTPApplication::event_callback( const ControllerEvent& ev, const OpenflowMessenger& messenger) { if (ev.get_type() == EVENT_ADD_GTP_TUNNEL) { + printf("pbs: reg add tun"); auto add_tunnel_event = static_cast(ev); add_uplink_tunnel_flow(add_tunnel_event, messenger); - add_downlink_tunnel_flow(add_tunnel_event, messenger, uplink_port_num_); - add_downlink_tunnel_flow(add_tunnel_event, messenger, mtr_port_num_); + add_downlink_tunnel_flow( + add_tunnel_event, messenger, uplink_port_num_, false); + add_downlink_tunnel_flow(add_tunnel_event, messenger, mtr_port_num_, false); add_downlink_arp_flow(add_tunnel_event, messenger, uplink_port_num_); add_downlink_arp_flow(add_tunnel_event, messenger, mtr_port_num_); } else if (ev.get_type() == EVENT_DELETE_GTP_TUNNEL) { @@ -62,6 +64,30 @@ void GTPApplication::event_callback( delete_downlink_tunnel_flow(del_tunnel_event, messenger, mtr_port_num_); delete_downlink_arp_flow(del_tunnel_event, messenger, uplink_port_num_); delete_downlink_arp_flow(del_tunnel_event, messenger, mtr_port_num_); + } else if (ev.get_type() == EVENT_ADD_GTP_S8_TUNNEL) { + printf("pbs: S8 add tun"); + + auto add_tunnel_event = static_cast(ev); + add_uplink_s8_tunnel_flow(add_tunnel_event, messenger); + int pgw_port = add_tunnel_event.get_pgw_gtp_portno(); + if (pgw_port == 0) { + pgw_port = GTPApplication::gtp0_port_num_; + } + add_downlink_tunnel_flow(add_tunnel_event, messenger, pgw_port, true); + add_downlink_tunnel_flow(add_tunnel_event, messenger, mtr_port_num_, true); + add_downlink_arp_flow(add_tunnel_event, messenger, mtr_port_num_); + } else if (ev.get_type() == EVENT_DELETE_GTP_S8_TUNNEL) { + auto del_tunnel_event = static_cast(ev); + // Same delete can be used, since uplink flow match is same for S8 tunnel. + delete_uplink_tunnel_flow(del_tunnel_event, messenger); + int pgw_port = del_tunnel_event.get_pgw_gtp_portno(); + if (pgw_port == 0) { + pgw_port = GTPApplication::gtp0_port_num_; + } + delete_downlink_tunnel_flow(del_tunnel_event, messenger, pgw_port); + + delete_downlink_tunnel_flow(del_tunnel_event, messenger, mtr_port_num_); + delete_downlink_arp_flow(del_tunnel_event, messenger, mtr_port_num_); } else if (ev.get_type() == EVENT_DISCARD_DATA_ON_GTP_TUNNEL) { auto discard_tunnel_flow = static_cast(ev); @@ -103,7 +129,7 @@ void GTPApplication::install_internal_pkt_fwd_flow( /* * Helper method to add matching for adding/deleting the uplink flow */ -void GTPApplication::add_uplink_match( +void GTPApplication::add_tunnel_match( of13::FlowMod& uplink_fm, uint32_t gtp_port, uint32_t i_tei) { if (gtp_port == 0) { gtp_port = GTPApplication::gtp0_port_num_; @@ -131,7 +157,7 @@ void GTPApplication::add_uplink_tunnel_flow( convert_precedence_to_priority(ev.get_dl_flow_precedence()); of13::FlowMod uplink_fm = messenger.create_default_flow_mod(0, of13::OFPFC_ADD, flow_priority); - add_uplink_match(uplink_fm, ev.get_enb_gtp_portno(), ev.get_in_tei()); + add_tunnel_match(uplink_fm, ev.get_enb_gtp_portno(), ev.get_in_tei()); // Set eth src and dst of13::ApplyActions apply_ul_inst; @@ -168,6 +194,19 @@ void GTPApplication::add_uplink_tunnel_flow( OAILOG_DEBUG_UE(LOG_GTPV1U, imsi, "Uplink flow added\n"); } +void GTPApplication::add_uplink_s8_tunnel_flow( + const AddGTPTunnelEvent& ev, const OpenflowMessenger& messenger) { + uint32_t flow_priority = + convert_precedence_to_priority(ev.get_dl_flow_precedence()); + of13::FlowMod uplink_fm = + messenger.create_default_flow_mod(0, of13::OFPFC_ADD, flow_priority); + add_tunnel_match(uplink_fm, ev.get_enb_gtp_portno(), ev.get_in_tei()); + + add_tunnel_flow_action( + ev.get_out_tei(), ev.get_imsi(), ev.get_pgw_ip(), ev.get_pgw_gtp_portno(), + ev.get_connection(), messenger, uplink_fm, "S8 Uplink", true); +} + void GTPApplication::delete_uplink_tunnel_flow( const DeleteGTPTunnelEvent& ev, const OpenflowMessenger& messenger) { of13::FlowMod uplink_fm = @@ -176,7 +215,7 @@ void GTPApplication::delete_uplink_tunnel_flow( uplink_fm.out_port(of13::OFPP_ANY); uplink_fm.out_group(of13::OFPG_ANY); - add_uplink_match(uplink_fm, ev.get_enb_gtp_portno(), ev.get_in_tei()); + add_tunnel_match(uplink_fm, ev.get_enb_gtp_portno(), ev.get_in_tei()); messenger.send_of_msg(uplink_fm, ev.get_connection()); } @@ -316,27 +355,34 @@ static void add_ded_brr_dl_match( /** * Helper function to add downlink flow action. */ -void GTPApplication::add_downlink_tunnel_flow_action( - const AddGTPTunnelEvent& ev, const OpenflowMessenger& messenger, - of13::FlowMod downlink_fm) { - auto imsi = IMSIEncoder::compact_imsi(ev.get_imsi()); +void GTPApplication::add_tunnel_flow_action( + uint32_t tei, std::string ue_imsi, struct in_addr remote_ip, + uint32_t egress_gtp_port, fluid_base::OFConnection* connection, + const OpenflowMessenger& messenger, of13::FlowMod downlink_fm, + const std::string& flow_type, bool passthrough) { of13::ApplyActions apply_dl_inst; + auto imsi = IMSIEncoder::compact_imsi(ue_imsi); // Set outgoing tunnel id and tunnel destination ip - of13::SetFieldAction set_out_tunnel(new of13::TUNNELId(ev.get_out_tei())); + of13::SetFieldAction set_out_tunnel(new of13::TUNNELId(tei)); apply_dl_inst.add_action(set_out_tunnel); of13::SetFieldAction set_tunnel_dst( - new of13::TunnelIPv4Dst(ev.get_enb_ip().s_addr)); + new of13::TunnelIPv4Dst(remote_ip.s_addr)); apply_dl_inst.add_action(set_tunnel_dst); - int gtp_port = ev.get_enb_gtp_portno(); + int gtp_port = egress_gtp_port; if (gtp_port == 0) { gtp_port = GTPApplication::gtp0_port_num_; } - of13::SetFieldAction set_tunnel_port(new of13::NXMReg8(gtp_port)); apply_dl_inst.add_action(set_tunnel_port); + if (passthrough) { + // Set register6, this is pipelineD internal details. + // Once GTP app is moved to pipelineD we can remove this hack. + of13::SetFieldAction set_passthrough(new of13::NXMRegX(6, 1)); + apply_dl_inst.add_action(set_passthrough); + } // add imsi to packet metadata to pass to other tables add_imsi_metadata(apply_dl_inst, imsi); @@ -347,61 +393,69 @@ void GTPApplication::add_downlink_tunnel_flow_action( downlink_fm.add_instruction(goto_inst); // Finally, send flow mod - messenger.send_of_msg(downlink_fm, ev.get_connection()); - OAILOG_DEBUG_UE(LOG_GTPV1U, imsi, "Downlink flow added\n"); + messenger.send_of_msg(downlink_fm, connection); + OAILOG_DEBUG_UE(LOG_GTPV1U, imsi, "%s flow added\n", flow_type); +} + +void GTPApplication::add_downlink_tunnel_flow_action( + const AddGTPTunnelEvent& ev, const OpenflowMessenger& messenger, + of13::FlowMod downlink_fm, bool passthrough) { + add_tunnel_flow_action( + ev.get_out_tei(), ev.get_imsi(), ev.get_enb_ip(), ev.get_enb_gtp_portno(), + ev.get_connection(), messenger, downlink_fm, "S1 Downlink", passthrough); } void GTPApplication::add_downlink_tunnel_flow_ipv4( const AddGTPTunnelEvent& ev, const OpenflowMessenger& messenger, - uint32_t port_number) { + uint32_t ingress_port, bool passthrough) { uint32_t flow_priority = convert_precedence_to_priority(ev.get_dl_flow_precedence()); of13::FlowMod downlink_fm = messenger.create_default_flow_mod(0, of13::OFPFC_ADD, flow_priority); - add_downlink_match(downlink_fm, ev.get_ue_ip(), port_number); - add_downlink_tunnel_flow_action(ev, messenger, downlink_fm); + add_downlink_match(downlink_fm, ev.get_ue_ip(), ingress_port); + add_downlink_tunnel_flow_action(ev, messenger, downlink_fm, passthrough); } void GTPApplication::add_downlink_tunnel_flow_ipv6( const AddGTPTunnelEvent& ev, const OpenflowMessenger& messenger, - uint32_t port_number) { + uint32_t ingress_port, bool passthrough) { uint32_t flow_priority = convert_precedence_to_priority(ev.get_dl_flow_precedence()); of13::FlowMod downlink_fm = messenger.create_default_flow_mod(0, of13::OFPFC_ADD, flow_priority); add_downlink_match_ipv6( - downlink_fm, ev.get_ue_info().get_ipv6(), port_number); - add_downlink_tunnel_flow_action(ev, messenger, downlink_fm); + downlink_fm, ev.get_ue_info().get_ipv6(), ingress_port); + add_downlink_tunnel_flow_action(ev, messenger, downlink_fm, passthrough); } void GTPApplication::add_downlink_tunnel_flow_ded_brr( const AddGTPTunnelEvent& ev, const OpenflowMessenger& messenger, - uint32_t port_number) { + uint32_t ingress_port, bool passthrough) { uint32_t flow_priority = convert_precedence_to_priority(ev.get_dl_flow_precedence()); of13::FlowMod downlink_fm = messenger.create_default_flow_mod(0, of13::OFPFC_ADD, flow_priority); - add_ded_brr_dl_match(downlink_fm, ev.get_dl_flow(), port_number); - add_downlink_tunnel_flow_action(ev, messenger, downlink_fm); + add_ded_brr_dl_match(downlink_fm, ev.get_dl_flow(), ingress_port); + add_downlink_tunnel_flow_action(ev, messenger, downlink_fm, passthrough); } void GTPApplication::add_downlink_tunnel_flow( const AddGTPTunnelEvent& ev, const OpenflowMessenger& messenger, - uint32_t port_number) { + uint32_t ingress_port, bool passthrough) { if (ev.is_dl_flow_valid()) { - add_downlink_tunnel_flow_ded_brr(ev, messenger, port_number); + add_downlink_tunnel_flow_ded_brr(ev, messenger, ingress_port, passthrough); return; } UeNetworkInfo ue_info = ev.get_ue_info(); if (ue_info.is_ue_ipv4_addr_valid()) { - add_downlink_tunnel_flow_ipv4(ev, messenger, port_number); + add_downlink_tunnel_flow_ipv4(ev, messenger, ingress_port, passthrough); } if (ue_info.is_ue_ipv6_addr_valid()) { - add_downlink_tunnel_flow_ipv6(ev, messenger, port_number); + add_downlink_tunnel_flow_ipv6(ev, messenger, ingress_port, passthrough); } } @@ -430,46 +484,46 @@ void GTPApplication::add_downlink_arp_flow_action( void GTPApplication::add_downlink_arp_flow( const AddGTPTunnelEvent& ev, const OpenflowMessenger& messenger, - uint32_t port_number) { + uint32_t ingress_port) { uint32_t flow_priority = convert_precedence_to_priority(ev.get_dl_flow_precedence()); of13::FlowMod downlink_fm = messenger.create_default_flow_mod(0, of13::OFPFC_ADD, flow_priority); - add_downlink_arp_match(downlink_fm, ev.get_ue_ip(), port_number); + add_downlink_arp_match(downlink_fm, ev.get_ue_ip(), ingress_port); add_downlink_arp_flow_action(ev, messenger, downlink_fm); } void GTPApplication::delete_downlink_tunnel_flow_ipv4( const DeleteGTPTunnelEvent& ev, const OpenflowMessenger& messenger, - uint32_t port_number) { + uint32_t ingress_port) { of13::FlowMod downlink_fm = messenger.create_default_flow_mod(0, of13::OFPFC_DELETE, 0); // match all ports and groups downlink_fm.out_port(of13::OFPP_ANY); downlink_fm.out_group(of13::OFPG_ANY); - add_downlink_match(downlink_fm, ev.get_ue_ip(), port_number); + add_downlink_match(downlink_fm, ev.get_ue_ip(), ingress_port); messenger.send_of_msg(downlink_fm, ev.get_connection()); } void GTPApplication::delete_downlink_tunnel_flow_ded_brr( const DeleteGTPTunnelEvent& ev, const OpenflowMessenger& messenger, - uint32_t port_number) { + uint32_t ingress_port) { of13::FlowMod downlink_fm = messenger.create_default_flow_mod(0, of13::OFPFC_DELETE, 0); // match all ports and groups downlink_fm.out_port(of13::OFPP_ANY); downlink_fm.out_group(of13::OFPG_ANY); - add_ded_brr_dl_match(downlink_fm, ev.get_dl_flow(), port_number); + add_ded_brr_dl_match(downlink_fm, ev.get_dl_flow(), ingress_port); messenger.send_of_msg(downlink_fm, ev.get_connection()); } void GTPApplication::delete_downlink_tunnel_flow_ipv6( const DeleteGTPTunnelEvent& ev, const OpenflowMessenger& messenger, - uint32_t port_number) { + uint32_t ingress_port) { of13::FlowMod downlink_fm = messenger.create_default_flow_mod(0, of13::OFPFC_DELETE, 0); // match all ports and groups @@ -477,37 +531,37 @@ void GTPApplication::delete_downlink_tunnel_flow_ipv6( downlink_fm.out_group(of13::OFPG_ANY); add_downlink_match_ipv6( - downlink_fm, ev.get_ue_info().get_ipv6(), port_number); + downlink_fm, ev.get_ue_info().get_ipv6(), ingress_port); messenger.send_of_msg(downlink_fm, ev.get_connection()); } void GTPApplication::delete_downlink_tunnel_flow( const DeleteGTPTunnelEvent& ev, const OpenflowMessenger& messenger, - uint32_t port_number) { + uint32_t ingress_port) { if (ev.is_dl_flow_valid()) { - delete_downlink_tunnel_flow_ded_brr(ev, messenger, port_number); + delete_downlink_tunnel_flow_ded_brr(ev, messenger, ingress_port); return; } UeNetworkInfo ue_info = ev.get_ue_info(); if (ue_info.is_ue_ipv4_addr_valid()) { - delete_downlink_tunnel_flow_ipv4(ev, messenger, port_number); + delete_downlink_tunnel_flow_ipv4(ev, messenger, ingress_port); } if (ue_info.is_ue_ipv6_addr_valid()) { - delete_downlink_tunnel_flow_ipv6(ev, messenger, port_number); + delete_downlink_tunnel_flow_ipv6(ev, messenger, ingress_port); } } void GTPApplication::delete_downlink_arp_flow( const DeleteGTPTunnelEvent& ev, const OpenflowMessenger& messenger, - uint32_t port_number) { + uint32_t ingress_port) { of13::FlowMod downlink_fm = messenger.create_default_flow_mod(0, of13::OFPFC_DELETE, 0); // match all ports and groups downlink_fm.out_port(of13::OFPP_ANY); downlink_fm.out_group(of13::OFPG_ANY); - add_downlink_arp_match(downlink_fm, ev.get_ue_ip(), port_number); + add_downlink_arp_match(downlink_fm, ev.get_ue_ip(), ingress_port); messenger.send_of_msg(downlink_fm, ev.get_connection()); } @@ -522,14 +576,14 @@ void GTPApplication::discard_uplink_tunnel_flow( uplink_fm.cookie(cookie); uplink_fm.cookie_mask(cookie); - add_uplink_match(uplink_fm, gtp0_port_num_, ev.get_in_tei()); + add_tunnel_match(uplink_fm, gtp0_port_num_, ev.get_in_tei()); messenger.send_of_msg(uplink_fm, ev.get_connection()); } void GTPApplication::discard_downlink_tunnel_flow( const HandleDataOnGTPTunnelEvent& ev, const OpenflowMessenger& messenger, - uint32_t port_number) { + uint32_t ingress_port) { of13::FlowMod downlink_fm = messenger.create_default_flow_mod( 0, of13::OFPFC_ADD, DEFAULT_PRIORITY + 1); // match all ports and groups @@ -539,9 +593,9 @@ void GTPApplication::discard_downlink_tunnel_flow( downlink_fm.cookie_mask(cookie + 1); if (ev.is_dl_flow_valid()) { - add_ded_brr_dl_match(downlink_fm, ev.get_dl_flow(), port_number); + add_ded_brr_dl_match(downlink_fm, ev.get_dl_flow(), ingress_port); } else { - add_downlink_match(downlink_fm, ev.get_ue_ip(), port_number); + add_downlink_match(downlink_fm, ev.get_ue_ip(), ingress_port); } messenger.send_of_msg(downlink_fm, ev.get_connection()); @@ -559,14 +613,14 @@ void GTPApplication::forward_uplink_tunnel_flow( uplink_fm.cookie(cookie); uplink_fm.cookie_mask(cookie); - add_uplink_match(uplink_fm, gtp0_port_num_, ev.get_in_tei()); + add_tunnel_match(uplink_fm, gtp0_port_num_, ev.get_in_tei()); messenger.send_of_msg(uplink_fm, ev.get_connection()); } void GTPApplication::forward_downlink_tunnel_flow( const HandleDataOnGTPTunnelEvent& ev, const OpenflowMessenger& messenger, - uint32_t port_number) { + uint32_t ingress_port) { uint32_t flow_priority = convert_precedence_to_priority(ev.get_dl_flow_precedence()); of13::FlowMod downlink_fm = messenger.create_default_flow_mod( @@ -578,9 +632,9 @@ void GTPApplication::forward_downlink_tunnel_flow( downlink_fm.cookie_mask(cookie + 1); if (ev.is_dl_flow_valid()) { - add_ded_brr_dl_match(downlink_fm, ev.get_dl_flow(), port_number); + add_ded_brr_dl_match(downlink_fm, ev.get_dl_flow(), ingress_port); } else { - add_downlink_match(downlink_fm, ev.get_ue_ip(), port_number); + add_downlink_match(downlink_fm, ev.get_ue_ip(), ingress_port); } messenger.send_of_msg(downlink_fm, ev.get_connection()); diff --git a/lte/gateway/c/oai/lib/openflow/controller/GTPApplication.h b/lte/gateway/c/oai/lib/openflow/controller/GTPApplication.h index 3f83d4430b7b..cb4692cab298 100644 --- a/lte/gateway/c/oai/lib/openflow/controller/GTPApplication.h +++ b/lte/gateway/c/oai/lib/openflow/controller/GTPApplication.h @@ -63,7 +63,13 @@ class GTPApplication : public Application { */ void add_downlink_tunnel_flow( const AddGTPTunnelEvent& ev, const OpenflowMessenger& messenger, - uint32_t port_number); + uint32_t port_number, bool passthrough); + + /* + * Add downlink tunnel flow for S8 + */ + void add_uplink_s8_tunnel_flow( + const AddGTPTunnelEvent& ev, const OpenflowMessenger& messenger); /* * Remove uplink tunnel flow on disconnect @@ -137,7 +143,7 @@ class GTPApplication : public Application { * @param gtp_port GTP port from event * @param i_tei tunnel id. */ - void add_uplink_match( + void add_tunnel_match( of13::FlowMod& uplink_fm, uint32_t gtp_port, uint32_t i_tei); private: @@ -164,19 +170,25 @@ class GTPApplication : public Application { const AddGTPTunnelEvent& ev, const OpenflowMessenger& messenger, of13::FlowMod downlink_fm); + void add_tunnel_flow_action( + uint32_t tei, std::string ue_imsi, struct in_addr remote_ip, + uint32_t egress_gtp_port, fluid_base::OFConnection* connection, + const OpenflowMessenger& messenger, of13::FlowMod downlink_fm, + const std::string& flow_type, bool passthrough); + void add_downlink_tunnel_flow_action( const AddGTPTunnelEvent& ev, const OpenflowMessenger& messenger, - of13::FlowMod downlink_fm); + of13::FlowMod downlink_fm, bool passthrough); void add_downlink_tunnel_flow_ipv4( const AddGTPTunnelEvent& ev, const OpenflowMessenger& messenger, - uint32_t port_number); + uint32_t port_number, bool passthrough); void add_downlink_tunnel_flow_ipv6( const AddGTPTunnelEvent& ev, const OpenflowMessenger& messenger, - uint32_t port_number); + uint32_t port_number, bool passthrough); void add_downlink_tunnel_flow_ded_brr( const AddGTPTunnelEvent& ev, const OpenflowMessenger& messenger, - uint32_t port_number); + uint32_t port_number, bool passthrough); void delete_downlink_tunnel_flow_ipv4( const DeleteGTPTunnelEvent& ev, const OpenflowMessenger& messenger, diff --git a/lte/gateway/c/oai/tasks/gtpv1-u/gtp_tunnel_openflow.c b/lte/gateway/c/oai/tasks/gtpv1-u/gtp_tunnel_openflow.c index 48f3c640a1a3..0f6d6e9f27d5 100644 --- a/lte/gateway/c/oai/tasks/gtpv1-u/gtp_tunnel_openflow.c +++ b/lte/gateway/c/oai/tasks/gtpv1-u/gtp_tunnel_openflow.c @@ -267,6 +267,30 @@ int openflow_del_tunnel( ue, ue_ipv6, i_tei, flow_dl, gtp_portno); } +/* S8 tunnel related APIs */ +int openflow_add_s8_tunnel( + struct in_addr ue, struct in6_addr* ue_ipv6, int vlan, struct in_addr enb, + struct in_addr pgw, uint32_t i_tei, uint32_t o_tei, Imsi_t imsi, + struct ip_flow_dl* flow_dl, uint32_t flow_precedence_dl) { + uint32_t enb_portno = find_gtp_port_no(enb); + uint32_t pgw_portno = find_gtp_port_no(pgw); + + return openflow_controller_add_gtp_s8_tunnel( + ue, ue_ipv6, vlan, enb, pgw, i_tei, o_tei, (const char*) imsi.digit, + flow_dl, flow_precedence_dl, enb_portno, pgw_portno); +} + +int openflow_del_s8_tunnel( + struct in_addr enb, struct in_addr pgw, struct in_addr ue, + struct in6_addr* ue_ipv6, uint32_t i_tei, uint32_t o_tei, + struct ip_flow_dl* flow_dl) { + uint32_t enb_portno = find_gtp_port_no(enb); + uint32_t pgw_portno = find_gtp_port_no(pgw); + + return openflow_controller_del_gtp_s8_tunnel( + ue, ue_ipv6, i_tei, flow_dl, enb_portno, pgw_portno); +} + int openflow_discard_data_on_tunnel( struct in_addr ue, struct in6_addr* ue_ipv6, uint32_t i_tei, struct ip_flow_dl* flow_dl) { @@ -346,6 +370,8 @@ static const struct gtp_tunnel_ops openflow_ops = { .reset = openflow_reset, .add_tunnel = openflow_add_tunnel, .del_tunnel = openflow_del_tunnel, + .add_s8_tunnel = openflow_add_s8_tunnel, + .del_s8_tunnel = openflow_del_s8_tunnel, .discard_data_on_tunnel = openflow_discard_data_on_tunnel, .forward_data_on_tunnel = openflow_forward_data_on_tunnel, .add_paging_rule = openflow_add_paging_rule, diff --git a/lte/gateway/c/oai/tasks/gtpv1-u/gtpv1u.h b/lte/gateway/c/oai/tasks/gtpv1-u/gtpv1u.h index 362600cf26c3..d598df06d03a 100644 --- a/lte/gateway/c/oai/tasks/gtpv1-u/gtpv1u.h +++ b/lte/gateway/c/oai/tasks/gtpv1-u/gtpv1u.h @@ -143,6 +143,14 @@ struct gtp_tunnel_ops { int (*del_tunnel)( struct in_addr enb, struct in_addr ue, struct in6_addr* ue_ipv6, uint32_t i_tei, uint32_t o_tei, struct ip_flow_dl* flow_dl); + int (*add_s8_tunnel)( + struct in_addr ue, struct in6_addr* ue_ipv6, int vlan, struct in_addr enb, + struct in_addr pgw, uint32_t i_tei, uint32_t o_tei, Imsi_t imsi, + struct ip_flow_dl* flow_dl, uint32_t flow_precedence_dl); + int (*del_s8_tunnel)( + struct in_addr enb, struct in_addr pgw, struct in_addr ue, + struct in6_addr* ue_ipv6, uint32_t i_tei, uint32_t o_tei, + struct ip_flow_dl* flow_dl); int (*discard_data_on_tunnel)( struct in_addr ue, struct in6_addr* ue_ipv6, uint32_t i_tei, struct ip_flow_dl* flow_dl); diff --git a/lte/gateway/c/oai/tasks/gtpv1-u/gtpv1u_task.c b/lte/gateway/c/oai/tasks/gtpv1-u/gtpv1u_task.c index d3d684986092..f492af7be8cf 100644 --- a/lte/gateway/c/oai/tasks/gtpv1-u/gtpv1u_task.c +++ b/lte/gateway/c/oai/tasks/gtpv1-u/gtpv1u_task.c @@ -200,6 +200,33 @@ int gtpv1u_add_tunnel( ue, ue_ipv6, vlan, enb, i_tei, o_tei, imsi, flow_dl, flow_precedence_dl); } +int gtpv1u_add_s8_tunnel( + struct in_addr ue, struct in6_addr* ue_ipv6, int vlan, struct in_addr enb, + struct in_addr pgw, uint32_t i_tei, uint32_t o_tei, Imsi_t imsi, + struct ip_flow_dl* flow_dl, uint32_t flow_precedence_dl) { + OAILOG_DEBUG(LOG_GTPV1U, "Add S8 tunnel ue %s", inet_ntoa(ue)); + if (gtp_tunnel_ops->add_s8_tunnel) { + return gtp_tunnel_ops->add_s8_tunnel( + ue, ue_ipv6, vlan, enb, pgw, i_tei, o_tei, imsi, flow_dl, + flow_precedence_dl); + } else { + return -EINVAL; + } +} + +int gtpv1u_del_s8_tunnel( + struct in_addr enb, struct in_addr pgw, struct in_addr ue, + struct in6_addr* ue_ipv6, uint32_t i_tei, uint32_t o_tei, + struct ip_flow_dl* flow_dl) { + OAILOG_DEBUG(LOG_GTPV1U, "Del S8 tunnel ue %s", inet_ntoa(ue)); + if (gtp_tunnel_ops->del_s8_tunnel) { + return gtp_tunnel_ops->del_s8_tunnel( + enb, pgw, ue, ue_ipv6, i_tei, o_tei, flow_dl); + } else { + return -EINVAL; + } +} + //------------------------------------------------------------------------------ void gtpv1u_exit(void) { gtp_tunnel_ops->uninit(); diff --git a/lte/gateway/c/oai/test/openflow/test_gtp_app.cpp b/lte/gateway/c/oai/test/openflow/test_gtp_app.cpp index dde5835873c2..80657d5ae2ba 100644 --- a/lte/gateway/c/oai/test/openflow/test_gtp_app.cpp +++ b/lte/gateway/c/oai/test/openflow/test_gtp_app.cpp @@ -66,6 +66,9 @@ class GTPApplicationTest : public ::testing::Test { new OpenflowController("127.0.0.1", 6666, 2, false, messenger)); controller->register_for_event(gtp_app, openflow::EVENT_ADD_GTP_TUNNEL); controller->register_for_event(gtp_app, openflow::EVENT_DELETE_GTP_TUNNEL); + controller->register_for_event(gtp_app, openflow::EVENT_ADD_GTP_S8_TUNNEL); + controller->register_for_event( + gtp_app, openflow::EVENT_DELETE_GTP_S8_TUNNEL); } virtual void TearDown() { @@ -897,6 +900,117 @@ TEST_F(GTPApplicationTest, TestDeleteTunnelDlFlowIpv6) { controller->dispatch_event(del_tunnel); } +/* + * Test that tunnel flows are added when an add S8 tunnel event is sent. + * This only tests the flow matchers for now, because it is not easy to verify + * the actions with the libfluid framework + */ +TEST_F(GTPApplicationTest, TestAddTunnelS8) { + struct in_addr ue_ip; + ue_ip.s_addr = inet_addr("0.0.0.1"); + struct in_addr enb_ip; + enb_ip.s_addr = inet_addr("0.0.0.2"); + struct in_addr pgw_ip; + enb_ip.s_addr = inet_addr("0.0.0.22"); + uint32_t in_tei = 1; + uint32_t out_tei = 2; + char imsi[] = "001010000000013"; + int vlan = 0; + int enb_port = 100; + int pgw_port = 200; + + AddGTPTunnelEvent add_tunnel( + ue_ip, NULL, vlan, enb_ip, pgw_ip, in_tei, out_tei, imsi, enb_port, + pgw_port); + // Uplink + EXPECT_CALL( + *messenger, + send_of_msg( + AllOf( + CheckTableId(0), CheckInPort(enb_port), CheckTunnelId(in_tei), + CheckCommandType(of13::OFPFC_ADD)), + _)) + .Times(1); + // downlink + EXPECT_CALL( + *messenger, + send_of_msg( + AllOf( + CheckTableId(0), CheckInPort(pgw_port), CheckEthType(0x0800), + CheckIPv4Dst(ue_ip), CheckCommandType(of13::OFPFC_ADD)), + _)) + .Times(1); + EXPECT_CALL( + *messenger, + send_of_msg( + AllOf( + CheckTableId(0), CheckInPort(TEST_MTR_PORT), CheckEthType(0x0800), + CheckIPv4Dst(ue_ip), CheckCommandType(of13::OFPFC_ADD)), + _)) + .Times(1); + + EXPECT_CALL( + *messenger, + send_of_msg( + AllOf( + CheckTableId(0), CheckInPort(TEST_MTR_PORT), CheckEthType(0x0806), + CheckArpTpa(ue_ip), CheckCommandType(of13::OFPFC_ADD)), + _)) + .Times(1); + + controller->dispatch_event(add_tunnel); +} + +/* + * Test that tunnel flows are deleted when a delete S8 tunnel event is sent + */ +TEST_F(GTPApplicationTest, TestDeleteTunnelS8) { + struct in_addr ue_ip; + ue_ip.s_addr = inet_addr("0.0.0.1"); + uint32_t in_tei = 1; + int enb_port = 100; + int pgw_port = 200; + + DeleteGTPTunnelEvent del_tunnel(ue_ip, NULL, in_tei, enb_port, pgw_port); + // Uplink + EXPECT_CALL( + *messenger, + send_of_msg( + AllOf( + CheckTableId(0), CheckInPort(enb_port), CheckTunnelId(in_tei), + CheckCommandType(of13::OFPFC_DELETE)), + _)) + .Times(1); + // downlink + EXPECT_CALL( + *messenger, + send_of_msg( + AllOf( + CheckTableId(0), CheckInPort(pgw_port), CheckEthType(0x0800), + CheckIPv4Dst(ue_ip), CheckCommandType(of13::OFPFC_DELETE)), + _)) + .Times(1); + EXPECT_CALL( + *messenger, + send_of_msg( + AllOf( + CheckTableId(0), CheckInPort(TEST_MTR_PORT), CheckEthType(0x0800), + CheckIPv4Dst(ue_ip), CheckCommandType(of13::OFPFC_DELETE)), + _)) + .Times(1); + + EXPECT_CALL( + *messenger, + send_of_msg( + AllOf( + CheckTableId(0), CheckInPort(TEST_MTR_PORT), CheckEthType(0x0806), + CheckArpTpa(ue_ip), CheckCommandType(of13::OFPFC_DELETE)), + _)) + .Times(1); + + controller->dispatch_event(del_tunnel); +} + int main(int argc, char** argv) { ::testing::InitGoogleTest(&argc, argv); return RUN_ALL_TESTS(); From ba11f6ae854005319c9676d55e09b214cdd4c5bb Mon Sep 17 00:00:00 2001 From: rashmi Date: Fri, 26 Mar 2021 23:36:52 +0530 Subject: [PATCH 02/91] Add code to handle create session response at sgw_s8 task Signed-off-by: rashmi --- lte/gateway/c/oai/include/messages_def.h | 1 + lte/gateway/c/oai/include/messages_types.h | 1 + .../c/oai/lib/s8_proxy/s8_client_api.cpp | 163 ++++++++- .../tasks/grpc_service/grpc_service_task.c | 3 +- lte/gateway/c/oai/tasks/gtpv1-u/gtpv1u.h | 5 + .../c/oai/tasks/sgw_s8/sgw_s8_handlers.c | 344 +++++++++++++++++- .../c/oai/tasks/sgw_s8/sgw_s8_s11_handlers.h | 7 +- .../oai/tasks/sgw_s8/sgw_s8_state_manager.cpp | 5 + lte/gateway/c/oai/tasks/sgw_s8/sgw_s8_task.c | 5 + 9 files changed, 511 insertions(+), 23 deletions(-) diff --git a/lte/gateway/c/oai/include/messages_def.h b/lte/gateway/c/oai/include/messages_def.h index 097c2b601a07..8d9a8bcdd256 100644 --- a/lte/gateway/c/oai/include/messages_def.h +++ b/lte/gateway/c/oai/include/messages_def.h @@ -47,3 +47,4 @@ #include "udp_messages_def.h" #include "ha_messages_def.h" #include "n11_messages_def.h" +#include "s8_messages_def.h" diff --git a/lte/gateway/c/oai/include/messages_types.h b/lte/gateway/c/oai/include/messages_types.h index 29f94d2c571a..0851f42f8ad9 100644 --- a/lte/gateway/c/oai/include/messages_types.h +++ b/lte/gateway/c/oai/include/messages_types.h @@ -49,5 +49,6 @@ #include "udp_messages_types.h" #include "ha_messages_types.h" #include "n11_messages_types.h" +#include "s8_messages_types.h" #endif /* FILE_MESSAGES_TYPES_SEEN */ diff --git a/lte/gateway/c/oai/lib/s8_proxy/s8_client_api.cpp b/lte/gateway/c/oai/lib/s8_proxy/s8_client_api.cpp index f56c2fe54727..857833661c8a 100644 --- a/lte/gateway/c/oai/lib/s8_proxy/s8_client_api.cpp +++ b/lte/gateway/c/oai/lib/s8_proxy/s8_client_api.cpp @@ -20,15 +20,125 @@ limitations under the License. extern "C" { #include "intertask_interface.h" #include "log.h" +#include "s8_messages_types.h" #include "common_defs.h" +#include "common_types.h" extern task_zmq_ctx_t grpc_service_task_zmq_ctx; } +static void convert_proto_msg_to_itti_csr( + magma::feg::CreateSessionResponsePgw& response, + s8_create_session_response_t** s5_response); + +static void get_qos_from_proto_msg( + const magma::feg::QosInformation& proto_qos, bearer_qos_t* bearer_qos) { + OAILOG_FUNC_IN(LOG_SGW_S8); + bearer_qos->pci = proto_qos.pci(); + bearer_qos->pl = proto_qos.priority_level(); + bearer_qos->pvi = proto_qos.preemption_vulnerability(); + bearer_qos->qci = proto_qos.qci(); + bearer_qos->gbr.br_ul = proto_qos.gbr().br_ul(); + bearer_qos->gbr.br_dl = proto_qos.gbr().br_dl(); + bearer_qos->mbr.br_ul = proto_qos.mbr().br_ul(); + bearer_qos->mbr.br_dl = proto_qos.mbr().br_dl(); + OAILOG_FUNC_OUT(LOG_SGW_S8); +} + +static void get_fteid_from_proto_msg( + const magma::feg::Fteid& proto_fteid, fteid_t* pgw_fteid) { + OAILOG_FUNC_IN(LOG_SGW_S8); + pgw_fteid->teid = proto_fteid.teid(); + if (proto_fteid.ipv4_address().c_str()) { + struct in_addr addr = {0}; + memcpy(&addr, proto_fteid.ipv4_address().c_str(), sizeof(in_addr)); + pgw_fteid->ipv4_address = addr; + } + if (proto_fteid.ipv6_address().c_str()) { + struct in6_addr ip6_addr; + memcpy(&ip6_addr, proto_fteid.ipv6_address().c_str(), sizeof(in6_addr)); + pgw_fteid->ipv6_address = ip6_addr; + } + OAILOG_FUNC_OUT(LOG_SGW_S8); +} + +static void get_paa_from_proto_msg( + const magma::feg::PDNType& proto_pdn_type, + const magma::feg::PdnAddressAllocation& proto_paa, paa_t* paa) { + OAILOG_FUNC_IN(LOG_SGW_S8); + switch (proto_pdn_type) { + case magma::feg::PDNType::IPV4: { + paa->pdn_type = IPv4; + auto ip = proto_paa.ipv4_address(); + memcpy(&paa->ipv4_address, ip.c_str(), sizeof(ip.c_str())); + break; + } + case magma::feg::PDNType::IPV6: { + paa->pdn_type = IPv6; + auto ip = proto_paa.ipv6_address(); + memcpy(&paa->ipv6_address, ip.c_str(), sizeof(ip.c_str())); + paa->ipv6_prefix_length = IPV6_PREFIX_LEN; + break; + } + case magma::feg::PDNType::IPV4V6: { + paa->pdn_type = IPv4_AND_v6; + auto ip = proto_paa.ipv4_address(); + memcpy(&paa->ipv4_address, ip.c_str(), sizeof(ip.c_str())); + auto ipv6 = proto_paa.ipv6_address(); + memcpy(&paa->ipv6_address, ipv6.c_str(), sizeof(ipv6.c_str())); + paa->ipv6_prefix_length = IPV6_PREFIX_LEN; + break; + } + case magma::feg::PDNType::NonIP: { + OAILOG_ERROR(LOG_SGW_S8, " pdn_type NonIP is not supported \n"); + break; + } + default: + OAILOG_ERROR( + LOG_SGW_S8, + "Received invalid pdn_type in create session response \n"); + break; + } + OAILOG_FUNC_OUT(LOG_SGW_S8); +} + static void recv_s8_create_session_response( imsi64_t imsi64, teid_t context_teid, const grpc::Status& status, magma::feg::CreateSessionResponsePgw& response) { OAILOG_FUNC_IN(LOG_SGW_S8); - /*TODO send create session response to sgw_s8 task */ + s8_create_session_response_t* s5_response = NULL; + MessageDef* message_p = NULL; + message_p = itti_alloc_new_message(TASK_GRPC_SERVICE, S8_CREATE_SESSION_RSP); + if (!message_p) { + OAILOG_ERROR_UE( + LOG_SGW_S8, imsi64, + "Failed to allocate memory for S8_CREATE_SESSION_RSP for " + "context_teid" TEID_FMT "\n", + context_teid); + OAILOG_FUNC_OUT(LOG_SGW_S8); + } + s5_response = &message_p->ittiMsg.s8_create_session_rsp; + message_p->ittiMsgHeader.imsi = imsi64; + s5_response->context_teid = context_teid; + if (status.ok()) { + convert_proto_msg_to_itti_csr(response, &s5_response); + } else { + OAILOG_ERROR( + LOG_SGW_S8, + "Received gRPC error for create session response for " + "context_teid " TEID_FMT "\n", + context_teid); + s5_response->cause = REMOTE_PEER_NOT_RESPONDING; + } + OAILOG_DEBUG(LOG_UTIL, "Sending create session response to sgw_s8 task"); + if ((send_msg_to_task(&grpc_service_task_zmq_ctx, TASK_SGW_S8, message_p)) != + RETURNok) { + OAILOG_ERROR_UE( + LOG_SGW_S8, imsi64, + "Failed to send S8 CREATE SESSION RESPONSE message to sgw_s8 task " + "for context_teid " TEID_FMT "\n", + context_teid); + OAILOG_FUNC_OUT(LOG_SGW_S8); + } OAILOG_FUNC_OUT(LOG_SGW_S8); } @@ -150,25 +260,36 @@ static void convert_bearer_context_to_proto( magma::feg::BearerContext* bc) { OAILOG_FUNC_IN(LOG_SGW_S8); bc->set_id(msg_bc->eps_bearer_id); - char sgw_s5s8_up_ip[INET_ADDRSTRLEN]; + char sgw_s8_up_ip[INET_ADDRSTRLEN]; inet_ntop( - AF_INET, &msg_bc->s5_s8_u_pgw_fteid.ipv4_address, sgw_s5s8_up_ip, + AF_INET, &msg_bc->s5_s8_u_sgw_fteid.ipv4_address.s_addr, sgw_s8_up_ip, INET_ADDRSTRLEN); - bc->mutable_user_plane_fteid()->set_ipv4_address(sgw_s5s8_up_ip); + bc->mutable_user_plane_fteid()->set_ipv4_address(sgw_s8_up_ip); bc->mutable_user_plane_fteid()->set_teid(msg_bc->s5_s8_u_sgw_fteid.teid); convert_qos_to_proto_msg(&msg_bc->bearer_level_qos, bc->mutable_qos()); OAILOG_FUNC_OUT(LOG_SGW_S8); } +static void get_msisdn_from_csr_req( + const itti_s11_create_session_request_t* msg, char* msisdn) { + OAILOG_FUNC_IN(LOG_SGW_S8); + uint8_t idx = 0; + for (; ((idx < msg->msisdn.length) && (idx < MSISDN_LENGTH)); idx++) { + msisdn[idx] = convert_digit_to_char(msg->msisdn.digit[idx]); + } + msisdn[idx] = '\0'; + OAILOG_FUNC_OUT(LOG_SGW_S8); +} + static void fill_s8_create_session_req( const itti_s11_create_session_request_t* msg, - magma::feg::CreateSessionRequestPgw* csr) { + magma::feg::CreateSessionRequestPgw* csr, teid_t sgw_s8_teid) { OAILOG_FUNC_IN(LOG_SGW_S8); csr->Clear(); char msisdn[MSISDN_LENGTH + 1]; - int msisdn_len = get_msisdn_from_session_req(msg, msisdn); + get_msisdn_from_csr_req(msg, msisdn); csr->set_imsi((char*) msg->imsi.digit, msg->imsi.length); - csr->set_msisdn((char*) msisdn, msisdn_len); + csr->set_msisdn((char*) msisdn, msg->msisdn.length); char mcc[3]; mcc[0] = convert_digit_to_char(msg->serving_network.mcc[0]); mcc[1] = convert_digit_to_char(msg->serving_network.mcc[1]); @@ -200,6 +321,7 @@ static void fill_s8_create_session_req( convert_bearer_context_to_proto( &msg->bearer_contexts_to_be_created.bearer_contexts[0], bc); } + csr->set_c_agw_teid(sgw_s8_teid); csr->set_charging_characteristics( msg->charging_characteristics.value, msg->charging_characteristics.length); @@ -215,11 +337,16 @@ void send_s8_create_session_request( OAILOG_FUNC_IN(LOG_SGW_S8); magma::feg::CreateSessionRequestPgw csr_req; + std::cout << "Sending create session request for for IMSI: " << imsi64 + << "and context_teid: " << sgw_s11_teid << std::endl; + // teid shall remain same for both sgw's s11 interface and s8 interface as + // teid is allocated per PDN OAILOG_INFO_UE( LOG_SGW_S8, imsi64, "Sending create session request for context_tied " TEID_FMT "\n", sgw_s11_teid); - fill_s8_create_session_req(msg, &csr_req); + + fill_s8_create_session_req(msg, &csr_req, sgw_s11_teid); magma::S8Client::s8_create_session_request( csr_req, @@ -228,3 +355,23 @@ void send_s8_create_session_request( recv_s8_create_session_response(imsi64, sgw_s11_teid, status, response); }); } + +static void convert_proto_msg_to_itti_csr( + magma::feg::CreateSessionResponsePgw& response, + s8_create_session_response_t** s5_response) { + OAILOG_FUNC_IN(LOG_SGW_S8); + (*s5_response)->apn_restriction_value = response.apn_restriction(); + get_fteid_from_proto_msg( + response.c_pgw_fteid(), &(*s5_response)->pgw_s8_cp_teid); + get_paa_from_proto_msg( + response.pdn_type(), response.paa(), &(*s5_response)->paa); + + s8_bearer_context_t s8_bc = (*s5_response)->bearer_context[0]; + s8_bc.eps_bearer_id = response.bearer_context().id(); + s8_bc.charging_id = response.bearer_context().charging_id(); + get_qos_from_proto_msg(response.bearer_context().qos(), &s8_bc.qos); + get_fteid_from_proto_msg( + response.bearer_context().user_plane_fteid(), &s8_bc.pgw_s8_up); + (*s5_response)->cause = response.mutable_gtp_error()->cause(); + OAILOG_FUNC_OUT(LOG_SGW_S8); +} diff --git a/lte/gateway/c/oai/tasks/grpc_service/grpc_service_task.c b/lte/gateway/c/oai/tasks/grpc_service/grpc_service_task.c index 43992f14e9e0..719480f75371 100644 --- a/lte/gateway/c/oai/tasks/grpc_service/grpc_service_task.c +++ b/lte/gateway/c/oai/tasks/grpc_service/grpc_service_task.c @@ -57,7 +57,8 @@ static int handle_message(zloop_t* loop, zsock_t* reader, void* arg) { static void* grpc_service_thread(__attribute__((unused)) void* args) { itti_mark_task_ready(TASK_GRPC_SERVICE); init_task_context( - TASK_GRPC_SERVICE, (task_id_t[]){TASK_SPGW_APP, TASK_HA, TASK_AMF_APP}, 3, + TASK_GRPC_SERVICE, + (task_id_t[]){TASK_SPGW_APP, TASK_HA, TASK_AMF_APP, TASK_SGW_S8}, 4, handle_message, &grpc_service_task_zmq_ctx); start_grpc_service(grpc_service_config->server_address); diff --git a/lte/gateway/c/oai/tasks/gtpv1-u/gtpv1u.h b/lte/gateway/c/oai/tasks/gtpv1-u/gtpv1u.h index d598df06d03a..ca01b53b0d6d 100644 --- a/lte/gateway/c/oai/tasks/gtpv1-u/gtpv1u.h +++ b/lte/gateway/c/oai/tasks/gtpv1-u/gtpv1u.h @@ -173,4 +173,9 @@ int gtpv1u_add_tunnel( struct in_addr ue, struct in6_addr* ue_ipv6, int vlan, struct in_addr enb, uint32_t i_tei, uint32_t o_tei, Imsi_t imsi, struct ip_flow_dl* flow_dl, uint32_t flow_precedence_dl); + +int gtpv1u_add_s8_tunnel( + struct in_addr ue, struct in6_addr* ue_ipv6, int vlan, struct in_addr enb, + struct in_addr pgw, uint32_t i_tei, uint32_t o_tei, Imsi_t imsi, + struct ip_flow_dl* flow_dl, uint32_t flow_precedence_dl); #endif /* FILE_GTPV1_U_SEEN */ diff --git a/lte/gateway/c/oai/tasks/sgw_s8/sgw_s8_handlers.c b/lte/gateway/c/oai/tasks/sgw_s8/sgw_s8_handlers.c index 62d3403ffc77..754bee54ebfc 100644 --- a/lte/gateway/c/oai/tasks/sgw_s8/sgw_s8_handlers.c +++ b/lte/gateway/c/oai/tasks/sgw_s8/sgw_s8_handlers.c @@ -13,14 +13,24 @@ limitations under the License. #include #include #include +#include "3gpp_29.274.h" #include "log.h" #include "common_defs.h" +#include "intertask_interface.h" #include "sgw_context_manager.h" #include "spgw_types.h" #include "sgw_s8_state.h" #include "sgw_s8_s11_handlers.h" #include "s8_client_api.h" +#include "gtpv1u.h" +#include "dynamic_memory_check.h" +extern task_zmq_ctx_t sgw_s8_task_zmq_ctx; +extern struct gtp_tunnel_ops* gtp_tunnel_ops; + +static int sgw_s8_add_gtp_tunnel( + sgw_eps_bearer_ctxt_t* eps_bearer_ctxt_p, + sgw_eps_bearer_context_information_t* sgw_context_p); uint32_t sgw_get_new_s1u_teid(sgw_state_t* state) { if (state->s1u_teid == 0) { state->s1u_teid = INITIAL_SGW_S8_S1U_TEID; @@ -34,6 +44,59 @@ uint32_t sgw_get_new_s5s8u_teid(sgw_state_t* state) { return (state->s5s8u_teid); } +void sgw_remove_sgw_bearer_context_information( + sgw_state_t* sgw_state, teid_t teid, imsi64_t imsi64) { + OAILOG_FUNC_IN(LOG_SGW_S8); + int rc = 0; + + hash_table_ts_t* state_imsi_ht = get_sgw_ue_state(); + rc = hashtable_ts_free(state_imsi_ht, teid); + if (rc != HASH_TABLE_OK) { + OAILOG_ERROR_UE( + LOG_SGW_S8, imsi64, "Failed to free teid from state_imsi_ht\n"); + OAILOG_FUNC_OUT(LOG_SGW_S8); + } + spgw_ue_context_t* ue_context_p = NULL; + hashtable_ts_get( + sgw_state->imsi_ue_context_htbl, (const hash_key_t) imsi64, + (void**) &ue_context_p); + if (ue_context_p) { + sgw_s11_teid_t* p1 = LIST_FIRST(&(ue_context_p->sgw_s11_teid_list)); + while (p1) { + if (p1->sgw_s11_teid == teid) { + LIST_REMOVE(p1, entries); + free_wrapper((void**) &p1); + break; + } + p1 = LIST_NEXT(p1, entries); + } + if (LIST_EMPTY(&ue_context_p->sgw_s11_teid_list)) { + rc = hashtable_ts_free( + sgw_state->imsi_ue_context_htbl, (const hash_key_t) imsi64); + if (rc != HASH_TABLE_OK) { + OAILOG_ERROR_UE( + LOG_SGW_S8, imsi64, + "Failed to free imsi64 from imsi_ue_context_htbl\n"); + OAILOG_FUNC_OUT(LOG_SGW_S8); + } + delete_sgw_ue_state(imsi64); + } + } + OAILOG_FUNC_OUT(LOG_SGW_S8); +} + +sgw_eps_bearer_context_information_t* sgw_get_sgw_eps_bearer_context( + teid_t teid) { + OAILOG_FUNC_IN(LOG_SGW_S8); + sgw_eps_bearer_context_information_t* sgw_bearer_context_info = NULL; + hash_table_ts_t* state_imsi_ht = get_sgw_ue_state(); + + hashtable_ts_get( + state_imsi_ht, (const hash_key_t) teid, + (void**) &sgw_bearer_context_info); + OAILOG_FUNC_RETURN(LOG_SGW_S8, sgw_bearer_context_info); +} + // Re-using the spgw_ue context structure, that contains the list sgw_s11_teids // and is common across both sgw_s8 and spgw tasks. spgw_ue_context_t* sgw_create_or_get_ue_context( @@ -52,7 +115,7 @@ spgw_ue_context_t* sgw_create_or_get_ue_context( (void*) ue_context_p); } else { OAILOG_ERROR_UE( - LOG_SGW_S8, imsi64, "Failed to allocate memory for UE context \n"); + LOG_SGW_S8, imsi64, "Failed to allocate memory for UE context\n"); } } OAILOG_FUNC_RETURN(LOG_SGW_S8, ue_context_p); @@ -117,8 +180,7 @@ sgw_create_bearer_context_information_in_collection(teid_t teid) { } void sgw_s8_handle_s11_create_session_request( - sgw_state_t* sgw_state, - const itti_s11_create_session_request_t* const session_req_pP, + sgw_state_t* sgw_state, itti_s11_create_session_request_t* session_req_pP, imsi64_t imsi64) { OAILOG_FUNC_IN(LOG_SGW_S8); OAILOG_INFO_UE( @@ -160,7 +222,7 @@ void sgw_s8_handle_s11_create_session_request( OAILOG_DEBUG_UE( LOG_SGW_S8, imsi64, "Rx CREATE-SESSION-REQUEST MME S11 teid " TEID_FMT - "SGW S11 teid " TEID_FMT " APN %s EPS bearer Id %d\n", + "SGW S11 teid " TEID_FMT " APN %s EPS bearer Id %u\n", sgw_s11_tunnel.remote_teid, sgw_s11_tunnel.local_teid, session_req_pP->apn, session_req_pP->bearer_contexts_to_be_created.bearer_contexts[0] @@ -207,17 +269,17 @@ void sgw_s8_handle_s11_create_session_request( } new_sgw_eps_context->pdn_connection.s_gw_teid_S5_S8_cp = sgw_s11_tunnel.local_teid; - bearer_context_to_be_created_t csr_bearer_context = - session_req_pP->bearer_contexts_to_be_created.bearer_contexts[0]; + bearer_context_to_be_created_t* csr_bearer_context = + &session_req_pP->bearer_contexts_to_be_created.bearer_contexts[0]; new_sgw_eps_context->pdn_connection.default_bearer = - csr_bearer_context.eps_bearer_id; + csr_bearer_context->eps_bearer_id; /* creating an eps bearer entry * copy informations from create session request to bearer context information */ eps_bearer_ctxt_p = sgw_cm_create_eps_bearer_ctxt_in_collection( - &new_sgw_eps_context->pdn_connection, csr_bearer_context.eps_bearer_id); + &new_sgw_eps_context->pdn_connection, csr_bearer_context->eps_bearer_id); if (eps_bearer_ctxt_p == NULL) { OAILOG_ERROR_UE( LOG_SGW_S8, imsi64, "Failed to create new EPS bearer entry\n"); @@ -226,13 +288,13 @@ void sgw_s8_handle_s11_create_session_request( "internal_software_error"); OAILOG_FUNC_OUT(LOG_SGW_S8); } - eps_bearer_ctxt_p->eps_bearer_qos = csr_bearer_context.bearer_level_qos; + eps_bearer_ctxt_p->eps_bearer_qos = csr_bearer_context->bearer_level_qos; eps_bearer_ctxt_p->s_gw_teid_S1u_S12_S4_up = sgw_get_new_s1u_teid(sgw_state); eps_bearer_ctxt_p->s_gw_teid_S5_S8_up = sgw_get_new_s5s8u_teid(sgw_state); - csr_bearer_context.s5_s8_u_sgw_fteid.teid = + csr_bearer_context->s5_s8_u_sgw_fteid.teid = eps_bearer_ctxt_p->s_gw_teid_S5_S8_up; - csr_bearer_context.s5_s8_u_sgw_fteid.ipv4 = 1; - csr_bearer_context.s5_s8_u_sgw_fteid.ipv4_address = + csr_bearer_context->s5_s8_u_sgw_fteid.ipv4 = 1; + csr_bearer_context->s5_s8_u_sgw_fteid.ipv4_address = sgw_state->sgw_ip_address_S5S8_up; send_s8_create_session_request( @@ -240,3 +302,261 @@ void sgw_s8_handle_s11_create_session_request( sgw_display_s11_bearer_context_information(new_sgw_eps_context); OAILOG_FUNC_OUT(LOG_SGW_S8); } + +static int update_bearer_context_info( + sgw_eps_bearer_context_information_t* sgw_context_p, + const s8_create_session_response_t* const session_rsp_p) { + OAILOG_FUNC_IN(LOG_SGW_S8); + sgw_eps_bearer_ctxt_t* default_bearer_ctx_p = sgw_cm_get_eps_bearer_entry( + &sgw_context_p->pdn_connection, session_rsp_p->eps_bearer_id); + if (!default_bearer_ctx_p) { + OAILOG_ERROR_UE( + LOG_SGW_S8, sgw_context_p->imsi64, + "Failed to get default eps bearer context for context teid " TEID_FMT + "and bearer_id :%u \n", + session_rsp_p->context_teid, session_rsp_p->eps_bearer_id); + OAILOG_FUNC_RETURN(LOG_SGW_S8, RETURNerror); + } + memcpy(&default_bearer_ctx_p->paa, &session_rsp_p->paa, sizeof(paa_t)); + if (session_rsp_p->eps_bearer_id != + session_rsp_p->bearer_context[0].eps_bearer_id) { + OAILOG_ERROR_UE( + LOG_SGW_S8, sgw_context_p->imsi64, + "Mismatch of eps bearer id between bearer context's bearer id:%d and " + "default eps bearer id:%u for context teid " TEID_FMT "\n", + session_rsp_p->bearer_context[0].eps_bearer_id, + session_rsp_p->eps_bearer_id, session_rsp_p->context_teid); + OAILOG_FUNC_RETURN(LOG_SGW_S8, RETURNerror); + } + s8_bearer_context_t s5s8_bearer_context = session_rsp_p->bearer_context[0]; + FTEID_T_2_IP_ADDRESS_T( + (&s5s8_bearer_context.pgw_s8_up), + (&default_bearer_ctx_p->p_gw_address_in_use_up)); + default_bearer_ctx_p->p_gw_teid_S5_S8_up = s5s8_bearer_context.pgw_s8_up.teid; + + memcpy( + &default_bearer_ctx_p->eps_bearer_qos, &s5s8_bearer_context.qos, + sizeof(bearer_qos_t)); + sgw_s8_add_gtp_tunnel(default_bearer_ctx_p, sgw_context_p); + OAILOG_FUNC_RETURN(LOG_SGW_S8, RETURNok); +} + +static int sgw_s8_send_create_session_response( + sgw_state_t* sgw_state, sgw_eps_bearer_context_information_t* sgw_context_p, + const s8_create_session_response_t* const session_rsp_p) { + OAILOG_FUNC_IN(LOG_SGW_S8); + MessageDef* message_p = NULL; + itti_s11_create_session_response_t* create_session_response_p = NULL; + + message_p = itti_alloc_new_message(TASK_SGW_S8, S11_CREATE_SESSION_RESPONSE); + if (message_p == NULL) { + OAILOG_CRITICAL_UE( + LOG_SGW_S8, sgw_context_p->imsi64, + "Failed to allocate memory for S11_create_session_response \n"); + OAILOG_FUNC_RETURN(LOG_SGW_S8, RETURNerror); + } + if (!sgw_context_p) { + OAILOG_ERROR_UE( + LOG_SGW_S8, sgw_context_p->imsi64, "sgw_context_p is NULL \n"); + OAILOG_FUNC_RETURN(LOG_SGW_S8, RETURNerror); + } + + create_session_response_p = &message_p->ittiMsg.s11_create_session_response; + if (!create_session_response_p) { + OAILOG_ERROR_UE( + LOG_SGW_S8, sgw_context_p->imsi64, + "create_session_response_p is NULL \n"); + OAILOG_FUNC_RETURN(LOG_SGW_S8, RETURNerror); + } + create_session_response_p->teid = sgw_context_p->mme_teid_S11; + create_session_response_p->cause.cause_value = session_rsp_p->cause; + create_session_response_p->s11_sgw_fteid.teid = + sgw_context_p->s_gw_teid_S11_S4; + create_session_response_p->s11_sgw_fteid.interface_type = S11_SGW_GTP_C; + create_session_response_p->s11_sgw_fteid.ipv4 = 1; + create_session_response_p->s11_sgw_fteid.ipv4_address.s_addr = + spgw_config.sgw_config.ipv4.S11.s_addr; + + if (session_rsp_p->cause == REQUEST_ACCEPTED) { + sgw_eps_bearer_ctxt_t* default_bearer_ctxt_p = sgw_cm_get_eps_bearer_entry( + &sgw_context_p->pdn_connection, session_rsp_p->eps_bearer_id); + if (!default_bearer_ctxt_p) { + OAILOG_ERROR_UE( + LOG_SGW_S8, sgw_context_p->imsi64, + "Failed to get default eps bearer context for sgw_s11_teid " TEID_FMT + "and bearer_id :%u \n", + session_rsp_p->context_teid, session_rsp_p->eps_bearer_id); + OAILOG_FUNC_RETURN(LOG_SGW_S8, RETURNerror); + } + memcpy( + &create_session_response_p->paa, &default_bearer_ctxt_p->paa, + sizeof(paa_t)); + create_session_response_p->bearer_contexts_created.num_bearer_context = 1; + bearer_context_created_t* bearer_context = + &create_session_response_p->bearer_contexts_created.bearer_contexts[0]; + + bearer_context->s1u_sgw_fteid.teid = + default_bearer_ctxt_p->s_gw_teid_S1u_S12_S4_up; + bearer_context->s1u_sgw_fteid.interface_type = S1_U_SGW_GTP_U; + bearer_context->s1u_sgw_fteid.ipv4 = 1; + bearer_context->s1u_sgw_fteid.ipv4_address.s_addr = + sgw_state->sgw_ip_address_S1u_S12_S4_up.s_addr; + bearer_context->eps_bearer_id = session_rsp_p->eps_bearer_id; + /* + * Set the Cause information from bearer context created. + * "Request accepted" is returned when the GTPv2 entity has accepted a + * control plane request. + */ + create_session_response_p->bearer_contexts_created.bearer_contexts[0] + .cause.cause_value = session_rsp_p->cause; + } else { + create_session_response_p->bearer_contexts_marked_for_removal + .num_bearer_context = 1; + bearer_context_marked_for_removal_t* bearer_context = + &create_session_response_p->bearer_contexts_marked_for_removal + .bearer_contexts[0]; + bearer_context->cause.cause_value = session_rsp_p->cause; + bearer_context->eps_bearer_id = session_rsp_p->eps_bearer_id; + create_session_response_p->trxn = sgw_context_p->trxn; + } + message_p->ittiMsgHeader.imsi = sgw_context_p->imsi64; + OAILOG_DEBUG_UE( + LOG_SGW_S8, sgw_context_p->imsi64, + "Sending S11 Create Session Response to mme for mme_s11_teid: " TEID_FMT + "\n", + create_session_response_p->teid); + + send_msg_to_task(&sgw_s8_task_zmq_ctx, TASK_MME_APP, message_p); + + OAILOG_FUNC_RETURN(LOG_SGW_S8, RETURNok); +} + +void sgw_s8_handle_create_session_response( + sgw_state_t* sgw_state, s8_create_session_response_t* session_rsp_p, + imsi64_t imsi64) { + OAILOG_FUNC_IN(LOG_SGW_S8); + if (!session_rsp_p) { + OAILOG_ERROR_UE( + LOG_SGW_S8, imsi64, + "Received null create session response from s8_proxy\n"); + OAILOG_FUNC_OUT(LOG_SGW_S8); + } + OAILOG_INFO_UE( + LOG_SGW_S8, imsi64, + " Rx S5S8_CREATE_SESSION_RSP for context_teid " TEID_FMT "\n", + session_rsp_p->context_teid); + + sgw_eps_bearer_context_information_t* sgw_context_p = + sgw_get_sgw_eps_bearer_context(session_rsp_p->context_teid); + if (!sgw_context_p) { + OAILOG_ERROR_UE( + LOG_SGW_S8, imsi64, + "Failed to fetch sgw_eps_bearer_context_info from " + "context_teid " TEID_FMT " \n", + session_rsp_p->context_teid); + OAILOG_FUNC_OUT(LOG_SGW_S8); + } + + if (session_rsp_p->cause == REQUEST_ACCEPTED) { + // update pdn details received from PGW + sgw_context_p->pdn_connection.p_gw_teid_S5_S8_cp = + session_rsp_p->pgw_s8_cp_teid.teid; + // update bearer context details received from PGW + if ((update_bearer_context_info(sgw_context_p, session_rsp_p)) != + RETURNok) { + /*TODO need to send delete session request to pgw */ + session_rsp_p->cause = CONTEXT_NOT_FOUND; + } + } + // send Create session response to mme + if ((sgw_s8_send_create_session_response( + sgw_state, sgw_context_p, session_rsp_p)) != RETURNok) { + OAILOG_ERROR_UE( + LOG_SGW_S8, imsi64, + "Failed to send create session response to mme for " + "sgw_s11_teid " TEID_FMT "\n", + session_rsp_p->context_teid); + } + if (session_rsp_p->cause != REQUEST_ACCEPTED) { + OAILOG_ERROR_UE( + LOG_SGW_S8, imsi64, + "Received failed create session response with cause: %d for " + "context_id: " TEID_FMT "\n", + session_rsp_p->cause, session_rsp_p->context_teid); + sgw_remove_sgw_bearer_context_information( + sgw_state, session_rsp_p->context_teid, imsi64); + } + OAILOG_FUNC_OUT(LOG_SGW_S8); +} + +// Helper function to add gtp tunnels for default bearers +static int sgw_s8_add_gtp_tunnel( + sgw_eps_bearer_ctxt_t* eps_bearer_ctxt_p, + sgw_eps_bearer_context_information_t* sgw_context_p) { + int rv = RETURNok; + struct in_addr enb = {.s_addr = 0}; + struct in_addr pgw = {.s_addr = 0}; + enb.s_addr = + eps_bearer_ctxt_p->enb_ip_address_S1u.address.ipv4_address.s_addr; + + pgw.s_addr = + eps_bearer_ctxt_p->p_gw_address_in_use_up.address.ipv4_address.s_addr; + struct in_addr ue_ipv4 = {.s_addr = 0}; + struct in6_addr* ue_ipv6 = NULL; + ue_ipv4.s_addr = eps_bearer_ctxt_p->paa.ipv4_address.s_addr; + if ((eps_bearer_ctxt_p->paa.pdn_type == IPv6) || + (eps_bearer_ctxt_p->paa.pdn_type == IPv4_AND_v6)) { + ue_ipv6 = &eps_bearer_ctxt_p->paa.ipv6_address; + } + + int vlan = eps_bearer_ctxt_p->paa.vlan; + Imsi_t imsi = sgw_context_p->imsi; + + char ip6_str[INET6_ADDRSTRLEN]; + if (ue_ipv6) { + inet_ntop(AF_INET6, ue_ipv6, ip6_str, INET6_ADDRSTRLEN); + } + /* UE is switching back to EPS services after the CS Fallback + * If Modify bearer Request is received in UE suspended mode, Resume PS + * data + */ + if (sgw_context_p->pdn_connection.ue_suspended_for_ps_handover) { + rv = gtp_tunnel_ops->forward_data_on_tunnel( + ue_ipv4, ue_ipv6, eps_bearer_ctxt_p->s_gw_teid_S1u_S12_S4_up, NULL, + DEFAULT_PRECEDENCE); + if (rv < 0) { + OAILOG_ERROR_UE( + LOG_SGW_S8, sgw_context_p->imsi64, + "ERROR in forwarding data on TUNNEL err=%d\n", rv); + } + } else { + OAILOG_DEBUG_UE( + LOG_SGW_S8, sgw_context_p->imsi64, + "Adding tunnel for bearer %u ue addr %x\n", + eps_bearer_ctxt_p->eps_bearer_id, ue_ipv4.s_addr); + if (eps_bearer_ctxt_p->eps_bearer_id == + sgw_context_p->pdn_connection.default_bearer) { + // Set default precedence and tft for default bearer + if (ue_ipv6) { + OAILOG_INFO_UE( + LOG_SGW_S8, sgw_context_p->imsi64, + "Adding tunnel for ipv6 ue addr %s, enb %x, " + "s_gw_teid_S1u_S12_S4_up %x, enb_teid_S1u %x pgw_up_ip %x " + "pgw_up_teid %x \n", + ip6_str, enb.s_addr, eps_bearer_ctxt_p->s_gw_teid_S1u_S12_S4_up, + eps_bearer_ctxt_p->enb_teid_S1u, pgw.s_addr, + eps_bearer_ctxt_p->p_gw_teid_S5_S8_up); + } + rv = gtpv1u_add_s8_tunnel( + ue_ipv4, ue_ipv6, vlan, enb, pgw, + eps_bearer_ctxt_p->s_gw_teid_S1u_S12_S4_up, + eps_bearer_ctxt_p->enb_teid_S1u, imsi, NULL, DEFAULT_PRECEDENCE); + if (rv < 0) { + OAILOG_ERROR_UE( + LOG_SGW_S8, sgw_context_p->imsi64, + "ERROR in setting up TUNNEL err=%d\n", rv); + } + } + } + OAILOG_FUNC_RETURN(LOG_SGW_S8, rv); +} diff --git a/lte/gateway/c/oai/tasks/sgw_s8/sgw_s8_s11_handlers.h b/lte/gateway/c/oai/tasks/sgw_s8/sgw_s8_s11_handlers.h index eb8febc60737..059f69b5064e 100644 --- a/lte/gateway/c/oai/tasks/sgw_s8/sgw_s8_s11_handlers.h +++ b/lte/gateway/c/oai/tasks/sgw_s8/sgw_s8_s11_handlers.h @@ -18,6 +18,9 @@ limitations under the License. #include "spgw_types.h" void sgw_s8_handle_s11_create_session_request( - sgw_state_t* sgw_state, - const itti_s11_create_session_request_t* const session_req_p, + sgw_state_t* sgw_state, itti_s11_create_session_request_t* session_req_p, + imsi64_t imsi64); + +void sgw_s8_handle_create_session_response( + sgw_state_t* sgw_state, s8_create_session_response_t* session_rsp_p, imsi64_t imsi64); diff --git a/lte/gateway/c/oai/tasks/sgw_s8/sgw_s8_state_manager.cpp b/lte/gateway/c/oai/tasks/sgw_s8/sgw_s8_state_manager.cpp index b3061a8d355e..122ce60d5cec 100644 --- a/lte/gateway/c/oai/tasks/sgw_s8/sgw_s8_state_manager.cpp +++ b/lte/gateway/c/oai/tasks/sgw_s8/sgw_s8_state_manager.cpp @@ -56,6 +56,9 @@ void SgwStateManager::create_state() { OAILOG_INFO(LOG_SGW_S8, "Creating SGW_S8 state "); bstring b = bfromcstr(S11_BEARER_CONTEXT_INFO_HT_NAME); + + // sgw_free_s11_bearer_context_information is called when hashtable_ts_free is + // invoked, so as to remove any contexts allocated within sgw_bearer context state_ue_ht = hashtable_ts_create( SGW_STATE_CONTEXT_HT_MAX_SIZE, nullptr, (void (*)(void**)) sgw_free_s11_bearer_context_information, b); @@ -68,6 +71,8 @@ void SgwStateManager::create_state() { state_cache_p->sgw_ip_address_S1u_S12_S4_up.s_addr = config_->ipv4.S1u_S12_S4_up.s_addr; + state_cache_p->sgw_ip_address_S5S8_up.s_addr = config_->ipv4.S5_S8_up.s_addr; + state_cache_p->imsi_ue_context_htbl = hashtable_ts_create( SGW_STATE_CONTEXT_HT_MAX_SIZE, nullptr, (void (*)(void**)) sgw_free_ue_context, nullptr); diff --git a/lte/gateway/c/oai/tasks/sgw_s8/sgw_s8_task.c b/lte/gateway/c/oai/tasks/sgw_s8/sgw_s8_task.c index dfb4e80c52aa..d48e6780b42b 100644 --- a/lte/gateway/c/oai/tasks/sgw_s8/sgw_s8_task.c +++ b/lte/gateway/c/oai/tasks/sgw_s8/sgw_s8_task.c @@ -71,6 +71,11 @@ static int handle_message(zloop_t* loop, zsock_t* reader, void* arg) { sgw_state, &received_message_p->ittiMsg.s11_create_session_request, imsi64); } break; + case S8_CREATE_SESSION_RSP: { + sgw_s8_handle_create_session_response( + sgw_state, &received_message_p->ittiMsg.s8_create_session_rsp, + imsi64); + } break; default: { OAILOG_DEBUG( From 70c31858de7296eb9945b51dfdca57938f25f87f Mon Sep 17 00:00:00 2001 From: rashmi Date: Tue, 30 Mar 2021 06:41:46 +0530 Subject: [PATCH 03/91] Incorporated review comments Signed-off-by: rashmi --- lte/gateway/c/oai/include/s8_messages_def.h | 17 ++++++++ lte/gateway/c/oai/include/s8_messages_types.h | 40 +++++++++++++++++++ .../c/oai/lib/s8_proxy/s8_client_api.cpp | 31 +++++++------- .../c/oai/tasks/sgw_s8/sgw_s8_handlers.c | 3 +- .../oai/tasks/sgw_s8/sgw_s8_state_manager.cpp | 2 +- 5 files changed, 75 insertions(+), 18 deletions(-) create mode 100644 lte/gateway/c/oai/include/s8_messages_def.h create mode 100644 lte/gateway/c/oai/include/s8_messages_types.h diff --git a/lte/gateway/c/oai/include/s8_messages_def.h b/lte/gateway/c/oai/include/s8_messages_def.h new file mode 100644 index 000000000000..790ca40cedd7 --- /dev/null +++ b/lte/gateway/c/oai/include/s8_messages_def.h @@ -0,0 +1,17 @@ +/* +Copyright 2020 The Magma Authors. + +This source code is licensed under the BSD-style license found in the +LICENSE file in the root directory of this source tree. + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +// WARNING: Do not include this header directly. Use intertask_interface.h +// instead. +MESSAGE_DEF( + S8_CREATE_SESSION_RSP, s8_create_session_response_t, s8_create_session_rsp) diff --git a/lte/gateway/c/oai/include/s8_messages_types.h b/lte/gateway/c/oai/include/s8_messages_types.h new file mode 100644 index 000000000000..04aa3906b11a --- /dev/null +++ b/lte/gateway/c/oai/include/s8_messages_types.h @@ -0,0 +1,40 @@ +/* +Copyright 2020 The Magma Authors. + +This source code is licensed under the BSD-style license found in the +LICENSE file in the root directory of this source tree. + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +#pragma once +#include "3gpp_23.003.h" +#include "3gpp_29.274.h" +#include "common_types.h" + +#define S8_CREATE_SESSION_RSP(mSGpTR) (mSGpTR)->ittiMsg.s8_create_session_rsp + +typedef struct s8_bearer_context_s { + ebi_t eps_bearer_id; + bearer_qos_t qos; + fteid_t pgw_s8_up; + uint32_t charging_id; +} s8_bearer_context_t; + +typedef struct s8_create_session_response_s { + uint8_t imsi_length; + char imsi[IMSI_BCD_DIGITS_MAX + 1]; + pdn_type_t pdn_type; + paa_t paa; + teid_t context_teid; // SGW_S11_teid, created per PDN + ebi_t eps_bearer_id; + s8_bearer_context_t bearer_context[BEARERS_PER_UE]; + uint8_t response_cause; + uint8_t apn_restriction_value; + fteid_t pgw_s8_cp_teid; + uint32_t cause; +} s8_create_session_response_t; diff --git a/lte/gateway/c/oai/lib/s8_proxy/s8_client_api.cpp b/lte/gateway/c/oai/lib/s8_proxy/s8_client_api.cpp index 857833661c8a..3d64ea391e37 100644 --- a/lte/gateway/c/oai/lib/s8_proxy/s8_client_api.cpp +++ b/lte/gateway/c/oai/lib/s8_proxy/s8_client_api.cpp @@ -28,7 +28,7 @@ extern task_zmq_ctx_t grpc_service_task_zmq_ctx; static void convert_proto_msg_to_itti_csr( magma::feg::CreateSessionResponsePgw& response, - s8_create_session_response_t** s5_response); + s8_create_session_response_t* s5_response); static void get_qos_from_proto_msg( const magma::feg::QosInformation& proto_qos, bearer_qos_t* bearer_qos) { @@ -105,8 +105,8 @@ static void recv_s8_create_session_response( imsi64_t imsi64, teid_t context_teid, const grpc::Status& status, magma::feg::CreateSessionResponsePgw& response) { OAILOG_FUNC_IN(LOG_SGW_S8); - s8_create_session_response_t* s5_response = NULL; - MessageDef* message_p = NULL; + s8_create_session_response_t* s5_response = NULL; + MessageDef* message_p = NULL; message_p = itti_alloc_new_message(TASK_GRPC_SERVICE, S8_CREATE_SESSION_RSP); if (!message_p) { OAILOG_ERROR_UE( @@ -120,7 +120,7 @@ static void recv_s8_create_session_response( message_p->ittiMsgHeader.imsi = imsi64; s5_response->context_teid = context_teid; if (status.ok()) { - convert_proto_msg_to_itti_csr(response, &s5_response); + convert_proto_msg_to_itti_csr(response, s5_response); } else { OAILOG_ERROR( LOG_SGW_S8, @@ -337,8 +337,6 @@ void send_s8_create_session_request( OAILOG_FUNC_IN(LOG_SGW_S8); magma::feg::CreateSessionRequestPgw csr_req; - std::cout << "Sending create session request for for IMSI: " << imsi64 - << "and context_teid: " << sgw_s11_teid << std::endl; // teid shall remain same for both sgw's s11 interface and s8 interface as // teid is allocated per PDN OAILOG_INFO_UE( @@ -358,20 +356,21 @@ void send_s8_create_session_request( static void convert_proto_msg_to_itti_csr( magma::feg::CreateSessionResponsePgw& response, - s8_create_session_response_t** s5_response) { + s8_create_session_response_t* s5_response) { OAILOG_FUNC_IN(LOG_SGW_S8); - (*s5_response)->apn_restriction_value = response.apn_restriction(); + s5_response->apn_restriction_value = response.apn_restriction(); get_fteid_from_proto_msg( - response.c_pgw_fteid(), &(*s5_response)->pgw_s8_cp_teid); + response.c_pgw_fteid(), &s5_response->pgw_s8_cp_teid); get_paa_from_proto_msg( - response.pdn_type(), response.paa(), &(*s5_response)->paa); + response.pdn_type(), response.paa(), &s5_response->paa); - s8_bearer_context_t s8_bc = (*s5_response)->bearer_context[0]; - s8_bc.eps_bearer_id = response.bearer_context().id(); - s8_bc.charging_id = response.bearer_context().charging_id(); - get_qos_from_proto_msg(response.bearer_context().qos(), &s8_bc.qos); + s8_bearer_context_t* s8_bc = &(s5_response->bearer_context[0]); + s8_bc->eps_bearer_id = response.bearer_context().id(); + s5_response->eps_bearer_id = s8_bc->eps_bearer_id; + s8_bc->charging_id = response.bearer_context().charging_id(); + get_qos_from_proto_msg(response.bearer_context().qos(), &s8_bc->qos); get_fteid_from_proto_msg( - response.bearer_context().user_plane_fteid(), &s8_bc.pgw_s8_up); - (*s5_response)->cause = response.mutable_gtp_error()->cause(); + response.bearer_context().user_plane_fteid(), &s8_bc->pgw_s8_up); + s5_response->cause = response.mutable_gtp_error()->cause(); OAILOG_FUNC_OUT(LOG_SGW_S8); } diff --git a/lte/gateway/c/oai/tasks/sgw_s8/sgw_s8_handlers.c b/lte/gateway/c/oai/tasks/sgw_s8/sgw_s8_handlers.c index 754bee54ebfc..765a6d9a58ce 100644 --- a/lte/gateway/c/oai/tasks/sgw_s8/sgw_s8_handlers.c +++ b/lte/gateway/c/oai/tasks/sgw_s8/sgw_s8_handlers.c @@ -31,6 +31,7 @@ extern struct gtp_tunnel_ops* gtp_tunnel_ops; static int sgw_s8_add_gtp_tunnel( sgw_eps_bearer_ctxt_t* eps_bearer_ctxt_p, sgw_eps_bearer_context_information_t* sgw_context_p); + uint32_t sgw_get_new_s1u_teid(sgw_state_t* state) { if (state->s1u_teid == 0) { state->s1u_teid = INITIAL_SGW_S8_S1U_TEID; @@ -417,7 +418,7 @@ static int sgw_s8_send_create_session_response( .bearer_contexts[0]; bearer_context->cause.cause_value = session_rsp_p->cause; bearer_context->eps_bearer_id = session_rsp_p->eps_bearer_id; - create_session_response_p->trxn = sgw_context_p->trxn; + create_session_response_p->trxn = sgw_context_p->trxn; } message_p->ittiMsgHeader.imsi = sgw_context_p->imsi64; OAILOG_DEBUG_UE( diff --git a/lte/gateway/c/oai/tasks/sgw_s8/sgw_s8_state_manager.cpp b/lte/gateway/c/oai/tasks/sgw_s8/sgw_s8_state_manager.cpp index 122ce60d5cec..b9931d827539 100644 --- a/lte/gateway/c/oai/tasks/sgw_s8/sgw_s8_state_manager.cpp +++ b/lte/gateway/c/oai/tasks/sgw_s8/sgw_s8_state_manager.cpp @@ -55,7 +55,7 @@ void SgwStateManager::create_state() { } OAILOG_INFO(LOG_SGW_S8, "Creating SGW_S8 state "); - bstring b = bfromcstr(S11_BEARER_CONTEXT_INFO_HT_NAME); + bstring b = bfromcstr(S11_BEARER_CONTEXT_INFO_HT_NAME); // sgw_free_s11_bearer_context_information is called when hashtable_ts_free is // invoked, so as to remove any contexts allocated within sgw_bearer context From edd5a6075c34802d31a07afe00f706b63bb342f3 Mon Sep 17 00:00:00 2001 From: rashmi Date: Tue, 30 Mar 2021 15:40:21 +0530 Subject: [PATCH 04/91] Added code to handle modify bearer request in s8_task Signed-off-by: rashmi --- .../c/oai/include/ip_forward_messages_types.h | 2 - .../c/oai/include/sgw_context_manager.h | 1 + .../c/oai/lib/s8_proxy/s8_client_api.cpp | 28 +- lte/gateway/c/oai/tasks/gtpv1-u/gtpv1u.h | 6 + .../c/oai/tasks/sgw/sgw_context_manager.c | 20 +- lte/gateway/c/oai/tasks/sgw/sgw_handlers.c | 58 ++-- lte/gateway/c/oai/tasks/sgw/sgw_handlers.h | 24 ++ .../c/oai/tasks/sgw_s8/sgw_s8_handlers.c | 289 +++++++++++++++++- .../c/oai/tasks/sgw_s8/sgw_s8_s11_handlers.h | 5 + lte/gateway/c/oai/tasks/sgw_s8/sgw_s8_task.c | 5 + 10 files changed, 381 insertions(+), 57 deletions(-) diff --git a/lte/gateway/c/oai/include/ip_forward_messages_types.h b/lte/gateway/c/oai/include/ip_forward_messages_types.h index d15d93e6d9e9..45d2f7919508 100644 --- a/lte/gateway/c/oai/include/ip_forward_messages_types.h +++ b/lte/gateway/c/oai/include/ip_forward_messages_types.h @@ -80,8 +80,6 @@ typedef struct { typedef struct { teid_t context_teid; ///< Tunnel Endpoint Identifier S11 - SGIStatus_t status; ///< Status of endpoint creation (Failed = 0xFF or - ///< Success = 0x0) uint8_t num_bearers_modified; bearer_cxt_t bearer_contexts_to_be_modified[BEARERS_PER_UE]; uint8_t num_bearers_removed; diff --git a/lte/gateway/c/oai/include/sgw_context_manager.h b/lte/gateway/c/oai/include/sgw_context_manager.h index 969a2cf3dcaf..c7680e8cf184 100644 --- a/lte/gateway/c/oai/include/sgw_context_manager.h +++ b/lte/gateway/c/oai/include/sgw_context_manager.h @@ -38,6 +38,7 @@ void sgw_display_sgw_eps_bearer_context( const sgw_eps_bearer_ctxt_t* eps_bearer_ctxt); void sgw_display_s11teid2mme(mme_sgw_tunnel_t* mme_sgw_tunnel); void sgw_display_s11_bearer_context_information( + log_proto_t module, sgw_eps_bearer_context_information_t* sgw_context_information); void pgw_lite_cm_free_apn(pgw_apn_t** apnP); diff --git a/lte/gateway/c/oai/lib/s8_proxy/s8_client_api.cpp b/lte/gateway/c/oai/lib/s8_proxy/s8_client_api.cpp index 3d64ea391e37..1dd4093c4b2f 100644 --- a/lte/gateway/c/oai/lib/s8_proxy/s8_client_api.cpp +++ b/lte/gateway/c/oai/lib/s8_proxy/s8_client_api.cpp @@ -49,14 +49,16 @@ static void get_fteid_from_proto_msg( OAILOG_FUNC_IN(LOG_SGW_S8); pgw_fteid->teid = proto_fteid.teid(); if (proto_fteid.ipv4_address().c_str()) { - struct in_addr addr = {0}; - memcpy(&addr, proto_fteid.ipv4_address().c_str(), sizeof(in_addr)); - pgw_fteid->ipv4_address = addr; + pgw_fteid->ipv4 = true; + inet_pton( + AF_INET, proto_fteid.ipv4_address().c_str(), + &(pgw_fteid->ipv4_address)); } if (proto_fteid.ipv6_address().c_str()) { - struct in6_addr ip6_addr; - memcpy(&ip6_addr, proto_fteid.ipv6_address().c_str(), sizeof(in6_addr)); - pgw_fteid->ipv6_address = ip6_addr; + pgw_fteid->ipv6 = true; + inet_pton( + AF_INET6, proto_fteid.ipv6_address().c_str(), + &(pgw_fteid->ipv6_address)); } OAILOG_FUNC_OUT(LOG_SGW_S8); } @@ -69,22 +71,22 @@ static void get_paa_from_proto_msg( case magma::feg::PDNType::IPV4: { paa->pdn_type = IPv4; auto ip = proto_paa.ipv4_address(); - memcpy(&paa->ipv4_address, ip.c_str(), sizeof(ip.c_str())); + inet_pton(AF_INET, ip.c_str(), &(paa->ipv4_address)); break; } case magma::feg::PDNType::IPV6: { paa->pdn_type = IPv6; auto ip = proto_paa.ipv6_address(); - memcpy(&paa->ipv6_address, ip.c_str(), sizeof(ip.c_str())); + inet_pton(AF_INET6, ip.c_str(), &(paa->ipv6_address)); paa->ipv6_prefix_length = IPV6_PREFIX_LEN; break; } case magma::feg::PDNType::IPV4V6: { paa->pdn_type = IPv4_AND_v6; auto ip = proto_paa.ipv4_address(); - memcpy(&paa->ipv4_address, ip.c_str(), sizeof(ip.c_str())); + inet_pton(AF_INET, ip.c_str(), &(paa->ipv4_address)); auto ipv6 = proto_paa.ipv6_address(); - memcpy(&paa->ipv6_address, ipv6.c_str(), sizeof(ipv6.c_str())); + inet_pton(AF_INET6, ipv6.c_str(), &(paa->ipv6_address)); paa->ipv6_prefix_length = IPV6_PREFIX_LEN; break; } @@ -371,6 +373,10 @@ static void convert_proto_msg_to_itti_csr( get_qos_from_proto_msg(response.bearer_context().qos(), &s8_bc->qos); get_fteid_from_proto_msg( response.bearer_context().user_plane_fteid(), &s8_bc->pgw_s8_up); - s5_response->cause = response.mutable_gtp_error()->cause(); + if (response.has_gtp_error()) { + s5_response->cause = response.mutable_gtp_error()->cause(); + } else { + s5_response->cause = REQUEST_ACCEPTED; + } OAILOG_FUNC_OUT(LOG_SGW_S8); } diff --git a/lte/gateway/c/oai/tasks/gtpv1-u/gtpv1u.h b/lte/gateway/c/oai/tasks/gtpv1-u/gtpv1u.h index ca01b53b0d6d..2fe4745ef102 100644 --- a/lte/gateway/c/oai/tasks/gtpv1-u/gtpv1u.h +++ b/lte/gateway/c/oai/tasks/gtpv1-u/gtpv1u.h @@ -178,4 +178,10 @@ int gtpv1u_add_s8_tunnel( struct in_addr ue, struct in6_addr* ue_ipv6, int vlan, struct in_addr enb, struct in_addr pgw, uint32_t i_tei, uint32_t o_tei, Imsi_t imsi, struct ip_flow_dl* flow_dl, uint32_t flow_precedence_dl); + +int gtpv1u_del_s8_tunnel( + struct in_addr enb, struct in_addr pgw, struct in_addr ue, + struct in6_addr* ue_ipv6, uint32_t i_tei, uint32_t o_tei, + struct ip_flow_dl* flow_dl); + #endif /* FILE_GTPV1_U_SEEN */ diff --git a/lte/gateway/c/oai/tasks/sgw/sgw_context_manager.c b/lte/gateway/c/oai/tasks/sgw/sgw_context_manager.c index 518d53c49439..49ae8700d9ac 100644 --- a/lte/gateway/c/oai/tasks/sgw/sgw_context_manager.c +++ b/lte/gateway/c/oai/tasks/sgw/sgw_context_manager.c @@ -65,41 +65,41 @@ void sgw_display_sgw_eps_bearer_context( //----------------------------------------------------------------------------- void sgw_display_s11_bearer_context_information( + log_proto_t module, sgw_eps_bearer_context_information_t* sgw_context_information) //----------------------------------------------------------------------------- { OAILOG_DEBUG( - LOG_SPGW_APP, "| KEY %" PRId64 ": \n", - sgw_context_information->imsi64); - OAILOG_DEBUG(LOG_SPGW_APP, "|\tsgw_eps_bearer_context_information: |\n"); + module, "| KEY %" PRId64 ": \n", sgw_context_information->imsi64); + OAILOG_DEBUG(module, "|\tsgw_eps_bearer_context_information: |\n"); // Imsi_t imsi; ///< IMSI // (International Mobile Subscriber Identity) is the subscriber permanent // identity. OAILOG_DEBUG( - LOG_SPGW_APP, "|\t\timsi_unauthenticated_indicator:\t%u\n", + module, "|\t\timsi_unauthenticated_indicator:\t%u\n", sgw_context_information->imsi_unauthenticated_indicator); // char msisdn[MSISDN_LENGTH]; ///< The basic MSISDN // of the UE. The presence is dictated by its storage in the HSS. OAILOG_DEBUG( - LOG_SPGW_APP, "|\t\tmme_teid_ S11: \t" TEID_FMT "\n", + module, "|\t\tmme_teid_ S11: \t" TEID_FMT "\n", sgw_context_information->mme_teid_S11); // ip_address_t mme_ip_address_for_S11; ///< MME IP address // the S11 interface. OAILOG_DEBUG( - LOG_SPGW_APP, "|\t\ts_gw_teid_S11_S4: \t" TEID_FMT "\n", + module, "|\t\ts_gw_teid_S11_S4: \t" TEID_FMT "\n", sgw_context_information->s_gw_teid_S11_S4); // ip_address_t s_gw_ip_address_for_S11_S4; ///< S-GW IP address // for the S11 interface and the S4 Interface (control plane). cgi_t // last_known_cell_Id; ///< This is the last location of the UE // known by the network - OAILOG_DEBUG(LOG_SPGW_APP, "|\t\tpdn_connection:\n"); + OAILOG_DEBUG(module, "|\t\tpdn_connection:\n"); OAILOG_DEBUG( - LOG_SPGW_APP, "|\t\t\tapn_in_use: %s\n", + module, "|\t\t\tapn_in_use: %s\n", sgw_context_information->pdn_connection.apn_in_use); OAILOG_DEBUG( - LOG_SPGW_APP, "|\t\t\tdefault_bearer: %u\n", + module, "|\t\t\tdefault_bearer: %u\n", sgw_context_information->pdn_connection.default_bearer); - OAILOG_DEBUG(LOG_SPGW_APP, "|\t\t\teps_bearers:\n"); + OAILOG_DEBUG(module, "|\t\t\teps_bearers:\n"); for (int ebix = 0; ebix < BEARERS_PER_UE; ebix++) { sgw_display_sgw_eps_bearer_context( sgw_context_information->pdn_connection.sgw_eps_bearers_array[ebix]); diff --git a/lte/gateway/c/oai/tasks/sgw/sgw_handlers.c b/lte/gateway/c/oai/tasks/sgw/sgw_handlers.c index d22d90b8a102..fffebbd5954a 100644 --- a/lte/gateway/c/oai/tasks/sgw/sgw_handlers.c +++ b/lte/gateway/c/oai/tasks/sgw/sgw_handlers.c @@ -78,9 +78,6 @@ static void _generate_dl_flow( packet_filter_contents_t* packet_filter, in_addr_t ipv4_s_addr, struct in6_addr* ue_ipv6, struct ip_flow_dl* dlflow); -static bool does_bearer_context_hold_valid_enb_ip( - ip_address_t enb_ip_address_S1u); - static void _add_tunnel_helper( s_plus_p_gw_eps_bearer_context_information_t* spgw_context, sgw_eps_bearer_ctxt_t* eps_bearer_ctxt_entry_p, imsi64_t imsi64); @@ -255,8 +252,8 @@ int sgw_handle_s11_create_session_request( session_req_pP->bearer_contexts_to_be_created.bearer_contexts[0] .eps_bearer_id); sgw_display_s11_bearer_context_information( - &s_plus_p_gw_eps_bearer_ctxt_info_p - ->sgw_eps_bearer_context_information); + LOG_SPGW_APP, &s_plus_p_gw_eps_bearer_ctxt_info_p + ->sgw_eps_bearer_context_information); if (eps_bearer_ctxt_p == NULL) { OAILOG_ERROR_UE( @@ -439,10 +436,11 @@ int sgw_handle_sgi_endpoint_created( /* Populates bearer contexts marked for removal structure in * modify bearer rsp message. */ -static void sgw_populate_mbr_bearer_contexts_not_found( +void sgw_populate_mbr_bearer_contexts_not_found( + log_proto_t module, const itti_sgi_update_end_point_response_t* const resp_pP, itti_s11_modify_bearer_response_t* modify_response_p) { - OAILOG_FUNC_IN(LOG_SPGW_APP); + OAILOG_FUNC_IN(module); uint8_t rsp_idx = 0; for (uint8_t idx = 0; idx < resp_pP->num_bearers_not_found; idx++) { modify_response_p->bearer_contexts_marked_for_removal @@ -453,23 +451,22 @@ static void sgw_populate_mbr_bearer_contexts_not_found( .cause.cause_value = CONTEXT_NOT_FOUND; modify_response_p->bearer_contexts_marked_for_removal.num_bearer_context++; } - OAILOG_FUNC_OUT(LOG_SPGW_APP); + OAILOG_FUNC_OUT(module); } //------------------------------------------------------------------------------ /* Populates bearer contexts marked for removal structure in * modify bearer rsp message */ -static void sgw_populate_mbr_bearer_contexts_removed( +void sgw_populate_mbr_bearer_contexts_removed( const itti_sgi_update_end_point_response_t* const resp_pP, imsi64_t imsi64, - s_plus_p_gw_eps_bearer_context_information_t* new_bearer_ctxt_info_p, + sgw_eps_bearer_context_information_t* sgw_context_p, itti_s11_modify_bearer_response_t* modify_response_p) { OAILOG_FUNC_IN(LOG_SPGW_APP); uint8_t rsp_idx = 0; sgw_eps_bearer_ctxt_t* eps_bearer_ctxt_p = NULL; for (uint8_t idx = 0; idx < resp_pP->num_bearers_removed; idx++) { eps_bearer_ctxt_p = sgw_cm_get_eps_bearer_entry( - &new_bearer_ctxt_info_p->sgw_eps_bearer_context_information - .pdn_connection, + &(sgw_context_p->pdn_connection), resp_pP->bearer_contexts_to_be_removed[idx]); /* If context is found, delete the context and set cause as * REQUEST_ACCEPTED. If context is not found set the cause as @@ -478,9 +475,8 @@ static void sgw_populate_mbr_bearer_contexts_removed( */ if (NULL != eps_bearer_ctxt_p) { sgw_free_eps_bearer_context( - &new_bearer_ctxt_info_p->sgw_eps_bearer_context_information - .pdn_connection.sgw_eps_bearers_array[EBI_TO_INDEX( - eps_bearer_ctxt_p->eps_bearer_id)]); + &(sgw_context_p->pdn_connection.sgw_eps_bearers_array[EBI_TO_INDEX( + eps_bearer_ctxt_p->eps_bearer_id)])); modify_response_p->bearer_contexts_marked_for_removal .bearer_contexts[rsp_idx] .cause.cause_value = REQUEST_ACCEPTED; @@ -672,8 +668,8 @@ void sgw_handle_sgi_endpoint_updated( OAILOG_DEBUG_UE( LOG_SPGW_APP, imsi64, - "Rx SGI_UPDATE_ENDPOINT_RESPONSE, Context teid " TEID_FMT " status %d\n", - resp_pP->context_teid, resp_pP->status); + "Rx SGI_UPDATE_ENDPOINT_RESPONSE, Context teid " TEID_FMT "\n", + resp_pP->context_teid); message_p = itti_alloc_new_message(TASK_SPGW_APP, S11_MODIFY_BEARER_RESPONSE); if (!message_p) { @@ -698,8 +694,11 @@ void sgw_handle_sgi_endpoint_updated( sgw_populate_mbr_bearer_contexts_modified( resp_pP, imsi64, new_bearer_ctxt_info_p, modify_response_p); sgw_populate_mbr_bearer_contexts_removed( - resp_pP, imsi64, new_bearer_ctxt_info_p, modify_response_p); - sgw_populate_mbr_bearer_contexts_not_found(resp_pP, modify_response_p); + resp_pP, imsi64, + &new_bearer_ctxt_info_p->sgw_eps_bearer_context_information, + modify_response_p); + sgw_populate_mbr_bearer_contexts_not_found( + LOG_SPGW_APP, resp_pP, modify_response_p); send_msg_to_task(&spgw_app_task_zmq_ctx, TASK_MME, message_p); } OAILOG_FUNC_OUT(LOG_SPGW_APP); @@ -860,7 +859,7 @@ int sgw_handle_sgi_endpoint_deleted( //------------------------------------------------------------------------------ // This function populates itti_sgi_update_end_point_response_t message -static void populate_sgi_end_point_update( +void populate_sgi_end_point_update( uint8_t sgi_rsp_idx, uint8_t idx, const itti_s11_modify_bearer_request_t* const modify_bearer_pP, sgw_eps_bearer_ctxt_t* eps_bearer_ctxt_p, @@ -887,18 +886,19 @@ static void populate_sgi_end_point_update( //------------------------------------------------------------------------------ // This function populates and sends MBR failure message to MME APP -static int send_mbr_failure( +int send_mbr_failure( + log_proto_t module, const itti_s11_modify_bearer_request_t* const modify_bearer_pP, imsi64_t imsi64) { int rv = RETURNok; - OAILOG_FUNC_IN(LOG_SPGW_APP); + OAILOG_FUNC_IN(module); MessageDef* message_p = itti_alloc_new_message(TASK_SPGW_APP, S11_MODIFY_BEARER_RESPONSE); if (!message_p) { OAILOG_ERROR( - LOG_SPGW_APP, "S11_MODIFY_BEARER_RESPONSE memory allocation failed\n"); - OAILOG_FUNC_RETURN(LOG_SPGW_APP, RETURNerror); + module, "S11_MODIFY_BEARER_RESPONSE memory allocation failed\n"); + OAILOG_FUNC_RETURN(module, RETURNerror); } itti_s11_modify_bearer_response_t* modify_response_p = @@ -921,13 +921,13 @@ static int send_mbr_failure( modify_response_p->cause.cause_value = CONTEXT_NOT_FOUND; modify_response_p->trxn = modify_bearer_pP->trxn; OAILOG_DEBUG_UE( - LOG_SPGW_APP, imsi64, + module, imsi64, "Rx MODIFY_BEARER_REQUEST, teid " TEID_FMT " CONTEXT_NOT_FOUND\n", modify_bearer_pP->teid); message_p->ittiMsgHeader.imsi = imsi64; rv = send_msg_to_task(&spgw_app_task_zmq_ctx, TASK_MME, message_p); - OAILOG_FUNC_RETURN(LOG_SPGW_APP, rv); + OAILOG_FUNC_RETURN(module, rv); } //------------------------------------------------------------------------------ @@ -956,7 +956,6 @@ int sgw_handle_modify_bearer_request( modify_bearer_pP->trxn; sgi_update_end_point_resp.context_teid = modify_bearer_pP->teid; - sgi_update_end_point_resp.status = 0; uint8_t sgi_rsp_idx = 0; for (idx = 0; idx < @@ -1031,7 +1030,7 @@ int sgw_handle_modify_bearer_request( } sgw_handle_sgi_endpoint_updated(&sgi_update_end_point_resp, imsi64); } else { // bearer_ctxt_info_p not found - rv = send_mbr_failure(modify_bearer_pP, imsi64); + rv = send_mbr_failure(LOG_SPGW_APP, modify_bearer_pP, imsi64); if (rv != RETURNok) { OAILOG_ERROR( LOG_SPGW_APP, @@ -2227,8 +2226,7 @@ static void _add_tunnel_helper( } } } -static bool does_bearer_context_hold_valid_enb_ip( - ip_address_t enb_ip_address_S1u) { +bool does_bearer_context_hold_valid_enb_ip(ip_address_t enb_ip_address_S1u) { OAILOG_FUNC_IN(LOG_SPGW_APP); static struct in6_addr ipv6_address = {0}; switch (enb_ip_address_S1u.pdn_type) { diff --git a/lte/gateway/c/oai/tasks/sgw/sgw_handlers.h b/lte/gateway/c/oai/tasks/sgw/sgw_handlers.h index 21f40af3d490..658164070882 100644 --- a/lte/gateway/c/oai/tasks/sgw/sgw_handlers.h +++ b/lte/gateway/c/oai/tasks/sgw/sgw_handlers.h @@ -69,4 +69,28 @@ int sgw_handle_ip_allocation_rsp( const itti_ip_allocation_response_t* ip_allocation_rsp, imsi64_t imsi64); bool is_enb_ip_address_same(const fteid_t* fte_p, ip_address_t* ip_p); uint32_t spgw_get_new_s1u_teid(spgw_state_t* state); +int send_mbr_failure( + log_proto_t module, + const itti_s11_modify_bearer_request_t* const modify_bearer_pP, + imsi64_t imsi64); +void sgw_populate_mbr_bearer_contexts_removed( + const itti_sgi_update_end_point_response_t* const resp_pP, imsi64_t imsi64, + sgw_eps_bearer_context_information_t* sgw_context_p, + itti_s11_modify_bearer_response_t* modify_response_p); +void sgw_populate_mbr_bearer_contexts_not_found( + log_proto_t module, + const itti_sgi_update_end_point_response_t* const resp_pP, + itti_s11_modify_bearer_response_t* modify_response_p); +void populate_sgi_end_point_update( + uint8_t sgi_rsp_idx, uint8_t idx, + const itti_s11_modify_bearer_request_t* const modify_bearer_pP, + sgw_eps_bearer_ctxt_t* eps_bearer_ctxt_p, + itti_sgi_update_end_point_response_t* sgi_update_end_point_resp); +bool does_bearer_context_hold_valid_enb_ip(ip_address_t enb_ip_address_S1u); +void populate_sgi_end_point_update( + uint8_t sgi_rsp_idx, uint8_t idx, + const itti_s11_modify_bearer_request_t* const modify_bearer_pP, + sgw_eps_bearer_ctxt_t* eps_bearer_ctxt_p, + itti_sgi_update_end_point_response_t* sgi_update_end_point_resp); + #endif /* FILE_SGW_HANDLERS_SEEN */ diff --git a/lte/gateway/c/oai/tasks/sgw_s8/sgw_s8_handlers.c b/lte/gateway/c/oai/tasks/sgw_s8/sgw_s8_handlers.c index 765a6d9a58ce..763e5e84da21 100644 --- a/lte/gateway/c/oai/tasks/sgw_s8/sgw_s8_handlers.c +++ b/lte/gateway/c/oai/tasks/sgw_s8/sgw_s8_handlers.c @@ -24,6 +24,7 @@ limitations under the License. #include "s8_client_api.h" #include "gtpv1u.h" #include "dynamic_memory_check.h" +#include "sgw_handlers.h" extern task_zmq_ctx_t sgw_s8_task_zmq_ctx; extern struct gtp_tunnel_ops* gtp_tunnel_ops; @@ -32,6 +33,14 @@ static int sgw_s8_add_gtp_tunnel( sgw_eps_bearer_ctxt_t* eps_bearer_ctxt_p, sgw_eps_bearer_context_information_t* sgw_context_p); +static int sgw_s8_add_gtp_s8_tunnel( + sgw_eps_bearer_ctxt_t* eps_bearer_ctxt_p, + sgw_eps_bearer_context_information_t* sgw_context_p); + +static void sgw_send_modify_bearer_response( + sgw_eps_bearer_context_information_t* sgw_context_p, + const itti_sgi_update_end_point_response_t* const resp_pP, imsi64_t imsi64); + uint32_t sgw_get_new_s1u_teid(sgw_state_t* state) { if (state->s1u_teid == 0) { state->s1u_teid = INITIAL_SGW_S8_S1U_TEID; @@ -44,6 +53,10 @@ uint32_t sgw_get_new_s5s8u_teid(sgw_state_t* state) { __sync_fetch_and_add(&state->s5s8u_teid, 1); return (state->s5s8u_teid); } +static void sgw_s8_populate_mbr_bearer_contexts_modified( + const itti_sgi_update_end_point_response_t* const resp_pP, imsi64_t imsi64, + sgw_eps_bearer_context_information_t* sgw_context_p, + itti_s11_modify_bearer_response_t* modify_response_p); void sgw_remove_sgw_bearer_context_information( sgw_state_t* sgw_state, teid_t teid, imsi64_t imsi64) { @@ -300,7 +313,7 @@ void sgw_s8_handle_s11_create_session_request( send_s8_create_session_request( sgw_s11_tunnel.local_teid, session_req_pP, imsi64); - sgw_display_s11_bearer_context_information(new_sgw_eps_context); + sgw_display_s11_bearer_context_information(LOG_SGW_S8, new_sgw_eps_context); OAILOG_FUNC_OUT(LOG_SGW_S8); } @@ -338,7 +351,6 @@ static int update_bearer_context_info( memcpy( &default_bearer_ctx_p->eps_bearer_qos, &s5s8_bearer_context.qos, sizeof(bearer_qos_t)); - sgw_s8_add_gtp_tunnel(default_bearer_ctx_p, sgw_context_p); OAILOG_FUNC_RETURN(LOG_SGW_S8, RETURNok); } @@ -533,8 +545,13 @@ static int sgw_s8_add_gtp_tunnel( } else { OAILOG_DEBUG_UE( LOG_SGW_S8, sgw_context_p->imsi64, - "Adding tunnel for bearer %u ue addr %x\n", - eps_bearer_ctxt_p->eps_bearer_id, ue_ipv4.s_addr); + "Adding tunnel for bearer %u ue addr %x enb " + "%x,s_gw_teid_S1u_S12_S4_up %x, enb_teid_S1u %x pgw_up_ip %x " + "pgw_up_teid %x \n", + eps_bearer_ctxt_p->eps_bearer_id, ue_ipv4.s_addr, enb.s_addr, + eps_bearer_ctxt_p->s_gw_teid_S1u_S12_S4_up, + eps_bearer_ctxt_p->enb_teid_S1u, pgw.s_addr, + eps_bearer_ctxt_p->p_gw_teid_S5_S8_up); if (eps_bearer_ctxt_p->eps_bearer_id == sgw_context_p->pdn_connection.default_bearer) { // Set default precedence and tft for default bearer @@ -561,3 +578,267 @@ static int sgw_s8_add_gtp_tunnel( } OAILOG_FUNC_RETURN(LOG_SGW_S8, rv); } + +void sgw_s8_handle_modify_bearer_request( + sgw_state_t* state, + const itti_s11_modify_bearer_request_t* const modify_bearer_pP, + imsi64_t imsi64) { + OAILOG_FUNC_IN(LOG_SGW_S8); + + uint8_t idx = 0; + uint8_t sgi_rsp_idx = 0; + itti_sgi_update_end_point_response_t sgi_update_end_point_resp = {0}; + struct in_addr enb = {.s_addr = 0}; + struct in_addr pgw = {.s_addr = 0}; + sgw_eps_bearer_ctxt_t* bearer_ctx_p = NULL; + + OAILOG_INFO_UE( + LOG_SGW_S8, imsi64, "Rx MODIFY_BEARER_REQUEST, teid " TEID_FMT "\n", + modify_bearer_pP->teid); + + sgw_eps_bearer_context_information_t* sgw_context_p = + sgw_get_sgw_eps_bearer_context(modify_bearer_pP->teid); + if (!sgw_context_p) { + OAILOG_ERROR_UE( + LOG_SGW_S8, imsi64, + "Failed to fetch sgw_eps_bearer_context_info from " + "context_teid " TEID_FMT " \n", + modify_bearer_pP->teid); + if ((send_mbr_failure(LOG_SGW_S8, modify_bearer_pP, imsi64) != RETURNok)) { + OAILOG_ERROR( + LOG_SGW_S8, + "Error in sending modify bearer response to MME App for context " + "teid " TEID_FMT "\n", + modify_bearer_pP->teid); + } + OAILOG_FUNC_OUT(LOG_SGW_S8); + } + sgw_context_p->trxn = modify_bearer_pP->trxn; + sgi_update_end_point_resp.context_teid = modify_bearer_pP->teid; + // Traversing through the list of bearers to be modified + for (; idx < + modify_bearer_pP->bearer_contexts_to_be_modified.num_bearer_context; + idx++) { + bearer_context_to_be_modified_t mbr_bearer_ctxt_p = + modify_bearer_pP->bearer_contexts_to_be_modified.bearer_contexts[idx]; + bearer_ctx_p = sgw_cm_get_eps_bearer_entry( + &sgw_context_p->pdn_connection, mbr_bearer_ctxt_p.eps_bearer_id); + if (!bearer_ctx_p) { + OAILOG_ERROR_UE( + LOG_SGW_S8, imsi64, + "Failed to get eps bearer context for context teid " TEID_FMT + "and bearer_id :%u \n", + modify_bearer_pP->teid, mbr_bearer_ctxt_p.eps_bearer_id); + sgi_update_end_point_resp.bearer_contexts_not_found[sgi_rsp_idx++] = + mbr_bearer_ctxt_p.eps_bearer_id; + sgi_update_end_point_resp.num_bearers_not_found++; + } else { + enb.s_addr = bearer_ctx_p->enb_ip_address_S1u.address.ipv4_address.s_addr; + pgw.s_addr = + bearer_ctx_p->p_gw_address_in_use_up.address.ipv4_address.s_addr; + + // Send end marker to eNB and then delete the tunnel if enb_ip is + // different + if (does_bearer_context_hold_valid_enb_ip( + bearer_ctx_p->enb_ip_address_S1u) && + is_enb_ip_address_same( + &mbr_bearer_ctxt_p.s1_eNB_fteid, + &bearer_ctx_p->enb_ip_address_S1u) == false) { + struct in_addr ue_ipv4 = bearer_ctx_p->paa.ipv4_address; + struct in6_addr* ue_ipv6 = NULL; + if ((bearer_ctx_p->paa.pdn_type == IPv6) || + (bearer_ctx_p->paa.pdn_type == IPv4_AND_v6)) { + ue_ipv6 = &bearer_ctx_p->paa.ipv6_address; + } + + OAILOG_DEBUG_UE( + LOG_SGW_S8, imsi64, + "Delete GTPv1-U tunnel for sgw_teid:" TEID_FMT "for bearer %u\n", + bearer_ctx_p->s_gw_teid_S1u_S12_S4_up, bearer_ctx_p->eps_bearer_id); + // This is best effort, ignore return code. + gtp_tunnel_ops->send_end_marker(enb, modify_bearer_pP->teid); + // delete GTPv1-U tunnel + gtpv1u_del_s8_tunnel( + enb, pgw, ue_ipv4, ue_ipv6, bearer_ctx_p->s_gw_teid_S1u_S12_S4_up, + bearer_ctx_p->enb_teid_S1u, NULL); + } + populate_sgi_end_point_update( + sgi_rsp_idx, idx, modify_bearer_pP, bearer_ctx_p, + &sgi_update_end_point_resp); + sgi_rsp_idx++; + } + } // for loop + + sgi_rsp_idx = 0; + for (idx = 0; + idx < modify_bearer_pP->bearer_contexts_to_be_removed.num_bearer_context; + idx++) { + bearer_ctx_p = sgw_cm_get_eps_bearer_entry( + &sgw_context_p->pdn_connection, + modify_bearer_pP->bearer_contexts_to_be_removed.bearer_contexts[idx] + .eps_bearer_id); + if (bearer_ctx_p) { + sgi_update_end_point_resp.bearer_contexts_to_be_removed[sgi_rsp_idx++] = + bearer_ctx_p->eps_bearer_id; + sgi_update_end_point_resp.num_bearers_removed++; + } + } + sgw_send_modify_bearer_response( + sgw_context_p, &sgi_update_end_point_resp, imsi64); + OAILOG_FUNC_OUT(LOG_SGW_S8); +} + +static void sgw_send_modify_bearer_response( + sgw_eps_bearer_context_information_t* sgw_context_p, + const itti_sgi_update_end_point_response_t* const resp_pP, + imsi64_t imsi64) { + OAILOG_FUNC_IN(LOG_SGW_S8); + itti_s11_modify_bearer_response_t* modify_response_p = NULL; + MessageDef* message_p = NULL; + + OAILOG_DEBUG_UE( + LOG_SGW_S8, imsi64, + "send modify bearer response for Context teid " TEID_FMT "\n", + resp_pP->context_teid); + message_p = itti_alloc_new_message(TASK_SGW_S8, S11_MODIFY_BEARER_RESPONSE); + + if (!message_p) { + OAILOG_ERROR_UE( + LOG_SGW_S8, imsi64, + "Failed to allocate memory for S11_MODIFY_BEARER_RESPONSE\n"); + OAILOG_FUNC_OUT(LOG_SGW_S8); + } + + modify_response_p = &message_p->ittiMsg.s11_modify_bearer_response; + + if (sgw_context_p) { + modify_response_p->teid = sgw_context_p->mme_teid_S11; + modify_response_p->cause.cause_value = REQUEST_ACCEPTED; + modify_response_p->trxn = sgw_context_p->trxn; + message_p->ittiMsgHeader.imsi = imsi64; + + sgw_s8_populate_mbr_bearer_contexts_modified( + resp_pP, imsi64, sgw_context_p, modify_response_p); + sgw_populate_mbr_bearer_contexts_removed( + resp_pP, imsi64, sgw_context_p, modify_response_p); + sgw_populate_mbr_bearer_contexts_not_found( + LOG_SGW_S8, resp_pP, modify_response_p); + send_msg_to_task(&sgw_s8_task_zmq_ctx, TASK_MME_APP, message_p); + } + OAILOG_FUNC_OUT(LOG_SGW_S8); +} + +static void sgw_s8_populate_mbr_bearer_contexts_modified( + const itti_sgi_update_end_point_response_t* const resp_pP, imsi64_t imsi64, + sgw_eps_bearer_context_information_t* sgw_context_p, + itti_s11_modify_bearer_response_t* modify_response_p) { + OAILOG_FUNC_IN(LOG_SGW_S8); + uint8_t rsp_idx = 0; + sgw_eps_bearer_ctxt_t* eps_bearer_ctxt_p = NULL; + + for (uint8_t idx = 0; idx < resp_pP->num_bearers_modified; idx++) { + eps_bearer_ctxt_p = sgw_cm_get_eps_bearer_entry( + &sgw_context_p->pdn_connection, + resp_pP->bearer_contexts_to_be_modified[idx].eps_bearer_id); + + if (NULL != eps_bearer_ctxt_p) { + OAILOG_DEBUG_UE( + LOG_SGW_S8, imsi64, + "Modify bearer request is accepted for bearer_id :%u\n", + resp_pP->bearer_contexts_to_be_modified[idx].eps_bearer_id); + modify_response_p->bearer_contexts_modified.bearer_contexts[rsp_idx] + .eps_bearer_id = + resp_pP->bearer_contexts_to_be_modified[idx].eps_bearer_id; + modify_response_p->bearer_contexts_modified.bearer_contexts[rsp_idx++] + .cause.cause_value = REQUEST_ACCEPTED; + modify_response_p->bearer_contexts_modified.num_bearer_context++; + + // setup GTPv1-U tunnel + sgw_s8_add_gtp_tunnel(eps_bearer_ctxt_p, sgw_context_p); + sgw_s8_add_gtp_s8_tunnel(eps_bearer_ctxt_p, sgw_context_p); + // may be removed TODO rashmi remove after testing + if (TRAFFIC_FLOW_TEMPLATE_NB_PACKET_FILTERS_MAX > + eps_bearer_ctxt_p->num_sdf) { + int i = 0; + while ((i < eps_bearer_ctxt_p->num_sdf) && + (SDF_ID_NGBR_DEFAULT != eps_bearer_ctxt_p->sdf_id[i])) + i++; + if (i >= eps_bearer_ctxt_p->num_sdf) { + eps_bearer_ctxt_p->sdf_id[eps_bearer_ctxt_p->num_sdf] = + SDF_ID_NGBR_DEFAULT; + eps_bearer_ctxt_p->num_sdf += 1; + } + } + } + } + OAILOG_FUNC_OUT(LOG_SGW_S8); +} + +// Helper function to add gtp tunnels for default bearers +static int sgw_s8_add_gtp_s8_tunnel( + sgw_eps_bearer_ctxt_t* eps_bearer_ctxt_p, + sgw_eps_bearer_context_information_t* sgw_context_p) { + int rv = RETURNok; + struct in_addr enb = {.s_addr = 0}; + struct in_addr pgw = {.s_addr = 0}; + pgw.s_addr = + eps_bearer_ctxt_p->p_gw_address_in_use_up.address.ipv4_address.s_addr; + if ((pgw.s_addr == 0) && (eps_bearer_ctxt_p->p_gw_teid_S5_S8_up == 0)) { + OAILOG_ERROR_UE( + LOG_SGW_S8, sgw_context_p->imsi64, + "bearer context has invalid pgw_s8_teid " TEID_FMT + "pgw_ip address :%x \n", + eps_bearer_ctxt_p->p_gw_teid_S5_S8_up, pgw.s_addr); + OAILOG_FUNC_RETURN(LOG_SGW_S8, RETURNerror); + } + enb.s_addr = + eps_bearer_ctxt_p->enb_ip_address_S1u.address.ipv4_address.s_addr; + + struct in_addr ue_ipv4 = {.s_addr = 0}; + struct in6_addr* ue_ipv6 = NULL; + ue_ipv4.s_addr = eps_bearer_ctxt_p->paa.ipv4_address.s_addr; + if ((eps_bearer_ctxt_p->paa.pdn_type == IPv6) || + (eps_bearer_ctxt_p->paa.pdn_type == IPv4_AND_v6)) { + ue_ipv6 = &eps_bearer_ctxt_p->paa.ipv6_address; + } + + int vlan = eps_bearer_ctxt_p->paa.vlan; + Imsi_t imsi = sgw_context_p->imsi; + + char ip6_str[INET6_ADDRSTRLEN]; + if (ue_ipv6) { + inet_ntop(AF_INET6, ue_ipv6, ip6_str, INET6_ADDRSTRLEN); + } + OAILOG_DEBUG_UE( + LOG_SGW_S8, sgw_context_p->imsi64, + "Adding tunnel for bearer_id %u ue addr %x enb %x s_gw_teid_S5_S8_up %x, " + "s_gw_ip_address_S5_S8_up %x pgw_up_ip %x pgw_up_teid %x \n", + eps_bearer_ctxt_p->eps_bearer_id, ue_ipv4.s_addr, enb.s_addr, + eps_bearer_ctxt_p->s_gw_teid_S5_S8_up, + eps_bearer_ctxt_p->s_gw_ip_address_S5_S8_up.address.ipv4_address.s_addr, + pgw.s_addr, eps_bearer_ctxt_p->p_gw_teid_S5_S8_up); + if (eps_bearer_ctxt_p->eps_bearer_id == + sgw_context_p->pdn_connection.default_bearer) { + // Set default precedence and tft for default bearer + if (ue_ipv6) { + OAILOG_INFO_UE( + LOG_SGW_S8, sgw_context_p->imsi64, + "Adding tunnel for ipv6 ue addr %s, enb %x, " + "s_gw_teid_S5_S8_up %x, s_gw_ip_address_S5_S8_up %x pgw_up_ip %x " + "pgw_up_teid %x \n", + ip6_str, enb.s_addr, eps_bearer_ctxt_p->s_gw_teid_S5_S8_up, + eps_bearer_ctxt_p->s_gw_ip_address_S5_S8_up.address.ipv4_address + .s_addr, + pgw.s_addr, eps_bearer_ctxt_p->p_gw_teid_S5_S8_up); + } + rv = gtpv1u_add_s8_tunnel( + ue_ipv4, ue_ipv6, vlan, enb, pgw, eps_bearer_ctxt_p->s_gw_teid_S5_S8_up, + eps_bearer_ctxt_p->p_gw_teid_S5_S8_up, imsi, NULL, DEFAULT_PRECEDENCE); + if (rv < 0) { + OAILOG_ERROR_UE( + LOG_SGW_S8, sgw_context_p->imsi64, + "ERROR in setting up TUNNEL err=%d\n", rv); + } + } + OAILOG_FUNC_RETURN(LOG_SGW_S8, rv); +} diff --git a/lte/gateway/c/oai/tasks/sgw_s8/sgw_s8_s11_handlers.h b/lte/gateway/c/oai/tasks/sgw_s8/sgw_s8_s11_handlers.h index 059f69b5064e..e6e13379c403 100644 --- a/lte/gateway/c/oai/tasks/sgw_s8/sgw_s8_s11_handlers.h +++ b/lte/gateway/c/oai/tasks/sgw_s8/sgw_s8_s11_handlers.h @@ -24,3 +24,8 @@ void sgw_s8_handle_s11_create_session_request( void sgw_s8_handle_create_session_response( sgw_state_t* sgw_state, s8_create_session_response_t* session_rsp_p, imsi64_t imsi64); + +void sgw_s8_handle_modify_bearer_request( + sgw_state_t* state, + const itti_s11_modify_bearer_request_t* const modify_bearer_pP, + imsi64_t imsi64); diff --git a/lte/gateway/c/oai/tasks/sgw_s8/sgw_s8_task.c b/lte/gateway/c/oai/tasks/sgw_s8/sgw_s8_task.c index d48e6780b42b..3a4c8787e438 100644 --- a/lte/gateway/c/oai/tasks/sgw_s8/sgw_s8_task.c +++ b/lte/gateway/c/oai/tasks/sgw_s8/sgw_s8_task.c @@ -76,6 +76,11 @@ static int handle_message(zloop_t* loop, zsock_t* reader, void* arg) { sgw_state, &received_message_p->ittiMsg.s8_create_session_rsp, imsi64); } break; + case S11_MODIFY_BEARER_REQUEST: { + sgw_s8_handle_modify_bearer_request( + sgw_state, &received_message_p->ittiMsg.s11_modify_bearer_request, + imsi64); + } break; default: { OAILOG_DEBUG( From 27bfb0043aee5c672084b7d664063b45531e8c09 Mon Sep 17 00:00:00 2001 From: rashmi Date: Tue, 23 Mar 2021 19:18:18 +0530 Subject: [PATCH 05/91] Added code to send delete session request Signed-off-by: rashmi --- lte/gateway/c/oai/lib/s8_proxy/S8Client.cpp | 19 ++++++ lte/gateway/c/oai/lib/s8_proxy/S8Client.h | 5 ++ .../c/oai/lib/s8_proxy/s8_client_api.cpp | 29 ++++++++ .../c/oai/lib/s8_proxy/s8_client_api.h | 4 ++ .../c/oai/tasks/sgw_s8/sgw_s8_handlers.c | 66 +++++++++++++++++++ lte/gateway/c/oai/tasks/sgw_s8/sgw_s8_task.c | 5 ++ 6 files changed, 128 insertions(+) diff --git a/lte/gateway/c/oai/lib/s8_proxy/S8Client.cpp b/lte/gateway/c/oai/lib/s8_proxy/S8Client.cpp index be649a823592..6e8e8a33e84e 100644 --- a/lte/gateway/c/oai/lib/s8_proxy/S8Client.cpp +++ b/lte/gateway/c/oai/lib/s8_proxy/S8Client.cpp @@ -60,4 +60,23 @@ void S8Client::s8_create_session_request( response->set_response_reader(std::move(response_reader)); } +void S8Client::s8_delete_session_request( + const DeleteSessionRequestPgw& dsr_req, + std::function callback) { + S8Client& client = get_instance(); + // Create a raw response pointer that stores a callback to be called when the + // gRPC call is answered + auto response = new AsyncLocalResponse( + std::move(callback), RESPONSE_TIMEOUT); + // Create a response reader for the `CreateSession` RPC call. This reader + // stores the client context, the request to pass in, and the queue to add + // the response to when done + auto response_reader = client.stub_->AsyncDeleteSession( + response->get_context(), dsr_req, &client.queue_); + // Set the reader for the local response. This executes the `DeleteSession` + // response using the response reader. When it is done, the callback stored in + // `local_response` will be called + response->set_response_reader(std::move(response_reader)); +} + } // namespace magma diff --git a/lte/gateway/c/oai/lib/s8_proxy/S8Client.h b/lte/gateway/c/oai/lib/s8_proxy/S8Client.h index 0df1fcca8a83..64c91a4206cc 100644 --- a/lte/gateway/c/oai/lib/s8_proxy/S8Client.h +++ b/lte/gateway/c/oai/lib/s8_proxy/S8Client.h @@ -47,6 +47,11 @@ class S8Client : public GRPCReceiver { const CreateSessionRequestPgw& csr_req, std::function callback); + // Send Delete Session Request + static void s8_delete_session_request( + const DeleteSessionRequestPgw& dsr_req, + std::function callback); + public: S8Client(S8Client const&) = delete; void operator=(S8Client const&) = delete; diff --git a/lte/gateway/c/oai/lib/s8_proxy/s8_client_api.cpp b/lte/gateway/c/oai/lib/s8_proxy/s8_client_api.cpp index 1dd4093c4b2f..a710d522d3da 100644 --- a/lte/gateway/c/oai/lib/s8_proxy/s8_client_api.cpp +++ b/lte/gateway/c/oai/lib/s8_proxy/s8_client_api.cpp @@ -102,6 +102,12 @@ static void get_paa_from_proto_msg( } OAILOG_FUNC_OUT(LOG_SGW_S8); } +static void recv_s8_delete_session_response( + imsi64_t imsi64, teid_t context_teid, const grpc::Status& status, + magma::feg::DeleteSessionResponsePgw& response) { + OAILOG_FUNC_IN(LOG_SGW_S8); + OAILOG_FUNC_OUT(LOG_SGW_S8); +} static void recv_s8_create_session_response( imsi64_t imsi64, teid_t context_teid, const grpc::Status& status, @@ -380,3 +386,26 @@ static void convert_proto_msg_to_itti_csr( } OAILOG_FUNC_OUT(LOG_SGW_S8); } + +void send_s8_delete_session_request( + imsi64_t imsi64, Imsi_t imsi, teid_t sgw_s11_teid, teid_t pgw_s5_teid, + ebi_t bearer_id) { + OAILOG_FUNC_IN(LOG_SGW_S8); + std::cout << "Sending delete session request for IMSI: " << imsi64 + << "and context_teid: " << sgw_s11_teid << std::endl; + + magma::feg::DeleteSessionRequestPgw dsr_req; + + dsr_req.Clear(); + dsr_req.set_imsi((char*) imsi.digit, imsi.length); + dsr_req.set_bearer_id(bearer_id); + dsr_req.mutable_c_pgw_fteid()->set_teid(pgw_s5_teid); + magma::S8Client::s8_delete_session_request( + dsr_req, + [imsi64, sgw_s11_teid]( + grpc::Status status, magma::feg::DeleteSessionResponsePgw response) { + recv_s8_delete_session_response(imsi64, sgw_s11_teid, status, response); + }); + + OAILOG_FUNC_OUT(LOG_SGW_S8); +} diff --git a/lte/gateway/c/oai/lib/s8_proxy/s8_client_api.h b/lte/gateway/c/oai/lib/s8_proxy/s8_client_api.h index 6bd66272e2f2..ac23e8e4c6d1 100644 --- a/lte/gateway/c/oai/lib/s8_proxy/s8_client_api.h +++ b/lte/gateway/c/oai/lib/s8_proxy/s8_client_api.h @@ -17,9 +17,13 @@ extern "C" { #endif #include "intertask_interface.h" #include "common_types.h" + void send_s8_create_session_request( teid_t sgw_s11_teid, const itti_s11_create_session_request_t* msg, imsi64_t imsi64); +void send_s8_delete_session_request( + imsi64_t imsi64, Imsi_t imsi, teid_t sgw_s11_teid, teid_t pgw_s5_teid, + ebi_t bearer_id); #ifdef __cplusplus } #endif diff --git a/lte/gateway/c/oai/tasks/sgw_s8/sgw_s8_handlers.c b/lte/gateway/c/oai/tasks/sgw_s8/sgw_s8_handlers.c index 763e5e84da21..f36790ad6dd9 100644 --- a/lte/gateway/c/oai/tasks/sgw_s8/sgw_s8_handlers.c +++ b/lte/gateway/c/oai/tasks/sgw_s8/sgw_s8_handlers.c @@ -842,3 +842,69 @@ static int sgw_s8_add_gtp_s8_tunnel( } OAILOG_FUNC_RETURN(LOG_SGW_S8, rv); } + +void sgw_s8_handle_s11_delete_session_request( + sgw_state_t* sgw_state, + const itti_s11_delete_session_request_t* const delete_session_req_p, + imsi64_t imsi64) { + OAILOG_FUNC_IN(LOG_SGW_S8); + gtpv2c_cause_value_t gtpv2c_cause = 0; + if (!delete_session_req_p) { + OAILOG_ERROR_UE( + LOG_SGW_S8, imsi64, + "Received NULL delete_session_req_p from mme app \n"); + OAILOG_FUNC_OUT(LOG_SGW_S8); + } + OAILOG_INFO_UE( + LOG_SGW_S8, imsi64, + "Received S11 DELETE SESSION REQUEST for sgw_s11_teid " TEID_FMT "\n", + delete_session_req_p->teid); + increment_counter("sgw_delete_session", 1, NO_LABELS); + if (delete_session_req_p->indication_flags.oi) { + OAILOG_DEBUG_UE( + LOG_SPGW_APP, imsi64, + "OI flag is set for this message indicating the request" + "should be forwarded to P-GW entity\n"); + } + + sgw_eps_bearer_context_information_t* sgw_context_p = + sgw_get_sgw_eps_bearer_context(delete_session_req_p->teid); + if (!sgw_context_p) { + OAILOG_ERROR_UE( + LOG_SGW_S8, imsi64, + "Failed to fetch sgw_eps_bearer_context_info from " + "sgw_s11_teid " TEID_FMT " \n", + delete_session_req_p->teid); + /*TODO Rashmi send DSrsp with context not found */ + gtpv2c_cause = CONTEXT_NOT_FOUND; + OAILOG_FUNC_OUT(LOG_SGW_S8); + } + if ((delete_session_req_p->sender_fteid_for_cp.ipv4) && + (delete_session_req_p->sender_fteid_for_cp.ipv6)) { + // Sender F-TEID IE present + if (delete_session_req_p->teid != sgw_context_p->mme_teid_S11) { + gtpv2c_cause = INVALID_PEER; + OAILOG_ERROR_UE( + LOG_SPGW_APP, imsi64, + "Mismatch in MME Teid for CP teid recevied in delete session " + "req: " TEID_FMT " teid present in sgw_context :" TEID_FMT "\n", + delete_session_req_p->teid, sgw_context_p->mme_teid_S11); + } + } + if (delete_session_req_p->lbi != + sgw_context_p->pdn_connection.default_bearer) { + OAILOG_ERROR_UE( + LOG_SPGW_APP, imsi64, + "Mismatch in default eps bearer_id, bearer_id recevied in delete " + "session req :%d and bearer_id present in sgw_context :%d \n", + delete_session_req_p->lbi, + sgw_context_p->pdn_connection.default_bearer); + } + + send_s8_delete_session_request( + sgw_context_p->imsi64, sgw_context_p->imsi, + sgw_context_p->s_gw_teid_S11_S4, + sgw_context_p->pdn_connection.p_gw_teid_S5_S8_cp, + sgw_context_p->pdn_connection.default_bearer); + OAILOG_FUNC_OUT(LOG_SGW_S8); +} diff --git a/lte/gateway/c/oai/tasks/sgw_s8/sgw_s8_task.c b/lte/gateway/c/oai/tasks/sgw_s8/sgw_s8_task.c index 3a4c8787e438..fec0aa69ae40 100644 --- a/lte/gateway/c/oai/tasks/sgw_s8/sgw_s8_task.c +++ b/lte/gateway/c/oai/tasks/sgw_s8/sgw_s8_task.c @@ -82,6 +82,11 @@ static int handle_message(zloop_t* loop, zsock_t* reader, void* arg) { imsi64); } break; + case S11_DELETE_SESSION_REQUEST: { + sgw_s8_handle_s11_delete_session_request( + sgw_state, &received_message_p->ittiMsg.s11_delete_session_request, + imsi64); + } break; default: { OAILOG_DEBUG( LOG_SGW_S8, "Unkwnon message ID %d: %s\n", From c2749bcadf557b8ca7bc42dde9242d11ab97c214 Mon Sep 17 00:00:00 2001 From: rashmi Date: Thu, 1 Apr 2021 08:12:52 +0530 Subject: [PATCH 06/91] Added code to handle delete session procedure and added routing logic to send s11 messages to either spgw task or sgw_s8 task based on federated mode of configuration Signed-off-by: rashmi --- lte/gateway/c/oai/include/s8_messages_def.h | 2 + lte/gateway/c/oai/include/s8_messages_types.h | 7 +- lte/gateway/c/oai/lib/s8_proxy/S8Client.cpp | 2 +- .../c/oai/lib/s8_proxy/s8_client_api.cpp | 51 ++++- .../c/oai/tasks/mme_app/mme_app_bearer.c | 51 +++-- .../c/oai/tasks/mme_app/mme_app_detach.c | 25 ++- .../tasks/mme_app/mme_app_itti_messaging.c | 64 +++++- .../tasks/mme_app/mme_app_itti_messaging.h | 1 + .../c/oai/tasks/sgw_s8/sgw_s8_handlers.c | 200 +++++++++++++++++- .../c/oai/tasks/sgw_s8/sgw_s8_s11_handlers.h | 4 + lte/gateway/c/oai/tasks/sgw_s8/sgw_s8_task.c | 6 + 11 files changed, 377 insertions(+), 36 deletions(-) diff --git a/lte/gateway/c/oai/include/s8_messages_def.h b/lte/gateway/c/oai/include/s8_messages_def.h index 790ca40cedd7..26082c5cb398 100644 --- a/lte/gateway/c/oai/include/s8_messages_def.h +++ b/lte/gateway/c/oai/include/s8_messages_def.h @@ -15,3 +15,5 @@ limitations under the License. // instead. MESSAGE_DEF( S8_CREATE_SESSION_RSP, s8_create_session_response_t, s8_create_session_rsp) +MESSAGE_DEF( + S8_DELETE_SESSION_RSP, s8_delete_session_response_t, s8_delete_session_rsp) diff --git a/lte/gateway/c/oai/include/s8_messages_types.h b/lte/gateway/c/oai/include/s8_messages_types.h index 04aa3906b11a..2f1281cd55c6 100644 --- a/lte/gateway/c/oai/include/s8_messages_types.h +++ b/lte/gateway/c/oai/include/s8_messages_types.h @@ -17,6 +17,7 @@ limitations under the License. #include "common_types.h" #define S8_CREATE_SESSION_RSP(mSGpTR) (mSGpTR)->ittiMsg.s8_create_session_rsp +#define S8_DELETE_SESSION_RSP(mSGpTR) (mSGpTR)->ittiMsg.s8_delete_session_rsp typedef struct s8_bearer_context_s { ebi_t eps_bearer_id; @@ -33,8 +34,12 @@ typedef struct s8_create_session_response_s { teid_t context_teid; // SGW_S11_teid, created per PDN ebi_t eps_bearer_id; s8_bearer_context_t bearer_context[BEARERS_PER_UE]; - uint8_t response_cause; uint8_t apn_restriction_value; fteid_t pgw_s8_cp_teid; uint32_t cause; } s8_create_session_response_t; + +typedef struct s8_delete_session_response_s { + teid_t context_teid; // SGW_S11_teid, created per PDN + uint32_t cause; +} s8_delete_session_response_t; diff --git a/lte/gateway/c/oai/lib/s8_proxy/S8Client.cpp b/lte/gateway/c/oai/lib/s8_proxy/S8Client.cpp index 6e8e8a33e84e..ec507d024ac7 100644 --- a/lte/gateway/c/oai/lib/s8_proxy/S8Client.cpp +++ b/lte/gateway/c/oai/lib/s8_proxy/S8Client.cpp @@ -68,7 +68,7 @@ void S8Client::s8_delete_session_request( // gRPC call is answered auto response = new AsyncLocalResponse( std::move(callback), RESPONSE_TIMEOUT); - // Create a response reader for the `CreateSession` RPC call. This reader + // Create a response reader for the `DeleteSession` RPC call. This reader // stores the client context, the request to pass in, and the queue to add // the response to when done auto response_reader = client.stub_->AsyncDeleteSession( diff --git a/lte/gateway/c/oai/lib/s8_proxy/s8_client_api.cpp b/lte/gateway/c/oai/lib/s8_proxy/s8_client_api.cpp index a710d522d3da..d285dc76d46a 100644 --- a/lte/gateway/c/oai/lib/s8_proxy/s8_client_api.cpp +++ b/lte/gateway/c/oai/lib/s8_proxy/s8_client_api.cpp @@ -102,10 +102,54 @@ static void get_paa_from_proto_msg( } OAILOG_FUNC_OUT(LOG_SGW_S8); } + static void recv_s8_delete_session_response( imsi64_t imsi64, teid_t context_teid, const grpc::Status& status, magma::feg::DeleteSessionResponsePgw& response) { OAILOG_FUNC_IN(LOG_SGW_S8); + + s8_delete_session_response_t* s8_delete_session_rsp = NULL; + MessageDef* message_p = NULL; + message_p = itti_alloc_new_message(TASK_GRPC_SERVICE, S8_DELETE_SESSION_RSP); + if (!message_p) { + OAILOG_ERROR_UE( + LOG_SGW_S8, imsi64, + "Failed to allocate memory for S8_DELETE_SESSION_RSP for " + "context_teid" TEID_FMT "\n", + context_teid); + OAILOG_FUNC_OUT(LOG_SGW_S8); + } + s8_delete_session_rsp = &message_p->ittiMsg.s8_delete_session_rsp; + message_p->ittiMsgHeader.imsi = imsi64; + s8_delete_session_rsp->context_teid = context_teid; + + if (status.ok()) { + if (response.has_gtp_error()) { + s8_delete_session_rsp->cause = response.mutable_gtp_error()->cause(); + } else { + s8_delete_session_rsp->cause = REQUEST_ACCEPTED; + } + } else { + OAILOG_ERROR_UE( + LOG_SGW_S8, imsi64, + "Received gRPC error for delete session response for " + "context_teid " TEID_FMT "\n", + context_teid); + s8_delete_session_rsp->cause = REMOTE_PEER_NOT_RESPONDING; + } + OAILOG_INFO_UE( + LOG_UTIL, imsi64, + "Sending delete session response to sgw_s8 task for " + "context_teid " TEID_FMT "\n", + context_teid); + if ((send_msg_to_task(&grpc_service_task_zmq_ctx, TASK_SGW_S8, message_p)) != + RETURNok) { + OAILOG_ERROR_UE( + LOG_SGW_S8, imsi64, + "Failed to send delete session response to sgw_s8 task for" + "context_teid " TEID_FMT "\n", + context_teid); + } OAILOG_FUNC_OUT(LOG_SGW_S8); } @@ -391,8 +435,10 @@ void send_s8_delete_session_request( imsi64_t imsi64, Imsi_t imsi, teid_t sgw_s11_teid, teid_t pgw_s5_teid, ebi_t bearer_id) { OAILOG_FUNC_IN(LOG_SGW_S8); - std::cout << "Sending delete session request for IMSI: " << imsi64 - << "and context_teid: " << sgw_s11_teid << std::endl; + OAILOG_INFO_UE( + LOG_SGW_S8, imsi64, + "Sending delete session request for context_teid:" TEID_FMT "\n", + sgw_s11_teid); magma::feg::DeleteSessionRequestPgw dsr_req; @@ -400,6 +446,7 @@ void send_s8_delete_session_request( dsr_req.set_imsi((char*) imsi.digit, imsi.length); dsr_req.set_bearer_id(bearer_id); dsr_req.mutable_c_pgw_fteid()->set_teid(pgw_s5_teid); + dsr_req.set_c_agw_teid(sgw_s11_teid); magma::S8Client::s8_delete_session_request( dsr_req, [imsi64, sgw_s11_teid]( diff --git a/lte/gateway/c/oai/tasks/mme_app/mme_app_bearer.c b/lte/gateway/c/oai/tasks/mme_app/mme_app_bearer.c index 98555be400d2..5b951c2e242f 100644 --- a/lte/gateway/c/oai/tasks/mme_app/mme_app_bearer.c +++ b/lte/gateway/c/oai/tasks/mme_app/mme_app_bearer.c @@ -84,6 +84,9 @@ extern task_zmq_ctx_t mme_app_task_zmq_ctx; extern int pdn_connectivity_delete(emm_context_t* emm_context, pdn_cid_t pid); +static void send_s11_modify_bearer_request( + ue_mm_context_t* ue_context_p, MessageDef* message_p); + int send_modify_bearer_req(mme_ue_s1ap_id_t ue_id, ebi_t ebi) { OAILOG_FUNC_IN(LOG_MME_APP); @@ -187,11 +190,7 @@ int send_modify_bearer_req(mme_ue_s1ap_id_t ue_id, ebi_t ebi) { message_p->ittiMsgHeader.imsi = ue_context_p->emm_context._imsi64; - OAILOG_INFO_UE( - LOG_MME_APP, ue_context_p->emm_context._imsi64, - "Sending S11_MODIFY_BEARER_REQUEST to SGW for ue" MME_UE_S1AP_ID_FMT "\n", - ue_id); - send_msg_to_task(&mme_app_task_zmq_ctx, TASK_SPGW, message_p); + send_s11_modify_bearer_request(ue_context_p, message_p); OAILOG_FUNC_RETURN(LOG_MME_APP, RETURNok); } @@ -1527,7 +1526,8 @@ static int mme_app_send_modify_bearer_request_for_active_pdns( mme_app_build_modify_bearer_request_message( ue_context_p, initial_ctxt_setup_rsp_p, s11_modify_bearer_request, &pid, &bc_to_be_removed_idx); - send_msg_to_task(&mme_app_task_zmq_ctx, TASK_SPGW, message_p); + + send_s11_modify_bearer_request(ue_context_p, message_p); } // end of for loop OAILOG_FUNC_RETURN(LOG_MME_APP, RETURNok); @@ -3431,11 +3431,7 @@ void mme_app_handle_path_switch_request( message_p->ittiMsgHeader.imsi = ue_context_p->emm_context._imsi64; - OAILOG_DEBUG_UE( - LOG_MME_APP, ue_context_p->emm_context._imsi64, - "MME_APP send S11_MODIFY_BEARER_REQUEST to teid %u \n", - s11_modify_bearer_request->teid); - send_msg_to_task(&mme_app_task_zmq_ctx, TASK_SPGW, message_p); + send_s11_modify_bearer_request(ue_context_p, message_p); ue_context_p->path_switch_req = true; OAILOG_FUNC_OUT(LOG_MME_APP); @@ -3942,11 +3938,7 @@ void mme_app_handle_e_rab_modification_ind( message_p->ittiMsgHeader.imsi = ue_context_p->emm_context._imsi64; - OAILOG_DEBUG_UE( - LOG_MME_APP, ue_context_p->emm_context._imsi64, - "MME_APP send S11_MODIFY_BEARER_REQUEST to teid %u \n", - s11_modify_bearer_request->teid); - send_msg_to_task(&mme_app_task_zmq_ctx, TASK_SPGW, message_p); + send_s11_modify_bearer_request(ue_context_p, message_p); OAILOG_FUNC_OUT(LOG_MME_APP); } //------------------------------------------------------------------------------ @@ -4086,3 +4078,30 @@ void mme_app_handle_modify_bearer_rsp( } OAILOG_FUNC_OUT(LOG_MME_APP); } + +void send_s11_modify_bearer_request( + ue_mm_context_t* ue_context_p, MessageDef* message_p) { + OAILOG_FUNC_IN(LOG_MME_APP); + Imsi_t imsi = {0}; + IMSI64_TO_STRING( + ue_context_p->emm_context._imsi64, (char*) (&imsi.digit), + ue_context_p->emm_context._imsi.length); + + if (mme_app_match_fed_mode_map( + imsi.digit, ue_context_p->emm_context._imsi64) == S8_SUBSCRIBER) { + OAILOG_INFO_UE( + LOG_MME_APP, ue_context_p->emm_context._imsi64, + "Sending S11 modify bearer req message to SGW_s8 task for " + "ue_id " MME_UE_S1AP_ID_FMT "\n", + ue_context_p->mme_ue_s1ap_id); + send_msg_to_task(&mme_app_task_zmq_ctx, TASK_SGW_S8, message_p); + } else { + OAILOG_INFO_UE( + LOG_MME_APP, ue_context_p->emm_context._imsi64, + "Sending S11 modify bearer req message to SPGW task for " + "ue_id " MME_UE_S1AP_ID_FMT "\n", + ue_context_p->mme_ue_s1ap_id); + send_msg_to_task(&mme_app_task_zmq_ctx, TASK_SPGW, message_p); + } + OAILOG_FUNC_OUT(LOG_MME_APP); +} diff --git a/lte/gateway/c/oai/tasks/mme_app/mme_app_detach.c b/lte/gateway/c/oai/tasks/mme_app/mme_app_detach.c index 5a0ce522fc8e..85589732fd71 100644 --- a/lte/gateway/c/oai/tasks/mme_app/mme_app_detach.c +++ b/lte/gateway/c/oai/tasks/mme_app/mme_app_detach.c @@ -32,6 +32,7 @@ #include #include "log.h" +#include "conversions.h" #include "intertask_interface.h" #include "gcc_diag.h" #include "mme_config.h" @@ -115,10 +116,26 @@ void mme_app_send_delete_session_request( message_p->ittiMsgHeader.imsi = ue_context_p->emm_context._imsi64; - send_msg_to_task(&mme_app_task_zmq_ctx, TASK_SPGW, message_p); - OAILOG_INFO( - LOG_MME_APP, "Send Delete session Req for teid " TEID_FMT "\n", - ue_context_p->mme_teid_s11); + Imsi_t imsi = {0}; + IMSI64_TO_STRING( + ue_context_p->emm_context._imsi64, (char*) (&imsi.digit), + ue_context_p->emm_context._imsi.length); + + if (mme_app_match_fed_mode_map( + imsi.digit, ue_context_p->emm_context._imsi64) == S8_SUBSCRIBER) { + OAILOG_INFO_UE( + LOG_MME_APP, ue_context_p->emm_context._imsi64, + "Send delete session Req for teid to sgw_s8 task " TEID_FMT "\n", + ue_context_p->mme_teid_s11); + send_msg_to_task(&mme_app_task_zmq_ctx, TASK_SGW_S8, message_p); + } else { + OAILOG_INFO_UE( + LOG_MME_APP, ue_context_p->emm_context._imsi64, + "Send delete session Req for teid to spgw task " TEID_FMT "\n", + ue_context_p->mme_teid_s11); + send_msg_to_task(&mme_app_task_zmq_ctx, TASK_SPGW, message_p); + } + increment_counter("mme_spgw_delete_session_req", 1, NO_LABELS); OAILOG_FUNC_OUT(LOG_MME_APP); } diff --git a/lte/gateway/c/oai/tasks/mme_app/mme_app_itti_messaging.c b/lte/gateway/c/oai/tasks/mme_app/mme_app_itti_messaging.c index f199cca7154a..1d55c1b020fc 100644 --- a/lte/gateway/c/oai/tasks/mme_app/mme_app_itti_messaging.c +++ b/lte/gateway/c/oai/tasks/mme_app/mme_app_itti_messaging.c @@ -374,19 +374,22 @@ int mme_app_send_s11_create_session_req( ue_mm_context->e_utran_cgi.plmn.mnc_digit3; session_request_p->selection_mode = MS_O_N_P_APN_S_V; - OAILOG_INFO_UE( - LOG_MME_APP, ue_mm_context->emm_context._imsi64, - "Sending S11 CREATE SESSION REQ message to SPGW for " - "ue_id " MME_UE_S1AP_ID_FMT "\n", - ue_mm_context->mme_ue_s1ap_id); - if ((send_msg_to_task(&mme_app_task_zmq_ctx, TASK_SPGW, message_p)) != - RETURNok) { - OAILOG_ERROR_UE( + if (mme_app_match_fed_mode_map( + session_request_p->imsi.digit, ue_mm_context->emm_context._imsi64) == + S8_SUBSCRIBER) { + OAILOG_INFO_UE( LOG_MME_APP, ue_mm_context->emm_context._imsi64, - "Failed to send S11 CREATE SESSION REQ message to SPGW for " + "Sending s11 create session req message to SGW_s8 task for " "ue_id " MME_UE_S1AP_ID_FMT "\n", ue_mm_context->mme_ue_s1ap_id); - OAILOG_FUNC_RETURN(LOG_MME_APP, RETURNerror); + send_msg_to_task(&mme_app_task_zmq_ctx, TASK_SGW_S8, message_p); + } else { + OAILOG_INFO_UE( + LOG_MME_APP, ue_mm_context->emm_context._imsi64, + "Sending s11 create session req message to SPGW task for " + "ue_id " MME_UE_S1AP_ID_FMT "\n", + ue_mm_context->mme_ue_s1ap_id); + send_msg_to_task(&mme_app_task_zmq_ctx, TASK_SPGW, message_p); } OAILOG_FUNC_RETURN(LOG_MME_APP, RETURNok); } @@ -642,3 +645,44 @@ void mme_app_itti_sgsap_ue_activity_ind( } OAILOG_FUNC_OUT(LOG_MME_APP); } + +// Extract MCC and MNC from the imsi received and match with +// configuration +int mme_app_match_fed_mode_map(const uint8_t* imsi, imsi64_t imsi64) { + uint8_t mcc_d1 = imsi[0] - '0'; + uint8_t mcc_d2 = imsi[1] - '0'; + uint8_t mcc_d3 = imsi[2] - '0'; + uint8_t mnc_d1 = imsi[3] - '0'; + uint8_t mnc_d2 = imsi[4] - '0'; + uint8_t mnc_d3 = imsi[5] - '0'; + if ((mcc_d1 < 0 || mcc_d1 > 9) || (mcc_d2 < 0 || mcc_d2 > 9) || + (mcc_d3 < 0 || mcc_d3 > 9) || (mnc_d1 < 0 || mnc_d1 > 9) || + (mnc_d2 < 0 || mnc_d2 > 9) || (mnc_d3 < 0 || mnc_d3 > 9)) { + OAILOG_ERROR_UE( + LOG_MME_APP, imsi64, "[ERROR] MCC/MNC is not a decimal digit \n"); + OAILOG_FUNC_RETURN(LOG_MME_APP, RETURNerror); + } + for (uint8_t itr = 0; itr < mme_config.mode_map_config.num; itr++) { + if (((mcc_d1 == mme_config.mode_map_config.mode_map[itr].plmn.mcc_digit1) && + (mcc_d2 == mme_config.mode_map_config.mode_map[itr].plmn.mcc_digit2) && + (mcc_d3 == mme_config.mode_map_config.mode_map[itr].plmn.mcc_digit3) && + (mnc_d1 == mme_config.mode_map_config.mode_map[itr].plmn.mnc_digit1) && + (mnc_d2 == + mme_config.mode_map_config.mode_map[itr].plmn.mnc_digit2))) { + if (mme_config.mode_map_config.mode_map[itr].plmn.mnc_digit3 != 0xf) { + if (mnc_d3 != + mme_config.mode_map_config.mode_map[itr].plmn.mnc_digit3) { + continue; + } + } + OAILOG_FUNC_RETURN( + LOG_MME_APP, mme_config.mode_map_config.mode_map[itr].mode); + } + } + // If the plmn is not configured set the default mode as hss + spgw_task. + OAILOG_INFO_UE( + LOG_MME_APP, imsi64, + "PLMN is not configured. Selecting default mode: SPGW_SUBSCRIBER \n"); + OAILOG_FUNC_RETURN(LOG_MME_APP, SPGW_SUBSCRIBER); +} + diff --git a/lte/gateway/c/oai/tasks/mme_app/mme_app_itti_messaging.h b/lte/gateway/c/oai/tasks/mme_app/mme_app_itti_messaging.h index 377507743a0a..bd794502f920 100644 --- a/lte/gateway/c/oai/tasks/mme_app/mme_app_itti_messaging.h +++ b/lte/gateway/c/oai/tasks/mme_app/mme_app_itti_messaging.h @@ -132,4 +132,5 @@ void mme_app_itti_sgsap_tmsi_reallocation_comp( void mme_app_itti_sgsap_ue_activity_ind( const char* imsi, const unsigned int imsi_len); +int mme_app_match_fed_mode_map(const uint8_t* imsi, imsi64_t imsi64); #endif /* FILE_MME_APP_ITTI_MESSAGING_SEEN */ diff --git a/lte/gateway/c/oai/tasks/sgw_s8/sgw_s8_handlers.c b/lte/gateway/c/oai/tasks/sgw_s8/sgw_s8_handlers.c index b5f0beef5318..4c8d0e1fe37d 100644 --- a/lte/gateway/c/oai/tasks/sgw_s8/sgw_s8_handlers.c +++ b/lte/gateway/c/oai/tasks/sgw_s8/sgw_s8_handlers.c @@ -41,6 +41,12 @@ static void sgw_send_modify_bearer_response( sgw_eps_bearer_context_information_t* sgw_context_p, const itti_sgi_update_end_point_response_t* const resp_pP, imsi64_t imsi64); +static void sgw_s8_send_failed_delete_session_response( + sgw_eps_bearer_context_information_t* sgw_context_p, + gtpv2c_cause_value_t cause, sgw_state_t* sgw_state, + const itti_s11_delete_session_request_t* const delete_session_req_p, + imsi64_t imsi64); + uint32_t sgw_get_new_s1u_teid(sgw_state_t* state) { if (state->s1u_teid == 0) { state->s1u_teid = INITIAL_SGW_S8_S1U_TEID; @@ -876,20 +882,24 @@ void sgw_s8_handle_s11_delete_session_request( "Failed to fetch sgw_eps_bearer_context_info from " "sgw_s11_teid " TEID_FMT " \n", delete_session_req_p->teid); - /*TODO Rashmi send DSrsp with context not found */ gtpv2c_cause = CONTEXT_NOT_FOUND; + sgw_s8_send_failed_delete_session_response( + sgw_context_p, gtpv2c_cause, sgw_state, delete_session_req_p, imsi64); OAILOG_FUNC_OUT(LOG_SGW_S8); } if ((delete_session_req_p->sender_fteid_for_cp.ipv4) && (delete_session_req_p->sender_fteid_for_cp.ipv6)) { // Sender F-TEID IE present if (delete_session_req_p->teid != sgw_context_p->mme_teid_S11) { - gtpv2c_cause = INVALID_PEER; OAILOG_ERROR_UE( LOG_SPGW_APP, imsi64, "Mismatch in MME Teid for CP teid recevied in delete session " "req: " TEID_FMT " teid present in sgw_context :" TEID_FMT "\n", delete_session_req_p->teid, sgw_context_p->mme_teid_S11); + gtpv2c_cause = INVALID_PEER; + sgw_s8_send_failed_delete_session_response( + sgw_context_p, gtpv2c_cause, sgw_state, delete_session_req_p, imsi64); + OAILOG_FUNC_OUT(LOG_SGW_S8); } } if (delete_session_req_p->lbi != @@ -900,6 +910,9 @@ void sgw_s8_handle_s11_delete_session_request( "session req :%d and bearer_id present in sgw_context :%d \n", delete_session_req_p->lbi, sgw_context_p->pdn_connection.default_bearer); + sgw_s8_send_failed_delete_session_response( + sgw_context_p, gtpv2c_cause, sgw_state, delete_session_req_p, imsi64); + OAILOG_FUNC_OUT(LOG_SGW_S8); } send_s8_delete_session_request( @@ -909,3 +922,186 @@ void sgw_s8_handle_s11_delete_session_request( sgw_context_p->pdn_connection.default_bearer); OAILOG_FUNC_OUT(LOG_SGW_S8); } + +static void delete_userplane_tunnels( + sgw_eps_bearer_context_information_t* sgw_context_p) { + OAILOG_FUNC_IN(LOG_SGW_S8); + struct in_addr enb = {.s_addr = 0}; + struct in_addr pgw = {.s_addr = 0}; + sgw_eps_bearer_ctxt_t* bearer_ctxt_p = NULL; + int rv = RETURNerror; + + for (int ebix = 0; ebix < BEARERS_PER_UE; ebix++) { + ebi_t ebi = INDEX_TO_EBI(ebix); + bearer_ctxt_p = + sgw_cm_get_eps_bearer_entry(&sgw_context_p->pdn_connection, ebi); + + if (bearer_ctxt_p) { + enb.s_addr = + bearer_ctxt_p->enb_ip_address_S1u.address.ipv4_address.s_addr; + pgw.s_addr = + bearer_ctxt_p->p_gw_address_in_use_up.address.ipv4_address.s_addr; + struct in6_addr* ue_ipv6 = NULL; + if ((bearer_ctxt_p->paa.pdn_type == IPv6) || + (bearer_ctxt_p->paa.pdn_type == IPv4_AND_v6)) { + ue_ipv6 = &bearer_ctxt_p->paa.ipv6_address; + } + // Delete S1-U tunnel + rv = openflow_del_s8_tunnel( + enb, pgw, bearer_ctxt_p->paa.ipv4_address, ue_ipv6, + bearer_ctxt_p->s_gw_teid_S1u_S12_S4_up, bearer_ctxt_p->enb_teid_S1u, + NULL); + if (rv < 0) { + OAILOG_ERROR_UE( + LOG_SPGW_APP, sgw_context_p->imsi64, + "ERROR in deleting S1-U TUNNEL " TEID_FMT + " (eNB) <-> (SGW) " TEID_FMT "\n", + bearer_ctxt_p->enb_teid_S1u, + bearer_ctxt_p->s_gw_teid_S1u_S12_S4_up); + } + // Delete S8-U tunnel + rv = openflow_del_s8_tunnel( + enb, pgw, bearer_ctxt_p->paa.ipv4_address, ue_ipv6, + bearer_ctxt_p->s_gw_teid_S5_S8_up, bearer_ctxt_p->p_gw_teid_S5_S8_up, + NULL); + if (rv < 0) { + OAILOG_ERROR_UE( + LOG_SPGW_APP, sgw_context_p->imsi64, + "ERROR in deleting S8-U TUNNEL " TEID_FMT + " (PGW) <-> (SGW) " TEID_FMT "\n", + bearer_ctxt_p->p_gw_teid_S5_S8_up, + bearer_ctxt_p->s_gw_teid_S5_S8_up); + } + } + } + OAILOG_FUNC_OUT(LOG_SGW_S8); +} + +void sgw_s8_handle_delete_session_response( + sgw_state_t* sgw_state, s8_delete_session_response_t* session_rsp_p, + imsi64_t imsi64) { + OAILOG_FUNC_IN(LOG_SGW_S8); + MessageDef* message_p = NULL; + itti_s11_delete_session_response_t* delete_session_response_p = NULL; + + if (!session_rsp_p) { + OAILOG_ERROR_UE( + LOG_SGW_S8, imsi64, + "Received null delete session response from s8_proxy\n"); + OAILOG_FUNC_OUT(LOG_SGW_S8); + } + OAILOG_INFO_UE( + LOG_SGW_S8, imsi64, + " Rx S5S8_DELETE_SESSION_RSP from s8_proxy for context_teid " TEID_FMT + "\n", + session_rsp_p->context_teid); + + sgw_eps_bearer_context_information_t* sgw_context_p = + sgw_get_sgw_eps_bearer_context(session_rsp_p->context_teid); + if (!sgw_context_p) { + OAILOG_ERROR_UE( + LOG_SGW_S8, imsi64, + "Failed to fetch sgw_eps_bearer_context_info from " + "context_teid " TEID_FMT " \n", + session_rsp_p->context_teid); + OAILOG_FUNC_OUT(LOG_SGW_S8); + } + message_p = itti_alloc_new_message(TASK_SGW_S8, S11_DELETE_SESSION_RESPONSE); + if (message_p == NULL) { + OAILOG_CRITICAL_UE( + LOG_SGW_S8, sgw_context_p->imsi64, + "Failed to allocate memory for S11_delete_session_response for " + "context_teid " TEID_FMT "\n", + session_rsp_p->context_teid); + OAILOG_FUNC_OUT(LOG_SGW_S8); + } + + delete_session_response_p = &message_p->ittiMsg.s11_delete_session_response; + message_p->ittiMsgHeader.imsi = imsi64; + if (!delete_session_response_p) { + OAILOG_ERROR_UE( + LOG_SGW_S8, sgw_context_p->imsi64, + "delete_session_response_p is NULL \n"); + OAILOG_FUNC_OUT(LOG_SGW_S8); + } + delete_session_response_p->teid = sgw_context_p->mme_teid_S11; + delete_session_response_p->cause.cause_value = session_rsp_p->cause; + delete_session_response_p->trxn = sgw_context_p->trxn; + delete_session_response_p->lbi = sgw_context_p->pdn_connection.default_bearer; + + // Delete ovs rules + delete_userplane_tunnels(sgw_context_p); + sgw_remove_sgw_bearer_context_information( + sgw_state, session_rsp_p->context_teid, imsi64); + // send delete session response to mme + if (send_msg_to_task(&sgw_s8_task_zmq_ctx, TASK_MME_APP, message_p) != + RETURNok) { + OAILOG_ERROR_UE( + LOG_SGW_S8, imsi64, + "Failed to send delete session response to mme for " + "sgw_s11_teid " TEID_FMT "\n", + session_rsp_p->context_teid); + } + increment_counter("sgw_delete_session", 1, 1, "result", "success"); + OAILOG_FUNC_OUT(LOG_SGW_S8); +} + +static void sgw_s8_send_failed_delete_session_response( + sgw_eps_bearer_context_information_t* sgw_context_p, + gtpv2c_cause_value_t cause, sgw_state_t* sgw_state, + const itti_s11_delete_session_request_t* const delete_session_req_p, + imsi64_t imsi64) { + OAILOG_FUNC_IN(LOG_SGW_S8); + MessageDef* message_p = NULL; + itti_s11_delete_session_response_t* delete_session_response_p = NULL; + teid_t teid = 0; + + message_p = itti_alloc_new_message(TASK_SGW_S8, S11_DELETE_SESSION_RESPONSE); + if (message_p == NULL) { + OAILOG_CRITICAL_UE( + LOG_SGW_S8, sgw_context_p->imsi64, + "Failed to allocate memory for S11_delete_session_response \n"); + OAILOG_FUNC_OUT(LOG_SGW_S8); + } + + delete_session_response_p = &message_p->ittiMsg.s11_delete_session_response; + message_p->ittiMsgHeader.imsi = imsi64; + if (!delete_session_response_p) { + OAILOG_ERROR_UE( + LOG_SGW_S8, sgw_context_p->imsi64, + "delete_session_response_p is NULL \n"); + OAILOG_FUNC_OUT(LOG_SGW_S8); + } + if (sgw_context_p) { + delete_session_response_p->teid = sgw_context_p->mme_teid_S11; + delete_session_response_p->cause.cause_value = cause; + delete_session_response_p->trxn = sgw_context_p->trxn; + delete_session_response_p->lbi = + sgw_context_p->pdn_connection.default_bearer; + + // Delete ovs rules + delete_userplane_tunnels(sgw_context_p); + sgw_remove_sgw_bearer_context_information( + sgw_state, sgw_context_p->s_gw_teid_S11_S4, imsi64); + teid = sgw_context_p->s_gw_teid_S11_S4; + } else { + if (delete_session_req_p) { + delete_session_response_p->teid = delete_session_req_p->local_teid; + delete_session_response_p->cause.cause_value = cause; + delete_session_response_p->trxn = delete_session_req_p->trxn; + delete_session_response_p->lbi = delete_session_req_p->lbi; + teid = delete_session_req_p->teid; + } + } + increment_counter("sgw_delete_session", 1, 1, "result", "failed"); + // send delete session response to mme + if (send_msg_to_task(&sgw_s8_task_zmq_ctx, TASK_MME_APP, message_p) != + RETURNok) { + OAILOG_ERROR_UE( + LOG_SGW_S8, imsi64, + "Failed to send delete session response to mme for " + "sgw_s11_teid " TEID_FMT "\n", + teid); + } + OAILOG_FUNC_OUT(LOG_SGW_S8); +} diff --git a/lte/gateway/c/oai/tasks/sgw_s8/sgw_s8_s11_handlers.h b/lte/gateway/c/oai/tasks/sgw_s8/sgw_s8_s11_handlers.h index e6e13379c403..71659b15f910 100644 --- a/lte/gateway/c/oai/tasks/sgw_s8/sgw_s8_s11_handlers.h +++ b/lte/gateway/c/oai/tasks/sgw_s8/sgw_s8_s11_handlers.h @@ -29,3 +29,7 @@ void sgw_s8_handle_modify_bearer_request( sgw_state_t* state, const itti_s11_modify_bearer_request_t* const modify_bearer_pP, imsi64_t imsi64); + +void sgw_s8_handle_delete_session_response( + sgw_state_t* sgw_state, s8_delete_session_response_t* session_rsp_p, + imsi64_t imsi64); diff --git a/lte/gateway/c/oai/tasks/sgw_s8/sgw_s8_task.c b/lte/gateway/c/oai/tasks/sgw_s8/sgw_s8_task.c index fec0aa69ae40..4afac9be6bfa 100644 --- a/lte/gateway/c/oai/tasks/sgw_s8/sgw_s8_task.c +++ b/lte/gateway/c/oai/tasks/sgw_s8/sgw_s8_task.c @@ -87,6 +87,12 @@ static int handle_message(zloop_t* loop, zsock_t* reader, void* arg) { sgw_state, &received_message_p->ittiMsg.s11_delete_session_request, imsi64); } break; + + case S8_DELETE_SESSION_RSP: { + sgw_s8_handle_delete_session_response( + sgw_state, &received_message_p->ittiMsg.s8_delete_session_rsp, + imsi64); + } break; default: { OAILOG_DEBUG( LOG_SGW_S8, "Unkwnon message ID %d: %s\n", From 983ed1f03ec8071235ae8f8385f9d4f6abb4c604 Mon Sep 17 00:00:00 2001 From: rashmi Date: Thu, 1 Apr 2021 17:07:48 +0530 Subject: [PATCH 07/91] Introduced a variable within pdn_context to route s11 messages appropraite task based on federated mode of configuration Signed-off-by: rashmi --- .../c/oai/include/mme_app_ue_context.h | 1 + .../c/oai/tasks/mme_app/mme_app_bearer.c | 23 +++++++++++-------- .../c/oai/tasks/mme_app/mme_app_detach.c | 3 +-- .../tasks/mme_app/mme_app_itti_messaging.c | 2 +- 4 files changed, 17 insertions(+), 12 deletions(-) diff --git a/lte/gateway/c/oai/include/mme_app_ue_context.h b/lte/gateway/c/oai/include/mme_app_ue_context.h index 51a002b48541..2c611059725e 100644 --- a/lte/gateway/c/oai/include/mme_app_ue_context.h +++ b/lte/gateway/c/oai/include/mme_app_ue_context.h @@ -239,6 +239,7 @@ typedef struct pdn_context_s { protocol_configuration_options_t* pco; bool ue_rej_act_def_ber_req; + bool route_s11_messages_to_s8_task; } pdn_context_t; typedef enum { diff --git a/lte/gateway/c/oai/tasks/mme_app/mme_app_bearer.c b/lte/gateway/c/oai/tasks/mme_app/mme_app_bearer.c index 5b951c2e242f..0c17df0845cc 100644 --- a/lte/gateway/c/oai/tasks/mme_app/mme_app_bearer.c +++ b/lte/gateway/c/oai/tasks/mme_app/mme_app_bearer.c @@ -85,7 +85,8 @@ extern task_zmq_ctx_t mme_app_task_zmq_ctx; extern int pdn_connectivity_delete(emm_context_t* emm_context, pdn_cid_t pid); static void send_s11_modify_bearer_request( - ue_mm_context_t* ue_context_p, MessageDef* message_p); + ue_mm_context_t* ue_context_p, pdn_context_t* pdn_context_p, + MessageDef* message_p); int send_modify_bearer_req(mme_ue_s1ap_id_t ue_id, ebi_t ebi) { OAILOG_FUNC_IN(LOG_MME_APP); @@ -190,7 +191,7 @@ int send_modify_bearer_req(mme_ue_s1ap_id_t ue_id, ebi_t ebi) { message_p->ittiMsgHeader.imsi = ue_context_p->emm_context._imsi64; - send_s11_modify_bearer_request(ue_context_p, message_p); + send_s11_modify_bearer_request(ue_context_p, pdn_context_p, message_p); OAILOG_FUNC_RETURN(LOG_MME_APP, RETURNok); } @@ -1527,7 +1528,8 @@ static int mme_app_send_modify_bearer_request_for_active_pdns( ue_context_p, initial_ctxt_setup_rsp_p, s11_modify_bearer_request, &pid, &bc_to_be_removed_idx); - send_s11_modify_bearer_request(ue_context_p, message_p); + send_s11_modify_bearer_request( + ue_context_p, ue_context_p->pdn_contexts[pid], message_p); } // end of for loop OAILOG_FUNC_RETURN(LOG_MME_APP, RETURNok); @@ -3431,7 +3433,9 @@ void mme_app_handle_path_switch_request( message_p->ittiMsgHeader.imsi = ue_context_p->emm_context._imsi64; - send_s11_modify_bearer_request(ue_context_p, message_p); + if (pdn_context) { + send_s11_modify_bearer_request(ue_context_p, pdn_context, message_p); + } ue_context_p->path_switch_req = true; OAILOG_FUNC_OUT(LOG_MME_APP); @@ -3937,8 +3941,9 @@ void mme_app_handle_e_rab_modification_ind( s11_modify_bearer_request->trxn = NULL; message_p->ittiMsgHeader.imsi = ue_context_p->emm_context._imsi64; - - send_s11_modify_bearer_request(ue_context_p, message_p); + if (pdn_context) { + send_s11_modify_bearer_request(ue_context_p, pdn_context, message_p); + } OAILOG_FUNC_OUT(LOG_MME_APP); } //------------------------------------------------------------------------------ @@ -4080,15 +4085,15 @@ void mme_app_handle_modify_bearer_rsp( } void send_s11_modify_bearer_request( - ue_mm_context_t* ue_context_p, MessageDef* message_p) { + ue_mm_context_t* ue_context_p, pdn_context_t* pdn_context_p, + MessageDef* message_p) { OAILOG_FUNC_IN(LOG_MME_APP); Imsi_t imsi = {0}; IMSI64_TO_STRING( ue_context_p->emm_context._imsi64, (char*) (&imsi.digit), ue_context_p->emm_context._imsi.length); - if (mme_app_match_fed_mode_map( - imsi.digit, ue_context_p->emm_context._imsi64) == S8_SUBSCRIBER) { + if (pdn_context_p->route_s11_messages_to_s8_task) { OAILOG_INFO_UE( LOG_MME_APP, ue_context_p->emm_context._imsi64, "Sending S11 modify bearer req message to SGW_s8 task for " diff --git a/lte/gateway/c/oai/tasks/mme_app/mme_app_detach.c b/lte/gateway/c/oai/tasks/mme_app/mme_app_detach.c index 85589732fd71..7c53714b7571 100644 --- a/lte/gateway/c/oai/tasks/mme_app/mme_app_detach.c +++ b/lte/gateway/c/oai/tasks/mme_app/mme_app_detach.c @@ -121,8 +121,7 @@ void mme_app_send_delete_session_request( ue_context_p->emm_context._imsi64, (char*) (&imsi.digit), ue_context_p->emm_context._imsi.length); - if (mme_app_match_fed_mode_map( - imsi.digit, ue_context_p->emm_context._imsi64) == S8_SUBSCRIBER) { + if (ue_context_p->pdn_contexts[cid]->route_s11_messages_to_s8_task) { OAILOG_INFO_UE( LOG_MME_APP, ue_context_p->emm_context._imsi64, "Send delete session Req for teid to sgw_s8 task " TEID_FMT "\n", diff --git a/lte/gateway/c/oai/tasks/mme_app/mme_app_itti_messaging.c b/lte/gateway/c/oai/tasks/mme_app/mme_app_itti_messaging.c index 1d55c1b020fc..78b0b22d3021 100644 --- a/lte/gateway/c/oai/tasks/mme_app/mme_app_itti_messaging.c +++ b/lte/gateway/c/oai/tasks/mme_app/mme_app_itti_messaging.c @@ -383,6 +383,7 @@ int mme_app_send_s11_create_session_req( "ue_id " MME_UE_S1AP_ID_FMT "\n", ue_mm_context->mme_ue_s1ap_id); send_msg_to_task(&mme_app_task_zmq_ctx, TASK_SGW_S8, message_p); + ue_mm_context->pdn_contexts[pdn_cid]->route_s11_messages_to_s8_task = true; } else { OAILOG_INFO_UE( LOG_MME_APP, ue_mm_context->emm_context._imsi64, @@ -685,4 +686,3 @@ int mme_app_match_fed_mode_map(const uint8_t* imsi, imsi64_t imsi64) { "PLMN is not configured. Selecting default mode: SPGW_SUBSCRIBER \n"); OAILOG_FUNC_RETURN(LOG_MME_APP, SPGW_SUBSCRIBER); } - From e48c77b752f76d4d0ed4c322c84194da45cc9909 Mon Sep 17 00:00:00 2001 From: rashmi Date: Fri, 2 Apr 2021 04:33:09 +0530 Subject: [PATCH 08/91] Incorporated review comments Signed-off-by: rashmi --- lte/gateway/c/oai/common/CMakeLists.txt | 11 +++++ lte/gateway/c/oai/lib/s6a_proxy/S6aClient.cpp | 49 +++---------------- .../tasks/mme_app/mme_app_itti_messaging.c | 47 ++---------------- 3 files changed, 22 insertions(+), 85 deletions(-) diff --git a/lte/gateway/c/oai/common/CMakeLists.txt b/lte/gateway/c/oai/common/CMakeLists.txt index 9bd6534f6e4b..ff0c662e8915 100644 --- a/lte/gateway/c/oai/common/CMakeLists.txt +++ b/lte/gateway/c/oai/common/CMakeLists.txt @@ -9,20 +9,30 @@ include($ENV{MAGMA_ROOT}/orc8r/gateway/c/common/CMakeProtoMacros.txt) # compile the needed protos set(COMMON_CPP_PROTOS common_types) set(REDIS_CPP_PROTOS redis) +set(MCONFIG_PROTOS mconfig/mconfigs) list(APPEND PROTO_SRCS "") list(APPEND PROTO_HDRS "") +create_proto_dir("orc8r" ORC8R_CPP_OUT_DIR) set(LTE_OUT_DIR "${CMAKE_CURRENT_BINARY_DIR}/lte/protos/oai") generate_cpp_protos("${COMMON_CPP_PROTOS}" "${PROTO_SRCS}" "${PROTO_HDRS}" ${STATE_PROTO_DIR} ${LTE_OUT_DIR}) create_proto_dir("orc8r" ORC8R_CPP_OUT_DIR) +set(RPC_ORC8R_CPP_PROTOS common) +generate_cpp_protos("${RPC_ORC8R_CPP_PROTOS}" "${PROTO_SRCS}" + "${PROTO_HDRS}" ${ORC8R_PROTO_DIR} ${ORC8R_CPP_OUT_DIR}) generate_cpp_protos("${REDIS_CPP_PROTOS}" "${PROTO_SRCS}" "${PROTO_HDRS}" ${ORC8R_PROTO_DIR} ${ORC8R_CPP_OUT_DIR}) +set(MCONFIG_OUT_DIR "${CMAKE_CURRENT_BINARY_DIR}/lte/protos") +generate_cpp_protos("${MCONFIG_PROTOS}" "${PROTO_SRCS}" + "${PROTO_HDRS}" ${LTE_PROTO_DIR} ${MCONFIG_OUT_DIR}) + message("Proto_srcs are ${PROTO_SRCS}") include_directories(${CMAKE_CURRENT_BINARY_DIR}) include_directories(${LTE_OUT_DIR}) +include_directories(${MCONFIG_OUT_DIR}) include_directories(${ORC8R_CPP_OUT_DIR}) @@ -49,6 +59,7 @@ set(COMMON_SRC shared_ts_log.c log.c state_converter.cpp + common_utility_funs.cpp ${PROTO_SRCS} ${PROTO_HDRS} ) diff --git a/lte/gateway/c/oai/lib/s6a_proxy/S6aClient.cpp b/lte/gateway/c/oai/lib/s6a_proxy/S6aClient.cpp index 01b2154b68fa..0f9801568f0f 100644 --- a/lte/gateway/c/oai/lib/s6a_proxy/S6aClient.cpp +++ b/lte/gateway/c/oai/lib/s6a_proxy/S6aClient.cpp @@ -27,6 +27,10 @@ #include "feg/protos/s6a_proxy.pb.h" #include "mme_config.h" #include "common_defs.h" +#include "common_utility_funs.h" +extern "C" { +#include "log.h" +} namespace grpc { class Status; @@ -99,45 +103,6 @@ S6aClient& S6aClient::get_subdb_instance() { static S6aClient subdb_instance(false); return subdb_instance; } - -// Extract MCC and MNC from the imsi received and match with -// configuration -int match_fed_mode_map(const char* imsi) { - uint8_t mcc_d1 = imsi[0] - '0'; - uint8_t mcc_d2 = imsi[1] - '0'; - uint8_t mcc_d3 = imsi[2] - '0'; - uint8_t mnc_d1 = imsi[3] - '0'; - uint8_t mnc_d2 = imsi[4] - '0'; - uint8_t mnc_d3 = imsi[5] - '0'; - if ((mcc_d1 < 0 || mcc_d1 > 9) || (mcc_d2 < 0 || mcc_d2 > 9) || - (mcc_d3 < 0 || mcc_d3 > 9) || (mnc_d1 < 0 || mnc_d1 > 9) || - (mnc_d2 < 0 || mnc_d2 > 9) || (mnc_d3 < 0 || mnc_d3 > 9)) { - std::cout << "[ERROR] MCC/MNC is not a decimal digit " << std::endl; - return -1; - } - for (uint8_t itr = 0; itr < mme_config.mode_map_config.num; itr++) { - if (((mcc_d1 == mme_config.mode_map_config.mode_map[itr].plmn.mcc_digit1) && - (mcc_d2 == mme_config.mode_map_config.mode_map[itr].plmn.mcc_digit2) && - (mcc_d3 == mme_config.mode_map_config.mode_map[itr].plmn.mcc_digit3) && - (mnc_d1 == mme_config.mode_map_config.mode_map[itr].plmn.mnc_digit1) && - (mnc_d2 == - mme_config.mode_map_config.mode_map[itr].plmn.mnc_digit2))) { - if (mme_config.mode_map_config.mode_map[itr].plmn.mnc_digit3 != 0xf) { - if (mnc_d3 != - mme_config.mode_map_config.mode_map[itr].plmn.mnc_digit3) { - continue; - } - } - return mme_config.mode_map_config.mode_map[itr].mode; - } - } - // If the plmn is not found/configured we still create a channel - // towards the FeG as the default mode is HSS + spgw_task. - std::cout << "[INFO] PLMN is not found/configured. Selecting default mode" - << std::endl; - return (magma::mconfig::ModeMapItem_FederatedMode_SPGW_SUBSCRIBER); -} - S6aClient::S6aClient(bool enable_s6a_proxy_channel) { // Create channel based on relay_enabled, enable_s6a_proxy_channel and // cloud_subscriberdb_enabled flags. @@ -165,7 +130,7 @@ S6aClient::S6aClient(bool enable_s6a_proxy_channel) { void S6aClient::purge_ue( const char* imsi, std::function callbk) { S6aClient* client_tmp; - int fed_mode = match_fed_mode_map(imsi); + int fed_mode = match_fed_mode_map(imsi, LOG_S6A); if ((fed_mode == magma::mconfig::ModeMapItem_FederatedMode_SPGW_SUBSCRIBER) || (fed_mode == magma::mconfig::ModeMapItem_FederatedMode_S8_SUBSCRIBER)) { client_tmp = &get_s6a_proxy_instance(); @@ -201,7 +166,7 @@ void S6aClient::authentication_info_req( const s6a_auth_info_req_t* const msg, std::function callbk) { S6aClient* client_tmp; - int fed_mode = match_fed_mode_map(msg->imsi); + int fed_mode = match_fed_mode_map(msg->imsi, LOG_S6A); if ((fed_mode == magma::mconfig::ModeMapItem_FederatedMode_SPGW_SUBSCRIBER) || (fed_mode == magma::mconfig::ModeMapItem_FederatedMode_S8_SUBSCRIBER)) { client_tmp = &get_s6a_proxy_instance(); @@ -237,7 +202,7 @@ void S6aClient::update_location_request( const s6a_update_location_req_t* const msg, std::function callbk) { S6aClient* client_tmp; - int fed_mode = match_fed_mode_map(msg->imsi); + int fed_mode = match_fed_mode_map(msg->imsi, LOG_S6A); if ((fed_mode == magma::mconfig::ModeMapItem_FederatedMode_SPGW_SUBSCRIBER) || (fed_mode == magma::mconfig::ModeMapItem_FederatedMode_S8_SUBSCRIBER)) { client_tmp = &get_s6a_proxy_instance(); diff --git a/lte/gateway/c/oai/tasks/mme_app/mme_app_itti_messaging.c b/lte/gateway/c/oai/tasks/mme_app/mme_app_itti_messaging.c index 78b0b22d3021..ff37c92449f9 100644 --- a/lte/gateway/c/oai/tasks/mme_app/mme_app_itti_messaging.c +++ b/lte/gateway/c/oai/tasks/mme_app/mme_app_itti_messaging.c @@ -48,6 +48,7 @@ #include "esm_data.h" #include "mme_app_desc.h" #include "s11_messages_types.h" +#include "common_utility_funs.h" #if EMBEDDED_SGW #define TASK_SPGW TASK_SPGW_APP @@ -373,10 +374,9 @@ int mme_app_send_s11_create_session_req( session_request_p->serving_network.mnc[2] = ue_mm_context->e_utran_cgi.plmn.mnc_digit3; session_request_p->selection_mode = MS_O_N_P_APN_S_V; - - if (mme_app_match_fed_mode_map( - session_request_p->imsi.digit, ue_mm_context->emm_context._imsi64) == - S8_SUBSCRIBER) { + int mode = + match_fed_mode_map((char*) session_request_p->imsi.digit, LOG_MME_APP); + if (mode == S8_SUBSCRIBER) { OAILOG_INFO_UE( LOG_MME_APP, ue_mm_context->emm_context._imsi64, "Sending s11 create session req message to SGW_s8 task for " @@ -647,42 +647,3 @@ void mme_app_itti_sgsap_ue_activity_ind( OAILOG_FUNC_OUT(LOG_MME_APP); } -// Extract MCC and MNC from the imsi received and match with -// configuration -int mme_app_match_fed_mode_map(const uint8_t* imsi, imsi64_t imsi64) { - uint8_t mcc_d1 = imsi[0] - '0'; - uint8_t mcc_d2 = imsi[1] - '0'; - uint8_t mcc_d3 = imsi[2] - '0'; - uint8_t mnc_d1 = imsi[3] - '0'; - uint8_t mnc_d2 = imsi[4] - '0'; - uint8_t mnc_d3 = imsi[5] - '0'; - if ((mcc_d1 < 0 || mcc_d1 > 9) || (mcc_d2 < 0 || mcc_d2 > 9) || - (mcc_d3 < 0 || mcc_d3 > 9) || (mnc_d1 < 0 || mnc_d1 > 9) || - (mnc_d2 < 0 || mnc_d2 > 9) || (mnc_d3 < 0 || mnc_d3 > 9)) { - OAILOG_ERROR_UE( - LOG_MME_APP, imsi64, "[ERROR] MCC/MNC is not a decimal digit \n"); - OAILOG_FUNC_RETURN(LOG_MME_APP, RETURNerror); - } - for (uint8_t itr = 0; itr < mme_config.mode_map_config.num; itr++) { - if (((mcc_d1 == mme_config.mode_map_config.mode_map[itr].plmn.mcc_digit1) && - (mcc_d2 == mme_config.mode_map_config.mode_map[itr].plmn.mcc_digit2) && - (mcc_d3 == mme_config.mode_map_config.mode_map[itr].plmn.mcc_digit3) && - (mnc_d1 == mme_config.mode_map_config.mode_map[itr].plmn.mnc_digit1) && - (mnc_d2 == - mme_config.mode_map_config.mode_map[itr].plmn.mnc_digit2))) { - if (mme_config.mode_map_config.mode_map[itr].plmn.mnc_digit3 != 0xf) { - if (mnc_d3 != - mme_config.mode_map_config.mode_map[itr].plmn.mnc_digit3) { - continue; - } - } - OAILOG_FUNC_RETURN( - LOG_MME_APP, mme_config.mode_map_config.mode_map[itr].mode); - } - } - // If the plmn is not configured set the default mode as hss + spgw_task. - OAILOG_INFO_UE( - LOG_MME_APP, imsi64, - "PLMN is not configured. Selecting default mode: SPGW_SUBSCRIBER \n"); - OAILOG_FUNC_RETURN(LOG_MME_APP, SPGW_SUBSCRIBER); -} From 981e0935f1c9b6fa2cb903e6e19ba864d1049603 Mon Sep 17 00:00:00 2001 From: rashmi Date: Fri, 2 Apr 2021 04:34:34 +0530 Subject: [PATCH 09/91] Adding common utility file to have common functions defined Signed-off-by: rashmi --- .../c/oai/common/common_utility_funs.cpp | 60 +++++++++++++++++++ .../c/oai/common/common_utility_funs.h | 15 +++++ .../tasks/mme_app/mme_app_itti_messaging.h | 2 - 3 files changed, 75 insertions(+), 2 deletions(-) create mode 100644 lte/gateway/c/oai/common/common_utility_funs.cpp create mode 100644 lte/gateway/c/oai/common/common_utility_funs.h diff --git a/lte/gateway/c/oai/common/common_utility_funs.cpp b/lte/gateway/c/oai/common/common_utility_funs.cpp new file mode 100644 index 000000000000..f3adf07f45cd --- /dev/null +++ b/lte/gateway/c/oai/common/common_utility_funs.cpp @@ -0,0 +1,60 @@ +/* +Copyright 2020 The Magma Authors. + +This source code is licensed under the BSD-style license found in the +LICENSE file in the root directory of this source tree. + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +#include +#include +#include "common_utility_funs.h" +#include "lte/protos/mconfig/mconfigs.pb.h" + +// Extract MCC and MNC from the imsi received and match with +// configuration +extern "C" int match_fed_mode_map(const char* imsi, log_proto_t module) { + OAILOG_FUNC_IN(module); + imsi64_t imsi64; + IMSI_STRING_TO_IMSI64(imsi, &imsi64); + uint8_t mcc_d1 = imsi[0] - '0'; + uint8_t mcc_d2 = imsi[1] - '0'; + uint8_t mcc_d3 = imsi[2] - '0'; + uint8_t mnc_d1 = imsi[3] - '0'; + uint8_t mnc_d2 = imsi[4] - '0'; + uint8_t mnc_d3 = imsi[5] - '0'; + if ((mcc_d1 < 0 || mcc_d1 > 9) || (mcc_d2 < 0 || mcc_d2 > 9) || + (mcc_d3 < 0 || mcc_d3 > 9) || (mnc_d1 < 0 || mnc_d1 > 9) || + (mnc_d2 < 0 || mnc_d2 > 9) || (mnc_d3 < 0 || mnc_d3 > 9)) { + OAILOG_ERROR_UE(module, imsi64, "MCC/MNC is not a decimal digit \n"); + OAILOG_FUNC_RETURN(module, RETURNerror); + } + for (uint8_t itr = 0; itr < mme_config.mode_map_config.num; itr++) { + if (((mcc_d1 == mme_config.mode_map_config.mode_map[itr].plmn.mcc_digit1) && + (mcc_d2 == mme_config.mode_map_config.mode_map[itr].plmn.mcc_digit2) && + (mcc_d3 == mme_config.mode_map_config.mode_map[itr].plmn.mcc_digit3) && + (mnc_d1 == mme_config.mode_map_config.mode_map[itr].plmn.mnc_digit1) && + (mnc_d2 == + mme_config.mode_map_config.mode_map[itr].plmn.mnc_digit2))) { + if (mme_config.mode_map_config.mode_map[itr].plmn.mnc_digit3 != 0xf) { + if (mnc_d3 != + mme_config.mode_map_config.mode_map[itr].plmn.mnc_digit3) { + continue; + } + } + OAILOG_FUNC_RETURN(module, mme_config.mode_map_config.mode_map[itr].mode); + } + } + // If the plmn is not configured set the default mode as hss + spgw_task. + OAILOG_INFO_UE( + module, imsi64, + "PLMN is not configured. Selecting default mode: SPGW_SUBSCRIBER \n"); + OAILOG_FUNC_RETURN( + module, magma::mconfig::ModeMapItem_FederatedMode_SPGW_SUBSCRIBER); +} + diff --git a/lte/gateway/c/oai/common/common_utility_funs.h b/lte/gateway/c/oai/common/common_utility_funs.h new file mode 100644 index 000000000000..f173ba6b204c --- /dev/null +++ b/lte/gateway/c/oai/common/common_utility_funs.h @@ -0,0 +1,15 @@ +#define pragma once + +#ifdef __cplusplus +extern "C" { +#endif + +#include "mme_config.h" +#include "log.h" +#include "conversions.h" +#include "common_defs.h" + +int match_fed_mode_map(const char* imsi, log_proto_t module); +#ifdef __cplusplus +} +#endif diff --git a/lte/gateway/c/oai/tasks/mme_app/mme_app_itti_messaging.h b/lte/gateway/c/oai/tasks/mme_app/mme_app_itti_messaging.h index bd794502f920..4f27d0dfbb53 100644 --- a/lte/gateway/c/oai/tasks/mme_app/mme_app_itti_messaging.h +++ b/lte/gateway/c/oai/tasks/mme_app/mme_app_itti_messaging.h @@ -131,6 +131,4 @@ void mme_app_itti_sgsap_tmsi_reallocation_comp( void mme_app_itti_sgsap_ue_activity_ind( const char* imsi, const unsigned int imsi_len); - -int mme_app_match_fed_mode_map(const uint8_t* imsi, imsi64_t imsi64); #endif /* FILE_MME_APP_ITTI_MESSAGING_SEEN */ From 3120ec56195cc976553d2d299ebc0ae3f273d95a Mon Sep 17 00:00:00 2001 From: Ulas Kozat Date: Fri, 2 Apr 2021 11:05:04 -0700 Subject: [PATCH 10/91] [lte][agw] Remove hardwired APN APMBR over S1 (#5886) * Send actual configured APN AMBR to RAN Signed-off-by: Ulas Kozat --- .../c/oai/tasks/mme_app/mme_app_pdn_context.c | 4 +- lte/gateway/c/oai/tasks/nas/emm/sap/emm_cn.c | 4 +- .../c/oai/tasks/nas/esm/sap/esm_send.c | 26 ++-- .../c/oai/tasks/nas/esm/sap/esm_send.h | 6 +- .../nas/ies/ApnAggregateMaximumBitRate.c | 55 ++++++++ .../nas/ies/ApnAggregateMaximumBitRate.h | 3 + .../c/oai/tasks/nas/ies/EpsQualityOfService.c | 120 +++++++++--------- 7 files changed, 142 insertions(+), 76 deletions(-) diff --git a/lte/gateway/c/oai/tasks/mme_app/mme_app_pdn_context.c b/lte/gateway/c/oai/tasks/mme_app/mme_app_pdn_context.c index df4ca9ddd80b..bfa328a78854 100644 --- a/lte/gateway/c/oai/tasks/mme_app/mme_app_pdn_context.c +++ b/lte/gateway/c/oai/tasks/mme_app/mme_app_pdn_context.c @@ -93,7 +93,9 @@ pdn_context_t* mme_app_create_pdn_context( &pdn_context->default_bearer_eps_subscribed_qos_profile, &apn_configuration->subscribed_qos, sizeof(eps_subscribed_qos_profile_t)); - // pdn_context->subscribed_apn_ambr = ; + memcpy( + &pdn_context->subscribed_apn_ambr, &apn_configuration->ambr, + sizeof(ambr_t)); // pdn_context->pgw_apn_ambr = ; pdn_context->is_active = true; pdn_context->apn_subscribed = blk2bstr( diff --git a/lte/gateway/c/oai/tasks/nas/emm/sap/emm_cn.c b/lte/gateway/c/oai/tasks/nas/emm/sap/emm_cn.c index 4dba0d73b0b9..c3f8398e8282 100644 --- a/lte/gateway/c/oai/tasks/nas/emm/sap/emm_cn.c +++ b/lte/gateway/c/oai/tasks/nas/emm/sap/emm_cn.c @@ -571,8 +571,8 @@ static int emm_cn_cs_response_success(emm_cn_cs_response_success_t* msg_pP) { rc = esm_send_activate_default_eps_bearer_context_request( msg_pP->pti, msg_pP->ebi, &esm_msg.activate_default_eps_bearer_context_request, - ue_mm_context->pdn_contexts[pdn_cid]->apn_subscribed, &msg_pP->pco, - esm_pdn_type, msg_pP->pdn_addr, &qos, + ue_mm_context->pdn_contexts[pdn_cid], &msg_pP->pco, esm_pdn_type, + msg_pP->pdn_addr, &qos, ue_mm_context->pdn_contexts[pdn_cid]->esm_data.esm_cause); clear_protocol_configuration_options(&msg_pP->pco); if (rc != RETURNerror) { diff --git a/lte/gateway/c/oai/tasks/nas/esm/sap/esm_send.c b/lte/gateway/c/oai/tasks/nas/esm/sap/esm_send.c index decab67bb4e7..6cb02ec321b2 100644 --- a/lte/gateway/c/oai/tasks/nas/esm/sap/esm_send.c +++ b/lte/gateway/c/oai/tasks/nas/esm/sap/esm_send.c @@ -220,12 +220,14 @@ int esm_send_pdn_disconnect_reject( ***************************************************************************/ int esm_send_activate_default_eps_bearer_context_request( pti_t pti, ebi_t ebi, activate_default_eps_bearer_context_request_msg* msg, - bstring apn, const protocol_configuration_options_t* pco, int pdn_type, - bstring pdn_addr, const EpsQualityOfService* qos, int esm_cause) { + pdn_context_t* pdn_context_p, const protocol_configuration_options_t* pco, + int pdn_type, bstring pdn_addr, const EpsQualityOfService* qos, + int esm_cause) { OAILOG_FUNC_IN(LOG_NAS_ESM); OAILOG_INFO( LOG_NAS_ESM, "ESM-SAP - Send Activate Default EPS Bearer Context Request message\n"); + bstring apn = pdn_context_p->apn_subscribed; /* * Mandatory - ESM message header */ @@ -312,20 +314,16 @@ int esm_send_activate_default_eps_bearer_context_request( &msg->protocolconfigurationoptions, pco); } //#pragma message "TEST LG FORCE APN-AMBR" - OAILOG_DEBUG(LOG_NAS_ESM, "ESM-SAP - FORCE APN-AMBR\n"); + OAILOG_DEBUG( + LOG_NAS_ESM, "ESM-SAP - FORCE APN-AMBR DL %lu UL %lu\n", + pdn_context_p->subscribed_apn_ambr.br_dl, + pdn_context_p->subscribed_apn_ambr.br_ul); msg->presencemask |= ACTIVATE_DEFAULT_EPS_BEARER_CONTEXT_REQUEST_APNAMBR_PRESENT; - // APN AMBR is hardcoded to DL AMBR = 200 Mbps and UL APN MBR = 100 Mbps - - // Which is ok for now for TDD 20 MHz - // TODO task#14477798 - need to change these to apm-subscribed values - msg->apnambr.apnambrfordownlink = 0xfe; // (8640kbps) - msg->apnambr.apnambrforuplink = 0xfe; // (8640kbps) - msg->apnambr.apnambrfordownlink_extended = 0xde; // (200Mbps) - msg->apnambr.apnambrforuplink_extended = 0x9e; // (100Mbps) - msg->apnambr.apnambrfordownlink_extended2 = 0; - msg->apnambr.apnambrforuplink_extended2 = 0; - msg->apnambr.extensions = - 0 | APN_AGGREGATE_MAXIMUM_BIT_RATE_MAXIMUM_EXTENSION_PRESENT; + bit_rate_value_to_eps_qos( + &msg->apnambr, pdn_context_p->subscribed_apn_ambr.br_dl, + pdn_context_p->subscribed_apn_ambr.br_ul); + OAILOG_INFO( LOG_NAS_ESM, "ESM-SAP - Send Activate Default EPS Bearer Context " diff --git a/lte/gateway/c/oai/tasks/nas/esm/sap/esm_send.h b/lte/gateway/c/oai/tasks/nas/esm/sap/esm_send.h index 6245926dba80..37a2c4c3f889 100644 --- a/lte/gateway/c/oai/tasks/nas/esm/sap/esm_send.h +++ b/lte/gateway/c/oai/tasks/nas/esm/sap/esm_send.h @@ -51,6 +51,7 @@ Description Defines functions executed at the ESM Service Access #include "3gpp_24.008.h" #include "EpsQualityOfService.h" #include "bstrlib.h" +#include "mme_app_ue_context.h" /****************************************************************************/ /********************* G L O B A L C O N S T A N T S *******************/ @@ -94,8 +95,9 @@ int esm_send_pdn_disconnect_reject( */ int esm_send_activate_default_eps_bearer_context_request( pti_t pti, ebi_t ebi, activate_default_eps_bearer_context_request_msg* msg, - bstring apn, const protocol_configuration_options_t* pco, int pdn_type, - bstring pdn_addr, const EpsQualityOfService* qos, int esm_cause); + pdn_context_t* pdn_context_p, const protocol_configuration_options_t* pco, + int pdn_type, bstring pdn_addr, const EpsQualityOfService* qos, + int esm_cause); int esm_send_activate_dedicated_eps_bearer_context_request( pti_t pti, ebi_t ebi, diff --git a/lte/gateway/c/oai/tasks/nas/ies/ApnAggregateMaximumBitRate.c b/lte/gateway/c/oai/tasks/nas/ies/ApnAggregateMaximumBitRate.c index 164287eea9dd..8e8396f8dc1e 100644 --- a/lte/gateway/c/oai/tasks/nas/ies/ApnAggregateMaximumBitRate.c +++ b/lte/gateway/c/oai/tasks/nas/ies/ApnAggregateMaximumBitRate.c @@ -107,3 +107,58 @@ int encode_apn_aggregate_maximum_bit_rate( *lenPtr = encoded - 1 - ((iei > 0) ? 1 : 0); return encoded; } + +// Use 3GPP TS 24.008 figure 10.5.136A, table 10.5.154A +void bit_rate_value_to_eps_qos( + ApnAggregateMaximumBitRate* apn_ambr, uint64_t ambr_dl, uint64_t ambr_ul) { + uint64_t ambr_dl_kbps = ambr_dl / 1000; // ambr_dl is expected in bps + uint64_t ambr_ul_kbps = ambr_ul / 1000; // ambr_ul is expected in bps + if (ambr_dl_kbps == 0) { + apn_ambr->apnambrfordownlink = 0xff; + } else if ((ambr_dl_kbps > 0) && (ambr_dl_kbps <= 63)) { + apn_ambr->apnambrfordownlink = ambr_dl_kbps; + } else if ((ambr_dl_kbps > 63) && (ambr_dl_kbps <= 575)) { + apn_ambr->apnambrfordownlink = ((ambr_dl_kbps - 64) / 8) + 64; + } else if ((ambr_dl_kbps > 575) && (ambr_dl_kbps <= 8640)) { + apn_ambr->apnambrfordownlink = ((ambr_dl_kbps - 576) / 64) + 128; + } else if (ambr_dl_kbps > 8640) { + apn_ambr->apnambrfordownlink = 0xfe; + apn_ambr->extensions = + APN_AGGREGATE_MAXIMUM_BIT_RATE_MAXIMUM_EXTENSION_PRESENT; + if ((ambr_dl_kbps >= 8600) && (ambr_dl_kbps <= 16000)) { + apn_ambr->apnambrfordownlink_extended = (ambr_dl_kbps - 8600) / 100; + } else if ((ambr_dl_kbps > 16000) && (ambr_dl_kbps <= 128000)) { + apn_ambr->apnambrfordownlink_extended = + ((ambr_dl_kbps - 16000) / 1000) + 74; + } else if ((ambr_dl_kbps > 128000) && (ambr_dl_kbps <= 256000)) { + apn_ambr->apnambrfordownlink_extended = + ((ambr_dl_kbps - 128000) / 2000) + 186; + } + } + + if (ambr_ul_kbps == 0) { + apn_ambr->apnambrforuplink = 0xff; + } else if ((ambr_ul_kbps > 0) && (ambr_ul_kbps <= 63)) { + apn_ambr->apnambrforuplink = ambr_ul_kbps; + } else if ((ambr_ul_kbps > 63) && (ambr_ul_kbps <= 575)) { + apn_ambr->apnambrforuplink = ((ambr_ul_kbps - 64) / 8) + 64; + } else if ((ambr_ul_kbps > 575) && (ambr_ul_kbps <= 8640)) { + apn_ambr->apnambrforuplink = ((ambr_ul_kbps - 576) / 64) + 128; + } else if (ambr_ul_kbps > 8640) { + apn_ambr->apnambrforuplink = 0xfe; + apn_ambr->extensions = + APN_AGGREGATE_MAXIMUM_BIT_RATE_MAXIMUM_EXTENSION_PRESENT; + if ((ambr_ul_kbps >= 8600) && (ambr_ul_kbps <= 16000)) { + apn_ambr->apnambrforuplink_extended = (ambr_ul_kbps - 8600) / 100; + } else if ((ambr_ul_kbps > 16000) && (ambr_ul_kbps <= 128000)) { + apn_ambr->apnambrforuplink_extended = + ((ambr_ul_kbps - 16000) / 1000) + 74; + } else if ((ambr_ul_kbps > 128000) && (ambr_ul_kbps <= 256000)) { + apn_ambr->apnambrforuplink_extended = + ((ambr_ul_kbps - 128000) / 2000) + 186; + } + } + + apn_ambr->apnambrfordownlink_extended2 = 0; + apn_ambr->apnambrforuplink_extended2 = 0; +} diff --git a/lte/gateway/c/oai/tasks/nas/ies/ApnAggregateMaximumBitRate.h b/lte/gateway/c/oai/tasks/nas/ies/ApnAggregateMaximumBitRate.h index d0ae7075aad2..35e76eb6656e 100644 --- a/lte/gateway/c/oai/tasks/nas/ies/ApnAggregateMaximumBitRate.h +++ b/lte/gateway/c/oai/tasks/nas/ies/ApnAggregateMaximumBitRate.h @@ -44,4 +44,7 @@ int decode_apn_aggregate_maximum_bit_rate( ApnAggregateMaximumBitRate* apnaggregatemaximumbitrate, uint8_t iei, uint8_t* buffer, uint32_t len); +void bit_rate_value_to_eps_qos( + ApnAggregateMaximumBitRate* apn_ambr, uint64_t ambr_dl, uint64_t ambr_ul); + #endif /* APN_AGGREGATE_MAXIMUM_BIT_RATE_SEEN */ diff --git a/lte/gateway/c/oai/tasks/nas/ies/EpsQualityOfService.c b/lte/gateway/c/oai/tasks/nas/ies/EpsQualityOfService.c index 74aacaeba7f0..cd4fa5836bd8 100644 --- a/lte/gateway/c/oai/tasks/nas/ies/EpsQualityOfService.c +++ b/lte/gateway/c/oai/tasks/nas/ies/EpsQualityOfService.c @@ -178,92 +178,98 @@ int qos_params_to_eps_qos( const qci_t qci, const bitrate_t mbr_dl, const bitrate_t mbr_ul, const bitrate_t gbr_dl, const bitrate_t gbr_ul, EpsQualityOfService* const eps_qos, bool is_default_bearer) { - // if someone volunteer for subroutines..., no time yet. + uint64_t mbr_dl_kbps = mbr_dl / 1000; // mbr_dl is expected in bps + uint64_t mbr_ul_kbps = mbr_ul / 1000; // mbr_ul is expected in bps + uint64_t gbr_dl_kbps = gbr_dl / 1000; // mbr_dl is expected in bps + uint64_t gbr_ul_kbps = gbr_ul / 1000; // mbr_ul is expected in bps + if (eps_qos) { memset(eps_qos, 0, sizeof(EpsQualityOfService)); eps_qos->qci = qci; if (!is_default_bearer) { eps_qos->bitRatesPresent = 1; - if (mbr_ul == 0) { + if (mbr_ul_kbps == 0) { eps_qos->bitRates.maxBitRateForUL = 0xff; - } else if ((mbr_ul > 0) && (mbr_ul <= 63)) { - eps_qos->bitRates.maxBitRateForUL = mbr_ul; - } else if ((mbr_ul > 63) && (mbr_ul <= 568)) { - eps_qos->bitRates.maxBitRateForUL = ((mbr_ul - 64) / 8) + 64; - } else if ((mbr_ul >= 576) && (mbr_ul <= 8640)) { - eps_qos->bitRates.maxBitRateForUL = ((mbr_ul - 128) / 64) + 128; - } else if (mbr_ul > 8640) { + } else if ((mbr_ul_kbps > 0) && (mbr_ul_kbps <= 63)) { + eps_qos->bitRates.maxBitRateForUL = mbr_ul_kbps; + } else if ((mbr_ul_kbps > 63) && (mbr_ul_kbps <= 575)) { + eps_qos->bitRates.maxBitRateForUL = ((mbr_ul_kbps - 64) / 8) + 64; + } else if ((mbr_ul_kbps > 575) && (mbr_ul_kbps <= 8640)) { + eps_qos->bitRates.maxBitRateForUL = ((mbr_ul_kbps - 576) / 64) + 128; + } else if (mbr_ul_kbps > 8640) { eps_qos->bitRates.maxBitRateForUL = 0xfe; eps_qos->bitRatesExtPresent = 1; - if ((mbr_ul >= 8600) && (mbr_ul <= 16000)) { - eps_qos->bitRatesExt.maxBitRateForUL = (mbr_ul - 8600) / 100; - } else if ((mbr_ul > 16000) && (mbr_ul <= 128000)) { - eps_qos->bitRatesExt.maxBitRateForUL = ((mbr_ul - 16000) / 1000) + 74; - } else if ((mbr_ul > 128000) && (mbr_ul <= 256000)) { + if ((mbr_ul_kbps >= 8600) && (mbr_ul_kbps <= 16000)) { + eps_qos->bitRatesExt.maxBitRateForUL = (mbr_ul_kbps - 8600) / 100; + } else if ((mbr_ul_kbps > 16000) && (mbr_ul_kbps <= 128000)) { + eps_qos->bitRatesExt.maxBitRateForUL = + ((mbr_ul_kbps - 16000) / 1000) + 74; + } else if ((mbr_ul_kbps > 128000) && (mbr_ul_kbps <= 256000)) { eps_qos->bitRatesExt.maxBitRateForUL = - ((mbr_ul - 128000) / 2000) + 186; + ((mbr_ul_kbps - 128000) / 2000) + 186; } } - if (mbr_dl == 0) { + if (mbr_dl_kbps == 0) { eps_qos->bitRates.maxBitRateForDL = 0xff; - } else if ((mbr_dl > 0) && (mbr_dl <= 63)) { - eps_qos->bitRates.maxBitRateForDL = mbr_dl; - } else if ((mbr_dl > 63) && (mbr_dl <= 568)) { - eps_qos->bitRates.maxBitRateForDL = ((mbr_dl - 64) / 8) + 64; - } else if ((mbr_dl >= 576) && (mbr_dl <= 8640)) { - eps_qos->bitRates.maxBitRateForDL = ((mbr_dl - 128) / 64) + 128; - } else if (mbr_dl > 8640) { + } else if ((mbr_dl_kbps > 0) && (mbr_dl_kbps <= 63)) { + eps_qos->bitRates.maxBitRateForDL = mbr_dl_kbps; + } else if ((mbr_dl_kbps > 63) && (mbr_dl_kbps <= 575)) { + eps_qos->bitRates.maxBitRateForDL = ((mbr_dl_kbps - 64) / 8) + 64; + } else if ((mbr_dl_kbps > 575) && (mbr_dl_kbps <= 8640)) { + eps_qos->bitRates.maxBitRateForDL = ((mbr_dl_kbps - 576) / 64) + 128; + } else if (mbr_dl_kbps > 8640) { eps_qos->bitRates.maxBitRateForDL = 0xfe; eps_qos->bitRatesExtPresent = 1; - if ((mbr_dl >= 8600) && (mbr_dl <= 16000)) { - eps_qos->bitRatesExt.maxBitRateForDL = (mbr_dl - 8600) / 100; - } else if ((mbr_dl > 16000) && (mbr_dl <= 128000)) { - eps_qos->bitRatesExt.maxBitRateForDL = ((mbr_dl - 16000) / 1000) + 74; - } else if ((mbr_dl > 128000) && (mbr_dl <= 256000)) { + if ((mbr_dl_kbps >= 8600) && (mbr_dl_kbps <= 16000)) { + eps_qos->bitRatesExt.maxBitRateForDL = (mbr_dl_kbps - 8600) / 100; + } else if ((mbr_dl_kbps > 16000) && (mbr_dl_kbps <= 128000)) { + eps_qos->bitRatesExt.maxBitRateForDL = + ((mbr_dl_kbps - 16000) / 1000) + 74; + } else if ((mbr_dl_kbps > 128000) && (mbr_dl_kbps <= 256000)) { eps_qos->bitRatesExt.maxBitRateForDL = - ((mbr_dl - 128000) / 2000) + 186; + ((mbr_dl_kbps - 128000) / 2000) + 186; } } - if (gbr_ul == 0) { + if (gbr_ul_kbps == 0) { eps_qos->bitRates.guarBitRateForUL = 0xff; - } else if ((gbr_ul > 0) && (gbr_ul <= 63)) { - eps_qos->bitRates.guarBitRateForUL = gbr_ul; - } else if ((gbr_ul > 63) && (gbr_ul <= 568)) { - eps_qos->bitRates.guarBitRateForUL = ((gbr_ul - 64) / 8) + 64; - } else if ((gbr_ul >= 576) && (gbr_ul <= 8640)) { - eps_qos->bitRates.guarBitRateForUL = ((gbr_ul - 128) / 64) + 128; - } else if (gbr_ul > 8640) { + } else if ((gbr_ul_kbps > 0) && (gbr_ul_kbps <= 63)) { + eps_qos->bitRates.guarBitRateForUL = gbr_ul_kbps; + } else if ((gbr_ul_kbps > 63) && (gbr_ul_kbps <= 575)) { + eps_qos->bitRates.guarBitRateForUL = ((gbr_ul_kbps - 64) / 8) + 64; + } else if ((gbr_ul_kbps > 575) && (gbr_ul_kbps <= 8640)) { + eps_qos->bitRates.guarBitRateForUL = ((gbr_ul_kbps - 576) / 64) + 128; + } else if (gbr_ul_kbps > 8640) { eps_qos->bitRates.guarBitRateForUL = 0xfe; eps_qos->bitRatesExtPresent = 1; - if ((gbr_ul >= 8600) && (gbr_ul <= 16000)) { - eps_qos->bitRatesExt.guarBitRateForUL = (gbr_ul - 8600) / 100; - } else if ((gbr_ul > 16000) && (gbr_ul <= 128000)) { + if ((gbr_ul_kbps >= 8600) && (gbr_ul_kbps <= 16000)) { + eps_qos->bitRatesExt.guarBitRateForUL = (gbr_ul_kbps - 8600) / 100; + } else if ((gbr_ul_kbps > 16000) && (gbr_ul_kbps <= 128000)) { eps_qos->bitRatesExt.guarBitRateForUL = - ((gbr_ul - 16000) / 1000) + 74; - } else if ((gbr_ul > 128000) && (gbr_ul <= 256000)) { + ((gbr_ul_kbps - 16000) / 1000) + 74; + } else if ((gbr_ul_kbps > 128000) && (gbr_ul_kbps <= 256000)) { eps_qos->bitRatesExt.guarBitRateForUL = - ((gbr_ul - 128000) / 2000) + 186; + ((gbr_ul_kbps - 128000) / 2000) + 186; } } - if (gbr_dl == 0) { + if (gbr_dl_kbps == 0) { eps_qos->bitRates.guarBitRateForDL = 0xff; - } else if ((gbr_dl > 0) && (gbr_dl <= 63)) { - eps_qos->bitRates.guarBitRateForDL = gbr_dl; - } else if ((gbr_dl > 63) && (gbr_dl <= 568)) { - eps_qos->bitRates.guarBitRateForDL = ((gbr_dl - 64) / 8) + 64; - } else if ((gbr_dl >= 576) && (gbr_dl <= 8640)) { - eps_qos->bitRates.guarBitRateForDL = ((gbr_dl - 128) / 64) + 128; - } else if (gbr_dl > 8640) { + } else if ((gbr_dl_kbps > 0) && (gbr_dl_kbps <= 63)) { + eps_qos->bitRates.guarBitRateForDL = gbr_dl_kbps; + } else if ((gbr_dl_kbps > 63) && (gbr_dl_kbps <= 575)) { + eps_qos->bitRates.guarBitRateForDL = ((gbr_dl_kbps - 64) / 8) + 64; + } else if ((gbr_dl_kbps >= 575) && (gbr_dl_kbps <= 8640)) { + eps_qos->bitRates.guarBitRateForDL = ((gbr_dl_kbps - 576) / 64) + 128; + } else if (gbr_dl_kbps > 8640) { eps_qos->bitRates.guarBitRateForDL = 0xfe; eps_qos->bitRatesExtPresent = 1; - if ((gbr_dl >= 8600) && (gbr_dl <= 16000)) { - eps_qos->bitRatesExt.guarBitRateForDL = (gbr_dl - 8600) / 100; - } else if ((gbr_dl > 16000) && (gbr_dl <= 128000)) { + if ((gbr_dl_kbps >= 8600) && (gbr_dl_kbps <= 16000)) { + eps_qos->bitRatesExt.guarBitRateForDL = (gbr_dl_kbps - 8600) / 100; + } else if ((gbr_dl_kbps > 16000) && (gbr_dl_kbps <= 128000)) { eps_qos->bitRatesExt.guarBitRateForDL = - ((gbr_dl - 16000) / 1000) + 74; - } else if ((gbr_dl > 128000) && (gbr_dl <= 256000)) { + ((gbr_dl_kbps - 16000) / 1000) + 74; + } else if ((gbr_dl_kbps > 128000) && (gbr_dl_kbps <= 256000)) { eps_qos->bitRatesExt.guarBitRateForDL = - ((gbr_dl - 128000) / 2000) + 186; + ((gbr_dl_kbps - 128000) / 2000) + 186; } } } From 525d7312137ee2c18adf90f697778f1b8fd10e9e Mon Sep 17 00:00:00 2001 From: Marie <37634144+themarwhal@users.noreply.github.com> Date: Fri, 2 Apr 2021 13:08:39 -0500 Subject: [PATCH 11/91] [AGW] Modify postinst script to append or replace the COMMIT_HASH env var (#5878) Signed-off-by: Marie Bremner --- lte/gateway/release/magma-postinst | 8 +++++++- lte/gateway/release/magma.lockfile | 9 +++++++++ 2 files changed, 16 insertions(+), 1 deletion(-) diff --git a/lte/gateway/release/magma-postinst b/lte/gateway/release/magma-postinst index e5f44f2d302b..da5e9db3d8b5 100644 --- a/lte/gateway/release/magma-postinst +++ b/lte/gateway/release/magma-postinst @@ -16,7 +16,13 @@ sed -i "s/.*OVS_CTL_OPTS.*/OVS_CTL_OPTS='--delete-bridges'/" /etc/default/openvs # Create /var/core directory mkdir -p /var/core -sudo cat /usr/local/share/magma/commit_hash > /etc/environment +value=`cat /usr/local/share/magma/commit_hash` +if sudo grep -q "COMMIT_HASH" /etc/environment +then + sudo sed -i -e "s/^COMMIT_HASH.*/$value/" /etc/environment +else + sudo cat "$value" >> /etc/environment +fi # Set magmad service to start on boot systemctl enable -f magma@magmad.service diff --git a/lte/gateway/release/magma.lockfile b/lte/gateway/release/magma.lockfile index d18de275df5a..d5d2b1be6739 100644 --- a/lte/gateway/release/magma.lockfile +++ b/lte/gateway/release/magma.lockfile @@ -438,6 +438,12 @@ "sysdep": "python3-scapy", "version": "2.4.4" }, + "sentry-sdk": { + "root": true, + "source": "pypi", + "sysdep": "python3-sentry-sdk", + "version": "1.0.0" + }, "simplejson": { "root": false, "source": "apt", @@ -1153,6 +1159,9 @@ "scapy": { "version": "2.4.4" }, + "sentry-sdk": { + "version": "1.0.0" + }, "setuptools": { "version": "49.6.0" }, From 3267bcb8061be9dafb9df5a4f5d2b70722a1f476 Mon Sep 17 00:00:00 2001 From: Marie <37634144+themarwhal@users.noreply.github.com> Date: Fri, 2 Apr 2021 13:18:11 -0500 Subject: [PATCH 12/91] [GITIGNORE] Ignore .venv in git (#5894) Signed-off-by: GitHub --- .gitignore | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitignore b/.gitignore index 1e3e6667fb3e..13d072b9e330 100644 --- a/.gitignore +++ b/.gitignore @@ -94,6 +94,7 @@ test_results_magma_converged_mme.html # VS Code .vscode/ +.venv/ # Internal fb From 7132e942e0d8f57116cb45f2331636355395b5aa Mon Sep 17 00:00:00 2001 From: Andrei Lee Date: Fri, 2 Apr 2021 12:18:09 -0700 Subject: [PATCH 13/91] [nms] NMS will use same DB as orc8r for 1.5 (#5858) Signed-off-by: Andrei Lee --- .../deploy/terraform/orc8r-aws/README.md | 11 ----- orc8r/cloud/deploy/terraform/orc8r-aws/db.tf | 21 --------- .../orc8r-aws/examples/basic/main.tf | 1 - .../deploy/terraform/orc8r-aws/outputs.tf | 21 --------- .../deploy/terraform/orc8r-aws/variables.tf | 45 ------------------- .../deploy/terraform/orc8r-helm-aws/README.md | 4 -- .../orc8r-helm-aws/examples/basic/main.tf | 6 --- .../examples/online-upgrade/README.md | 2 - .../examples/online-upgrade/main.tf | 10 ----- .../examples/online-upgrade/variables.tf | 21 --------- .../orc8r-helm-aws/examples/remote/main.tf | 7 --- .../deploy/terraform/orc8r-helm-aws/main.tf | 33 -------------- .../orc8r-helm-aws/templates/orc8r-values.tpl | 6 +-- .../terraform/orc8r-helm-aws/variables.tf | 20 --------- 14 files changed, 3 insertions(+), 205 deletions(-) diff --git a/orc8r/cloud/deploy/terraform/orc8r-aws/README.md b/orc8r/cloud/deploy/terraform/orc8r-aws/README.md index 88560408b080..0d71af3da4b5 100644 --- a/orc8r/cloud/deploy/terraform/orc8r-aws/README.md +++ b/orc8r/cloud/deploy/terraform/orc8r-aws/README.md @@ -52,13 +52,6 @@ Everything below this line was generated by | elasticsearch\_instance\_type | AWS instance type for ES domain. | `string` | `null` | no | | elasticsearch\_version | ES version for ES domain. | `string` | `"7.1"` | no | | global\_tags | n/a | `map` | `{}` | no | -| nms\_db\_engine\_version | MySQL engine version for NMS DB. | `string` | `"5.7"` | no | -| nms\_db\_identifier | Identifier for the RDS instance for NMS. | `string` | `"nmsdb"` | no | -| nms\_db\_instance\_class | RDS instance type for NMS DB. | `string` | `"db.m4.large"` | no | -| nms\_db\_name | DB name for NMS RDS instance. | `string` | `"magma"` | no | -| nms\_db\_password | Password for the NMS DB. | `string` | n/a | yes | -| nms\_db\_storage\_gb | Capacity in GB to allocate for NMS RDS instance. | `number` | `16` | no | -| nms\_db\_username | Username for default DB user for NMS DB. | `string` | `"magma"` | no | | orc8r\_db\_engine\_version | Postgres engine version for Orchestrator DB. | `string` | `"9.6.15"` | no | | orc8r\_db\_identifier | Identifier for the RDS instance for Orchestrator. | `string` | `"orc8rdb"` | no | | orc8r\_db\_instance\_class | RDS instance type for Orchestrator DB. | `string` | `"db.m4.large"` | no | @@ -87,10 +80,6 @@ Everything below this line was generated by | es\_endpoint | Endpoint of the ES cluster if deployed. | | external\_dns\_role\_arn | IAM role ARN for external-dns | | kubeconfig | kubectl config file to access the EKS cluster | -| nms\_db\_host | Hostname of the NMS RDS instance | -| nms\_db\_name | Database name for NMS RDS instance | -| nms\_db\_pass | NMS DB password | -| nms\_db\_user | Database username for NMS RDS instance | | orc8r\_db\_host | Hostname of the Orchestrator RDS instance | | orc8r\_db\_name | Database name for Orchestrator RDS instance | | orc8r\_db\_pass | Orchestrator DB password | diff --git a/orc8r/cloud/deploy/terraform/orc8r-aws/db.tf b/orc8r/cloud/deploy/terraform/orc8r-aws/db.tf index 8abdfbde555d..f96fbbffe70c 100644 --- a/orc8r/cloud/deploy/terraform/orc8r-aws/db.tf +++ b/orc8r/cloud/deploy/terraform/orc8r-aws/db.tf @@ -31,24 +31,3 @@ resource "aws_db_instance" "default" { # this won't actually create a final snapshot on destroy final_snapshot_identifier = "foo" } - -resource "aws_db_instance" "nms" { - identifier = var.nms_db_identifier - allocated_storage = var.nms_db_storage_gb - engine = "mysql" - engine_version = var.nms_db_engine_version - instance_class = var.nms_db_instance_class - - name = var.nms_db_name - username = var.nms_db_username - password = var.nms_db_password - - vpc_security_group_ids = [aws_security_group.default.id] - - db_subnet_group_name = module.vpc.database_subnet_group - - skip_final_snapshot = true - # we only need this as a placeholder value for `terraform destroy` to work, - # this won't actually create a final snapshot on destroy - final_snapshot_identifier = "nms-foo" -} diff --git a/orc8r/cloud/deploy/terraform/orc8r-aws/examples/basic/main.tf b/orc8r/cloud/deploy/terraform/orc8r-aws/examples/basic/main.tf index 9057a4feb4fb..093c541f35e8 100644 --- a/orc8r/cloud/deploy/terraform/orc8r-aws/examples/basic/main.tf +++ b/orc8r/cloud/deploy/terraform/orc8r-aws/examples/basic/main.tf @@ -16,7 +16,6 @@ module orc8r { region = "us-west-2" - nms_db_password = "Faceb00k12345" orc8r_db_password = "Faceb00k12345" secretsmanager_orc8r_secret = "magma-orc8r-test" deployment_secrets_bucket = "magma.orc8r.test" diff --git a/orc8r/cloud/deploy/terraform/orc8r-aws/outputs.tf b/orc8r/cloud/deploy/terraform/orc8r-aws/outputs.tf index e403cc2906ff..f365b975dc00 100644 --- a/orc8r/cloud/deploy/terraform/orc8r-aws/outputs.tf +++ b/orc8r/cloud/deploy/terraform/orc8r-aws/outputs.tf @@ -78,27 +78,6 @@ output "orc8r_db_pass" { sensitive = true } -output "nms_db_host" { - description = "Hostname of the NMS RDS instance" - value = aws_db_instance.nms.address -} - -output "nms_db_name" { - description = "Database name for NMS RDS instance" - value = aws_db_instance.nms.name -} - -output "nms_db_user" { - description = "Database username for NMS RDS instance" - value = aws_db_instance.nms.username -} - -output "nms_db_pass" { - description = "NMS DB password" - value = aws_db_instance.nms.password - sensitive = true -} - output "route53_zone_id" { description = "Route53 zone ID for Orchestrator domain name" value = aws_route53_zone.orc8r.id diff --git a/orc8r/cloud/deploy/terraform/orc8r-aws/variables.tf b/orc8r/cloud/deploy/terraform/orc8r-aws/variables.tf index 9c9de1807799..63fcbc86a24e 100644 --- a/orc8r/cloud/deploy/terraform/orc8r-aws/variables.tf +++ b/orc8r/cloud/deploy/terraform/orc8r-aws/variables.tf @@ -239,51 +239,6 @@ variable "orc8r_db_engine_version" { default = "9.6.15" } -############################################################################## -# NMS DB Specs -############################################################################## - -variable "nms_db_identifier" { - description = "Identifier for the RDS instance for NMS." - type = string - default = "nmsdb" -} - -variable "nms_db_storage_gb" { - description = "Capacity in GB to allocate for NMS RDS instance." - type = number - default = 16 -} - -variable "nms_db_instance_class" { - description = "RDS instance type for NMS DB." - type = string - default = "db.m4.large" -} - -variable "nms_db_name" { - description = "DB name for NMS RDS instance." - type = string - default = "magma" -} - -variable "nms_db_username" { - description = "Username for default DB user for NMS DB." - type = string - default = "magma" -} - -variable "nms_db_password" { - description = "Password for the NMS DB. Must be at least 8 characters." - type = string -} - -variable "nms_db_engine_version" { - description = "MySQL engine version for NMS DB." - type = string - default = "5.7" -} - ############################################################################## # Secretmanager configuration ############################################################################## diff --git a/orc8r/cloud/deploy/terraform/orc8r-helm-aws/README.md b/orc8r/cloud/deploy/terraform/orc8r-helm-aws/README.md index 01a43af6d609..6cd16e582cb1 100644 --- a/orc8r/cloud/deploy/terraform/orc8r-helm-aws/README.md +++ b/orc8r/cloud/deploy/terraform/orc8r-helm-aws/README.md @@ -53,10 +53,6 @@ Everything below this line was generated by | install\_tiller | Install Tiller in the cluster or not. | `bool` | `true` | no | | lte_orc8r\_chart\_version | Version of the Orchestrator lte module Helm chart to install. | `string` | 0.2.1 | yes | | monitoring\_kubernetes\_namespace | K8s namespace to install Orchestrator monitoring components into. | `string` | `"monitoring"` | no | -| nms\_db\_host | DB hostname for NMS database connection. | `string` | n/a | yes | -| nms\_db\_name | DB name for NMS database connection. | `string` | n/a | yes | -| nms\_db\_pass | NMS DB password. | `string` | n/a | yes | -| nms\_db\_user | DB username for NMS database connection. | `string` | n/a | yes | | orc8r\_chart\_version | Version of the core Orchestrator Helm chart to install. | `string` | 1.5.8 | yes | | orc8r\_controller\_replicas | Replica count for Orchestrator controller pods. | `number` | `2` | no | | orc8r\_db\_host | DB hostname for Orchestrator database connection. | `string` | n/a | yes | diff --git a/orc8r/cloud/deploy/terraform/orc8r-helm-aws/examples/basic/main.tf b/orc8r/cloud/deploy/terraform/orc8r-helm-aws/examples/basic/main.tf index 64504b6f1e49..dd106a5c7137 100644 --- a/orc8r/cloud/deploy/terraform/orc8r-helm-aws/examples/basic/main.tf +++ b/orc8r/cloud/deploy/terraform/orc8r-helm-aws/examples/basic/main.tf @@ -17,7 +17,6 @@ module "orc8r" { region = "us-west-2" - nms_db_password = "mypassword" # must be at least 8 characters orc8r_db_password = "mypassword" # must be at least 8 characters secretsmanager_orc8r_secret = "orc8r-secrets" orc8r_domain_name = "orc8r.example.com" @@ -55,11 +54,6 @@ module "orc8r-app" { orc8r_db_user = module.orc8r.orc8r_db_user orc8r_db_pass = module.orc8r.orc8r_db_pass - nms_db_host = module.orc8r.nms_db_host - nms_db_name = module.orc8r.nms_db_name - nms_db_user = module.orc8r.nms_db_user - nms_db_pass = module.orc8r.nms_db_pass - # Note that this can be any container registry provider -- the example below # provides the URL format for Docker Hub, where the user and pass are your # Docker Hub username and access token, respectively diff --git a/orc8r/cloud/deploy/terraform/orc8r-helm-aws/examples/online-upgrade/README.md b/orc8r/cloud/deploy/terraform/orc8r-helm-aws/examples/online-upgrade/README.md index 77111cc6cad0..a85c0e47fa7a 100644 --- a/orc8r/cloud/deploy/terraform/orc8r-helm-aws/examples/online-upgrade/README.md +++ b/orc8r/cloud/deploy/terraform/orc8r-helm-aws/examples/online-upgrade/README.md @@ -48,8 +48,6 @@ https://magma.github.io/magma. | helm\_user | Username for your Helm repo | `string` | n/a | yes | | metrics\_worker\_subnet\_id | Subnet ID of the metrics worker instance. Find this in the EC2 console (the instance will have the tag orc8r-node-type: orc8r-prometheus-node). | `string` | n/a | yes | | new\_deployment\_name | New name for the v1.1.0 Helm deployment. This must be different than your old v1.0 deployment (which was probably 'orc8r') | `string` | n/a | yes | -| nms\_db\_configuration | Configuration of the NMS MySQL instance. This should match the v1.0 Terraform. |
object({
identifier = string
storage_gb = number
engine_version = string
instance_class = string
})
|
{
"engine_version": "5.7",
"identifier": "nmsdb",
"instance_class": "db.m4.large",
"storage_gb": 16
}
| no | -| nms\_db\_password | Password for the NMS MySQL instance. This should match the v1.0 Terraform. | `string` | n/a | yes | | orc8r\_chart\_version | Chart version for the Helm deployment | `string` | `"1.4.21"` | no | | orc8r\_container\_tag | Container tag to deploy | `string` | n/a | yes | | orc8r\_controller\_replicas | How many controller pod replicas to deploy | `number` | `2` | no | diff --git a/orc8r/cloud/deploy/terraform/orc8r-helm-aws/examples/online-upgrade/main.tf b/orc8r/cloud/deploy/terraform/orc8r-helm-aws/examples/online-upgrade/main.tf index d7930b78a339..a27e1346fc4d 100644 --- a/orc8r/cloud/deploy/terraform/orc8r-helm-aws/examples/online-upgrade/main.tf +++ b/orc8r/cloud/deploy/terraform/orc8r-helm-aws/examples/online-upgrade/main.tf @@ -29,11 +29,6 @@ module orc8r { orc8r_db_engine_version = var.orc8r_db_configuration.engine_version orc8r_db_instance_class = var.orc8r_db_configuration.instance_class orc8r_db_password = var.orc8r_db_password - nms_db_identifier = var.nms_db_configuration.identifier - nms_db_storage_gb = var.nms_db_configuration.storage_gb - nms_db_engine_version = var.nms_db_configuration.engine_version - nms_db_instance_class = var.nms_db_configuration.instance_class - nms_db_password = var.nms_db_password secretsmanager_orc8r_secret = var.secretsmanager_secret_name @@ -123,11 +118,6 @@ module orc8r-app { orc8r_db_user = module.orc8r.orc8r_db_user orc8r_db_pass = module.orc8r.orc8r_db_pass - nms_db_host = module.orc8r.nms_db_host - nms_db_name = module.orc8r.nms_db_name - nms_db_user = module.orc8r.nms_db_user - nms_db_pass = module.orc8r.nms_db_pass - docker_registry = var.docker_registry docker_user = var.docker_user docker_pass = var.docker_pass diff --git a/orc8r/cloud/deploy/terraform/orc8r-helm-aws/examples/online-upgrade/variables.tf b/orc8r/cloud/deploy/terraform/orc8r-helm-aws/examples/online-upgrade/variables.tf index 5be3a3def00a..a7fbca7a8911 100644 --- a/orc8r/cloud/deploy/terraform/orc8r-helm-aws/examples/online-upgrade/variables.tf +++ b/orc8r/cloud/deploy/terraform/orc8r-helm-aws/examples/online-upgrade/variables.tf @@ -51,27 +51,6 @@ variable "orc8r_db_password" { type = string } -variable "nms_db_configuration" { - description = "Configuration of the NMS MySQL instance. This should match the v1.0 Terraform." - type = object({ - identifier = string - storage_gb = number - engine_version = string - instance_class = string - }) - default = { - identifier = "nmsdb" - storage_gb = 16 - engine_version = "5.7" - instance_class = "db.m4.large" - } -} - -variable "nms_db_password" { - description = "Password for the NMS MySQL instance. This should match the v1.0 Terraform." - type = string -} - variable "secretsmanager_secret_name" { description = "Name for the Secretsmanager secret that the orc8r-aws module will create." type = string diff --git a/orc8r/cloud/deploy/terraform/orc8r-helm-aws/examples/remote/main.tf b/orc8r/cloud/deploy/terraform/orc8r-helm-aws/examples/remote/main.tf index bd7a1e988b3c..f122f303da97 100644 --- a/orc8r/cloud/deploy/terraform/orc8r-helm-aws/examples/remote/main.tf +++ b/orc8r/cloud/deploy/terraform/orc8r-helm-aws/examples/remote/main.tf @@ -47,7 +47,6 @@ resource "aws_dynamodb_table" "terraform_locks" { # This secretsmanager secret needs to be manually created and populated in the # AWS console. For this example, you would set the following key-value pairs: -# nms_db_pass # orc8r_db_pass # docker_registry # docker_user @@ -69,7 +68,6 @@ module "orc8r" { region = local.region - nms_db_password = jsondecode(data.aws_secretsmanager_secret_version.root_secrets.secret_string)["nms_db_pass"] orc8r_db_password = jsondecode(data.aws_secretsmanager_secret_version.root_secrets.secret_string)["orc8r_db_pass"] secretsmanager_orc8r_secret = "orc8r-secrets" orc8r_domain_name = "orc8r.example.com" @@ -118,11 +116,6 @@ module "orc8r-app" { orc8r_db_user = module.orc8r.orc8r_db_user orc8r_db_pass = module.orc8r.orc8r_db_pass - nms_db_host = module.orc8r.nms_db_host - nms_db_name = module.orc8r.nms_db_name - nms_db_user = module.orc8r.nms_db_user - nms_db_pass = module.orc8r.nms_db_pass - docker_registry = jsondecode(data.aws_secretsmanager_secret_version.root_secrets.secret_string)["docker_registry"] docker_user = jsondecode(data.aws_secretsmanager_secret_version.root_secrets.secret_string)["docker_user"] docker_pass = jsondecode(data.aws_secretsmanager_secret_version.root_secrets.secret_string)["docker_pass"] diff --git a/orc8r/cloud/deploy/terraform/orc8r-helm-aws/main.tf b/orc8r/cloud/deploy/terraform/orc8r-helm-aws/main.tf index 201679b93a1a..396566ed5af8 100644 --- a/orc8r/cloud/deploy/terraform/orc8r-helm-aws/main.tf +++ b/orc8r/cloud/deploy/terraform/orc8r-helm-aws/main.tf @@ -40,11 +40,6 @@ resource "helm_release" "orc8r" { name = "controller.spec.database.pass" value = var.orc8r_db_pass } - - set_sensitive { - name = "nms.magmalte.env.mysql_pass" - value = var.nms_db_pass - } } resource "helm_release" "lte-orc8r" { @@ -69,11 +64,6 @@ resource "helm_release" "lte-orc8r" { name = "controller.spec.database.pass" value = var.orc8r_db_pass } - - set_sensitive { - name = "nms.magmalte.env.mysql_pass" - value = var.nms_db_pass - } } resource "helm_release" "feg-orc8r" { @@ -97,11 +87,6 @@ resource "helm_release" "feg-orc8r" { name = "controller.spec.database.pass" value = var.orc8r_db_pass } - - set_sensitive { - name = "nms.magmalte.env.mysql_pass" - value = var.nms_db_pass - } } resource "helm_release" "cwf-orc8r" { @@ -121,11 +106,6 @@ resource "helm_release" "cwf-orc8r" { name = "controller.spec.database.pass" value = var.orc8r_db_pass } - - set_sensitive { - name = "nms.magmalte.env.mysql_pass" - value = var.nms_db_pass - } } @@ -147,11 +127,6 @@ resource "helm_release" "fbinternal-orc8r" { name = "controller.spec.database.pass" value = var.orc8r_db_pass } - - set_sensitive { - name = "nms.magmalte.env.mysql_pass" - value = var.nms_db_pass - } } resource "helm_release" "wifi-orc8r" { @@ -172,11 +147,6 @@ resource "helm_release" "wifi-orc8r" { name = "controller.spec.database.pass" value = var.orc8r_db_pass } - - set_sensitive { - name = "nms.magmalte.env.mysql_pass" - value = var.nms_db_pass - } } @@ -209,9 +179,6 @@ data "template_file" "orc8r_values" { orc8r_db_user = var.orc8r_db_user deploy_nms = var.deploy_nms - nms_db_name = var.nms_db_name - nms_db_host = var.nms_db_host - nms_db_user = var.nms_db_user metrics_pvc_promcfg = kubernetes_persistent_volume_claim.storage["promcfg"].metadata.0.name metrics_pvc_promdata = kubernetes_persistent_volume_claim.storage["promdata"].metadata.0.name diff --git a/orc8r/cloud/deploy/terraform/orc8r-helm-aws/templates/orc8r-values.tpl b/orc8r/cloud/deploy/terraform/orc8r-helm-aws/templates/orc8r-values.tpl index 1ec4d8439b9c..ab2c0d28363d 100644 --- a/orc8r/cloud/deploy/terraform/orc8r-helm-aws/templates/orc8r-values.tpl +++ b/orc8r/cloud/deploy/terraform/orc8r-helm-aws/templates/orc8r-values.tpl @@ -183,9 +183,9 @@ nms: env: api_host: ${api_hostname} - mysql_db: ${nms_db_name} - mysql_host: ${nms_db_host} - mysql_user: ${nms_db_user} + mysql_db: ${orc8r_db_name} + mysql_host: ${orc8r_db_host} + mysql_user: ${orc8r_db_user} grafana_address: ${user_grafana_hostname} nginx: diff --git a/orc8r/cloud/deploy/terraform/orc8r-helm-aws/variables.tf b/orc8r/cloud/deploy/terraform/orc8r-helm-aws/variables.tf index c490b49ca40f..5ee930d521a3 100644 --- a/orc8r/cloud/deploy/terraform/orc8r-helm-aws/variables.tf +++ b/orc8r/cloud/deploy/terraform/orc8r-helm-aws/variables.tf @@ -112,21 +112,6 @@ variable "orc8r_db_user" { type = string } -variable "nms_db_name" { - description = "DB name for NMS database connection." - type = string -} - -variable "nms_db_host" { - description = "DB hostname for NMS database connection." - type = string -} - -variable "nms_db_user" { - description = "DB username for NMS database connection." - type = string -} - ############################################################################## # Helm configuration ############################################################################## @@ -272,11 +257,6 @@ variable "orc8r_db_pass" { type = string } -variable "nms_db_pass" { - description = "NMS DB password." - type = string -} - variable "docker_registry" { description = "Docker registry to pull Orchestrator containers from." type = string From 065c757560babf622eb77868a552a5ee62379b11 Mon Sep 17 00:00:00 2001 From: Alex Rodriguez Date: Fri, 2 Apr 2021 15:14:54 -0700 Subject: [PATCH 14/91] [lte][agw] Add hash-based validation to reduce redis writes operations on MME (#5868) * Redis client write perf improvements Signed-off-by: Alex Rodriguez * Removing state conversion for static PCC rules on SPGW Signed-off-by: Alex Rodriguez * Adding hash based versioning check for redis writes Signed-off-by: Alex Rodriguez --- .../c/oai/common/redis_utils/redis_client.cpp | 23 ++------ .../c/oai/common/redis_utils/redis_client.h | 22 +++---- lte/gateway/c/oai/include/state_manager.h | 58 ++++++++++++++----- .../c/oai/tasks/ngap/ngap_state_manager.cpp | 12 +++- .../c/oai/tasks/ngap/ngap_state_manager.h | 1 + .../c/oai/tasks/s1ap/s1ap_state_manager.cpp | 16 ++++- .../c/oai/tasks/s1ap/s1ap_state_manager.h | 3 +- 7 files changed, 84 insertions(+), 51 deletions(-) diff --git a/lte/gateway/c/oai/common/redis_utils/redis_client.cpp b/lte/gateway/c/oai/common/redis_utils/redis_client.cpp index 342be88906a3..490e352d94b1 100644 --- a/lte/gateway/c/oai/common/redis_utils/redis_client.cpp +++ b/lte/gateway/c/oai/common/redis_utils/redis_client.cpp @@ -84,26 +84,11 @@ std::string RedisClient::read(const std::string& key) { return db_read_reply.as_string(); } -int RedisClient::write_proto(const std::string& key, const Message& proto_msg) { - std::string inner_val; - if (serialize(proto_msg, inner_val) != RETURNok) { - return RETURNerror; - } - - // Read the existing key for current version if it exists. - // Bump the version number of the wrapper and set its wrapped message. +int RedisClient::write_proto_str( + const std::string& key, const std::string& proto_msg, uint64_t version) { orc8r::RedisState wrapper_proto = orc8r::RedisState(); - try { - if (key_exists(key)) { - if (read_redis_state(key, wrapper_proto) != RETURNok) { - return RETURNerror; - } - } - } catch (const std::runtime_error& e) { - return RETURNerror; - } - wrapper_proto.set_serialized_msg(inner_val); - wrapper_proto.set_version(wrapper_proto.version() + 1); + wrapper_proto.set_serialized_msg(proto_msg); + wrapper_proto.set_version(version); std::string str_value; if (serialize(wrapper_proto, str_value) != RETURNok) { diff --git a/lte/gateway/c/oai/common/redis_utils/redis_client.h b/lte/gateway/c/oai/common/redis_utils/redis_client.h index f9d8351e7931..306e3dbd63db 100644 --- a/lte/gateway/c/oai/common/redis_utils/redis_client.h +++ b/lte/gateway/c/oai/common/redis_utils/redis_client.h @@ -57,10 +57,20 @@ class RedisClient { * Writes a protobuf object to redis * @param key * @param proto_msg + * @param version * @return response code of operation */ - int write_proto( - const std::string& key, const google::protobuf::Message& proto_msg); + int write_proto_str( + const std::string& key, const std::string& proto_msg, uint64_t version); + + /** + * Converts protobuf Message and parses it to string + * @param proto_msg + * @param str_to_serialize + */ + int serialize( + const google::protobuf::Message& proto_msg, + std::string& str_to_serialize); /** * Reads value from redis mapped to key and returns proto object @@ -95,14 +105,6 @@ class RedisClient { */ bool key_exists(const std::string& key); - /** - * Converts protobuf Message and parses it to string - * @param proto_msg - * @param str_to_serialize - */ - int serialize( - const google::protobuf::Message& proto_msg, - std::string& str_to_serialize); /** * Takes a string and parses it to protobuf Message * @param proto_msg diff --git a/lte/gateway/c/oai/include/state_manager.h b/lte/gateway/c/oai/include/state_manager.h index 2afe949a1d31..2e62389fbc52 100644 --- a/lte/gateway/c/oai/include/state_manager.h +++ b/lte/gateway/c/oai/include/state_manager.h @@ -130,15 +130,22 @@ class StateManager { if (persist_state_enabled) { ProtoType state_proto = ProtoType(); StateConverter::state_to_proto(state_cache_p, &state_proto); - - if (redis_client->write_proto(table_key, state_proto) != RETURNok) { - OAILOG_ERROR(log_task, "Failed to write state to db"); - return; + std::string proto_str; + redis_client->serialize(state_proto, proto_str); + std::size_t new_hash = std::hash{}(proto_str); + + if (new_hash != this->task_state_hash) { + if (redis_client->write_proto_str( + table_key, proto_str, this->task_state_version) != RETURNok) { + OAILOG_ERROR(log_task, "Failed to write state to db"); + return; + } + OAILOG_DEBUG(log_task, "Finished writing state"); + this->task_state_version++; + this->state_dirty = false; + this->task_state_hash = new_hash; } - OAILOG_DEBUG(log_task, "Finished writing state"); } - - this->state_dirty = false; } virtual void write_ue_state_to_db( @@ -147,17 +154,27 @@ class StateManager { is_initialized, "StateManager init() function should be called to initialize state"); + std::string proto_str; ProtoUe ue_proto = ProtoUe(); StateConverter::ue_to_proto(ue_context, &ue_proto); - std::string key = IMSI_PREFIX + imsi_str + ":" + task_name; - if (redis_client->write_proto(key, ue_proto) != RETURNok) { - OAILOG_ERROR( - log_task, "Failed to write UE state to db for IMSI %s", - imsi_str.c_str()); - return; + redis_client->serialize(ue_proto, proto_str); + std::size_t new_hash = std::hash{}(proto_str); + + if (new_hash != this->ue_state_hash) { + std::string key = IMSI_PREFIX + imsi_str + ":" + task_name; + if (redis_client->write_proto_str(key, proto_str, ue_state_version) != + RETURNok) { + OAILOG_ERROR( + log_task, "Failed to write UE state to db for IMSI %s", + imsi_str.c_str()); + return; + } + + this->ue_state_version++; + this->ue_state_hash = new_hash; + OAILOG_DEBUG( + log_task, "Finished writing UE state for IMSI %s", imsi_str.c_str()); } - OAILOG_DEBUG( - log_task, "Finished writing UE state for IMSI %s", imsi_str.c_str()); } std::string get_imsi_str(imsi64_t imsi64) { @@ -201,6 +218,10 @@ class StateManager { is_initialized(false), state_dirty(false), persist_state_enabled(false), + task_state_hash(0), + ue_state_hash(0), + task_state_version(0), + ue_state_version(0), log_task(LOG_UTIL) {} virtual ~StateManager() = default; @@ -225,10 +246,15 @@ class StateManager { // Flag for check asserting if the state has been initialized. bool is_initialized; // Flag for check asserting that write should be done after read. - // TODO: Convert this to state versioning variable bool state_dirty; // Flag for enabling writing and reading to db. bool persist_state_enabled; + // State version counters for task and ue context + uint64_t task_state_version; + uint64_t ue_state_version; + // Last written hash values for task and ue context + std::size_t task_state_hash; + std::size_t ue_state_hash; protected: std::string table_key; diff --git a/lte/gateway/c/oai/tasks/ngap/ngap_state_manager.cpp b/lte/gateway/c/oai/tasks/ngap/ngap_state_manager.cpp index 65eb79ddb689..2771c4f8b2da 100644 --- a/lte/gateway/c/oai/tasks/ngap/ngap_state_manager.cpp +++ b/lte/gateway/c/oai/tasks/ngap/ngap_state_manager.cpp @@ -33,7 +33,8 @@ using magma::lte::oai::Ngap_UeDescription; namespace magma5g { -NgapStateManager::NgapStateManager() : max_gnbs_(0), max_ues_(0) {} +NgapStateManager::NgapStateManager() + : max_gnbs_(0), max_ues_(0), ngap_imsi_map_hash_(0) {} NgapStateManager::~NgapStateManager() { free_state(); @@ -107,7 +108,6 @@ void NgapStateManager::free_state() { ht_rc = hashtable_ts_get( &state_cache_p->gnbs, (hash_key_t) assoc_id, (void**) &gnb); AssertFatal(ht_rc == HASH_TABLE_OK, "eNB UE id not in assoc_id"); - AssertFatal(ht_rc == HASH_TABLE_OK, "eNB UE id not in assoc_id"); } FREE_HASHTABLE_KEY_ARRAY(keys); } @@ -182,7 +182,13 @@ void NgapStateManager::put_ngap_imsi_map() { } oai::NgapImsiMap imsi_proto = oai::NgapImsiMap(); NgapStateConverter::ngap_imsi_map_to_proto(ngap_imsi_map_, &imsi_proto); - redis_client->write_proto(NGAP_IMSI_MAP_TABLE_NAME, imsi_proto); + std::string proto_msg; + redis_client->serialize(imsi_proto, proto_msg); + std::size_t new_hash = std::hash{}(proto_msg); + if (new_hash != this->ngap_imsi_map_hash_) { + redis_client->write_proto_str(NGAP_IMSI_MAP_TABLE_NAME, proto_msg, 0); + this->ngap_imsi_map_hash_ = new_hash; + } } } // namespace magma5g diff --git a/lte/gateway/c/oai/tasks/ngap/ngap_state_manager.h b/lte/gateway/c/oai/tasks/ngap/ngap_state_manager.h index 11bb94b3fd23..303126810a7b 100644 --- a/lte/gateway/c/oai/tasks/ngap/ngap_state_manager.h +++ b/lte/gateway/c/oai/tasks/ngap/ngap_state_manager.h @@ -110,5 +110,6 @@ class NgapStateManager uint32_t max_ues_; uint32_t max_gnbs_; ngap_imsi_map_t* ngap_imsi_map_; + std::size_t ngap_imsi_map_hash_; }; } // namespace magma5g diff --git a/lte/gateway/c/oai/tasks/s1ap/s1ap_state_manager.cpp b/lte/gateway/c/oai/tasks/s1ap/s1ap_state_manager.cpp index 6cec1082f896..3cf0a24a1a56 100644 --- a/lte/gateway/c/oai/tasks/s1ap/s1ap_state_manager.cpp +++ b/lte/gateway/c/oai/tasks/s1ap/s1ap_state_manager.cpp @@ -28,7 +28,11 @@ using magma::lte::oai::UeDescription; namespace magma { namespace lte { -S1apStateManager::S1apStateManager() : max_ues_(0), max_enbs_(0) {} +S1apStateManager::S1apStateManager() + : max_ues_(0), + max_enbs_(0), + s1ap_imsi_map_hash_(0), + s1ap_imsi_map_(nullptr) {} S1apStateManager::~S1apStateManager() { free_state(); @@ -194,7 +198,15 @@ void S1apStateManager::write_s1ap_imsi_map_to_db() { } oai::S1apImsiMap imsi_proto = oai::S1apImsiMap(); S1apStateConverter::s1ap_imsi_map_to_proto(s1ap_imsi_map_, &imsi_proto); - redis_client->write_proto(S1AP_IMSI_MAP_TABLE_NAME, imsi_proto); + std::string proto_msg; + redis_client->serialize(imsi_proto, proto_msg); + std::size_t new_hash = std::hash{}(proto_msg); + + // s1ap_imsi_map is not state service synced, so version will not be updated + if (new_hash != this->s1ap_imsi_map_hash_) { + redis_client->write_proto_str(S1AP_IMSI_MAP_TABLE_NAME, proto_msg, 0); + this->s1ap_imsi_map_hash_ = new_hash; + } } } // namespace lte diff --git a/lte/gateway/c/oai/tasks/s1ap/s1ap_state_manager.h b/lte/gateway/c/oai/tasks/s1ap/s1ap_state_manager.h index 3abfc5e9c498..2ed0652797ac 100644 --- a/lte/gateway/c/oai/tasks/s1ap/s1ap_state_manager.h +++ b/lte/gateway/c/oai/tasks/s1ap/s1ap_state_manager.h @@ -89,7 +89,7 @@ class S1apStateManager private: S1apStateManager(); - ~S1apStateManager(); + ~S1apStateManager() override; /** * Allocates new s1ap_state_t struct and its properties @@ -102,6 +102,7 @@ class S1apStateManager uint32_t max_ues_; uint32_t max_enbs_; s1ap_imsi_map_t* s1ap_imsi_map_; + std::size_t s1ap_imsi_map_hash_; }; } // namespace lte } // namespace magma From 0b0118b64a7b656f0ee4a65f244771247691ca47 Mon Sep 17 00:00:00 2001 From: Pravin Shelar Date: Sun, 4 Apr 2021 22:10:34 +0530 Subject: [PATCH 15/91] AGW: GTP driver: improve debuggability of datapath. (#5890) Signed-off-by: Pravin B Shelar --- .../kernel-4.9/gtp-v4.9-backport/gtp.c | 32 +++++++++---------- 1 file changed, 15 insertions(+), 17 deletions(-) diff --git a/third_party/gtp_ovs/kernel-4.9/gtp-v4.9-backport/gtp.c b/third_party/gtp_ovs/kernel-4.9/gtp-v4.9-backport/gtp.c index 8c8c950f1727..defe23766a94 100644 --- a/third_party/gtp_ovs/kernel-4.9/gtp-v4.9-backport/gtp.c +++ b/third_party/gtp_ovs/kernel-4.9/gtp-v4.9-backport/gtp.c @@ -235,11 +235,9 @@ static int gtp_rx(struct gtp_dev *gtp, struct sk_buff *skb, !net_eq(sock_net(sk), dev_net(gtp->dev)))) return -1; - netdev_dbg(gtp->dev, "forwarding packet from GGSN to uplink\n"); - if (tun_dst) { skb_dst_set(skb, (struct dst_entry *)tun_dst); - netdev_dbg(gtp->dev, "attaching metadata_dst to skb\n"); + netdev_dbg(gtp->dev, "rcv: attaching metadata_dst to skb\n"); } /* Now that the UDP and the GTP header have been removed, set up the @@ -248,19 +246,19 @@ static int gtp_rx(struct gtp_dev *gtp, struct sk_buff *skb, */ skb_reset_network_header(skb); if (check_header(skb, sizeof(struct iphdr))) { - netdev_dbg(gtp->dev, "inner pkt: could not parse"); + netdev_dbg(gtp->dev, "rcv: inner pkt: could not parse"); goto rx_err; } iph = ip_hdr(skb); if (iph->version == 4) { - netdev_dbg(gtp->dev, "inner pkt: ipv4"); + netdev_dbg(gtp->dev, "rcv: inner pkt: ipv4, len %d", skb->len); skb->protocol = htons(ETH_P_IP); } else if (iph->version == 6) { - netdev_dbg(gtp->dev, "inner pkt: ipv6"); + netdev_dbg(gtp->dev, "rcv: inner pkt: ipv6, len %d", skb->len); skb->protocol = htons(ETH_P_IPV6); } else { - netdev_dbg(gtp->dev, "inner pkt error: Unknown type"); + netdev_dbg(gtp->dev, "rcv: inner pkt error: Unknown type"); goto rx_err; } skb->dev = gtp->dev; @@ -427,8 +425,6 @@ static int gtp_encap_recv(struct sock *sk, struct sk_buff *skb) if (!gtp) return 1; - netdev_dbg(gtp->dev, "encap_recv sk=%p\n", sk); - switch (udp_sk(sk)->encap_type) { case UDP_ENCAP_GTP0: netdev_dbg(gtp->dev, "received GTP0 packet\n"); @@ -616,8 +612,8 @@ static int gtp_build_skb_ip4(struct sk_buff *skb, struct net_device *dev, if (info->key.tun_flags & TUNNEL_OAM) set_qfi = 5; - netdev_dbg(dev, "flow-based GTP1U encap: tunnel id %d\n", - be32_to_cpu(tun_id)); + netdev_dbg(dev, "xmit: flow-based GTP1U encap: tunnel id %x len %d\n", + be32_to_cpu(tun_id), skb->len); } else { struct iphdr *iph; @@ -635,7 +631,7 @@ static int gtp_build_skb_ip4(struct sk_buff *skb, struct net_device *dev, pctx = ipv4_pdp_find(gtp, iph->daddr); if (!pctx) { - netdev_dbg(dev, "no PDP ctx found for %pI4, skip\n", + netdev_dbg(dev, "xmit: no PDP ctx found for %pI4, skip\n", &iph->daddr); return -ENOENT; } @@ -648,19 +644,19 @@ static int gtp_build_skb_ip4(struct sk_buff *skb, struct net_device *dev, sk = pctx->sk; tos = iph->tos; df = iph->frag_off; - netdev_dbg(dev, "gtp -> IP src: %pI4 dst: %pI4\n", + netdev_dbg(dev, "xmit: gtp -> IP src: %pI4 dst: %pI4\n", &iph->saddr, &iph->daddr); } rt = ip4_route_output_gtp(&fl4, sk, daddr, saddr); if (IS_ERR(rt)) { - netdev_dbg(dev, "no route to SSGN %pI4\n", &daddr); + netdev_dbg(dev, "xmit: no route to SSGN %pI4\n", &daddr); dev->stats.tx_carrier_errors++; goto err; } if (rt->dst.dev == dev) { - netdev_dbg(dev, "circular route to SSGN %pI4\n", &daddr); + netdev_dbg(dev, "xmit: circular route to SSGN %pI4\n", &daddr); dev->stats.collisions++; goto err_rt; } @@ -687,7 +683,7 @@ static int gtp_build_skb_ip4(struct sk_buff *skb, struct net_device *dev, if (!skb_is_gso(skb) && (df & htons(IP_DF)) && mtu < skb->len) { - netdev_dbg(dev, "packet too big, fragmentation needed\n"); + netdev_dbg(dev, "xmit: packet too big, fragmentation needed\n"); memset(IPCB(skb), 0, sizeof(*IPCB(skb))); icmp_send(skb, ICMP_DEST_UNREACH, ICMP_FRAG_NEEDED, htonl(mtu)); @@ -734,6 +730,7 @@ static netdev_tx_t gtp_dev_xmit(struct sk_buff *skb, struct net_device *dev) err = gtp_build_skb_ip4(skb, dev, &pktinfo); break; default: + netdev_dbg(dev, "xmit: invalid proto %x", proto); err = -EOPNOTSUPP; break; } @@ -750,6 +747,7 @@ static netdev_tx_t gtp_dev_xmit(struct sk_buff *skb, struct net_device *dev) pktinfo.gtph_port, pktinfo.gtph_port, false, false); + netdev_dbg(dev, "xmit: sent src: %pI4 dst: %pI4\n", &pktinfo.fl4.saddr, &pktinfo.fl4.daddr); return NETDEV_TX_OK; tx_err: dev->stats.tx_errors++; @@ -1705,7 +1703,7 @@ static int __init gtp_init(void) if (err < 0) goto unreg_genl_family; - pr_info("Flow-based GTP module loaded (pdp ctx size %zd bytes) : v8h\n", + pr_info("Flow-based GTP module loaded (pdp ctx size %zd bytes) : v9\n", sizeof(struct pdp_ctx)); return 0; From 5bd0a76a7ac6c2f0fe58f20a9fcac6e79fa8f21f Mon Sep 17 00:00:00 2001 From: Scott Moeller Date: Mon, 5 Apr 2021 10:54:43 -0400 Subject: [PATCH 16/91] [IWYU] Non-Functional fixup of or8r c includes (#5895) Signed-off-by: Scott Harrison Moeller Applied IWYU tooling to the `orc8r/gateway/c` sub-targets and corrected all findings (missing and extraneous include headers). This to work towards #4868 which regularly causes build failures as we migrate or update includes / build infrastructure. Co-authored-by: Scott Harrison Moeller --- .../c/oai/common/redis_utils/redis_client.cpp | 1 + .../lib/mobility_client/MobilityClientAPI.cpp | 1 + .../c/session_manager/GrpcMagmaUtils.cpp | 10 +++-- .../c/session_manager/GrpcMagmaUtils.h | 3 +- .../c/session_manager/RedisStoreClient.cpp | 24 ++++++++++- .../c/session_manager/RedisStoreClient.h | 13 +++--- .../c/session_manager/SpgwServiceClient.cpp | 16 +++++++- .../c/session_manager/SpgwServiceClient.h | 21 +++++++--- .../c/common/async_grpc/GRPCReceiver.cpp | 4 +- .../c/common/async_grpc/GRPCReceiver.h | 12 ++++-- .../gateway/c/common/config/MConfigLoader.cpp | 15 ++++--- orc8r/gateway/c/common/config/MConfigLoader.h | 4 +- .../c/common/config/ServiceConfigLoader.cpp | 12 +++--- .../c/common/config/ServiceConfigLoader.h | 4 +- orc8r/gateway/c/common/config/YAMLUtils.cpp | 6 +-- orc8r/gateway/c/common/config/YAMLUtils.h | 3 +- .../c/common/datastore/Serializers.cpp | 4 +- .../gateway/c/common/datastore/Serializers.h | 6 ++- .../gateway/c/common/eventd/EventdClient.cpp | 21 ++++++---- orc8r/gateway/c/common/eventd/EventdClient.h | 15 +++---- .../c/common/policydb/PolicyLoader.cpp | 20 +++++++-- .../gateway/c/common/policydb/PolicyLoader.h | 10 +++-- .../c/common/service303/MagmaService.cpp | 41 +++++++++++-------- .../c/common/service303/MagmaService.h | 27 +++++++++--- .../c/common/service303/MetricsHelpers.cpp | 3 +- .../c/common/service303/MetricsHelpers.h | 3 +- .../c/common/service303/MetricsSingleton.cpp | 9 ++++ .../c/common/service303/MetricsSingleton.h | 20 ++++++--- .../c/common/service303/ProcFileUtils.cpp | 7 +--- .../c/common/service303/ProcFileUtils.h | 3 +- .../ServiceRegistrySingleton.cpp | 14 ++++--- .../ServiceRegistrySingleton.h | 10 +++-- 32 files changed, 243 insertions(+), 119 deletions(-) diff --git a/lte/gateway/c/oai/common/redis_utils/redis_client.cpp b/lte/gateway/c/oai/common/redis_utils/redis_client.cpp index 490e352d94b1..700e7fbd8b43 100644 --- a/lte/gateway/c/oai/common/redis_utils/redis_client.cpp +++ b/lte/gateway/c/oai/common/redis_utils/redis_client.cpp @@ -28,6 +28,7 @@ extern "C" { #endif #include "ServiceConfigLoader.h" +#include // IWYU pragma: keep using google::protobuf::Message; diff --git a/lte/gateway/c/oai/lib/mobility_client/MobilityClientAPI.cpp b/lte/gateway/c/oai/lib/mobility_client/MobilityClientAPI.cpp index dc1483f624cb..75d8d85708be 100644 --- a/lte/gateway/c/oai/lib/mobility_client/MobilityClientAPI.cpp +++ b/lte/gateway/c/oai/lib/mobility_client/MobilityClientAPI.cpp @@ -28,6 +28,7 @@ #include "spgw_types.h" #include "intertask_interface.h" #include "common_types.h" +#include "log.h" #include "MobilityServiceClient.h" diff --git a/lte/gateway/c/session_manager/GrpcMagmaUtils.cpp b/lte/gateway/c/session_manager/GrpcMagmaUtils.cpp index cede7be897f8..124271405e24 100644 --- a/lte/gateway/c/session_manager/GrpcMagmaUtils.cpp +++ b/lte/gateway/c/session_manager/GrpcMagmaUtils.cpp @@ -11,10 +11,14 @@ * limitations under the License. */ -#include -#include "magma_logging.h" -#include #include "GrpcMagmaUtils.h" +#include // for COMPACT_GOOGLE_LOG_INFO, Log... +#include // for Descriptor +#include // for Message +#include // for getenv, NULL +#include // for operator<<, basic_ostream +#include // for string, operator<<, operator== +#include "magma_logging.h" // for MINFO, MLOG #define MAGMA_PRINT_GRPC_PAYLOAD "MAGMA_PRINT_GRPC_PAYLOAD" diff --git a/lte/gateway/c/session_manager/GrpcMagmaUtils.h b/lte/gateway/c/session_manager/GrpcMagmaUtils.h index ec576f96efe5..e1df2a779ba1 100644 --- a/lte/gateway/c/session_manager/GrpcMagmaUtils.h +++ b/lte/gateway/c/session_manager/GrpcMagmaUtils.h @@ -12,7 +12,8 @@ */ #pragma once -#include "GRPCReceiver.h" +#include // for string +namespace google { namespace protobuf { class Message; } } std::string get_env_var(std::string const& key); diff --git a/lte/gateway/c/session_manager/RedisStoreClient.cpp b/lte/gateway/c/session_manager/RedisStoreClient.cpp index a4d7cc0ce036..dae57340e762 100644 --- a/lte/gateway/c/session_manager/RedisStoreClient.cpp +++ b/lte/gateway/c/session_manager/RedisStoreClient.cpp @@ -11,9 +11,29 @@ * limitations under the License. */ -#include "SessionState.h" #include "RedisStoreClient.h" -#include "magma_logging.h" +#include // for operator<<, StringPiece +#include // for dynamic::dynamic, dynamic::~dyn... +#include // for dynamic +#include // for parseJson, toJson +#include // for COMPACT_GOOGLE_LOG_INFO, LogMes... +#include // for size_t +#include // for uint32_t +#include // IWYU pragma: keep +#include // for max +#include // for client, client::connect_state +#include // for reply +#include // for redis_error +#include // for future +#include // for operator<<, basic_ostream, size_t +#include // for _Node_iterator, unordered_map +#include // for move, pair +#include // for vector +#include "ServiceConfigLoader.h" // for ServiceConfigLoader +#include "SessionState.h" // for SessionState +#include "StoredState.h" // for deserialize_stored_session, ser... +#include "magma_logging.h" // for MERROR, MLOG +namespace magma { class StaticRuleStore; } namespace magma { namespace lte { diff --git a/lte/gateway/c/session_manager/RedisStoreClient.h b/lte/gateway/c/session_manager/RedisStoreClient.h index a60f21b2d2f3..0f572bad4866 100644 --- a/lte/gateway/c/session_manager/RedisStoreClient.h +++ b/lte/gateway/c/session_manager/RedisStoreClient.h @@ -14,13 +14,12 @@ #pragma once #include -#include -#include -#include - -#include "StoreClient.h" -#include "StoredState.h" -#include "ServiceConfigLoader.h" +#include // IWYU pragma: keep +#include // for shared_ptr +#include // for set +#include // for string +#include "StoreClient.h" // for SessionMap, SessionVector, StoreClient +namespace magma { class StaticRuleStore; } namespace magma { namespace lte { diff --git a/lte/gateway/c/session_manager/SpgwServiceClient.cpp b/lte/gateway/c/session_manager/SpgwServiceClient.cpp index 7dcfea792475..54e1b2131e78 100644 --- a/lte/gateway/c/session_manager/SpgwServiceClient.cpp +++ b/lte/gateway/c/session_manager/SpgwServiceClient.cpp @@ -12,8 +12,20 @@ */ #include "SpgwServiceClient.h" -#include "ServiceRegistrySingleton.h" -#include "magma_logging.h" +#include // for COMPACT_GOOGLE_LOG... +#include // for Channel +#include // for default_delete +#include // for Status +#include // for copy +#include // for uint32_t +#include // for operator<<, basic_... +#include // for move +#include "ServiceRegistrySingleton.h" // for ServiceRegistrySin... +#include "lte/protos/policydb.pb.h" // for RepeatedField, Rep... +#include "lte/protos/spgw_service.grpc.pb.h" // for SpgwService::Stub +#include "lte/protos/spgw_service.pb.h" // for DeleteBearerRequest +#include "magma_logging.h" // for MLOG, MERROR, MINFO +namespace grpc { class Channel; } using grpc::Status; diff --git a/lte/gateway/c/session_manager/SpgwServiceClient.h b/lte/gateway/c/session_manager/SpgwServiceClient.h index 4ccd6336ff25..41ea8f80bf89 100644 --- a/lte/gateway/c/session_manager/SpgwServiceClient.h +++ b/lte/gateway/c/session_manager/SpgwServiceClient.h @@ -12,12 +12,21 @@ */ #pragma once -#include - -#include -#include - -#include "GRPCReceiver.h" +#include // for SpgwService::Stub, Spgw... +#include // for uint32_t +#include // for function +#include // for shared_ptr, unique_ptr +#include // for string +#include // for vector +#include "GRPCReceiver.h" // for GRPCReceiver +#include "lte/protos/subscriberdb.pb.h" // for lte +namespace grpc { class Channel; } +namespace grpc { class Status; } +namespace grpc { class Status; } +namespace magma { namespace lte { class CreateBearerRequest; } } +namespace magma { namespace lte { class CreateBearerResult; } } +namespace magma { namespace lte { class DeleteBearerRequest; } } +namespace magma { namespace lte { class DeleteBearerResult; } } using grpc::Status; diff --git a/orc8r/gateway/c/common/async_grpc/GRPCReceiver.cpp b/orc8r/gateway/c/common/async_grpc/GRPCReceiver.cpp index 5bf897a3d03a..ede96a227f13 100644 --- a/orc8r/gateway/c/common/async_grpc/GRPCReceiver.cpp +++ b/orc8r/gateway/c/common/async_grpc/GRPCReceiver.cpp @@ -10,8 +10,10 @@ * See the License for the specific language governing permissions and * limitations under the License. */ + #include "GRPCReceiver.h" -#include "magma_logging.h" +#include // for operator<<, char_traits +#include "magma_logging.h" // for MLOG namespace magma { diff --git a/orc8r/gateway/c/common/async_grpc/GRPCReceiver.h b/orc8r/gateway/c/common/async_grpc/GRPCReceiver.h index 9c6c151790b1..fdf9a31e9772 100644 --- a/orc8r/gateway/c/common/async_grpc/GRPCReceiver.h +++ b/orc8r/gateway/c/common/async_grpc/GRPCReceiver.h @@ -12,9 +12,15 @@ */ #pragma once -#include -#include -#include +#include // for ClientContext +#include // for CompletionQueue +#include // for Status +#include // for uint32_t +#include // for atomic +#include // for operator+, seconds +#include // for function +#include // for unique_ptr +namespace grpc { template class ClientAsyncResponseReader; } namespace magma { diff --git a/orc8r/gateway/c/common/config/MConfigLoader.cpp b/orc8r/gateway/c/common/config/MConfigLoader.cpp index 60eca8937517..b4e99b9fed1d 100644 --- a/orc8r/gateway/c/common/config/MConfigLoader.cpp +++ b/orc8r/gateway/c/common/config/MConfigLoader.cpp @@ -11,15 +11,14 @@ * limitations under the License. */ -#include -#include -#include -#include // JSON library -#include -#include - #include "MConfigLoader.h" -#include "magma_logging.h" +#include // for Status +#include // for JsonStringToMessage +#include // for getenv +#include // for operator<<, char_traits +#include // for basic_json<>::iterator +#include "magma_logging.h" // for MLOG +namespace google { namespace protobuf { class Message; } } using json = nlohmann::json; diff --git a/orc8r/gateway/c/common/config/MConfigLoader.h b/orc8r/gateway/c/common/config/MConfigLoader.h index c1c915ce1123..982a063118a2 100644 --- a/orc8r/gateway/c/common/config/MConfigLoader.h +++ b/orc8r/gateway/c/common/config/MConfigLoader.h @@ -12,7 +12,9 @@ */ #pragma once -#include +#include // for ifstream +#include // for string +namespace google { namespace protobuf { class Message; } } namespace magma { diff --git a/orc8r/gateway/c/common/config/ServiceConfigLoader.cpp b/orc8r/gateway/c/common/config/ServiceConfigLoader.cpp index 16e46f9387e8..9895cd2c02cb 100644 --- a/orc8r/gateway/c/common/config/ServiceConfigLoader.cpp +++ b/orc8r/gateway/c/common/config/ServiceConfigLoader.cpp @@ -11,12 +11,14 @@ * limitations under the License. */ -#include -#include - #include "ServiceConfigLoader.h" -#include "YAMLUtils.h" -#include "magma_logging.h" +#include // for BadFile +#include // for Node::Node, Node::~Node +#include // for LoadFile +#include // for operator<<, basic_ostream +#include // for allocator, operator+, char_traits +#include "YAMLUtils.h" // for YAMLUtils +#include "magma_logging.h" // for MLOG namespace magma { diff --git a/orc8r/gateway/c/common/config/ServiceConfigLoader.h b/orc8r/gateway/c/common/config/ServiceConfigLoader.h index b7685a69e416..844eb0ed8a02 100644 --- a/orc8r/gateway/c/common/config/ServiceConfigLoader.h +++ b/orc8r/gateway/c/common/config/ServiceConfigLoader.h @@ -12,8 +12,8 @@ */ #pragma once -#include -#include "yaml-cpp/yaml.h" +#include // for Node +#include // for string namespace magma { diff --git a/orc8r/gateway/c/common/config/YAMLUtils.cpp b/orc8r/gateway/c/common/config/YAMLUtils.cpp index d4320c0509a1..adef4fcfc5e0 100644 --- a/orc8r/gateway/c/common/config/YAMLUtils.cpp +++ b/orc8r/gateway/c/common/config/YAMLUtils.cpp @@ -11,10 +11,10 @@ * limitations under the License. */ -#include - #include "YAMLUtils.h" -#include "magma_logging.h" +#include // IWYU pragma: keep +#include // for operator!=, iterator_f... +#include // for string namespace magma { diff --git a/orc8r/gateway/c/common/config/YAMLUtils.h b/orc8r/gateway/c/common/config/YAMLUtils.h index 0d0b429edbf2..f29d5d70f9da 100644 --- a/orc8r/gateway/c/common/config/YAMLUtils.h +++ b/orc8r/gateway/c/common/config/YAMLUtils.h @@ -12,8 +12,7 @@ */ #pragma once -#include -#include "yaml-cpp/yaml.h" +#include // for Node namespace magma { diff --git a/orc8r/gateway/c/common/datastore/Serializers.cpp b/orc8r/gateway/c/common/datastore/Serializers.cpp index 09c3a840e01c..88e636c54c61 100644 --- a/orc8r/gateway/c/common/datastore/Serializers.cpp +++ b/orc8r/gateway/c/common/datastore/Serializers.cpp @@ -10,8 +10,10 @@ * See the License for the specific language governing permissions and * limitations under the License. */ + #include "Serializers.h" -#include +#include // for Message +#include // for RedisState using google::protobuf::Message; using magma::orc8r::RedisState; diff --git a/orc8r/gateway/c/common/datastore/Serializers.h b/orc8r/gateway/c/common/datastore/Serializers.h index 41d5b6bb5149..04b3d526452c 100644 --- a/orc8r/gateway/c/common/datastore/Serializers.h +++ b/orc8r/gateway/c/common/datastore/Serializers.h @@ -12,8 +12,10 @@ */ #pragma once -#include -#include +#include // for uint64_t +#include // for function +#include // for string +namespace google { namespace protobuf { class Message; } } using google::protobuf::Message; namespace magma { diff --git a/orc8r/gateway/c/common/eventd/EventdClient.cpp b/orc8r/gateway/c/common/eventd/EventdClient.cpp index be15cbfbfd13..c3e81ea7a1cb 100644 --- a/orc8r/gateway/c/common/eventd/EventdClient.cpp +++ b/orc8r/gateway/c/common/eventd/EventdClient.cpp @@ -11,17 +11,22 @@ * limitations under the License. */ #include "EventdClient.h" - -#include "ServiceRegistrySingleton.h" - -using grpc::ClientContext; -using grpc::Status; -using magma::orc8r::Event; -using magma::orc8r::EventService; -using magma::orc8r::Void; +#include // for Channel +#include // for default_delete +#include // for move +#include "ServiceRegistrySingleton.h" // for ServiceRegistrySin... +#include "orc8r/protos/common.pb.h" // for Void +#include "orc8r/protos/eventd.grpc.pb.h" // for EventService::Stub +namespace grpc { class ClientContext; } +namespace grpc { class Status; } +namespace magma { namespace orc8r { class Event; } } namespace magma { +using orc8r::Event; +using orc8r::EventService; +using orc8r::Void; + AsyncEventdClient& AsyncEventdClient::getInstance() { static AsyncEventdClient instance; return instance; diff --git a/orc8r/gateway/c/common/eventd/EventdClient.h b/orc8r/gateway/c/common/eventd/EventdClient.h index 0fb36ed76ae1..edc217304a19 100644 --- a/orc8r/gateway/c/common/eventd/EventdClient.h +++ b/orc8r/gateway/c/common/eventd/EventdClient.h @@ -12,13 +12,14 @@ */ #pragma once -#include -#include - -#include -#include - -#include "GRPCReceiver.h" +#include // for EventService::Stub, EventSe... +#include // for uint32_t +#include // for function +#include // for unique_ptr +#include "GRPCReceiver.h" // for GRPCReceiver +namespace grpc { class Status; } +namespace magma { namespace orc8r { class Event; } } +namespace magma { namespace orc8r { class Void; } } using grpc::Status; diff --git a/orc8r/gateway/c/common/policydb/PolicyLoader.cpp b/orc8r/gateway/c/common/policydb/PolicyLoader.cpp index d2f78f6c5c5f..3f6a8321aaa7 100644 --- a/orc8r/gateway/c/common/policydb/PolicyLoader.cpp +++ b/orc8r/gateway/c/common/policydb/PolicyLoader.cpp @@ -10,11 +10,23 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -#include "RedisMap.hpp" -#include "Serializers.h" #include "PolicyLoader.h" -#include "ServiceConfigLoader.h" -#include "magma_logging.h" +#include // for COMPACT_GOOGLE_LOG_INFO, LogMes... +#include // IWYU pragma: keep +#include // for seconds +#include // for client, client::connect_state +#include // for redis_error +#include // for uint32_t +#include // for make_shared, __shared_ptr, shar... +#include // for operator<<, basic_ostream, size_t +#include // for string, char_traits, operator<< +#include // for sleep_for +#include "ObjectMap.h" // for SUCCESS +#include "RedisMap.hpp" // for RedisMap +#include "Serializers.h" // for get_proto_deserializer, get_pro... +#include "ServiceConfigLoader.h" // for ServiceConfigLoader +#include "lte/protos/policydb.pb.h" // for PolicyRule +#include "magma_logging.h" // for MLOG, MERROR, MDEBUG, MINFO namespace magma { diff --git a/orc8r/gateway/c/common/policydb/PolicyLoader.h b/orc8r/gateway/c/common/policydb/PolicyLoader.h index 6d21eef0d939..fe9ad48f2626 100644 --- a/orc8r/gateway/c/common/policydb/PolicyLoader.h +++ b/orc8r/gateway/c/common/policydb/PolicyLoader.h @@ -10,10 +10,12 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -#include -#include -#include -#include +#include // for uint32_t +#include // for atomic +#include // for function +#include // for vector +#include "lte/protos/subscriberdb.pb.h" // for lte +namespace magma { namespace lte { class PolicyRule; } } namespace magma { using namespace lte; diff --git a/orc8r/gateway/c/common/service303/MagmaService.cpp b/orc8r/gateway/c/common/service303/MagmaService.cpp index 868c37554ab5..3c879f38d3d5 100644 --- a/orc8r/gateway/c/common/service303/MagmaService.cpp +++ b/orc8r/gateway/c/common/service303/MagmaService.cpp @@ -10,27 +10,34 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -#include -#include -#include -#include -#include - -#include - -#include -#include -#include #include "MagmaService.h" -#include "MetricsRegistry.h" -#include "MetricsSingleton.h" -#include "ProcFileUtils.h" -#include "ServiceRegistrySingleton.h" -#include "magma_logging.h" +#include // for Map +#include // for ServerCompletionQueue +#include // for InsecureServerCred... +#include // for FATAL, LogLevel +#include // for ServiceInfo, Reloa... +#include // for va_list +#include // for seconds, duration +#include // for raise, SIGTERM +#include // for time +#include // for string, stoi +#include // for enable_if<>::type +#include // for operator== +#include // for move +#include // for vector +#include "MetricsRegistry.h" // for Registry +#include "MetricsSingleton.h" // for MetricsSingleton +#include "ProcFileUtils.h" // for ProcFileUtils::mem... +#include "ServiceRegistrySingleton.h" // for ServiceRegistrySin... +#include "magma_logging.h" // for set_verbosity +#include "orc8r/protos/metrics.pb.h" // for MetricFamily +#include "orc8r/protos/metricsd.pb.h" // for MetricsContainer +#include "registry.h" // for Registry +namespace grpc { class ServerContext; } +namespace grpc { class Service; } using grpc::InsecureServerCredentials; -using grpc::ServerContext; using grpc::Status; using io::prometheus::client::MetricFamily; using magma::orc8r::GetOperationalStatesResponse; diff --git a/orc8r/gateway/c/common/service303/MagmaService.h b/orc8r/gateway/c/common/service303/MagmaService.h index 80cc3a2097c1..435f5a7879f9 100644 --- a/orc8r/gateway/c/common/service303/MagmaService.h +++ b/orc8r/gateway/c/common/service303/MagmaService.h @@ -12,16 +12,31 @@ */ #pragma once -#include -#include -#include - -#include "MetricsRegistry.h" -#include "MetricsSingleton.h" +#include // for Server, ServerBuilder +#include // for Status +#include // for Service303, Service303:... +#include // for steady_clock, steady_cl... +#include // for function +#include // for list +#include // for map +#include // for unique_ptr +#include // for string +#include "orc8r/protos/service303.pb.h" // for ServiceInfo, ServiceInf... +namespace grpc { class ServerCompletionQueue; } +namespace grpc { class ServerContext; } +namespace grpc { class ServerContext; } +namespace grpc { class Service; } +namespace magma { namespace orc8r { class MetricsContainer; } } +namespace magma { namespace orc8r { class Void; } } +namespace magma { namespace service303 { class MetricsSingleton; } } using grpc::Server; using grpc::ServerContext; using grpc::Status; +using magma::orc8r::GetOperationalStatesResponse; +using magma::orc8r::LogLevelMessage; +using magma::orc8r::MetricsContainer; +using magma::orc8r::ReloadConfigResponse; using magma::orc8r::Service303; using magma::orc8r::ServiceInfo; using magma::orc8r::State; diff --git a/orc8r/gateway/c/common/service303/MetricsHelpers.cpp b/orc8r/gateway/c/common/service303/MetricsHelpers.cpp index c581bedf0f0d..9dc47481c217 100644 --- a/orc8r/gateway/c/common/service303/MetricsHelpers.cpp +++ b/orc8r/gateway/c/common/service303/MetricsHelpers.cpp @@ -12,7 +12,8 @@ */ #include "MetricsHelpers.h" -#include "MetricsSingleton.h" +#include // for va_end, va_list, va_start +#include "MetricsSingleton.h" // for MetricsSingleton namespace magma { namespace service303 { diff --git a/orc8r/gateway/c/common/service303/MetricsHelpers.h b/orc8r/gateway/c/common/service303/MetricsHelpers.h index 9c755d714eb9..9f708f4bcfc7 100644 --- a/orc8r/gateway/c/common/service303/MetricsHelpers.h +++ b/orc8r/gateway/c/common/service303/MetricsHelpers.h @@ -13,8 +13,7 @@ #pragma once -#include -#include +#include // for size_t namespace magma { namespace service303 { diff --git a/orc8r/gateway/c/common/service303/MetricsSingleton.cpp b/orc8r/gateway/c/common/service303/MetricsSingleton.cpp index 6e94ba505f8e..c2f5cd19d752 100644 --- a/orc8r/gateway/c/common/service303/MetricsSingleton.cpp +++ b/orc8r/gateway/c/common/service303/MetricsSingleton.cpp @@ -10,7 +10,16 @@ * See the License for the specific language governing permissions and * limitations under the License. */ + #include "MetricsSingleton.h" +#include // for vector +#include "counter.h" // for Counter +#include "counter_builder.h" // for BuildCounter, CounterBuilder +#include "gauge.h" // for Gauge +#include "gauge_builder.h" // for GaugeBuilder, BuildGauge +#include "histogram.h" // for Histogram, Histogram::BucketBoundaries +#include "histogram_builder.h" // for BuildHistogram, HistogramBuilder +#include "registry.h" // for Registry using magma::service303::MetricsSingleton; using prometheus::BuildCounter; diff --git a/orc8r/gateway/c/common/service303/MetricsSingleton.h b/orc8r/gateway/c/common/service303/MetricsSingleton.h index 162ea8434f1e..693e6f774a34 100644 --- a/orc8r/gateway/c/common/service303/MetricsSingleton.h +++ b/orc8r/gateway/c/common/service303/MetricsSingleton.h @@ -12,12 +12,20 @@ */ #pragma once -#include - -#include -#include - -#include "MetricsRegistry.h" +#include // for va_list +#include // for size_t +#include // for map +#include // for shared_ptr +#include // for string +#include "MetricsRegistry.h" // for MetricsRegistry, Registry +namespace grpc { class Server; } +namespace prometheus { class Counter; } +namespace prometheus { class Gauge; } +namespace prometheus { class Histogram; } +namespace prometheus { class Registry; } +namespace prometheus { namespace detail { class CounterBuilder; } } +namespace prometheus { namespace detail { class GaugeBuilder; } } +namespace prometheus { namespace detail { class HistogramBuilder; } } using grpc::Server; using magma::service303::MetricsRegistry; diff --git a/orc8r/gateway/c/common/service303/ProcFileUtils.cpp b/orc8r/gateway/c/common/service303/ProcFileUtils.cpp index c578dd710477..505f593d4bfb 100644 --- a/orc8r/gateway/c/common/service303/ProcFileUtils.cpp +++ b/orc8r/gateway/c/common/service303/ProcFileUtils.cpp @@ -12,12 +12,9 @@ * limitations under the License. */ -#include -#include -#include -#include - #include "ProcFileUtils.h" +#include // for basic_istream, ifstream +#include // for string, operator>>, stod namespace magma { namespace service303 { diff --git a/orc8r/gateway/c/common/service303/ProcFileUtils.h b/orc8r/gateway/c/common/service303/ProcFileUtils.h index c26018a303dc..cbb12f4d2f56 100644 --- a/orc8r/gateway/c/common/service303/ProcFileUtils.h +++ b/orc8r/gateway/c/common/service303/ProcFileUtils.h @@ -12,7 +12,8 @@ */ #pragma once -#include +#include // for ifstream +#include // for stringnamespace magma { namespace magma { namespace service303 { diff --git a/orc8r/gateway/c/common/service_registry/ServiceRegistrySingleton.cpp b/orc8r/gateway/c/common/service_registry/ServiceRegistrySingleton.cpp index df68774145c6..e93f780b5293 100644 --- a/orc8r/gateway/c/common/service_registry/ServiceRegistrySingleton.cpp +++ b/orc8r/gateway/c/common/service_registry/ServiceRegistrySingleton.cpp @@ -10,13 +10,17 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -#include -#include -#include -#include #include "ServiceRegistrySingleton.h" +#include // for assert +#include // for CreateCustomChannel +#include // for string +#include // for SslCredentials, SslCre... +#include // for ChannelArguments +#include // for basic_ostream, basic_o... +#include // for invalid_argument +#include // for string, allocator, ope... +namespace grpc { class Channel; } -using grpc::Channel; using grpc::CreateCustomChannel; using grpc::InsecureChannelCredentials; using grpc::SslCredentials; diff --git a/orc8r/gateway/c/common/service_registry/ServiceRegistrySingleton.h b/orc8r/gateway/c/common/service_registry/ServiceRegistrySingleton.h index 1a0a797416e9..42240d0111c0 100644 --- a/orc8r/gateway/c/common/service_registry/ServiceRegistrySingleton.h +++ b/orc8r/gateway/c/common/service_registry/ServiceRegistrySingleton.h @@ -11,11 +11,13 @@ * limitations under the License. */ #pragma once -#include -#include -#include "yaml-cpp/yaml.h" -#include "ServiceConfigLoader.h" +#include // IWYU pragma: keep +#include // for shared_ptr, unique_ptr +#include // for string +#include "ServiceConfigLoader.h" // for ServiceConfigLoader +namespace grpc { class Channel; } +namespace grpc { class ChannelCredentials; } using grpc::Channel; using grpc::ChannelCredentials; From 60b5b29bfebc845f02c7f0b86fe1d16094ed542d Mon Sep 17 00:00:00 2001 From: Timothee Dzik Date: Mon, 5 Apr 2021 12:53:08 -0400 Subject: [PATCH 17/91] [CI][AGW]Force setuptools version (#5911) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Force setuptools version Signed-off-by: Timothée Dzik * Force setuptools version Signed-off-by: Timothée Dzik --- .circleci/config.yml | 4 +++- orc8r/tools/ansible/roles/python_dev/tasks/main.yml | 2 -- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index 4c0ec6f309e0..ca937349a2ef 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -893,13 +893,15 @@ jobs: SWAGGER_CODEGEN_JAR: /home/circleci/project/.codegen/swagger-codegen-cli.jar steps: - checkout + - python/set_version - build/determinator: <<: *lte_build_verify - run: mkdir -p /var/tmp/test_results - run: mkdir -p /var/tmp/codecovs - run: sudo apt-get update -y - run: sudo apt-get install -y libsystemd-dev pkg-config curl zip unzip - - run: sudo apt-get install -y virtualenv python-babel python-dev build-essential python3-setuptools python-setuptools autogen autoconf libtool python3-apt python3-aioeventlet python3-requests python3-pip python-protobuf + - run: sudo apt-get install -y virtualenv python-babel python-dev build-essential autogen autoconf libtool python3-apt python3-aioeventlet python3-requests python3-pip python-protobuf + - run: pip3 install setuptools==49.6.0 - run: command: | sudo curl -Lfs https://github.com/google/protobuf/releases/download/v3.1.0/protoc-3.1.0-linux-x86_64.zip -o protoc3.zip diff --git a/orc8r/tools/ansible/roles/python_dev/tasks/main.yml b/orc8r/tools/ansible/roles/python_dev/tasks/main.yml index 36d828b570d6..42035ed57be8 100644 --- a/orc8r/tools/ansible/roles/python_dev/tasks/main.yml +++ b/orc8r/tools/ansible/roles/python_dev/tasks/main.yml @@ -82,8 +82,6 @@ - pkg-config # Packaging dependencies - build-essential - - python3-setuptools - - python-setuptools # Building and shipping client release - autogen - autoconf From 29f67d24f5410afe2c255be77b7d73f5b2983881 Mon Sep 17 00:00:00 2001 From: Ulas Kozat Date: Mon, 5 Apr 2021 09:56:12 -0700 Subject: [PATCH 18/91] Bug fix in handling ULA message (#5905) Signed-off-by: Ulas Kozat --- lte/gateway/c/oai/tasks/mme_app/mme_app_location.c | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/lte/gateway/c/oai/tasks/mme_app/mme_app_location.c b/lte/gateway/c/oai/tasks/mme_app/mme_app_location.c index 2e994986cc92..827fd91faa08 100644 --- a/lte/gateway/c/oai/tasks/mme_app/mme_app_location.c +++ b/lte/gateway/c/oai/tasks/mme_app/mme_app_location.c @@ -258,10 +258,19 @@ int mme_app_handle_s6a_update_location_ans( } } - // Stop ULR Response timer if running + // Stop ULR Response timer. + // If expired its timer id should be MME_APP_TIMER_INACTIVE_ID and + // it should be already treated as failure if (ue_mm_context->ulr_response_timer.id != MME_APP_TIMER_INACTIVE_ID) { mme_app_stop_timer(ue_mm_context->ulr_response_timer.id); ue_mm_context->ulr_response_timer.id = MME_APP_TIMER_INACTIVE_ID; + } else { + OAILOG_ERROR( + LOG_MME_APP, + "ULR Response Timer has invalid id. This implies that the timer has " + "expired and ULR has been handled as failure. \n ", + ue_mm_context->mme_ue_s1ap_id); + OAILOG_FUNC_RETURN(LOG_MME_APP, RETURNerror); } ue_mm_context->subscription_known = SUBSCRIPTION_KNOWN; From b9cff0f6f7ea8dd072b725182180abf6482c0b55 Mon Sep 17 00:00:00 2001 From: Ulas Kozat Date: Mon, 5 Apr 2021 10:01:14 -0700 Subject: [PATCH 19/91] Eliminate some of the unnecessary state syncs on s1ap (#5888) Signed-off-by: Ulas Kozat --- lte/gateway/c/oai/lib/hashtable/hashtable.h | 1 + .../c/oai/lib/hashtable/hashtable_uint64.c | 3 +-- lte/gateway/c/oai/tasks/s1ap/s1ap_mme.c | 25 ++++++++++++++++--- .../oai/tasks/s1ap/s1ap_mme_itti_messaging.c | 4 +-- .../oai/tasks/s1ap/s1ap_mme_nas_procedures.c | 15 ++++++----- .../oai/tasks/s1ap/s1ap_mme_nas_procedures.h | 3 ++- 6 files changed, 36 insertions(+), 15 deletions(-) diff --git a/lte/gateway/c/oai/lib/hashtable/hashtable.h b/lte/gateway/c/oai/lib/hashtable/hashtable.h index 5a6542542d31..c4d43eedb1fd 100644 --- a/lte/gateway/c/oai/lib/hashtable/hashtable.h +++ b/lte/gateway/c/oai/lib/hashtable/hashtable.h @@ -55,6 +55,7 @@ typedef enum hashtable_return_code_e { HASH_TABLE_KEY_NOT_EXISTS, HASH_TABLE_SEARCH_NO_RESULT, HASH_TABLE_KEY_ALREADY_EXISTS, + HASH_TABLE_SAME_KEY_VALUE_EXISTS, HASH_TABLE_BAD_PARAMETER_HASHTABLE, HASH_TABLE_BAD_PARAMETER_KEY, HASH_TABLE_SYSTEM_ERROR, diff --git a/lte/gateway/c/oai/lib/hashtable/hashtable_uint64.c b/lte/gateway/c/oai/lib/hashtable/hashtable_uint64.c index 7fa032ede1ec..b7260047304d 100644 --- a/lte/gateway/c/oai/lib/hashtable/hashtable_uint64.c +++ b/lte/gateway/c/oai/lib/hashtable/hashtable_uint64.c @@ -680,12 +680,11 @@ hashtable_rc_t hashtable_uint64_ts_insert( __FUNCTION__, bdata(hashtblP->name), keyP, dataP); return HASH_TABLE_INSERT_OVERWRITTEN_DATA; } - node->data = dataP; pthread_mutex_unlock(&hashtblP->lock_nodes[hash]); PRINT_HASHTABLE( hashtblP, "%s(%s,key 0x%" PRIx64 " data %" PRIx64 ") return OK\n", __FUNCTION__, bdata(hashtblP->name), keyP, dataP); - return HASH_TABLE_OK; + return HASH_TABLE_SAME_KEY_VALUE_EXISTS; } node = node->next; diff --git a/lte/gateway/c/oai/tasks/s1ap/s1ap_mme.c b/lte/gateway/c/oai/tasks/s1ap/s1ap_mme.c index bbfc9adf646b..9b1d762b5f44 100644 --- a/lte/gateway/c/oai/tasks/s1ap/s1ap_mme.c +++ b/lte/gateway/c/oai/tasks/s1ap/s1ap_mme.c @@ -112,12 +112,16 @@ static int handle_message(zloop_t* loop, zsock_t* reader, void* arg) { state = get_s1ap_state(false); AssertFatal(state != NULL, "failed to retrieve s1ap state (was null)"); + bool is_state_same = false; + switch (ITTI_MSG_ID(received_message_p)) { case ACTIVATE_MESSAGE: { + is_state_same = true; // does not modify state hss_associated = true; } break; case MESSAGE_TEST: + is_state_same = true; // does not modify state OAILOG_DEBUG(LOG_S1AP, "Received MESSAGE_TEST\n"); break; @@ -145,6 +149,7 @@ static int handle_message(zloop_t* loop, zsock_t* reader, void* arg) { } break; case SCTP_DATA_CNF: + is_state_same = true; // the following handler does not modify state s1ap_mme_itti_nas_downlink_cnf( SCTP_DATA_CNF(received_message_p).agw_ue_xap_id, SCTP_DATA_CNF(received_message_p).is_success); @@ -174,15 +179,18 @@ static int handle_message(zloop_t* loop, zsock_t* reader, void* arg) { s1ap_generate_downlink_nas_transport( state, S1AP_NAS_DL_DATA_REQ(received_message_p).enb_ue_s1ap_id, S1AP_NAS_DL_DATA_REQ(received_message_p).mme_ue_s1ap_id, - &S1AP_NAS_DL_DATA_REQ(received_message_p).nas_msg, imsi64); + &S1AP_NAS_DL_DATA_REQ(received_message_p).nas_msg, imsi64, + &is_state_same); } break; case S1AP_E_RAB_SETUP_REQ: { + is_state_same = true; // the following handler does not modify state s1ap_generate_s1ap_e_rab_setup_req( state, &S1AP_E_RAB_SETUP_REQ(received_message_p)); } break; case S1AP_E_RAB_MODIFICATION_CNF: { + is_state_same = true; // the following handler does not modify state s1ap_mme_generate_erab_modification_confirm( state, &received_message_p->ittiMsg.s1ap_e_rab_modification_cnf); } break; @@ -195,6 +203,7 @@ static int handle_message(zloop_t* loop, zsock_t* reader, void* arg) { } break; case MME_APP_CONNECTION_ESTABLISHMENT_CNF: { + is_state_same = true; // the following handler does not modify state s1ap_handle_conn_est_cnf( state, &MME_APP_CONNECTION_ESTABLISHMENT_CNF(received_message_p)); } break; @@ -205,11 +214,13 @@ static int handle_message(zloop_t* loop, zsock_t* reader, void* arg) { } break; case S1AP_ENB_INITIATED_RESET_ACK: { + is_state_same = true; // the following handler does not modify state s1ap_handle_enb_initiated_reset_ack( &S1AP_ENB_INITIATED_RESET_ACK(received_message_p), imsi64); } break; case S1AP_PAGING_REQUEST: { + is_state_same = true; // the following handler does not modify state if (s1ap_handle_paging_request( state, &S1AP_PAGING_REQUEST(received_message_p), imsi64) != RETURNok) { @@ -218,23 +229,27 @@ static int handle_message(zloop_t* loop, zsock_t* reader, void* arg) { } break; case S1AP_UE_CONTEXT_MODIFICATION_REQUEST: { + is_state_same = true; // the following handler does not modify state s1ap_handle_ue_context_mod_req( state, &received_message_p->ittiMsg.s1ap_ue_context_mod_request, imsi64); } break; case S1AP_E_RAB_REL_CMD: { + is_state_same = true; // the following handler does not modify state s1ap_generate_s1ap_e_rab_rel_cmd( state, &S1AP_E_RAB_REL_CMD(received_message_p)); } break; case S1AP_PATH_SWITCH_REQUEST_ACK: { + is_state_same = true; // the following handler does not modify state s1ap_handle_path_switch_req_ack( state, &received_message_p->ittiMsg.s1ap_path_switch_request_ack, imsi64); } break; case S1AP_PATH_SWITCH_REQUEST_FAILURE: { + is_state_same = true; // the following handler does not modify state s1ap_handle_path_switch_req_failure( state, &received_message_p->ittiMsg.s1ap_path_switch_request_failure, imsi64); @@ -298,9 +313,11 @@ static int handle_message(zloop_t* loop, zsock_t* reader, void* arg) { } break; } - put_s1ap_state(); - put_s1ap_imsi_map(); - put_s1ap_ue_state(imsi64); + if (!is_state_same) { + put_s1ap_state(); + put_s1ap_imsi_map(); + put_s1ap_ue_state(imsi64); + } itti_free_msg_content(received_message_p); free(received_message_p); return 0; diff --git a/lte/gateway/c/oai/tasks/s1ap/s1ap_mme_itti_messaging.c b/lte/gateway/c/oai/tasks/s1ap/s1ap_mme_itti_messaging.c index 3c4c4658dfa1..590eebbc47e8 100644 --- a/lte/gateway/c/oai/tasks/s1ap/s1ap_mme_itti_messaging.c +++ b/lte/gateway/c/oai/tasks/s1ap/s1ap_mme_itti_messaging.c @@ -106,11 +106,11 @@ int s1ap_mme_itti_nas_downlink_cnf( if (!is_success) { OAILOG_ERROR( LOG_S1AP, - "ERROR: Failed to send connection less S1AP message to eNB. " + "ERROR: Failed to send connectionless S1AP message to eNB. " "mme_ue_s1ap_id = %d \n", ue_id); } - // Drop this cnf message here since this is related to connection less S1AP + // Drop this cnf message here since this is related to connectionless S1AP // message hence no need to send it to NAS module OAILOG_FUNC_RETURN(LOG_S1AP, RETURNok); } diff --git a/lte/gateway/c/oai/tasks/s1ap/s1ap_mme_nas_procedures.c b/lte/gateway/c/oai/tasks/s1ap/s1ap_mme_nas_procedures.c index 4e3d35de67a2..494c9b66d8ef 100644 --- a/lte/gateway/c/oai/tasks/s1ap/s1ap_mme_nas_procedures.c +++ b/lte/gateway/c/oai/tasks/s1ap/s1ap_mme_nas_procedures.c @@ -471,7 +471,7 @@ int s1ap_mme_handle_nas_non_delivery( int s1ap_generate_downlink_nas_transport( s1ap_state_t* state, const enb_ue_s1ap_id_t enb_ue_s1ap_id, const mme_ue_s1ap_id_t ue_id, STOLEN_REF bstring* payload, - const imsi64_t imsi64) { + const imsi64_t imsi64, bool* is_state_same) { ue_description_t* ue_ref = NULL; uint8_t* buffer_p = NULL; uint32_t length = 0; @@ -480,7 +480,7 @@ int s1ap_generate_downlink_nas_transport( OAILOG_FUNC_IN(LOG_S1AP); - // Try to retrieve SCTP assoication id using mme_ue_s1ap_id + // Try to retrieve SCTP association id using mme_ue_s1ap_id if (HASH_TABLE_OK == hashtable_ts_get( &state->mmeid2associd, (const hash_key_t) ue_id, (void**) &id)) { @@ -513,12 +513,15 @@ int s1ap_generate_downlink_nas_transport( OAILOG_FUNC_RETURN(LOG_S1AP, RETURNerror); } else { /* - * We have fount the UE in the list. + * We have found the UE in the list. * * * * Create new IE list message and encode it. */ s1ap_imsi_map_t* imsi_map = get_s1ap_imsi_map(); - hashtable_uint64_ts_insert( - imsi_map->mme_ue_id_imsi_htbl, (const hash_key_t) ue_id, imsi64); + if (hashtable_uint64_ts_insert( + imsi_map->mme_ue_id_imsi_htbl, (const hash_key_t) ue_id, imsi64) == + HASH_TABLE_SAME_KEY_VALUE_EXISTS) { + is_state_same = true; + } S1ap_DownlinkNASTransport_IEs_t* ie = NULL; S1ap_DownlinkNASTransport_t* out = NULL; @@ -536,7 +539,7 @@ int s1ap_generate_downlink_nas_transport( if (ue_ref->s1_ue_state == S1AP_UE_WAITING_CRR) { OAILOG_ERROR_UE( LOG_S1AP, imsi64, - "Already triggred UE Context Release Command and UE is" + "Already triggered UE Context Release Command and UE is" "in S1AP_UE_WAITING_CRR, so dropping the DownlinkNASTransport \n"); OAILOG_FUNC_RETURN(LOG_S1AP, RETURNerror); } else { diff --git a/lte/gateway/c/oai/tasks/s1ap/s1ap_mme_nas_procedures.h b/lte/gateway/c/oai/tasks/s1ap/s1ap_mme_nas_procedures.h index 2f5cc6c258a5..d9d8cf30fa41 100644 --- a/lte/gateway/c/oai/tasks/s1ap/s1ap_mme_nas_procedures.h +++ b/lte/gateway/c/oai/tasks/s1ap/s1ap_mme_nas_procedures.h @@ -70,7 +70,8 @@ void s1ap_handle_conn_est_cnf( int s1ap_generate_downlink_nas_transport( s1ap_state_t* state, const enb_ue_s1ap_id_t enb_ue_s1ap_id, - const mme_ue_s1ap_id_t ue_id, STOLEN_REF bstring* payload, imsi64_t imsi64); + const mme_ue_s1ap_id_t ue_id, STOLEN_REF bstring* payload, imsi64_t imsi64, + bool* is_state_same); void s1ap_handle_mme_ue_id_notification( s1ap_state_t* state, From 87a70bb6c53d400b241d5add64c0b026bfc0eb7e Mon Sep 17 00:00:00 2001 From: Pravin Shelar Date: Mon, 5 Apr 2021 22:37:29 +0530 Subject: [PATCH 20/91] AGW: OVS: handle L3 port to L3 port packet transfer. (#5889) OVS action transation code throws asserts on packet that traverse from L3 to L3 port. This is due to incorrect assumpltion of packet state. Following patch removes incurrect assert. This PR also fixes build-ovs.sh by moving OVS patches to 2.8.10 dir. Signed-off-by: Pravin B Shelar --- lte/gateway/release/build-ovs.sh | 2 +- ...port-gtp-for-GPRS-Tunneling-Protocol.patch | 0 ...002-userspace-Add-L3-tunnel-type-GTP.patch | 0 ...r-gtp-vport-linux-4.9-module-require.patch | 0 ...s-datapath-fix-stretch-kernel-update.patch | 0 ...-custom-IPDR-fields-for-IPFIX-export.patch | 0 ...6-ovs-Handle-spaces-in-ovs-arguments.patch | 0 ...t_epoch-custom-field-to-IPFIX-export.patch | 0 .../kernel-4.9/2.8.10/0008-odp-act.patch | 65 +++++++++++++++++++ 9 files changed, 66 insertions(+), 1 deletion(-) rename third_party/gtp_ovs/kernel-4.9/{2.8.9 => 2.8.10}/0001-datapath-add-vport-gtp-for-GPRS-Tunneling-Protocol.patch (100%) rename third_party/gtp_ovs/kernel-4.9/{2.8.9 => 2.8.10}/0002-userspace-Add-L3-tunnel-type-GTP.patch (100%) rename third_party/gtp_ovs/kernel-4.9/{2.8.9 => 2.8.10}/0003-Build-symbols-for-gtp-vport-linux-4.9-module-require.patch (100%) rename third_party/gtp_ovs/kernel-4.9/{2.8.9 => 2.8.10}/0004-ovs-datapath-fix-stretch-kernel-update.patch (100%) rename third_party/gtp_ovs/kernel-4.9/{2.8.9 => 2.8.10}/0005-Add-custom-IPDR-fields-for-IPFIX-export.patch (100%) rename third_party/gtp_ovs/kernel-4.9/{2.8.9 => 2.8.10}/0006-ovs-Handle-spaces-in-ovs-arguments.patch (100%) rename third_party/gtp_ovs/kernel-4.9/{2.8.9 => 2.8.10}/0007-Add-pdp_start_epoch-custom-field-to-IPFIX-export.patch (100%) create mode 100644 third_party/gtp_ovs/kernel-4.9/2.8.10/0008-odp-act.patch diff --git a/lte/gateway/release/build-ovs.sh b/lte/gateway/release/build-ovs.sh index 07265f9d6166..74013fec0332 100755 --- a/lte/gateway/release/build-ovs.sh +++ b/lte/gateway/release/build-ovs.sh @@ -51,7 +51,7 @@ FLOWBASED_PATH=$(readlink -f ${MAGMA_ROOT}/third_party/gtp_ovs/kernel-4.9/gtp-v4 PATCH_ROOT=$(readlink -f "$GTP_PATCH_PATH/$OVS_VERSION_SHORT/") VLAN_FIX="3cf2b424bb" # be sure to increment this to enable upgrade from package repo when rebuilding identical upstream versions -LOCAL_REV=1 +LOCAL_REV=2 # The resulting package is placed in $OUTPUT_DIR # or in the cwd. diff --git a/third_party/gtp_ovs/kernel-4.9/2.8.9/0001-datapath-add-vport-gtp-for-GPRS-Tunneling-Protocol.patch b/third_party/gtp_ovs/kernel-4.9/2.8.10/0001-datapath-add-vport-gtp-for-GPRS-Tunneling-Protocol.patch similarity index 100% rename from third_party/gtp_ovs/kernel-4.9/2.8.9/0001-datapath-add-vport-gtp-for-GPRS-Tunneling-Protocol.patch rename to third_party/gtp_ovs/kernel-4.9/2.8.10/0001-datapath-add-vport-gtp-for-GPRS-Tunneling-Protocol.patch diff --git a/third_party/gtp_ovs/kernel-4.9/2.8.9/0002-userspace-Add-L3-tunnel-type-GTP.patch b/third_party/gtp_ovs/kernel-4.9/2.8.10/0002-userspace-Add-L3-tunnel-type-GTP.patch similarity index 100% rename from third_party/gtp_ovs/kernel-4.9/2.8.9/0002-userspace-Add-L3-tunnel-type-GTP.patch rename to third_party/gtp_ovs/kernel-4.9/2.8.10/0002-userspace-Add-L3-tunnel-type-GTP.patch diff --git a/third_party/gtp_ovs/kernel-4.9/2.8.9/0003-Build-symbols-for-gtp-vport-linux-4.9-module-require.patch b/third_party/gtp_ovs/kernel-4.9/2.8.10/0003-Build-symbols-for-gtp-vport-linux-4.9-module-require.patch similarity index 100% rename from third_party/gtp_ovs/kernel-4.9/2.8.9/0003-Build-symbols-for-gtp-vport-linux-4.9-module-require.patch rename to third_party/gtp_ovs/kernel-4.9/2.8.10/0003-Build-symbols-for-gtp-vport-linux-4.9-module-require.patch diff --git a/third_party/gtp_ovs/kernel-4.9/2.8.9/0004-ovs-datapath-fix-stretch-kernel-update.patch b/third_party/gtp_ovs/kernel-4.9/2.8.10/0004-ovs-datapath-fix-stretch-kernel-update.patch similarity index 100% rename from third_party/gtp_ovs/kernel-4.9/2.8.9/0004-ovs-datapath-fix-stretch-kernel-update.patch rename to third_party/gtp_ovs/kernel-4.9/2.8.10/0004-ovs-datapath-fix-stretch-kernel-update.patch diff --git a/third_party/gtp_ovs/kernel-4.9/2.8.9/0005-Add-custom-IPDR-fields-for-IPFIX-export.patch b/third_party/gtp_ovs/kernel-4.9/2.8.10/0005-Add-custom-IPDR-fields-for-IPFIX-export.patch similarity index 100% rename from third_party/gtp_ovs/kernel-4.9/2.8.9/0005-Add-custom-IPDR-fields-for-IPFIX-export.patch rename to third_party/gtp_ovs/kernel-4.9/2.8.10/0005-Add-custom-IPDR-fields-for-IPFIX-export.patch diff --git a/third_party/gtp_ovs/kernel-4.9/2.8.9/0006-ovs-Handle-spaces-in-ovs-arguments.patch b/third_party/gtp_ovs/kernel-4.9/2.8.10/0006-ovs-Handle-spaces-in-ovs-arguments.patch similarity index 100% rename from third_party/gtp_ovs/kernel-4.9/2.8.9/0006-ovs-Handle-spaces-in-ovs-arguments.patch rename to third_party/gtp_ovs/kernel-4.9/2.8.10/0006-ovs-Handle-spaces-in-ovs-arguments.patch diff --git a/third_party/gtp_ovs/kernel-4.9/2.8.9/0007-Add-pdp_start_epoch-custom-field-to-IPFIX-export.patch b/third_party/gtp_ovs/kernel-4.9/2.8.10/0007-Add-pdp_start_epoch-custom-field-to-IPFIX-export.patch similarity index 100% rename from third_party/gtp_ovs/kernel-4.9/2.8.9/0007-Add-pdp_start_epoch-custom-field-to-IPFIX-export.patch rename to third_party/gtp_ovs/kernel-4.9/2.8.10/0007-Add-pdp_start_epoch-custom-field-to-IPFIX-export.patch diff --git a/third_party/gtp_ovs/kernel-4.9/2.8.10/0008-odp-act.patch b/third_party/gtp_ovs/kernel-4.9/2.8.10/0008-odp-act.patch new file mode 100644 index 000000000000..200e068e8aab --- /dev/null +++ b/third_party/gtp_ovs/kernel-4.9/2.8.10/0008-odp-act.patch @@ -0,0 +1,65 @@ +From d55271909b3a7d820375d7326c788118a2ddbc76 Mon Sep 17 00:00:00 2001 +From: Pravin B Shelar +Date: Wed, 31 Mar 2021 15:02:26 +0000 +Subject: [PATCH 8/8] ODP-action: Fix L3 port to L3 port traffic. + +Signed-off-by: Pravin B Shelar +--- + lib/odp-util.c | 27 +++++++++++++++++++++++++-- + 1 file changed, 25 insertions(+), 2 deletions(-) + +diff --git a/lib/odp-util.c b/lib/odp-util.c +index 21607b36e..f3e921ba8 100644 +--- a/lib/odp-util.c ++++ b/lib/odp-util.c +@@ -6921,6 +6921,27 @@ odp_put_encap_nsh_action(struct ofpbuf *odp_actions, + &encap_nsh, sizeof(encap_nsh)); + } + ++static void OVS_PRINTF_FORMAT(2, 3) ++log_flow(const struct flow *flow, const char *format, ...) ++{ ++ static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 5); ++ if (VLOG_DROP_DBG(&rl)) { ++ return; ++ } ++ ++ struct ds s = DS_EMPTY_INITIALIZER; ++ va_list args; ++ va_start(args, format); ++ ds_put_format_valist(&s, format, args); ++ va_end(args); ++ ++ ds_put_cstr(&s, " Unexpected state while processing "); ++ flow_format(&s, flow, NULL); ++ VLOG_DBG("%s", ds_cstr(&s)); ++ ds_destroy(&s); ++} ++ ++ + static void + commit_encap_decap_action(const struct flow *flow, + struct flow *base_flow, +@@ -6951,7 +6972,8 @@ commit_encap_decap_action(const struct flow *flow, + default: + /* Only the above protocols are supported for encap. + * The check is done at action translation. */ +- OVS_NOT_REACHED(); ++ log_flow(flow, "pending encap"); ++ return; + } + } else if (pending_decap || flow->packet_type != base_flow->packet_type) { + /* This is an explicit or implicit decap case. */ +@@ -6972,7 +6994,8 @@ commit_encap_decap_action(const struct flow *flow, + break; + default: + /* Checks are done during translation. */ +- OVS_NOT_REACHED(); ++ log_flow(flow, "pending dencap"); ++ return; + } + } + } +-- +2.11.0 + From 02c4bdbc1382aa3929a784394a6182619ef7a454 Mon Sep 17 00:00:00 2001 From: Timothee Dzik Date: Mon, 5 Apr 2021 13:33:31 -0400 Subject: [PATCH 21/91] Revert changes to push to new registry (#5915) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Timothée Dzik --- .circleci/config.yml | 54 ++++++++++++++++++++++---------------------- 1 file changed, 27 insertions(+), 27 deletions(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index ca937349a2ef..32b179177272 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -566,13 +566,13 @@ jobs: project: orc8r images: "nginx|controller" tag-latest: true - # - tag-push-docker: - # project: orc8r - # images: "nginx|controller" - # tag-latest: true - # registry: $JFROG_DOCKER_ORC8R_REGISTRY - # username: $JFROG_USERNAME - # password: $JFROG_PASSWORD + - tag-push-docker: + project: orc8r + images: "nginx|controller" + tag-latest: true + registry: $JFROG_DOCKER_ORC8R_REGISTRY + username: $JFROG_USERNAME + password: $JFROG_PASSWORD - persist-githash-version: file_prefix: orc8r - notify-magma: @@ -672,12 +672,12 @@ jobs: project: feg images: "gateway_go|gateway_python" registry: $DOCKER_FEG_REGISTRY - # - tag-push-docker: - # project: feg - # images: "gateway_go|gateway_python" - # registry: $JFROG_DOCKER_FEG_REGISTRY - # username: $JFROG_USERNAME - # password: $JFROG_PASSWORD + - tag-push-docker: + project: feg + images: "gateway_go|gateway_python" + registry: $JFROG_DOCKER_FEG_REGISTRY + username: $JFROG_USERNAME + password: $JFROG_PASSWORD - persist-githash-version: file_prefix: feg - notify-magma: @@ -754,14 +754,14 @@ jobs: tag: <> registry: $DOCKER_MAGMA_REGISTRY tag-latest: <> - # - tag-push-docker: - # project: cwf - # images: <> - # tag: <> - # tag-latest: <> - # registry: $JFROG_DOCKER_CWF_REGISTRY - # username: $JFROG_USERNAME - # password: $JFROG_PASSWORD + - tag-push-docker: + project: cwf + images: <> + tag: <> + tag-latest: <> + registry: $JFROG_DOCKER_CWF_REGISTRY + username: $JFROG_USERNAME + password: $JFROG_PASSWORD - persist-githash-version: file_prefix: cwag - notify-magma: @@ -992,12 +992,12 @@ jobs: project: cwf images: "operator" registry: $DOCKER_MAGMA_REGISTRY - # - tag-push-docker: - # project: cwf - # images: "operator" - # registry: $JFROG_DOCKER_CWF_REGISTRY - # username: $JFROG_USERNAME - # password: $JFROG_PASSWORD + - tag-push-docker: + project: cwf + images: "operator" + registry: $JFROG_DOCKER_CWF_REGISTRY + username: $JFROG_USERNAME + password: $JFROG_PASSWORD - persist-githash-version: file_prefix: cwf_operator - notify-magma: From b8fc8c459f11f14ddd144c7ae9876e825b81603e Mon Sep 17 00:00:00 2001 From: Marie <37634144+themarwhal@users.noreply.github.com> Date: Mon, 5 Apr 2021 13:07:01 -0500 Subject: [PATCH 22/91] [SessionD] Update rule versions on install/uninstalling rules (#5787) --- .../c/session_manager/LocalEnforcer.cpp | 149 +++++--- .../c/session_manager/PipelinedClient.cpp | 13 + .../c/session_manager/SessionState.cpp | 321 ++++++++++-------- lte/gateway/c/session_manager/SessionState.h | 115 ++++--- lte/gateway/c/session_manager/Types.h | 2 + lte/gateway/c/session_manager/test/Matchers.h | 11 +- .../session_manager/test/SessionStateTester.h | 35 +- .../test/test_local_enforcer.cpp | 38 ++- .../test/test_session_state.cpp | 39 ++- 9 files changed, 451 insertions(+), 272 deletions(-) diff --git a/lte/gateway/c/session_manager/LocalEnforcer.cpp b/lte/gateway/c/session_manager/LocalEnforcer.cpp index 5ea7c978651c..4964962d85d8 100644 --- a/lte/gateway/c/session_manager/LocalEnforcer.cpp +++ b/lte/gateway/c/session_manager/LocalEnforcer.cpp @@ -587,13 +587,20 @@ void LocalEnforcer::schedule_static_rule_activation( << "during installation of static rule " << rule_id; return; } + PolicyRule rule; + if (!rule_store_->get_rule(rule_id, &rule)) { + MLOG(MWARNING) << "Could not find static rules definition for " + << rule_id; + return; + } + auto& session = **session_it; auto& uc = session_update[imsi][session_id]; std::time_t current_time = time(nullptr); // don't install the rule if the current time is out of lifetime - if (session->should_rule_be_active(rule_id, current_time)) { - session->deactivate_scheduled_static_rule(rule_id, uc); + if (!session->should_rule_be_active(rule_id, current_time)) { + session->deactivate_scheduled_static_rule(rule_id); session_store_.update_sessions(session_update); return; } @@ -605,11 +612,10 @@ void LocalEnforcer::schedule_static_rule_activation( const auto ambr = config.get_apn_ambr(); const std::string msisdn = config.common_context.msisdn(); - session->install_scheduled_static_rule(rule_id, uc); - PolicyRule rule; - rule_store_->get_rule(rule_id, &rule); + uint32_t version = session->activate_static_rule( + rule_id, session->get_rule_lifetime(rule_id), uc); RulesToProcess to_process; - to_process.rules = std::vector{rule}; + to_process.append_versioned_policy(rule, version); pipelined_client_->activate_flows_for_rules( imsi, ip_addr, ipv6_addr, teids, msisdn, ambr, to_process, @@ -647,7 +653,7 @@ void LocalEnforcer::schedule_dynamic_rule_activation( } // don't install the rule if the current time is out of lifetime std::time_t current_time = time(nullptr); - if (session->should_rule_be_active(rule_id, current_time)) { + if (!session->should_rule_be_active(rule_id, current_time)) { session->remove_scheduled_dynamic_rule(rule_id, nullptr, session_uc); session_store_.update_sessions(session_update); return; @@ -660,11 +666,17 @@ void LocalEnforcer::schedule_dynamic_rule_activation( const auto ambr = config.get_apn_ambr(); const auto msisdn = config.common_context.msisdn(); - session->install_scheduled_dynamic_rule(rule_id, session_uc); - PolicyRule policy; - session->get_scheduled_dynamic_rules().get_rule(rule_id, &policy); + PolicyRule rule; + if (!session->remove_scheduled_dynamic_rule( + rule_id, &rule, session_uc)) { + MLOG(MWARNING) << "Dynamic rule " << rule_id << " doesn't exist for " + << session_id; + return; + } + uint32_t version = session->insert_dynamic_rule( + rule, session->get_rule_lifetime(rule_id), session_uc); RulesToProcess to_process; - to_process.rules = std::vector{policy}; + to_process.append_versioned_policy(rule, version); pipelined_client_->activate_flows_for_rules( imsi, ip_addr, ipv6_addr, teids, msisdn, ambr, to_process, @@ -691,8 +703,8 @@ void LocalEnforcer::schedule_static_rule_deactivation( SessionSearchCriteria criteria(imsi, IMSI_AND_SESSION_ID, session_id); auto session_it = session_store_.find_session(session_map, criteria); if (!session_it) { - MLOG(MWARNING) << "Could not find session " << session_id - << "during removal of static rule " << rule_id; + MLOG(MERROR) << "Could not find session " << session_id + << "during removal of static rule " << rule_id; return; } auto& session = **session_it; @@ -708,17 +720,21 @@ void LocalEnforcer::schedule_static_rule_deactivation( auto ip_addr = session->get_config().common_context.ue_ipv4(); auto ipv6_addr = session->get_config().common_context.ue_ipv6(); const Teids teids = session->get_config().common_context.teids(); - RulesToProcess to_process; - to_process.rules = std::vector{rule}; + auto& session_uc = session_update[imsi][session_id]; + optional op_version = + session->deactivate_static_rule(rule_id, session_uc); + if (!op_version) { + MLOG(MERROR) << "Could not find rule " << rule_id << " for " + << session_id << " during static rule removal"; + return; + } + + RulesToProcess to_process; + to_process.append_versioned_policy(rule, *op_version); pipelined_client_->deactivate_flows_for_rules( imsi, ip_addr, ipv6_addr, teids, to_process, RequestOriginType::GX); - auto& session_uc = session_update[imsi][session_id]; - if (!session->deactivate_static_rule(rule_id, session_uc)) { - MLOG(MWARNING) << "Could not find rule " << rule_id << " for " - << session_id << " during static rule removal"; - } session_store_.update_sessions(session_update); }, delta.count()); @@ -751,13 +767,16 @@ void LocalEnforcer::schedule_dynamic_rule_deactivation( const Teids teids = session->get_config().common_context.teids(); PolicyRule policy; - session->get_scheduled_dynamic_rules().get_rule(rule_id, &policy); - RulesToProcess to_process; - to_process.rules = std::vector{policy}; - pipelined_client_->deactivate_flows_for_rules( - imsi, ip_addr, ipv6_addr, teids, to_process, RequestOriginType::GX); auto& uc = session_update[imsi][session_id]; - session->remove_dynamic_rule(policy.id(), nullptr, uc); + optional op_version = + session->remove_dynamic_rule(policy.id(), &policy, uc); + if (op_version) { + RulesToProcess to_process; + to_process.append_versioned_policy(policy, *op_version); + pipelined_client_->deactivate_flows_for_rules( + imsi, ip_addr, ipv6_addr, teids, to_process, + RequestOriginType::GX); + } session_store_.update_sessions(session_update); }, delta.count()); @@ -1169,7 +1188,7 @@ void LocalEnforcer::remove_rules_for_suspended_credit( // Remove pipelined rules RulesToProcess rules_to_remove; - session->get_rules_per_credit_key(ckey, rules_to_remove); + session->get_rules_per_credit_key(ckey, rules_to_remove, session_uc); auto imsi = session->get_config().common_context.sid().id(); propagate_rule_updates_to_pipelined( imsi, session->get_config(), RulesToProcess{}, rules_to_remove, false); @@ -1208,7 +1227,7 @@ void LocalEnforcer::add_rules_for_unsuspended_credit( // add pipelined rules RulesToProcess rules_to_add; - session->get_rules_per_credit_key(ckey, rules_to_add); + session->get_rules_per_credit_key(ckey, rules_to_add, session_uc); auto imsi = session->get_config().common_context.sid().id(); propagate_rule_updates_to_pipelined( imsi, session->get_config(), rules_to_add, RulesToProcess{}, false); @@ -1644,19 +1663,36 @@ void LocalEnforcer::process_rules_to_remove( rules_to_remove, RulesToProcess& rules_to_deactivate, SessionStateUpdateCriteria& uc) { for (const auto& rule_id : rules_to_remove) { - // Try to remove as dynamic rule first - PolicyRule dy_rule, st_rule; - bool is_dynamic = session->remove_dynamic_rule(rule_id, &dy_rule, uc); - if (is_dynamic) { // dynamic rule - rules_to_deactivate.rules.push_back(dy_rule); - } else if ( // static rule - rule_store_->get_rule(rule_id, &st_rule) && - session->deactivate_static_rule(rule_id, uc)) { - rules_to_deactivate.rules.push_back(st_rule); - } else { + optional p_type = session->get_policy_type(rule_id); + if (!p_type) { MLOG(MWARNING) << "Could not find rule " << rule_id << " for " << imsi << " during static rule removal"; + continue; + } + optional op_version = {}; + PolicyRule rule; + switch (*p_type) { + case DYNAMIC: { + op_version = session->remove_dynamic_rule(rule_id, &rule, uc); + break; + } + case STATIC: { + if (!rule_store_->get_rule(rule_id, &rule)) { + MLOG(MERROR) << "Static rule " << rule_id << " not found"; + continue; + } + op_version = session->deactivate_static_rule(rule_id, uc); + break; + } + default: + break; + } + if (!op_version) { + MLOG(MERROR) << "Failed to remove " << rule_id << " for " + << session->get_session_id(); + continue; } + rules_to_deactivate.append_versioned_policy(rule, *op_version); } } @@ -1701,6 +1737,7 @@ void LocalEnforcer::process_rules_to_install( if (!rule_store_->get_rule(id, &static_rule)) { MLOG(MERROR) << "static rule " << id << " is not found, skipping install..."; + continue; } RuleLifetime lifetime(rule_install); @@ -1709,9 +1746,9 @@ void LocalEnforcer::process_rules_to_install( schedule_static_rule_activation( imsi, session_id, id, lifetime.activation_time); } else { - session.activate_static_rule(id, lifetime, uc); + uint32_t version = session.activate_static_rule(id, lifetime, uc); // Set up rules_to_activate - rules_to_activate.rules.push_back(static_rule); + rules_to_activate.append_versioned_policy(static_rule, version); } if (lifetime.deactivation_time > current_time) { @@ -1719,12 +1756,13 @@ void LocalEnforcer::process_rules_to_install( imsi, session_id, id, lifetime.deactivation_time); } else if (lifetime.deactivation_time > 0) { // 0: never scheduled to deactivate - if (!session.deactivate_static_rule(id, uc)) { + optional op_version = session.deactivate_static_rule(id, uc); + if (!op_version) { MLOG(MWARNING) << "Could not find rule " << id << "for " << session_id << " during static rule removal"; + } else { + rules_to_deactivate.append_versioned_policy(static_rule, *op_version); } - - rules_to_deactivate.rules.push_back(static_rule); } } @@ -1737,15 +1775,19 @@ void LocalEnforcer::process_rules_to_install( schedule_dynamic_rule_activation( imsi, session_id, rule_id, lifetime.activation_time); } else { - session.insert_dynamic_rule(dynamic_rule, lifetime, uc); - rules_to_activate.rules.push_back(dynamic_rule); + uint32_t version = + session.insert_dynamic_rule(dynamic_rule, lifetime, uc); + rules_to_activate.append_versioned_policy(dynamic_rule, version); } if (lifetime.deactivation_time > current_time) { schedule_dynamic_rule_deactivation( imsi, session_id, rule_id, lifetime.deactivation_time); } else if (lifetime.deactivation_time > 0) { - session.remove_dynamic_rule(rule_id, nullptr, uc); - rules_to_deactivate.rules.push_back(dynamic_rule); + optional op_version = + session.remove_dynamic_rule(rule_id, nullptr, uc); + if (op_version) { + rules_to_deactivate.append_versioned_policy(dynamic_rule, *op_version); + } } } } @@ -1995,22 +2037,23 @@ void LocalEnforcer::remove_rule_due_to_bearer_creation_failure( } PolicyRule rule; - bool found = false; + optional op_version = {}; switch (*policy_type) { case STATIC: - session.deactivate_static_rule(rule_id, uc); - found = rule_store_->get_rule(rule_id, &rule); + if (rule_store_->get_rule(rule_id, &rule)) { + op_version = session.deactivate_static_rule(rule_id, uc); + } break; case DYNAMIC: { - found = session.remove_dynamic_rule(rule_id, &rule, uc); + op_version = session.remove_dynamic_rule(rule_id, &rule, uc); break; } } - if (found) { + if (op_version) { auto config = session.get_config().common_context; RulesToProcess to_process; - to_process.rules = std::vector{rule}; + to_process.append_versioned_policy(rule, *op_version); pipelined_client_->deactivate_flows_for_rules( imsi, config.ue_ipv4(), config.ue_ipv6(), config.teids(), to_process, RequestOriginType::GX); diff --git a/lte/gateway/c/session_manager/PipelinedClient.cpp b/lte/gateway/c/session_manager/PipelinedClient.cpp index 4b63640744c0..c59ed534866e 100644 --- a/lte/gateway/c/session_manager/PipelinedClient.cpp +++ b/lte/gateway/c/session_manager/PipelinedClient.cpp @@ -66,6 +66,12 @@ magma::DeactivateFlowsRequest create_deactivate_req( for (const auto& rule : to_process.rules) { ids->Add()->assign(rule.id()); } + auto mut_versioned_rules = req.mutable_policies(); + for (uint index = 0; index < to_process.rules.size(); ++index) { + auto versioned_policy = mut_versioned_rules->Add(); + versioned_policy->set_version(to_process.versions[index]); + versioned_policy->set_rule_id(to_process.rules[index].id()); + } return req; } @@ -87,10 +93,17 @@ magma::ActivateFlowsRequest create_activate_req( if (ambr) { req.mutable_apn_ambr()->CopyFrom(*ambr); } + // TODO depracate dynamic rules fields auto mut_dyn_rules = req.mutable_dynamic_rules(); for (const auto& dyn_rule : to_process.rules) { mut_dyn_rules->Add()->CopyFrom(dyn_rule); } + auto mut_versioned_rules = req.mutable_policies(); + for (uint index = 0; index < to_process.rules.size(); ++index) { + auto versioned_policy = mut_versioned_rules->Add(); + versioned_policy->set_version(to_process.versions[index]); + versioned_policy->mutable_rule()->CopyFrom(to_process.rules[index]); + } return req; } diff --git a/lte/gateway/c/session_manager/SessionState.cpp b/lte/gateway/c/session_manager/SessionState.cpp index e9bb7a76465d..f23133e045d8 100644 --- a/lte/gateway/c/session_manager/SessionState.cpp +++ b/lte/gateway/c/session_manager/SessionState.cpp @@ -523,11 +523,12 @@ void SessionState::apply_session_static_rule_set( if (!is_static_rule_installed(static_rule_id)) { MLOG(MINFO) << "Installing static rule " << static_rule_id << " for " << session_id_; - activate_static_rule(static_rule_id, lifetime, uc); - rules_to_activate.rules.push_back(rule); + uint32_t version = activate_static_rule(static_rule_id, lifetime, uc); + // Set up rules_to_activate + rules_to_activate.append_versioned_policy(rule, version); } } - std::vector static_rules_to_deactivate; + std::vector static_rules_to_deactivate; // Go through the existing rules and uninstall any rule not in the rule set for (const auto static_rule_id : active_static_rules_) { @@ -538,16 +539,22 @@ void SessionState::apply_session_static_rule_set( << " is not found. Skipping deactivation"; continue; } - static_rules_to_deactivate.push_back(static_rule_id); - rules_to_deactivate.rules.push_back(rule); + static_rules_to_deactivate.push_back(rule); } } // Do the actual removal separately so we're not modifying the vector while // looping - for (const auto static_rule_id : static_rules_to_deactivate) { - MLOG(MINFO) << "Removing static rule " << static_rule_id << " for " + for (const PolicyRule static_rule : static_rules_to_deactivate) { + MLOG(MINFO) << "Removing static rule " << static_rule.id() << " for " << session_id_; - deactivate_static_rule(static_rule_id, uc); + optional op_version = + deactivate_static_rule(static_rule.id(), uc); + if (!op_version) { + MLOG(MWARNING) << "Failed to deactivate static rule " << static_rule.id() + << " for " << session_id_; + } else { + rules_to_deactivate.append_versioned_policy(static_rule, *op_version); + } } } @@ -561,18 +568,21 @@ void SessionState::apply_session_dynamic_rule_set( if (!is_dynamic_rule_installed(dynamic_rule_pair.first)) { MLOG(MINFO) << "Installing dynamic rule " << dynamic_rule_pair.first << " for " << session_id_; - insert_dynamic_rule(dynamic_rule_pair.second, lifetime, uc); - rules_to_activate.rules.push_back(dynamic_rule_pair.second); + uint32_t version = + insert_dynamic_rule(dynamic_rule_pair.second, lifetime, uc); + rules_to_activate.append_versioned_policy( + dynamic_rule_pair.second, version); } } std::vector active_dynamic_rules; dynamic_rules_.get_rules(active_dynamic_rules); for (const auto& dynamic_rule : active_dynamic_rules) { if (dynamic_rules.find(dynamic_rule.id()) == dynamic_rules.end()) { + optional op_version = + remove_dynamic_rule(dynamic_rule.id(), nullptr, uc); MLOG(MINFO) << "Removing dynamic rule " << dynamic_rule.id() << " for " << session_id_; - remove_dynamic_rule(dynamic_rule.id(), nullptr, uc); - rules_to_deactivate.rules.push_back(dynamic_rule); + rules_to_deactivate.append_versioned_policy(dynamic_rule, *op_version); } } } @@ -835,10 +845,20 @@ void SessionState::get_session_info(SessionState::SessionInfo& info) { dynamic_rules_.get_rules(info.gx_rules.rules); gy_dynamic_rules_.get_rules(info.gy_dynamic_rules.rules); + // Set versions + for (const PolicyRule rule : info.gx_rules.rules) { + info.gx_rules.versions.push_back(get_current_rule_version(rule.id())); + } + for (const PolicyRule rule : info.gy_dynamic_rules.rules) { + info.gy_dynamic_rules.versions.push_back( + get_current_rule_version(rule.id())); + } + for (const std::string& rule_id : active_static_rules_) { PolicyRule rule; if (static_rules_.get_rule(rule_id, &rule)) { - info.gx_rules.rules.push_back(rule); + info.gx_rules.append_versioned_policy( + rule, get_current_rule_version(rule_id)); } } } @@ -866,7 +886,7 @@ void SessionState::remove_all_rules_for_termination( } gy_dynamic_rules_.get_rules(gy_dynamic_rules); for (PolicyRule& policy : gy_dynamic_rules) { - remove_gy_dynamic_rule(policy.id(), nullptr, session_uc); + remove_gy_rule(policy.id(), nullptr, session_uc); } for (const std::string& rule_id : active_static_rules_) { deactivate_static_rule(rule_id, session_uc); @@ -874,7 +894,7 @@ void SessionState::remove_all_rules_for_termination( // remove scheduled rules for (const std::string& rule_id : scheduled_static_rules_) { - deactivate_scheduled_static_rule(rule_id, session_uc); + deactivate_scheduled_static_rule(rule_id); } scheduled_dynamic_rules_.get_rules(scheduled_dynamic_rules); for (PolicyRule& policy : scheduled_dynamic_rules) { @@ -946,49 +966,53 @@ bool SessionState::is_static_rule_installed(const std::string& rule_id) { rule_id) != active_static_rules_.end(); } -void SessionState::insert_dynamic_rule( +uint32_t SessionState::insert_dynamic_rule( const PolicyRule& rule, RuleLifetime& lifetime, - SessionStateUpdateCriteria& update_criteria) { - if (is_dynamic_rule_installed(rule.id())) { - return; - } + SessionStateUpdateCriteria& session_uc) { rule_lifetimes_[rule.id()] = lifetime; dynamic_rules_.insert_rule(rule); - update_criteria.dynamic_rules_to_install.push_back(rule); - update_criteria.new_rule_lifetimes[rule.id()] = lifetime; + session_uc.dynamic_rules_to_install.push_back(rule); + session_uc.new_rule_lifetimes[rule.id()] = lifetime; + + increment_rule_stats(rule.id(), session_uc); + return get_current_rule_version(rule.id()); } -void SessionState::insert_gy_dynamic_rule( +uint32_t SessionState::insert_gy_rule( const PolicyRule& rule, RuleLifetime& lifetime, - SessionStateUpdateCriteria& update_criteria) { - if (is_gy_dynamic_rule_installed(rule.id())) { - MLOG(MDEBUG) << "Tried to insert " << rule.id() - << " (gy dynamic rule), but it already existed"; - return; - } + SessionStateUpdateCriteria& session_uc) { rule_lifetimes_[rule.id()] = lifetime; gy_dynamic_rules_.insert_rule(rule); - update_criteria.gy_dynamic_rules_to_install.push_back(rule); - update_criteria.new_rule_lifetimes[rule.id()] = lifetime; + session_uc.gy_dynamic_rules_to_install.push_back(rule); + session_uc.new_rule_lifetimes[rule.id()] = lifetime; + + increment_rule_stats(rule.id(), session_uc); + return get_current_rule_version(rule.id()); } -void SessionState::activate_static_rule( +uint32_t SessionState::activate_static_rule( const std::string& rule_id, RuleLifetime& lifetime, - SessionStateUpdateCriteria& update_criteria) { + SessionStateUpdateCriteria& session_uc) { rule_lifetimes_[rule_id] = lifetime; active_static_rules_.push_back(rule_id); - update_criteria.static_rules_to_install.insert(rule_id); - update_criteria.new_rule_lifetimes[rule_id] = lifetime; -} + session_uc.static_rules_to_install.insert(rule_id); + session_uc.new_rule_lifetimes[rule_id] = lifetime; + + increment_rule_stats(rule_id, session_uc); + return get_current_rule_version(rule_id); +}; -bool SessionState::remove_dynamic_rule( +optional SessionState::remove_dynamic_rule( const std::string& rule_id, PolicyRule* rule_out, - SessionStateUpdateCriteria& update_criteria) { + SessionStateUpdateCriteria& session_uc) { bool removed = dynamic_rules_.remove_rule(rule_id, rule_out); - if (removed) { - update_criteria.dynamic_rules_to_uninstall.insert(rule_id); + if (!removed) { + return {}; } - return removed; + + session_uc.dynamic_rules_to_uninstall.insert(rule_id); + increment_rule_stats(rule_id, session_uc); + return get_current_rule_version(rule_id); } bool SessionState::remove_scheduled_dynamic_rule( @@ -1001,30 +1025,36 @@ bool SessionState::remove_scheduled_dynamic_rule( return removed; } -bool SessionState::remove_gy_dynamic_rule( +optional SessionState::remove_gy_rule( const std::string& rule_id, PolicyRule* rule_out, - SessionStateUpdateCriteria& update_criteria) { + SessionStateUpdateCriteria& session_uc) { bool removed = gy_dynamic_rules_.remove_rule(rule_id, rule_out); - if (removed) { - update_criteria.gy_dynamic_rules_to_uninstall.insert(rule_id); + if (!removed) { + return {}; } - return removed; + session_uc.gy_dynamic_rules_to_uninstall.insert(rule_id); + + increment_rule_stats(rule_id, session_uc); + return get_current_rule_version(rule_id); } -bool SessionState::deactivate_static_rule( - const std::string& rule_id, SessionStateUpdateCriteria& update_criteria) { +optional SessionState::deactivate_static_rule( + const std::string& rule_id, SessionStateUpdateCriteria& session_uc) { auto it = std::find( active_static_rules_.begin(), active_static_rules_.end(), rule_id); if (it == active_static_rules_.end()) { - return false; + return {}; } - update_criteria.static_rules_to_uninstall.insert(rule_id); + + session_uc.static_rules_to_uninstall.insert(rule_id); active_static_rules_.erase(it); - return true; + + increment_rule_stats(rule_id, session_uc); + return get_current_rule_version(rule_id); } bool SessionState::deactivate_scheduled_static_rule( - const std::string& rule_id, SessionStateUpdateCriteria& update_criteria) { + const std::string& rule_id) { if (scheduled_static_rules_.count(rule_id) == 0) { return false; } @@ -1033,21 +1063,22 @@ bool SessionState::deactivate_scheduled_static_rule( } void SessionState::sync_rules_to_time( - std::time_t current_time, SessionStateUpdateCriteria& update_criteria) { + std::time_t current_time, SessionStateUpdateCriteria& session_uc) { // Update active static rules for (const std::string& rule_id : active_static_rules_) { if (should_rule_be_deactivated(rule_id, current_time)) { - deactivate_static_rule(rule_id, update_criteria); + deactivate_static_rule(rule_id, session_uc); } } // Update scheduled static rules std::set scheduled_rule_ids = scheduled_static_rules_; for (const std::string& rule_id : scheduled_rule_ids) { if (should_rule_be_active(rule_id, current_time)) { - install_scheduled_static_rule(rule_id, update_criteria); + scheduled_static_rules_.erase(rule_id); + activate_static_rule(rule_id, rule_lifetimes_[rule_id], session_uc); } else if (should_rule_be_deactivated(rule_id, current_time)) { scheduled_static_rules_.erase(rule_id); - update_criteria.static_rules_to_uninstall.insert(rule_id); + deactivate_static_rule(rule_id, session_uc); } } // Update active dynamic rules @@ -1055,7 +1086,7 @@ void SessionState::sync_rules_to_time( dynamic_rules_.get_rule_ids(dynamic_rule_ids); for (const std::string& rule_id : dynamic_rule_ids) { if (should_rule_be_deactivated(rule_id, current_time)) { - remove_dynamic_rule(rule_id, NULL, update_criteria); + remove_dynamic_rule(rule_id, NULL, session_uc); } } // Update scheduled dynamic rules @@ -1063,9 +1094,11 @@ void SessionState::sync_rules_to_time( scheduled_dynamic_rules_.get_rule_ids(dynamic_rule_ids); for (const std::string& rule_id : dynamic_rule_ids) { if (should_rule_be_active(rule_id, current_time)) { - install_scheduled_dynamic_rule(rule_id, update_criteria); + PolicyRule dy_rule; + remove_scheduled_dynamic_rule(rule_id, &dy_rule, session_uc); + insert_dynamic_rule(dy_rule, rule_lifetimes_[rule_id], session_uc); } else if (should_rule_be_deactivated(rule_id, current_time)) { - remove_scheduled_dynamic_rule(rule_id, NULL, update_criteria); + remove_scheduled_dynamic_rule(rule_id, NULL, session_uc); } } } @@ -1123,32 +1156,6 @@ void SessionState::schedule_static_rule( scheduled_static_rules_.insert(rule_id); } -void SessionState::install_scheduled_dynamic_rule( - const std::string& rule_id, SessionStateUpdateCriteria& update_criteria) { - PolicyRule dynamic_rule; - bool removed = scheduled_dynamic_rules_.remove_rule(rule_id, &dynamic_rule); - if (!removed) { - MLOG(MERROR) << "Failed to mark a scheduled dynamic rule as installed " - << "with rule_id: " << rule_id; - return; - } - update_criteria.dynamic_rules_to_install.push_back(dynamic_rule); - dynamic_rules_.insert_rule(dynamic_rule); -} - -void SessionState::install_scheduled_static_rule( - const std::string& rule_id, SessionStateUpdateCriteria& update_criteria) { - auto it = scheduled_static_rules_.find(rule_id); - if (it == scheduled_static_rules_.end()) { - MLOG(MERROR) << "Failed to mark a scheduled static rule as installed " - "with rule_id: " - << rule_id; - } - update_criteria.static_rules_to_install.insert(rule_id); - scheduled_static_rules_.erase(rule_id); - active_static_rules_.push_back(rule_id); -} - uint32_t SessionState::get_credit_key_count() { return credit_map_.size() + monitor_map_.size(); } @@ -1377,30 +1384,29 @@ bool SessionState::is_credit_suspended(const CreditKey& charging_key) { return false; } -void SessionState::get_unsuspended_rules(RulesToProcess& rulesToProcess) { - for (auto const& it : credit_map_) { - CreditKey ckey = it.first; - if (!it.second->get_suspended()) { - get_rules_per_credit_key(ckey, rulesToProcess); - } - } -} - void SessionState::get_rules_per_credit_key( - CreditKey charging_key, RulesToProcess& rulesToProcess) { - std::vector static_rules; - static_rules_.get_rule_ids_for_charging_key(charging_key, static_rules); - for (auto rule_id : static_rules) { - PolicyRule rule; - if (static_rules_.get_rule(rule_id, &rule)) { - rulesToProcess.rules.push_back(rule); - } else { - MLOG(MWARNING) << "Static rule " << rule_id - << " is not found in the system"; + CreditKey charging_key, RulesToProcess& to_process, + SessionStateUpdateCriteria& session_uc) { + std::vector static_rules, dynamic_rules; + static_rules_.get_rule_definitions_for_charging_key( + charging_key, static_rules); + for (PolicyRule rule : static_rules) { + // Since the static rule store is shared across sessions, we should check + // that the rule is activated for the session + bool is_installed = is_static_rule_installed(rule.id()); + if (is_installed) { + increment_rule_stats(rule.id(), session_uc); + to_process.append_versioned_policy( + rule, get_current_rule_version(rule.id())); } } dynamic_rules_.get_rule_definitions_for_charging_key( - charging_key, rulesToProcess.rules); + charging_key, dynamic_rules); + for (PolicyRule rule : dynamic_rules) { + increment_rule_stats(rule.id(), session_uc); + to_process.append_versioned_policy( + rule, get_current_rule_version(rule.id())); + } } uint64_t SessionState::get_charging_credit( @@ -1515,8 +1521,8 @@ CreditUsageUpdate SessionState::make_credit_usage_update_req( fill_protos_tgpp_context(req.mutable_tgpp_ctx()); req.mutable_common_context()->CopyFrom(config_.common_context); - // TODO keep RAT specific fields separate for now as we may not always want to - // send the entire context + // TODO keep RAT specific fields separate for now as we may not always want + // to send the entire context if (config_.rat_specific_context.has_lte_context()) { const auto& lte_context = config_.rat_specific_context.lte_context(); req.set_spgw_ipv4(lte_context.spgw_ipv4()); @@ -1563,10 +1569,8 @@ void SessionState::get_charging_updates( PolicyRule redirect_rule = make_redirect_rule(grant); if (!is_gy_dynamic_rule_installed(redirect_rule.id())) { - RuleLifetime lifetime; - insert_gy_dynamic_rule(redirect_rule, lifetime, uc); - fill_service_action_with_context(action, action_type, key); - fill_service_action_for_redirect(action, key, grant, redirect_rule); + fill_service_action_for_redirect( + action, key, grant, redirect_rule, uc); actions_out->push_back(std::move(action)); } @@ -1579,14 +1583,12 @@ void SessionState::get_charging_updates( } grant->set_service_state(SERVICE_RESTRICTED, *credit_uc); - fill_service_action_with_context(action, action_type, key); - fill_service_action_for_restrict(action, key, grant); + fill_service_action_for_restrict(action, key, grant, uc); actions_out->push_back(std::move(action)); break; } case ACTIVATE_SERVICE: - fill_service_action_with_context(action, action_type, key); - fill_service_action_for_activate(action, key); + fill_service_action_for_activate(action, key, uc); actions_out->push_back(std::move(action)); grant->set_suspended(false, credit_uc); break; @@ -1643,20 +1645,32 @@ optional SessionState::get_update_for_continue_service( } void SessionState::fill_service_action_for_activate( - std::unique_ptr& action_p, const CreditKey& key) { - std::vector rules; - static_rules_.get_rules_by_ids(active_static_rules_, rules); - dynamic_rules_.get_rule_definitions_for_charging_key(key, rules); + std::unique_ptr& action_p, const CreditKey& key, + SessionStateUpdateCriteria& session_uc) { + std::vector static_rules, dynamic_rules; + fill_service_action_with_context(action_p, ACTIVATE_SERVICE, key); + static_rules_.get_rules_by_ids(active_static_rules_, static_rules); + dynamic_rules_.get_rule_definitions_for_charging_key(key, dynamic_rules); RulesToProcess* to_install = action_p->get_mutable_gx_rules_to_install(); - for (PolicyRule rule : rules) { - to_install->rules.push_back(rule); + for (PolicyRule rule : static_rules) { + RuleLifetime lifetime; + uint32_t version = activate_static_rule(rule.id(), lifetime, session_uc); + to_install->append_versioned_policy(rule, version); + } + for (PolicyRule rule : dynamic_rules) { + RuleLifetime lifetime; + uint32_t version = insert_dynamic_rule(rule, lifetime, session_uc); + to_install->append_versioned_policy(rule, version); } } void SessionState::fill_service_action_for_restrict( std::unique_ptr& action_p, const CreditKey& key, - std::unique_ptr& grant) { + std::unique_ptr& grant, + SessionStateUpdateCriteria& session_uc) { + fill_service_action_with_context(action_p, RESTRICT_ACCESS, key); + RulesToProcess* gy_to_install = action_p->get_mutable_gy_rules_to_install(); for (auto& rule_id : grant->final_action_info.restrict_rules) { PolicyRule rule; @@ -1664,9 +1678,10 @@ void SessionState::fill_service_action_for_restrict( MLOG(MWARNING) << "Static rule " << rule_id << " requested as a restrict rule is not found."; continue; - } else { - gy_to_install->rules.push_back(rule); } + RuleLifetime lifetime; + uint32_t version = insert_gy_rule(rule, lifetime, session_uc); + gy_to_install->append_versioned_policy(rule, version); } } @@ -1705,9 +1720,14 @@ PolicyRule SessionState::make_redirect_rule( void SessionState::fill_service_action_for_redirect( std::unique_ptr& action_p, const CreditKey& key, - std::unique_ptr& grant, PolicyRule redirect_rule) { + std::unique_ptr& grant, PolicyRule redirect_rule, + SessionStateUpdateCriteria& session_uc) { + fill_service_action_with_context(action_p, REDIRECT, key); + RulesToProcess* gy_to_install = action_p->get_mutable_gy_rules_to_install(); - gy_to_install->rules.push_back(make_redirect_rule(grant)); + RuleLifetime lifetime; + uint32_t version = insert_gy_rule(redirect_rule, lifetime, session_uc); + gy_to_install->append_versioned_policy(redirect_rule, version); } void SessionState::fill_service_action_with_context( @@ -2004,8 +2024,8 @@ void SessionState::get_event_trigger_updates( new_req->set_event_trigger(REVALIDATION_TIMEOUT); request_number_++; update_criteria.request_number_increment++; - // todo we might want to make sure that the update went successfully before - // clearing here + // todo we might want to make sure that the update went successfully + // before clearing here remove_event_trigger(REVALIDATION_TIMEOUT, update_criteria); } } @@ -2077,16 +2097,19 @@ RulesToProcess SessionState::remove_all_final_action_rules( switch (final_action_info.final_action) { case ChargingCredit_FinalAction_REDIRECT: { PolicyRule rule; - if (remove_gy_dynamic_rule("redirect", &rule, session_uc)) { - to_process.rules.push_back(rule); + optional op_version = + remove_gy_rule("redirect", &rule, session_uc); + if (op_version) { + to_process.append_versioned_policy(rule, *op_version); } } break; case ChargingCredit_FinalAction_RESTRICT_ACCESS: for (std::string rule_id : final_action_info.restrict_rules) { PolicyRule rule; - if (static_rules_.get_rule(rule_id, &rule)) { - to_process.rules.push_back(rule); - deactivate_static_rule(rule_id, session_uc); + optional op_version = + remove_gy_rule(rule_id, &rule, session_uc); + if (op_version) { + to_process.append_versioned_policy(rule, *op_version); } } break; @@ -2295,4 +2318,36 @@ bool RulesToProcess::empty() const { return rules.empty(); } +void RulesToProcess::append_versioned_policy( + PolicyRule rule, uint32_t version) { + rules.push_back(rule); + versions.push_back(version); +} + +uint32_t SessionState::get_current_rule_version(const std::string& rule_id) { + if (policy_version_and_stats_.find(rule_id) == + policy_version_and_stats_.end()) { + MLOG(MWARNING) << "RuleID " << rule_id + << " doesn't have a version registered for " << session_id_ + << ", this is unexpected"; + return 0; + } + return policy_version_and_stats_[rule_id].current_version; +} + +void SessionState::increment_rule_stats( + const std::string& rule_id, SessionStateUpdateCriteria& session_uc) { + if (policy_version_and_stats_.find(rule_id) == + policy_version_and_stats_.end()) { + policy_version_and_stats_[rule_id] = StatsPerPolicy(); + policy_version_and_stats_[rule_id].current_version = 0; + policy_version_and_stats_[rule_id].last_reported_version = 0; + } + policy_version_and_stats_[rule_id].current_version++; + + if (!session_uc.policy_version_and_stats) { + session_uc.policy_version_and_stats = policy_version_and_stats_; + } +} + } // namespace magma diff --git a/lte/gateway/c/session_manager/SessionState.h b/lte/gateway/c/session_manager/SessionState.h index db6859399e88..d0c174b23842 100644 --- a/lte/gateway/c/session_manager/SessionState.h +++ b/lte/gateway/c/session_manager/SessionState.h @@ -38,7 +38,6 @@ typedef std::unordered_map< CreditKey, SessionCredit::Summary, decltype(&ccHash), decltype(&ccEqual)> ChargingCreditSummaries; typedef std::unordered_map> MonitorMap; -static SessionStateUpdateCriteria UNUSED_UPDATE_CRITERIA; // Used to transform the proto message RuleSet into a more useful structure struct RuleSetToApply { @@ -300,6 +299,14 @@ class SessionState { */ optional get_policy_type(const std::string& rule_id); + /** + * @brief Get the current rule version object + * + * @param rule_id + * @return uint32_t + */ + uint32_t get_current_rule_version(const std::string& rule_id); + bool is_dynamic_rule_installed(const std::string& rule_id); bool is_gy_dynamic_rule_installed(const std::string& rule_id); @@ -307,22 +314,41 @@ class SessionState { bool is_static_rule_installed(const std::string& rule_id); /** - * Add a dynamic rule to the session which is currently active. + * @brief Add a dynamic rule into dynamic rule store. Increment the associated + * version and return the new version. + * + * @param rule + * @param lifetime + * @param session_uc + * @return uint32_t updated version */ - void insert_dynamic_rule( + uint32_t insert_dynamic_rule( const PolicyRule& rule, RuleLifetime& lifetime, - SessionStateUpdateCriteria& update_criteria); + SessionStateUpdateCriteria& session_uc); /** - * Add a static rule to the session which is currently active. + * @brief Insert a static rule into active_static_rules_. Increment the + * associated version and return the new version. + * + * @param rule_id + * @param lifetime + * @param session_uc + * @return uint32_t updated version */ - void activate_static_rule( + uint32_t activate_static_rule( const std::string& rule_id, RuleLifetime& lifetime, - SessionStateUpdateCriteria& update_criteria); - - void insert_gy_dynamic_rule( + SessionStateUpdateCriteria& session_uc); + /** + * @brief Insert a PolicyRule into gy_dynamic_rules_ + * + * @param rule + * @param lifetime + * @param update_criteria + * @return uint32_t updated version + */ + uint32_t insert_gy_rule( const PolicyRule& rule, RuleLifetime& lifetime, - SessionStateUpdateCriteria& update_criteria); + SessionStateUpdateCriteria& session_uc); /** * Remove a currently active dynamic rule to mark it as deactivated. @@ -332,9 +358,9 @@ class SessionState { * @param update_criteria Tracks updates to the session. To be passed back to * the SessionStore to resolve issues of concurrent * updates to a session. - * @return True if successfully removed. + * @return optional updated version if success, {} if failure */ - bool remove_dynamic_rule( + optional remove_dynamic_rule( const std::string& rule_id, PolicyRule* rule_out, SessionStateUpdateCriteria& update_criteria); @@ -342,24 +368,32 @@ class SessionState { const std::string& rule_id, PolicyRule* rule_out, SessionStateUpdateCriteria& update_criteria); - bool remove_gy_dynamic_rule( + /** + * @brief Remove a Gy rule from SessionState and increment the corresponding + * version + * + * @param rule_id + * @param rule_out + * @param session_uc + * @return optional updated version if success, {} if failure + */ + optional remove_gy_rule( const std::string& rule_id, PolicyRule* rule_out, - SessionStateUpdateCriteria& update_criteria); + SessionStateUpdateCriteria& session_uc); /** * Remove a currently active static rule to mark it as deactivated. * * @param rule_id ID of the rule to be removed. - * @param update_criteria Tracks updates to the session. To be passed back to + * @param session_uc Tracks updates to the session. To be passed back to * the SessionStore to resolve issues of concurrent * updates to a session. - * @return True if successfully removed. + * @return new version if successfully removed. otherwise returns {} */ - bool deactivate_static_rule( - const std::string& rule_id, SessionStateUpdateCriteria& update_criteria); + optional deactivate_static_rule( + const std::string& rule_id, SessionStateUpdateCriteria& session_uc); - bool deactivate_scheduled_static_rule( - const std::string& rule_id, SessionStateUpdateCriteria& update_criteria); + bool deactivate_scheduled_static_rule(const std::string& rule_id); std::vector& get_static_rules(); @@ -378,6 +412,8 @@ class SessionState { const PolicyRule& rule, RuleLifetime& lifetime, SessionStateUpdateCriteria& update_criteria); + bool is_static_rule_scheduled(const std::string& rule_id); + /** * Schedule a static rule for activation in the future. */ @@ -385,18 +421,6 @@ class SessionState { const std::string& rule_id, RuleLifetime& lifetime, SessionStateUpdateCriteria& update_criteria); - /** - * Mark a scheduled dynamic rule as activated. - */ - void install_scheduled_dynamic_rule( - const std::string& rule_id, SessionStateUpdateCriteria& update_criteria); - - /** - * Mark a scheduled static rule as activated. - */ - void install_scheduled_static_rule( - const std::string& rule_id, SessionStateUpdateCriteria& update_criteria); - void set_suspend_credit( const CreditKey& charging_key, bool new_suspended, SessionStateUpdateCriteria& update_criteria); @@ -423,10 +447,9 @@ class SessionState { SessionFsmState get_state(); - void get_unsuspended_rules(RulesToProcess& rulesToProcess); - void get_rules_per_credit_key( - CreditKey charging_key, RulesToProcess& rulesToProcess); + CreditKey charging_key, RulesToProcess& rulesToProcess, + SessionStateUpdateCriteria& session_uc); /** * Remove all active/scheduled static/dynamic rules and reflect the change in @@ -576,7 +599,7 @@ class SessionState { // Used between create session and activate session. Empty afterwards CreateSessionResponse create_session_response_; - // Track version tracking information + // Track version tracking information used for LTE/WLAN PolicyStatsMap policy_version_and_stats_; // All static rules synced from policy DB @@ -643,17 +666,20 @@ class SessionState { SessionStateUpdateCriteria& session_uc); void fill_service_action_for_activate( - std::unique_ptr& action, const CreditKey& key); + std::unique_ptr& action, const CreditKey& key, + SessionStateUpdateCriteria& session_uc); void fill_service_action_for_restrict( std::unique_ptr& action_p, const CreditKey& key, - std::unique_ptr& grant); + std::unique_ptr& grant, + SessionStateUpdateCriteria& session_uc); PolicyRule make_redirect_rule(std::unique_ptr& grant); void fill_service_action_for_redirect( std::unique_ptr& action_p, const CreditKey& key, - std::unique_ptr& grant, PolicyRule redirect_rule); + std::unique_ptr& grant, PolicyRule redirect_rule, + SessionStateUpdateCriteria& session_uc); void fill_service_action_with_context( std::unique_ptr& action, ServiceActionType action_type, @@ -732,8 +758,6 @@ class SessionState { UpdateSessionRequest& update_request_out, SessionStateUpdateCriteria& update_criteria); - bool is_static_rule_scheduled(const std::string& rule_id); - /** apply static_rules which is the desired state for the session's rules **/ void apply_session_static_rule_set( std::unordered_set static_rules, @@ -793,6 +817,15 @@ class SessionState { */ void update_data_metrics( const char* counter_name, uint64_t bytes_tx, uint64_t bytes_rx); + + // PolicyStatsMap functions + /** + * + * @param rule_id + * @param session_uc + */ + void increment_rule_stats( + const std::string& rule_id, SessionStateUpdateCriteria& session_uc); }; } // namespace magma diff --git a/lte/gateway/c/session_manager/Types.h b/lte/gateway/c/session_manager/Types.h index 6f0e940a1005..8cba4d0decd8 100644 --- a/lte/gateway/c/session_manager/Types.h +++ b/lte/gateway/c/session_manager/Types.h @@ -185,7 +185,9 @@ struct RulesToProcess { // If this vector is set, then it has PolicyRule definitions for both static // and dynamic rules std::vector rules; + std::vector versions; bool empty() const; + void append_versioned_policy(PolicyRule rule, uint32_t version); }; struct StatsPerPolicy { diff --git a/lte/gateway/c/session_manager/test/Matchers.h b/lte/gateway/c/session_manager/test/Matchers.h index 4d52353347b8..8af5fbcd6903 100644 --- a/lte/gateway/c/session_manager/test/Matchers.h +++ b/lte/gateway/c/session_manager/test/Matchers.h @@ -102,9 +102,9 @@ MATCHER_P3(CheckTerminateRequestCount, imsi, monitorCount, chargingCount, "") { req.monitor_usages().size() == monitorCount; } -MATCHER_P5( +MATCHER_P6( CheckSessionInfos, imsi_list, ip_address_list, ipv6_address_list, cfg, - rule_ids_lists, "") { + rule_ids_lists, versions_lists, "") { auto infos = static_cast>(arg); if (infos.size() != imsi_list.size()) { @@ -131,6 +131,13 @@ MATCHER_P5( if (info.gx_rules.rules[r_index].id() != expected_gx_rules[r_index]) return false; } + + std::vector expected_versions = versions_lists[i]; + for (size_t r_index = 0; i < info.gx_rules.versions.size(); i++) { + if (info.gx_rules.versions[r_index] != expected_versions[r_index]) + return false; + } + // check ambr field if config has qos_info if (cfg.rat_specific_context.has_lte_context() && cfg.rat_specific_context.lte_context().has_qos_info()) { diff --git a/lte/gateway/c/session_manager/test/SessionStateTester.h b/lte/gateway/c/session_manager/test/SessionStateTester.h index dae0b25304ad..8565ff24213c 100644 --- a/lte/gateway/c/session_manager/test/SessionStateTester.h +++ b/lte/gateway/c/session_manager/test/SessionStateTester.h @@ -38,10 +38,6 @@ class SessionStateTest : public ::testing::Test { pdp_start_time, CreateSessionResponse{}); update_criteria = get_default_update_criteria(); } - enum RuleType { - STATIC = 0, - DYNAMIC = 1, - }; void insert_static_rule_into_store( uint32_t rating_group, const std::string& m_key, @@ -51,9 +47,9 @@ class SessionStateTest : public ::testing::Test { rule_store->insert_rule(rule); } - void insert_rule( + uint32_t insert_rule( uint32_t rating_group, const std::string& m_key, - const std::string& rule_id, RuleType rule_type, + const std::string& rule_id, PolicyType rule_type, std::time_t activation_time, std::time_t deactivation_time) { PolicyRule rule; create_policy_rule(rule_id, m_key, rating_group, &rule); @@ -63,17 +59,19 @@ class SessionStateTest : public ::testing::Test { // insert into list of existing rules rule_store->insert_rule(rule); // mark the rule as active in session - session_state->activate_static_rule(rule_id, lifetime, update_criteria); - break; + return session_state->activate_static_rule( + rule_id, lifetime, update_criteria); case DYNAMIC: - session_state->insert_dynamic_rule(rule, lifetime, update_criteria); + return session_state->insert_dynamic_rule( + rule, lifetime, update_criteria); break; } + return 0; } void schedule_rule( uint32_t rating_group, const std::string& m_key, - const std::string& rule_id, RuleType rule_type, + const std::string& rule_id, PolicyType rule_type, std::time_t activation_time, std::time_t deactivation_time) { PolicyRule rule; create_policy_rule(rule_id, m_key, rating_group, &rule); @@ -108,7 +106,7 @@ class SessionStateTest : public ::testing::Test { } } - void insert_gy_redirection_rule(const std::string& rule_id) { + uint32_t insert_gy_redirection_rule(const std::string& rule_id) { PolicyRule redirect_rule; redirect_rule.set_id(rule_id); redirect_rule.set_priority(999); @@ -126,7 +124,7 @@ class SessionStateTest : public ::testing::Test { redirect_server.redirect_server_address()); RuleLifetime lifetime{}; - session_state->insert_gy_dynamic_rule( + return session_state->insert_gy_rule( redirect_rule, lifetime, update_criteria); } @@ -163,9 +161,9 @@ class SessionStateTest : public ::testing::Test { session_state->receive_monitor(monitor_resp, update_criteria); } - void activate_rule( + uint32_t activate_rule( uint32_t rating_group, const std::string& m_key, - const std::string& rule_id, RuleType rule_type, + const std::string& rule_id, PolicyType rule_type, std::time_t activation_time, std::time_t deactivation_time) { PolicyRule rule; create_policy_rule(rule_id, m_key, rating_group, &rule); @@ -173,12 +171,17 @@ class SessionStateTest : public ::testing::Test { switch (rule_type) { case STATIC: rule_store->insert_rule(rule); - session_state->activate_static_rule(rule_id, lifetime, update_criteria); + return session_state->activate_static_rule( + rule_id, lifetime, update_criteria); break; case DYNAMIC: - session_state->insert_dynamic_rule(rule, lifetime, update_criteria); + return session_state->insert_dynamic_rule( + rule, lifetime, update_criteria); + break; + default: break; } + return 0; } protected: diff --git a/lte/gateway/c/session_manager/test/test_local_enforcer.cpp b/lte/gateway/c/session_manager/test/test_local_enforcer.cpp index 037efdfa9380..0111ce191dc5 100644 --- a/lte/gateway/c/session_manager/test/test_local_enforcer.cpp +++ b/lte/gateway/c/session_manager/test/test_local_enforcer.cpp @@ -762,12 +762,18 @@ TEST_F(LocalEnforcerTest, test_sync_sessions_on_restart) { .activation_time = std::time_t(15), .deactivation_time = std::time_t(20), }; - auto& uc = session_update[IMSI1][SESSION_ID_1]; - session_map_2[IMSI1].front()->activate_static_rule("rule1", lifetime1, uc); + auto& uc = session_update[IMSI1][SESSION_ID_1]; + uint32_t v1 = session_map_2[IMSI1].front()->activate_static_rule( + "rule1", lifetime1, uc); session_map_2[IMSI1].front()->schedule_static_rule("rule2", lifetime2, uc); session_map_2[IMSI1].front()->schedule_static_rule("rule3", lifetime3, uc); session_map_2[IMSI1].front()->schedule_static_rule("rule4", lifetime4, uc); + EXPECT_EQ(v1, 1); + + EXPECT_TRUE(uc.policy_version_and_stats); + EXPECT_EQ((*uc.policy_version_and_stats)["rule1"].current_version, 1); + EXPECT_EQ(uc.static_rules_to_install.count("rule1"), 1); EXPECT_EQ(uc.new_scheduled_static_rules.count("rule2"), 1); EXPECT_EQ(uc.new_scheduled_static_rules.count("rule3"), 1); @@ -1088,6 +1094,7 @@ TEST_F(LocalEnforcerTest, test_credit_init_with_transient_error_redirect) { // insert initial session credit CreateSessionResponse response; auto credits = response.mutable_credits(); + response.mutable_static_rules()->Add()->set_rule_id("rule1"); create_credit_update_response_with_error( IMSI1, SESSION_ID_1, 1, false, DIAMETER_CREDIT_LIMIT_REACHED, ChargingCredit_FinalAction_REDIRECT, "12.7.7.4", "", credits->Add()); @@ -1169,12 +1176,16 @@ TEST_F(LocalEnforcerTest, test_update_with_transient_error) { insert_static_rule(1, "", "rule2"); insert_static_rule(2, "", "rule3"); - // insert initial session credit + // insert initial session credit + rules CreateSessionResponse response; create_credit_update_response( IMSI1, SESSION_ID_1, 1, 1024, response.mutable_credits()->Add()); create_credit_update_response( IMSI1, SESSION_ID_1, 2, 1024, response.mutable_credits()->Add()); + response.mutable_static_rules()->Add()->set_rule_id("rule1"); + response.mutable_static_rules()->Add()->set_rule_id("rule2"); + response.mutable_static_rules()->Add()->set_rule_id("rule3"); + local_enforcer->init_session( session_map, IMSI1, SESSION_ID_1, test_cfg_, response); local_enforcer->update_tunnel_ids( @@ -1218,6 +1229,7 @@ TEST_F(LocalEnforcerTest, test_reauth_with_redirected_suspended_credit) { // 1- INITIAL SET UP TO CREATE A REDIRECTED due to SUSPENDED CREDIT // insert initial suspended and redirected credit CreateSessionResponse response; + response.mutable_static_rules()->Add()->set_rule_id("rule1"); auto credits = response.mutable_credits(); test_cfg_.common_context.mutable_sid()->set_id(IMSI1); create_credit_update_response_with_error( @@ -1427,7 +1439,7 @@ TEST_F(LocalEnforcerTest, test_installing_rules_with_activation_time) { CreateSessionResponse response; create_credit_update_response( IMSI1, SESSION_ID_1, 1, 1024, true, response.mutable_credits()->Add()); - auto now = time(NULL); + auto now = time(nullptr); // add a dynamic rule without activation time auto dynamic_rule = response.mutable_dynamic_rules()->Add(); @@ -1826,7 +1838,7 @@ TEST_F(LocalEnforcerTest, test_rar_create_dedicated_bearer) { std::vector usage_monitoring_credits; create_policy_reauth_request( SESSION_ID_1, IMSI1, rules_to_remove, rules_to_install, - dynamic_rules_to_install, event_triggers, time(NULL), + dynamic_rules_to_install, event_triggers, time(nullptr), usage_monitoring_credits, &rar); auto rar_qos_info = rar.mutable_qos_info(); rar_qos_info->set_qci(QCI_1); @@ -2180,7 +2192,7 @@ TEST_F(LocalEnforcerTest, test_rar_session_not_found) { // verify session validity by passing in an invalid IMSI PolicyReAuthRequest rar; create_policy_reauth_request( - "session1", IMSI1, {}, {}, {}, {}, time(NULL), {}, &rar); + "session1", IMSI1, {}, {}, {}, {}, time(nullptr), {}, &rar); PolicyReAuthAnswer raa; auto update = SessionStore::get_default_session_update(session_map); local_enforcer->init_policy_reauth(session_map, rar, raa, update); @@ -2212,7 +2224,7 @@ TEST_F(LocalEnforcerTest, test_revalidation_timer_on_init) { monitor); response.add_event_triggers(EventTrigger::REVALIDATION_TIMEOUT); - response.mutable_revalidation_time()->set_seconds(time(NULL)); + response.mutable_revalidation_time()->set_seconds(time(nullptr)); StaticRuleInstall static_rule_install; static_rule_install.set_rule_id("rule1"); @@ -2267,7 +2279,7 @@ TEST_F(LocalEnforcerTest, test_revalidation_timer_on_rar) { // Create a RaR with a REVALIDATION event trigger create_policy_reauth_request( - SESSION_ID_1, IMSI1, {}, {}, {}, event_triggers, time(NULL), {}, &rar); + SESSION_ID_1, IMSI1, {}, {}, {}, event_triggers, time(nullptr), {}, &rar); auto update = SessionStore::get_default_session_update(session_map); // This should trigger a revalidation to be scheduled @@ -2330,7 +2342,7 @@ TEST_F(LocalEnforcerTest, test_revalidation_timer_on_update) { EXPECT_EQ(session_map[IMSI1].size(), 1); EXPECT_EQ(session_map[IMSI2].size(), 1); - auto revalidation_timer = time(NULL); + auto revalidation_timer = time(nullptr); // IMSI1 has two separate monitors with the same revalidation timer // IMSI2 does not have a revalidation timer auto monitor = update_response.mutable_usage_monitor_responses()->Add(); @@ -2398,7 +2410,7 @@ TEST_F(LocalEnforcerTest, test_revalidation_timer_on_update_no_monitor) { monitor->set_sid(IMSI1); monitor->set_session_id(SESSION_ID_1); monitor->add_event_triggers(EventTrigger::REVALIDATION_TIMEOUT); - monitor->mutable_revalidation_time()->set_seconds(time(NULL)); + monitor->mutable_revalidation_time()->set_seconds(time(nullptr)); auto update = SessionStore::get_default_session_update(session_map); // This should trigger a revalidation to be scheduled local_enforcer->update_session_credits_and_rules( @@ -2467,6 +2479,7 @@ TEST_F(LocalEnforcerTest, test_pipelined_cwf_setup) { std::vector ipv6_address_list = {"", ""}; std::vector> rule_list = {{"rule22"}, {"rule1", "rule2"}}; + std::vector> version_list = {{1}, {1, 1}}; std::vector ue_mac_addrs = {"00:00:00:00:00:02", "11:22:00:00:22:11"}; @@ -2478,7 +2491,7 @@ TEST_F(LocalEnforcerTest, test_pipelined_cwf_setup) { *pipelined_client, setup_cwf( CheckSessionInfos( imsi_list, ip_address_list, ipv6_address_list, - test_cwf_cfg2, rule_list), + test_cwf_cfg2, rule_list, version_list), testing::_, ue_mac_addrs, msisdns, apn_mac_addrs, apn_names, testing::_, testing::_, testing::_)) .Times(1); @@ -2534,6 +2547,7 @@ TEST_F(LocalEnforcerTest, test_pipelined_lte_setup) { std::vector ipv6_address_list = {IPv6_1, ""}; std::vector> rule_list = {{"rule22"}, {"rule1", "rule2"}}; + std::vector> version_list = {{1}, {1, 1}}; std::vector ue_mac_addrs = {"00:00:00:00:00:02", "11:22:00:00:22:11"}; @@ -2545,7 +2559,7 @@ TEST_F(LocalEnforcerTest, test_pipelined_lte_setup) { *pipelined_client, setup_lte( CheckSessionInfos( imsi_list, ip_address_list, ipv6_address_list, - test_cfg_, rule_list), + test_cfg_, rule_list, version_list), testing::_, testing::_)) .Times(1); diff --git a/lte/gateway/c/session_manager/test/test_session_state.cpp b/lte/gateway/c/session_manager/test/test_session_state.cpp index 724c85ce9e07..ef739b4ca6e2 100644 --- a/lte/gateway/c/session_manager/test/test_session_state.cpp +++ b/lte/gateway/c/session_manager/test/test_session_state.cpp @@ -48,12 +48,14 @@ TEST_F(SessionStateTest, test_session_rules) { EXPECT_EQ(session_state->is_static_rule_installed("rule3"), true); EXPECT_EQ(session_state->is_static_rule_installed("rule_DNE"), false); + EXPECT_EQ(session_state->get_current_rule_version("rule2"), 1); + EXPECT_EQ(session_state->get_current_rule_version("rule3"), 1); + // Test rule removals PolicyRule rule_out; session_state->deactivate_static_rule("rule2", update_criteria); EXPECT_EQ(1, session_state->total_monitored_rules_count()); - EXPECT_EQ( - true, + EXPECT_TRUE( session_state->remove_dynamic_rule("rule1", &rule_out, update_criteria)); EXPECT_EQ("m1", rule_out.monitoring_key()); EXPECT_EQ(0, session_state->total_monitored_rules_count()); @@ -98,13 +100,20 @@ TEST_F(SessionStateTest, test_rule_scheduling) { // Now suppose some time has passed, and it's time to mark scheduled rules // as active. The responsibility is given to the session owner to make // these calls - session_state->install_scheduled_dynamic_rule("rule1", _uc); + PolicyRule rule; + session_state->remove_scheduled_dynamic_rule("rule1", &rule, _uc); + session_state->insert_dynamic_rule( + rule, session_state->get_rule_lifetime("rule1"), _uc); EXPECT_EQ(1, session_state->total_monitored_rules_count()); EXPECT_TRUE(session_state->is_dynamic_rule_installed("rule1")); - session_state->install_scheduled_static_rule("rule2", _uc); + session_state->activate_static_rule( + "rule2", session_state->get_rule_lifetime("rule2"), _uc); EXPECT_EQ(2, session_state->total_monitored_rules_count()); EXPECT_TRUE(session_state->is_static_rule_installed("rule2")); + + EXPECT_EQ(session_state->get_current_rule_version("rule1"), 1); + EXPECT_EQ(session_state->get_current_rule_version("rule2"), 1); } /** @@ -150,7 +159,6 @@ TEST_F(SessionStateTest, test_rule_time_sync) { EXPECT_TRUE(uc.dynamic_rules_to_uninstall.count("d3")); EXPECT_TRUE(uc.static_rules_to_install.count("s1")); - EXPECT_TRUE(uc.static_rules_to_uninstall.count("s3")); // Update the time once more, sync again, and check expectations test_time = std::time_t(16); @@ -290,12 +298,10 @@ TEST_F(SessionStateTest, test_add_rule_usage) { EXPECT_EQ(update.usage_monitors_size(), 2); PolicyRule policy_out; - EXPECT_EQ( - true, session_state->remove_dynamic_rule( - "dyn_rule1", &policy_out, update_criteria)); - EXPECT_EQ( - true, session_state->deactivate_static_rule("rule1", update_criteria)); - EXPECT_EQ(false, session_state->active_monitored_rules_exist()); + EXPECT_TRUE(session_state->remove_dynamic_rule( + "dyn_rule1", &policy_out, update_criteria)); + EXPECT_TRUE(session_state->deactivate_static_rule("rule1", update_criteria)); + EXPECT_FALSE(session_state->active_monitored_rules_exist()); EXPECT_TRUE( std::find( update_criteria.dynamic_rules_to_uninstall.begin(), @@ -642,7 +648,8 @@ TEST_F(SessionStateTest, test_get_total_credit_usage_multiple_rule_shared_key) { } TEST_F(SessionStateTest, test_install_gy_rules) { - insert_gy_redirection_rule("redirect"); + uint32_t version = insert_gy_redirection_rule("redirect"); + EXPECT_EQ(1, version); std::vector rules_out{}; std::vector& rules_out_ptr = rules_out; @@ -655,9 +662,11 @@ TEST_F(SessionStateTest, test_install_gy_rules) { EXPECT_EQ(update_criteria.gy_dynamic_rules_to_install.size(), 1); PolicyRule rule_out; - EXPECT_EQ( - true, session_state->remove_gy_dynamic_rule( - "redirect", &rule_out, update_criteria)); + optional op_version = + session_state->remove_gy_rule("redirect", &rule_out, update_criteria); + EXPECT_TRUE(op_version); + EXPECT_EQ(*op_version, 2); + // basic sanity checks to see it's properly deleted rules_out = {}; session_state->get_gy_dynamic_rules().get_rule_ids(rules_out_ptr); From 69649c38486a2251baa808c8f2254eadf47b2960 Mon Sep 17 00:00:00 2001 From: Oriol Batalla Date: Mon, 5 Apr 2021 12:44:12 -0700 Subject: [PATCH 23/91] [feg] Add ability to use orc8r with S6a_cli (#5677) --- feg/gateway/services/s8_proxy/client_api.go | 13 ++++++- .../services/s8_proxy/servicers/s8_proxy.go | 13 +++++++ .../s8_proxy/servicers/s8_proxy_test.go | 35 +++++++++++++++++-- feg/gateway/tools/s6a_cli/main.go | 10 ++++++ feg/gateway/tools/s8_cli/main.go | 26 ++++++++++---- 5 files changed, 87 insertions(+), 10 deletions(-) diff --git a/feg/gateway/services/s8_proxy/client_api.go b/feg/gateway/services/s8_proxy/client_api.go index 428eb5783a68..fde77898f2b1 100644 --- a/feg/gateway/services/s8_proxy/client_api.go +++ b/feg/gateway/services/s8_proxy/client_api.go @@ -17,11 +17,14 @@ import ( "context" "errors" "fmt" + "strings" "magma/feg/cloud/go/protos" "magma/feg/gateway/registry" + "magma/orc8r/lib/go/util" "github.com/golang/glog" + "google.golang.org/grpc" ) type s8ProxyClient struct { @@ -62,7 +65,15 @@ func SendEcho(req *protos.EchoRequest) (*protos.EchoResponse, error) { } func getS8ProxyClient() (*s8ProxyClient, error) { - conn, err := registry.GetConnection(registry.S8_PROXY) + var ( + conn *grpc.ClientConn + err error + ) + if util.GetEnvBool("USE_REMOTE_S8_PROXY") { + conn, err = registry.Get().GetSharedCloudConnection(strings.ToLower(registry.S8_PROXY)) + } else { + conn, err = registry.GetConnection(registry.S8_PROXY) + } if err != nil { errMsg := fmt.Sprintf("S8 Proxy client initialization error: %s", err) glog.Error(errMsg) diff --git a/feg/gateway/services/s8_proxy/servicers/s8_proxy.go b/feg/gateway/services/s8_proxy/servicers/s8_proxy.go index 441d09c0b896..d988c3863527 100644 --- a/feg/gateway/services/s8_proxy/servicers/s8_proxy.go +++ b/feg/gateway/services/s8_proxy/servicers/s8_proxy.go @@ -108,6 +108,12 @@ func (s *S8Proxy) CreateSession(ctx context.Context, req *protos.CreateSessionRe } func (s *S8Proxy) DeleteSession(ctx context.Context, req *protos.DeleteSessionRequestPgw) (*protos.DeleteSessionResponsePgw, error) { + err := validateDeleteSessionRequest(req) + if err != nil { + err = fmt.Errorf("Delete Session failed for IMSI %s:, couldn't validate request: %s", req.Imsi, err) + glog.Error(err) + return nil, err + } cPgwUDPAddr, err := s.configOrRequestedPgwAddress(req.PgwAddrs) if err != nil { err = fmt.Errorf("Delete Session failed for IMSI %s: %s", req.Imsi, err) @@ -158,3 +164,10 @@ func validateCreateSessionRequest(csr *protos.CreateSessionRequestPgw) error { } return nil } + +func validateDeleteSessionRequest(dsr *protos.DeleteSessionRequestPgw) error { + if dsr.CPgwFteid == nil || dsr.Imsi == "" { + return fmt.Errorf("DeleteSessionRequest missing fields %+v", dsr) + } + return nil +} diff --git a/feg/gateway/services/s8_proxy/servicers/s8_proxy_test.go b/feg/gateway/services/s8_proxy/servicers/s8_proxy_test.go index f5eb19b700af..9cbcd8832b22 100644 --- a/feg/gateway/services/s8_proxy/servicers/s8_proxy_test.go +++ b/feg/gateway/services/s8_proxy/servicers/s8_proxy_test.go @@ -154,6 +154,23 @@ func TestS8proxyRepeatedCreateSession(t *testing.T) { assert.Equal(t, PgwTEIDc, csRes.CPgwFteid.Teid) } +func TestS8proxyCreateWithMissingParam(t *testing.T) { + // set up client ans server + s8p, mockPgw := startSgwAndPgw(t, GtpTimeoutForTest) + defer mockPgw.Close() + + // ------------------------ + // ---- Create Session ---- + csReq := getDefaultCreateSessionRequest(mockPgw.LocalAddr().String()) + csReq.BearerContext = nil + + // Send and receive Create Session Request + _, err := s8p.CreateSession(context.Background(), csReq) + assert.Error(t, err) +} + +// TestS8ProxyDeleteSessionAfterClientRestars test if s8_proxy is able to handle an already +// created session after s8 has been restarted. func TestS8ProxyDeleteSessionAfterClientRestars(t *testing.T) { // set up client ans server s8p, mockPgw := startSgwAndPgw(t, time.Second*600) @@ -203,8 +220,8 @@ func TestS8ProxyDeleteInexistentSession(t *testing.T) { // ------------------------ // ---- Delete Session inexistent session ---- - cdReq := &protos.DeleteSessionRequestPgw{Imsi: "000000000000015"} - cdReq = &protos.DeleteSessionRequestPgw{ + dsReq := &protos.DeleteSessionRequestPgw{Imsi: "000000000000015"} + dsReq = &protos.DeleteSessionRequestPgw{ PgwAddrs: mockPgw.LocalAddr().String(), Imsi: "000000000000015", BearerId: 4, @@ -214,11 +231,23 @@ func TestS8ProxyDeleteInexistentSession(t *testing.T) { Teid: 87, }, } - _, err := s8p.DeleteSession(context.Background(), cdReq) + _, err := s8p.DeleteSession(context.Background(), dsReq) assert.Error(t, err) assert.Equal(t, mockPgw.LastTEIDc, uint32(87)) } +func TestS8ProxyDeleteWithMissingParamaters(t *testing.T) { + s8p, mockPgw := startSgwAndPgw(t, 200*time.Millisecond) + defer mockPgw.Close() + + // ------------------------ + // ---- Delete Session inexistent session ---- + // create a bad create session request + dsReq := getDeleteSessionRequest(mockPgw.LocalAddr().String(), nil) + _, err := s8p.DeleteSession(context.Background(), dsReq) + assert.Error(t, err) +} + func TestS8proxyCreateSessionWithErrors(t *testing.T) { // set up client ans server s8p, mockPgw := startSgwAndPgw(t, GtpTimeoutForTest) diff --git a/feg/gateway/tools/s6a_cli/main.go b/feg/gateway/tools/s6a_cli/main.go index 20e8ac719277..9d764e81d9bf 100644 --- a/feg/gateway/tools/s6a_cli/main.go +++ b/feg/gateway/tools/s6a_cli/main.go @@ -48,6 +48,7 @@ const ( var ( cmdRegistry = new(commands.Map) proxyAddr string + remoteS6a bool mncLen int = 3 s6aAddr string network string = "sctp" @@ -99,6 +100,7 @@ func init() { f.PrintDefaults() } f.StringVar(&proxyAddr, "proxy", proxyAddr, "s6a proxy address") + f.BoolVar(&remoteS6a, "remote_s6a", remoteS6a, "Use orc8r to get to the s6a_proxy (Run it on AGW without proxy flag)") f.StringVar(&s6aAddr, "hss_addr", s6aAddr, "s6a server (HSS) address - overwrites proxy address and starts local s6a proxy") f.StringVar(&network, "network", network, "s6a server (HSS) network: tcp/sctp") @@ -180,6 +182,7 @@ func air(cmd *commands.Command, args []string) int { var cli s6aCli var peerAddr string if len(s6aAddr) > 0 || useMconfig { // use direct HSS connection if address is provided + fmt.Println("Using builtin S6a_proxy") if useMconfig { conf = servicers.GetS6aProxyConfigs() } @@ -194,6 +197,13 @@ func air(cmd *commands.Command, args []string) int { cli = s6aBuiltIn{impl: localProxy} peerAddr = conf.ServerCfg.Addr } else { + if remoteS6a { + fmt.Println("Using S6a_proxy through Orc8r") + os.Setenv("USE_REMOTE_S6A_PROXY", "true") + } else { + fmt.Println("Using local S6a_proxy") + } + cli = s6aProxyCli{} currAddr, _ := registry.GetServiceAddress(registry.S6A_PROXY) if currAddr != proxyAddr { diff --git a/feg/gateway/tools/s8_cli/main.go b/feg/gateway/tools/s8_cli/main.go index da4b88324fb9..73753d8a880c 100644 --- a/feg/gateway/tools/s8_cli/main.go +++ b/feg/gateway/tools/s8_cli/main.go @@ -38,6 +38,7 @@ import ( var ( cmdRegistry = new(commands.Map) proxyAddr string + remoteS8 bool IMSI string = "123456789012345" useMconfig bool useBuiltinCli bool @@ -81,6 +82,8 @@ func init() { csFlags.StringVar(&localPort, "localport", localPort, "S8 local port to run the server") + csFlags.BoolVar(&remoteS8, "remote_s8", remoteS8, "Use orc8r to get to the s0_proxy (Run it on AGW without proxy flag)") + csFlags.StringVar(&pgwServerAddr, "server", pgwServerAddr, "PGW IP:port to send request with format ip:port") @@ -125,6 +128,8 @@ func init() { eFlags.BoolVar(&testServer, "test", testServer, fmt.Sprintf("Start local test s8 server bound to default PGW address (%s)", testServerAddr)) + eFlags.BoolVar(&remoteS8, "remote_s8", remoteS8, "Use orc8r to get to the s0_proxy (Run it on AGW without proxy flag)") + eFlags.StringVar(&localPort, "localport", localPort, "S8 local port to run the server") @@ -153,7 +158,7 @@ func createSession(cmd *commands.Command, args []string) int { if err != nil { fmt.Println(err) cmd.Usage() - return 1 + return 2 } // Create Session Request messagea @@ -229,10 +234,16 @@ func createSession(cmd *commands.Command, args []string) int { if err != nil { fmt.Printf("=> Create Session cli command failed: %s\n", err) - return 9 + return 3 } printGRPCMessage("Received GRPC message: ", csRes) + // check if message was received but GTP message recived was in fact an error + if csRes.GtpError != nil { + fmt.Printf("Received a GTP error (see the GRPC message before): %d\n", csRes.GtpError.Cause) + return 4 + } + // Delete recently created session (if enableD) if createDeleteTimeout != -1 { fmt.Printf("\n=> Sleeping for %ds before deleting....", createDeleteTimeout) @@ -251,7 +262,7 @@ func createSession(cmd *commands.Command, args []string) int { dsRes, err := cli.DeleteSession(dsReq) if err != nil { fmt.Printf("=> Delete session failed: %s\n", err) - return 9 + return 5 } printGRPCMessage("Received GRPC message: ", dsRes) } @@ -315,11 +326,14 @@ func initialize(cmd *commands.Command, args []string) (s8Cli, *flag.FlagSet, err if err != nil { return nil, nil, fmt.Errorf("=> BuiltIn S8 Proxy initialization error: %v\n", err) } - cli = s8BuiltIn{localProxy} } else { - fmt.Println("Using local S8_proxy") - // TODO: use local proxy running on the gateway + if remoteS8 { + fmt.Println("Using S8_proxy through Orc8r") + os.Setenv("USE_REMOTE_S8_PROXY", "true") + } else { + fmt.Println("Using local S8_proxy") + } proxyAddr, _ = registry.GetServiceAddress(registry.S8_PROXY) cli = s8CliImpl{} } From 661b0e984edb94c6898dc2a99f5e99c459f9827f Mon Sep 17 00:00:00 2001 From: Marie <37634144+themarwhal@users.noreply.github.com> Date: Mon, 5 Apr 2021 15:42:26 -0500 Subject: [PATCH 24/91] [AGW] Commit leftover formatting changes on master (#5920) Signed-off-by: Marie Bremner --- .../c/oai/common/redis_utils/redis_client.cpp | 2 +- .../c/session_manager/GrpcMagmaUtils.h | 6 ++- .../c/session_manager/RedisStoreClient.cpp | 4 +- .../c/session_manager/RedisStoreClient.h | 14 ++++--- .../c/session_manager/SpgwServiceClient.cpp | 4 +- .../c/session_manager/SpgwServiceClient.h | 36 ++++++++++++++---- .../c/common/async_grpc/GRPCReceiver.h | 5 ++- .../gateway/c/common/config/MConfigLoader.cpp | 6 ++- orc8r/gateway/c/common/config/MConfigLoader.h | 6 ++- .../gateway/c/common/datastore/Serializers.h | 6 ++- .../gateway/c/common/eventd/EventdClient.cpp | 14 +++++-- orc8r/gateway/c/common/eventd/EventdClient.h | 16 ++++++-- .../gateway/c/common/policydb/PolicyLoader.h | 6 ++- .../c/common/service303/MagmaService.cpp | 8 +++- .../c/common/service303/MagmaService.h | 34 +++++++++++++---- .../c/common/service303/MetricsSingleton.h | 38 +++++++++++++++---- .../ServiceRegistrySingleton.cpp | 4 +- .../ServiceRegistrySingleton.h | 8 +++- 18 files changed, 169 insertions(+), 48 deletions(-) diff --git a/lte/gateway/c/oai/common/redis_utils/redis_client.cpp b/lte/gateway/c/oai/common/redis_utils/redis_client.cpp index 700e7fbd8b43..263a051cb4d7 100644 --- a/lte/gateway/c/oai/common/redis_utils/redis_client.cpp +++ b/lte/gateway/c/oai/common/redis_utils/redis_client.cpp @@ -28,7 +28,7 @@ extern "C" { #endif #include "ServiceConfigLoader.h" -#include // IWYU pragma: keep +#include // IWYU pragma: keep using google::protobuf::Message; diff --git a/lte/gateway/c/session_manager/GrpcMagmaUtils.h b/lte/gateway/c/session_manager/GrpcMagmaUtils.h index e1df2a779ba1..181c951cec30 100644 --- a/lte/gateway/c/session_manager/GrpcMagmaUtils.h +++ b/lte/gateway/c/session_manager/GrpcMagmaUtils.h @@ -13,7 +13,11 @@ #pragma once #include // for string -namespace google { namespace protobuf { class Message; } } +namespace google { +namespace protobuf { +class Message; +} +} // namespace google std::string get_env_var(std::string const& key); diff --git a/lte/gateway/c/session_manager/RedisStoreClient.cpp b/lte/gateway/c/session_manager/RedisStoreClient.cpp index dae57340e762..96352073242b 100644 --- a/lte/gateway/c/session_manager/RedisStoreClient.cpp +++ b/lte/gateway/c/session_manager/RedisStoreClient.cpp @@ -33,7 +33,9 @@ #include "SessionState.h" // for SessionState #include "StoredState.h" // for deserialize_stored_session, ser... #include "magma_logging.h" // for MERROR, MLOG -namespace magma { class StaticRuleStore; } +namespace magma { +class StaticRuleStore; +} namespace magma { namespace lte { diff --git a/lte/gateway/c/session_manager/RedisStoreClient.h b/lte/gateway/c/session_manager/RedisStoreClient.h index 0f572bad4866..1777098e7e87 100644 --- a/lte/gateway/c/session_manager/RedisStoreClient.h +++ b/lte/gateway/c/session_manager/RedisStoreClient.h @@ -14,12 +14,14 @@ #pragma once #include -#include // IWYU pragma: keep -#include // for shared_ptr -#include // for set -#include // for string -#include "StoreClient.h" // for SessionMap, SessionVector, StoreClient -namespace magma { class StaticRuleStore; } +#include // IWYU pragma: keep +#include // for shared_ptr +#include // for set +#include // for string +#include "StoreClient.h" // for SessionMap, SessionVector, StoreClient +namespace magma { +class StaticRuleStore; +} namespace magma { namespace lte { diff --git a/lte/gateway/c/session_manager/SpgwServiceClient.cpp b/lte/gateway/c/session_manager/SpgwServiceClient.cpp index 54e1b2131e78..1f488f249b82 100644 --- a/lte/gateway/c/session_manager/SpgwServiceClient.cpp +++ b/lte/gateway/c/session_manager/SpgwServiceClient.cpp @@ -25,7 +25,9 @@ #include "lte/protos/spgw_service.grpc.pb.h" // for SpgwService::Stub #include "lte/protos/spgw_service.pb.h" // for DeleteBearerRequest #include "magma_logging.h" // for MLOG, MERROR, MINFO -namespace grpc { class Channel; } +namespace grpc { +class Channel; +} using grpc::Status; diff --git a/lte/gateway/c/session_manager/SpgwServiceClient.h b/lte/gateway/c/session_manager/SpgwServiceClient.h index 41ea8f80bf89..f5158ffa2f25 100644 --- a/lte/gateway/c/session_manager/SpgwServiceClient.h +++ b/lte/gateway/c/session_manager/SpgwServiceClient.h @@ -20,13 +20,35 @@ #include // for vector #include "GRPCReceiver.h" // for GRPCReceiver #include "lte/protos/subscriberdb.pb.h" // for lte -namespace grpc { class Channel; } -namespace grpc { class Status; } -namespace grpc { class Status; } -namespace magma { namespace lte { class CreateBearerRequest; } } -namespace magma { namespace lte { class CreateBearerResult; } } -namespace magma { namespace lte { class DeleteBearerRequest; } } -namespace magma { namespace lte { class DeleteBearerResult; } } +namespace grpc { +class Channel; +} +namespace grpc { +class Status; +} +namespace grpc { +class Status; +} +namespace magma { +namespace lte { +class CreateBearerRequest; +} +} // namespace magma +namespace magma { +namespace lte { +class CreateBearerResult; +} +} // namespace magma +namespace magma { +namespace lte { +class DeleteBearerRequest; +} +} // namespace magma +namespace magma { +namespace lte { +class DeleteBearerResult; +} +} // namespace magma using grpc::Status; diff --git a/orc8r/gateway/c/common/async_grpc/GRPCReceiver.h b/orc8r/gateway/c/common/async_grpc/GRPCReceiver.h index fdf9a31e9772..7047309c9f15 100644 --- a/orc8r/gateway/c/common/async_grpc/GRPCReceiver.h +++ b/orc8r/gateway/c/common/async_grpc/GRPCReceiver.h @@ -20,7 +20,10 @@ #include // for operator+, seconds #include // for function #include // for unique_ptr -namespace grpc { template class ClientAsyncResponseReader; } +namespace grpc { +template +class ClientAsyncResponseReader; +} namespace magma { diff --git a/orc8r/gateway/c/common/config/MConfigLoader.cpp b/orc8r/gateway/c/common/config/MConfigLoader.cpp index b4e99b9fed1d..50d18680ab4c 100644 --- a/orc8r/gateway/c/common/config/MConfigLoader.cpp +++ b/orc8r/gateway/c/common/config/MConfigLoader.cpp @@ -18,7 +18,11 @@ #include // for operator<<, char_traits #include // for basic_json<>::iterator #include "magma_logging.h" // for MLOG -namespace google { namespace protobuf { class Message; } } +namespace google { +namespace protobuf { +class Message; +} +} // namespace google using json = nlohmann::json; diff --git a/orc8r/gateway/c/common/config/MConfigLoader.h b/orc8r/gateway/c/common/config/MConfigLoader.h index 982a063118a2..de24f7b56b4a 100644 --- a/orc8r/gateway/c/common/config/MConfigLoader.h +++ b/orc8r/gateway/c/common/config/MConfigLoader.h @@ -14,7 +14,11 @@ #include // for ifstream #include // for string -namespace google { namespace protobuf { class Message; } } +namespace google { +namespace protobuf { +class Message; +} +} // namespace google namespace magma { diff --git a/orc8r/gateway/c/common/datastore/Serializers.h b/orc8r/gateway/c/common/datastore/Serializers.h index 04b3d526452c..8c7917840d70 100644 --- a/orc8r/gateway/c/common/datastore/Serializers.h +++ b/orc8r/gateway/c/common/datastore/Serializers.h @@ -15,7 +15,11 @@ #include // for uint64_t #include // for function #include // for string -namespace google { namespace protobuf { class Message; } } +namespace google { +namespace protobuf { +class Message; +} +} // namespace google using google::protobuf::Message; namespace magma { diff --git a/orc8r/gateway/c/common/eventd/EventdClient.cpp b/orc8r/gateway/c/common/eventd/EventdClient.cpp index c3e81ea7a1cb..6d278c4adb56 100644 --- a/orc8r/gateway/c/common/eventd/EventdClient.cpp +++ b/orc8r/gateway/c/common/eventd/EventdClient.cpp @@ -17,9 +17,17 @@ #include "ServiceRegistrySingleton.h" // for ServiceRegistrySin... #include "orc8r/protos/common.pb.h" // for Void #include "orc8r/protos/eventd.grpc.pb.h" // for EventService::Stub -namespace grpc { class ClientContext; } -namespace grpc { class Status; } -namespace magma { namespace orc8r { class Event; } } +namespace grpc { +class ClientContext; +} +namespace grpc { +class Status; +} +namespace magma { +namespace orc8r { +class Event; +} +} // namespace magma namespace magma { diff --git a/orc8r/gateway/c/common/eventd/EventdClient.h b/orc8r/gateway/c/common/eventd/EventdClient.h index edc217304a19..274fcdd4c535 100644 --- a/orc8r/gateway/c/common/eventd/EventdClient.h +++ b/orc8r/gateway/c/common/eventd/EventdClient.h @@ -17,9 +17,19 @@ #include // for function #include // for unique_ptr #include "GRPCReceiver.h" // for GRPCReceiver -namespace grpc { class Status; } -namespace magma { namespace orc8r { class Event; } } -namespace magma { namespace orc8r { class Void; } } +namespace grpc { +class Status; +} +namespace magma { +namespace orc8r { +class Event; +} +} // namespace magma +namespace magma { +namespace orc8r { +class Void; +} +} // namespace magma using grpc::Status; diff --git a/orc8r/gateway/c/common/policydb/PolicyLoader.h b/orc8r/gateway/c/common/policydb/PolicyLoader.h index fe9ad48f2626..1563c28d2c8c 100644 --- a/orc8r/gateway/c/common/policydb/PolicyLoader.h +++ b/orc8r/gateway/c/common/policydb/PolicyLoader.h @@ -15,7 +15,11 @@ #include // for function #include // for vector #include "lte/protos/subscriberdb.pb.h" // for lte -namespace magma { namespace lte { class PolicyRule; } } +namespace magma { +namespace lte { +class PolicyRule; +} +} // namespace magma namespace magma { using namespace lte; diff --git a/orc8r/gateway/c/common/service303/MagmaService.cpp b/orc8r/gateway/c/common/service303/MagmaService.cpp index 3c879f38d3d5..7e94934048e0 100644 --- a/orc8r/gateway/c/common/service303/MagmaService.cpp +++ b/orc8r/gateway/c/common/service303/MagmaService.cpp @@ -34,8 +34,12 @@ #include "orc8r/protos/metrics.pb.h" // for MetricFamily #include "orc8r/protos/metricsd.pb.h" // for MetricsContainer #include "registry.h" // for Registry -namespace grpc { class ServerContext; } -namespace grpc { class Service; } +namespace grpc { +class ServerContext; +} +namespace grpc { +class Service; +} using grpc::InsecureServerCredentials; using grpc::Status; diff --git a/orc8r/gateway/c/common/service303/MagmaService.h b/orc8r/gateway/c/common/service303/MagmaService.h index 435f5a7879f9..0ed62898ac15 100644 --- a/orc8r/gateway/c/common/service303/MagmaService.h +++ b/orc8r/gateway/c/common/service303/MagmaService.h @@ -22,13 +22,33 @@ #include // for unique_ptr #include // for string #include "orc8r/protos/service303.pb.h" // for ServiceInfo, ServiceInf... -namespace grpc { class ServerCompletionQueue; } -namespace grpc { class ServerContext; } -namespace grpc { class ServerContext; } -namespace grpc { class Service; } -namespace magma { namespace orc8r { class MetricsContainer; } } -namespace magma { namespace orc8r { class Void; } } -namespace magma { namespace service303 { class MetricsSingleton; } } +namespace grpc { +class ServerCompletionQueue; +} +namespace grpc { +class ServerContext; +} +namespace grpc { +class ServerContext; +} +namespace grpc { +class Service; +} +namespace magma { +namespace orc8r { +class MetricsContainer; +} +} // namespace magma +namespace magma { +namespace orc8r { +class Void; +} +} // namespace magma +namespace magma { +namespace service303 { +class MetricsSingleton; +} +} // namespace magma using grpc::Server; using grpc::ServerContext; diff --git a/orc8r/gateway/c/common/service303/MetricsSingleton.h b/orc8r/gateway/c/common/service303/MetricsSingleton.h index 693e6f774a34..75f4f1d215cf 100644 --- a/orc8r/gateway/c/common/service303/MetricsSingleton.h +++ b/orc8r/gateway/c/common/service303/MetricsSingleton.h @@ -18,14 +18,36 @@ #include // for shared_ptr #include // for string #include "MetricsRegistry.h" // for MetricsRegistry, Registry -namespace grpc { class Server; } -namespace prometheus { class Counter; } -namespace prometheus { class Gauge; } -namespace prometheus { class Histogram; } -namespace prometheus { class Registry; } -namespace prometheus { namespace detail { class CounterBuilder; } } -namespace prometheus { namespace detail { class GaugeBuilder; } } -namespace prometheus { namespace detail { class HistogramBuilder; } } +namespace grpc { +class Server; +} +namespace prometheus { +class Counter; +} +namespace prometheus { +class Gauge; +} +namespace prometheus { +class Histogram; +} +namespace prometheus { +class Registry; +} +namespace prometheus { +namespace detail { +class CounterBuilder; +} +} // namespace prometheus +namespace prometheus { +namespace detail { +class GaugeBuilder; +} +} // namespace prometheus +namespace prometheus { +namespace detail { +class HistogramBuilder; +} +} // namespace prometheus using grpc::Server; using magma::service303::MetricsRegistry; diff --git a/orc8r/gateway/c/common/service_registry/ServiceRegistrySingleton.cpp b/orc8r/gateway/c/common/service_registry/ServiceRegistrySingleton.cpp index e93f780b5293..7b2475cbadb8 100644 --- a/orc8r/gateway/c/common/service_registry/ServiceRegistrySingleton.cpp +++ b/orc8r/gateway/c/common/service_registry/ServiceRegistrySingleton.cpp @@ -19,7 +19,9 @@ #include // for basic_ostream, basic_o... #include // for invalid_argument #include // for string, allocator, ope... -namespace grpc { class Channel; } +namespace grpc { +class Channel; +} using grpc::CreateCustomChannel; using grpc::InsecureChannelCredentials; diff --git a/orc8r/gateway/c/common/service_registry/ServiceRegistrySingleton.h b/orc8r/gateway/c/common/service_registry/ServiceRegistrySingleton.h index 42240d0111c0..7c1bd22fe86b 100644 --- a/orc8r/gateway/c/common/service_registry/ServiceRegistrySingleton.h +++ b/orc8r/gateway/c/common/service_registry/ServiceRegistrySingleton.h @@ -16,8 +16,12 @@ #include // for shared_ptr, unique_ptr #include // for string #include "ServiceConfigLoader.h" // for ServiceConfigLoader -namespace grpc { class Channel; } -namespace grpc { class ChannelCredentials; } +namespace grpc { +class Channel; +} +namespace grpc { +class ChannelCredentials; +} using grpc::Channel; using grpc::ChannelCredentials; From ee6242d133e21ed495e1862e4ed53dcbd4d78e5c Mon Sep 17 00:00:00 2001 From: Andrei Lee Date: Mon, 5 Apr 2021 14:36:27 -0700 Subject: [PATCH 25/91] Update NMS db migration script to use @fbcnms/sequelize-models 0.1.5 (#5924) Signed-off-by: GitHub Signed-off-by: Andrei Lee Co-authored-by: Marie <37634144+themarwhal@users.noreply.github.com> --- .../scripts/fuji-upgrade/pre-upgrade-migration.sh | 8 ++++---- .../packages/magmalte/scripts/fuji-upgrade/runs-on-nms.sh | 4 ++-- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/nms/app/packages/magmalte/scripts/fuji-upgrade/pre-upgrade-migration.sh b/nms/app/packages/magmalte/scripts/fuji-upgrade/pre-upgrade-migration.sh index 09dcd2949a50..14392aab5208 100644 --- a/nms/app/packages/magmalte/scripts/fuji-upgrade/pre-upgrade-migration.sh +++ b/nms/app/packages/magmalte/scripts/fuji-upgrade/pre-upgrade-migration.sh @@ -36,19 +36,19 @@ PRE-REQUISITES EOF # Get the Magma k8s namespace -read -p "Enter Kubernetes namespace [magma]: " magma_namespace -magma_namespace=${magma_namespace:-magma} +read -p "Enter Kubernetes namespace [orc8r]: " magma_namespace +magma_namespace=${magma_namespace:-orc8r} echo "" # Find NMS pod name -nms_pod_name=$(kubectl -n magma get pods --no-headers -o custom-columns=":metadata.name" | grep nms-magmalte) +nms_pod_name=$(kubectl -n $magma_namespace get pods --no-headers -o custom-columns=":metadata.name" | grep nms-magmalte) echo "Found Magma NMS pod name: $nms_pod_name" read -p "Enter NMS pod name [$nms_pod_name]: " input nms_pod_name=${input:-$nms_pod_name} echo "" # Find configurator pod name -orc8r_pod_name=$(kubectl -n magma get pods --no-headers -o custom-columns=":metadata.name" | grep orc8r-configurator) +orc8r_pod_name=$(kubectl -n $magma_namespace get pods --no-headers -o custom-columns=":metadata.name" | grep orc8r-configurator) echo "Found Magma configurator pod name: $orc8r_pod_name" read -p "Enter configurator pod name [$orc8r_pod_name]: " input orc8r_pod_name=${input:-$orc8r_pod_name} diff --git a/nms/app/packages/magmalte/scripts/fuji-upgrade/runs-on-nms.sh b/nms/app/packages/magmalte/scripts/fuji-upgrade/runs-on-nms.sh index 3df4729fd622..741054b3586d 100644 --- a/nms/app/packages/magmalte/scripts/fuji-upgrade/runs-on-nms.sh +++ b/nms/app/packages/magmalte/scripts/fuji-upgrade/runs-on-nms.sh @@ -44,11 +44,11 @@ done cat << EOF -------------------------------------------------------------------------------- - Upgrading @fbcnms/sequelize-models to ^0.1.4 + Upgrading @fbcnms/sequelize-models to ^0.1.5 -------------------------------------------------------------------------------- EOF pushd /usr/src/packages/magmalte -yarn upgrade @fbcnms/sequelize-models@^0.1.4 +yarn upgrade @fbcnms/sequelize-models@^0.1.5 yarn popd cat << EOF From de9ea90a250787e580fafdb6b80b695494a606bf Mon Sep 17 00:00:00 2001 From: wallyrb <37117037+wallyrb@users.noreply.github.com> Date: Mon, 5 Apr 2021 18:07:49 -0400 Subject: [PATCH 26/91] Add troubleshooting documents to github docs[modified v1.3] (#5078) Signed-off-by: Wally Rodriguez B --- docs/docusaurus/i18n/en.json | 12 ++ docs/docusaurus/sidebars.json | 10 ++ docs/readmes/assets/lte/attach_flow.png | Bin 0 -> 356049 bytes .../troubleshooting/agw_unable_to_checkin.md | 85 ++++++++++++++ .../generate_admin_operator_certificates.md | 59 ++++++++++ .../troubleshooting/update_certificates.md | 110 ++++++++++++++++++ .../troubleshooting/user_unable_to_attach.md | 91 +++++++++++++++ 7 files changed, 367 insertions(+) create mode 100644 docs/readmes/assets/lte/attach_flow.png create mode 100644 docs/readmes/howtos/troubleshooting/agw_unable_to_checkin.md create mode 100644 docs/readmes/howtos/troubleshooting/generate_admin_operator_certificates.md create mode 100644 docs/readmes/howtos/troubleshooting/update_certificates.md create mode 100644 docs/readmes/howtos/troubleshooting/user_unable_to_attach.md diff --git a/docs/docusaurus/i18n/en.json b/docs/docusaurus/i18n/en.json index 4ca0d07e2006..94bc086e0ed9 100644 --- a/docs/docusaurus/i18n/en.json +++ b/docs/docusaurus/i18n/en.json @@ -82,6 +82,18 @@ "howtos/thanos": { "title": "Thanos" }, + "howtos/troubleshooting/agw_unable_to_checkin": { + "title": "Access Gateway Unable to Check-in to Orchestrator" + }, + "howtos/troubleshooting/generate_admin_operator_certificates": { + "title": "Generate and update admin_operator certificates" + }, + "howtos/troubleshooting/update_certificates": { + "title": "Update rootCA and controller SSL certificates" + }, + "howtos/troubleshooting/user_unable_to_attach": { + "title": "User is unable to attach to Magma" + }, "howtos/config_agw_bridged": { "title": "AGW Bridged Mode" }, diff --git a/docs/docusaurus/sidebars.json b/docs/docusaurus/sidebars.json index 28995c820203..74d89712659e 100644 --- a/docs/docusaurus/sidebars.json +++ b/docs/docusaurus/sidebars.json @@ -46,6 +46,16 @@ "nms/alerts", "nms/tips_and_tricks" ] + }, + { + "type": "subcategory", + "label": "Troubleshooting", + "ids": [ + "howtos/troubleshooting/agw_unable_to_checkin", + "howtos/troubleshooting/user_unable_to_attach", + "howtos/troubleshooting/update_certificates", + "howtos/troubleshooting/generate_admin_operator_certificates" + ] } ], "Architecture": [ diff --git a/docs/readmes/assets/lte/attach_flow.png b/docs/readmes/assets/lte/attach_flow.png new file mode 100644 index 0000000000000000000000000000000000000000..3dab25531d8c4363273e51e45689990fe8e31665 GIT binary patch literal 356049 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 WG|&D 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 JygP)s@lUg9{eS=f literal 0 HcmV?d00001 diff --git a/docs/readmes/howtos/troubleshooting/agw_unable_to_checkin.md b/docs/readmes/howtos/troubleshooting/agw_unable_to_checkin.md new file mode 100644 index 000000000000..3c8e38533234 --- /dev/null +++ b/docs/readmes/howtos/troubleshooting/agw_unable_to_checkin.md @@ -0,0 +1,85 @@ +--- +id: agw_unable_to_checkin +title: Access Gateway Unable to Check-in to Orchestrator +hide_title: true +--- +# Access Gateway Unable to Check-in to Orchestrator + +**Description:** After deploying AGW and Orchestrator, it is time to make AGW accessible from Orchestrator. After following github Magma AGW configuration [guide](https://magma.github.io/magma/docs/next/lte/config_agw), it was observed that AGW is not able to check-in to Orchestrator. + +**Environment:** AGW and Orc8r deployed. + +**Affected components:** AGW, Orchestrator + +**Triaging steps:** + +1. Diagnose AGW and Orchestrator setup with script checkin_cli.py. If the test is not successful, the script would provide potential root cause for a problem. A successful script will look like below: + +``` +AGW$ sudo checkin_cli.py + +1. -- Testing TCP connection to controller-staging.magma.etagecom.io:443 -- +2. -- Testing Certificate -- +3. -- Testing SSL -- +4. -- Creating direct cloud checkin -- +5. -- Creating proxy cloud checkin -- +``` + +If the output is not successful, the script will recommend some steps to resolve the problem. After following the steps the problem has not been resolved, follow below steps. + +2. Make sure that the hostnames and ports specified in control_proxy.yml file in AGW are properly set. +Sample control_proxy.yml file + +``` +cloud_address: controller.yourdomain.com +cloud_port: 443 +bootstrap_address: bootstrapper-controller.yourdomain.com +bootstrap_port: 443 + +rootca_cert: /var/opt/magma/tmp/certs/rootCA.pem +``` + +3. Verify the certificate rootCA.pem is in the correct location defined in rootca_cert (specified in control_proxy.yml) + +4. Make sure the certificates have not expired. + Note: To obtain certificate information you can use `openSSL x509 -in certificate -noout -text` + - In AGW: rootCA.pem + - In Orc8r: rootCA.pem, controller.cert + +5. Verify the domain is consistent across AGW and Orc8r and the CN matches with the domain + - CN in rootCA.pem AGW + - CN in Orc8r for root and controller certificates. + - The domain in `main.tf` + +6. Verify connectivity between AGW and Orc8r. Choose the port and domain obtained in `control_proxy.yml`. You can use telnet, example below: + `telnet bootstrapper-controller.yourdomain.com 443` + + +7. Verify the DNS resolution of the bootstrap and controller domain. + - In AGW: You can ping or telnet to your bootstrap and controller domain from AGW to verify which AWS address is being resolved. + - In Orc8r: Verify which external-IP your cluster is assigned. You can use the command: `kubectl get services` + + The address resolved in AGW should be the same defined in Orc8r. If not, verify your DNS resolution. + + +8. Verify that there are no errors in AGW magmad service. + + `AGW$ sudo tail -f /var/log/syslog | grep -i "magmad"` + + + +9. From Orchestrator, get all pods and find pod orc8r-controller-* + +``` +kubectl -n magma get pods +kubectl -n magma logs -f +``` + +First command will list all pods and next command can be used to check logs of a particular pod. Check if there is any problematic log for the related pod. + +10. Try restarting magmad services. +``` +AGW$ sudo service magma@magmad restart +``` + +11. If issue still persists, please file github issues or ask in our support channels https://www.magmacore.org/community/ diff --git a/docs/readmes/howtos/troubleshooting/generate_admin_operator_certificates.md b/docs/readmes/howtos/troubleshooting/generate_admin_operator_certificates.md new file mode 100644 index 000000000000..b5e37c878acc --- /dev/null +++ b/docs/readmes/howtos/troubleshooting/generate_admin_operator_certificates.md @@ -0,0 +1,59 @@ +--- +id: generate_admin_operator_certificates +title: Generate and update admin_operator certificates +hide_title: true +--- +# Generate and update admin_operator certificates + +**Description:** NMS is unable to communicate with the controller or API access is no longer functional due to certificate used for the TLS handshake has expired. Below guide provide the steps to generate new admin_operator certificates and upload it to the NMS k8s pod for NMS access and to the browser for the API access. + +**Environment:** Orchestrator, NMS in Kubernetes/AWS + +**Affected components:** Orchestrator, NMS + +**Configuration steps:** + + +1. Log on to a host that has kubectl access to your orc8r cluster. +2. Access the shell on your controller pod. + +`export CNTLR_POD=$(kubectl -n orc8r get pod -l app.kubernetes.io/component=controller -o jsonpath='{.items[0].metadata.name}')` + +`kubectl exec -it ${CNTLR_POD} bash` + +3. Run the following commands inside the k8s controller pod to generate a new `admin_operator` cert. The `admin_operator.pfx` file is only for API access for the user to get to the browser. + +``` +# The following commands are to be run inside the pod +(pod)$ cd /var/opt/magma/bin +(pod)$ envdir /var/opt/magma/envdir ./accessc add-admin -cert admin_operator admin_operator +(pod)$ openssl pkcs12 -export -out admin_operator.pfx -inkey admin_operator.key.pem -in admin_operator.pem + +Enter Export Password: +Verifying - Enter Export Password: +``` + +4. Copy the k8s certs from the controller k8s pod to the local directoy where all your secrets are held. + +NOTE: Make sure the location where these certificates are being copied to is the same location which is referenced in the main.tf file for the seed_certs_dir variable. +``` +cd ~/secrets/certs +for certfile in admin_operator.pem admin_operator.key.pem admin_operator.pfx +do + kubectl cp orc8r/${CNTLR_POD}:/var/opt/magma/bin/${certfile} ./${certfile} +done +``` +5. At this point the new certs have been generated, you can use the `admin_operator.pfx` to validate that you can reach the API endpoint. However, they still need to be applied to the k8s secrets manager so that they can be mounted in the nms pod. To do this, initialize your terraform first using terraform init -upgrade. + +6. To replace certs, we have to first taint the secrets in terraform so that terraform knows to destroy those secrets first and then re-apply them. Taint them using terraform `taint module.orc8r-app.null_resource.orc8r_seed_secrets` + +7. Once the secrets have been tainted, you can then go ahead and apply the new secrets by running `terraform apply -target=module.orc8rapp.null_resource.orc8r_seed_secrets` followed by `terraform apply` + +NOTE: `terraform apply` command outputs the “plan” of what it intends to add,destroy,modify. Please scrutinize this output before typing “yes” on the confirm prompt. If there are any changes that are not consistent with your expectations, please cancel the run. You can specifically target the secrets portion by doing `terraform apply -target=module.` + +8. Once the terraform apply succeeds, the NMS will not automatically get the new certificates until we destroy the existing pod and force the replication controller to instantiate another instance of the pod. To do this, run the following commands: +``` +export NMS_POD=$(kubectl -n orc8r get pod -l app.kubernetes.io/component=magmalte -o jsonpath='{.items[0].metadata.name}') +kubectl -n orc8r delete pod NMS_POD +``` +9. Once the pod reinitializes, it should have the latest admin_operator certs and be able to re-establish communication with the controller. diff --git a/docs/readmes/howtos/troubleshooting/update_certificates.md b/docs/readmes/howtos/troubleshooting/update_certificates.md new file mode 100644 index 000000000000..901a77dd960a --- /dev/null +++ b/docs/readmes/howtos/troubleshooting/update_certificates.md @@ -0,0 +1,110 @@ +--- +id: update_certificates +title: Update rootCA and controller SSL certificates +hide_title: true +--- +# Update rootCA and controller SSL certificates + +**Description:** This document describes the steps to update certificates `controller.crt`, `controller.key`, `rootCA.pem` and `rootCA.key` on Orchestrator/Access Gateway. These steps should follow on an Orc8r already deployed where is required to extend the expiration date of the certificates. This shouldn't be use for change of CN. + +**Environment:** Orchestrator in Kubernetes/AWS + +**Affected components:** AGW, Orchestrator + +**Configuration steps on Orchestrator:** + + +1. Create a new rootCA.pem, rootCA.key, controller.crt and controller.key. + + - The public SSL certificate for your Orchestrator domain, + with `CN=*.yourdomain.com`. This can be an SSL certificate chain, but it must be + in one file + + - The private key which corresponds to the above SSL certificate + + - The root CA certificate which verifies your SSL certificate + + If you aren't worried about a browser warning, you can generate self-signed + versions of these certs + + ```bash + ${MAGMA_ROOT}/orc8r/cloud/deploy/scripts/self_sign_certs.sh yourdomain.com + ``` + + Alternatively, if you already have these certs, rename and move them as follows + + - Rename your public SSL certificate to `controller.crt` + + - Rename your SSL certificate's private key to `controller.key` + + - Rename your SSL certificate's root CA certificate to `rootCA.pem` + + +2. Move these certificates into `~/secrets/certs`. + +3. Other certificates `bootstrapper.key`, `certifier.key`, `certifier.pem`, `fluentd.key`, `fluentd.pem`, `admin_operator.key.pem,` `admin_operator.pem` and `admin_operator.pfx` **don't** need to change and you can copy into the folder `~/secrets/certs`. The certs directory should now look like this + +```bash +$ ls -1 ~/secrets/certs/ + +admin_operator.key.pem +admin_operator.pem +admin_operator.pfx +bootstrapper.key +certifier.key +certifier.pem +controller.crt +controller.key +fluentd.key +fluentd.pem +rootCA.pem +rootCA.key +``` + + +4. Opt to upgrade modules and plugins as part of their respective installation steps. + +`terraform init –upgrade` + +5. Run the following commands: + +`$ terraform taint module.orc8r-app.null_resource.orc8r_seed_secrets` + +`$ terraform apply -target=module.orc8rapp.null_resource.orc8r_seed_secrets` + +`$ terraform apply` + + +NOTE: `terraform apply` command outputs the “plan” of what it intends to add,destroy,modify. Please scrutinize this output before typing “yes” on the confirm prompt. If there are any changes that are not consistent with your expectations, please cancel the run. You can specifically target the secrets portion by doing `terraform apply -target=module.` + +6. You can remove the secrets folder from your local disk. But make sure you have a copy of the `rootCA.key` + +7. Kill Controllers and Proxy pods one by one. + +`kubectl delete pods ` + +**Configuration steps on Access Gateway:** + +1. Update the new `rootCA.pem` in `/var/opt/magma/tmp/certs/rootCA.pem` + +2. Restart magmad service + +`AGW$ sudo service magma@magmad restart` + +3. You can validate the connection between your AGW and Orchestrator: + +```bash +AGW$ journalctl -u magma@magmad -f +# Look for the following logs +# INFO:root:Checkin Successful! +# INFO:root:[SyncRPC] Got heartBeat from cloud +# INFO:root:Processing config update gateway_id + +AGW$ sudo checkin_cli.py + +1. -- Testing TCP connection to controller-staging.magma.etagecom.io:443 -- +2. -- Testing Certificate -- +3. -- Testing SSL -- +4. -- Creating direct cloud checkin -- +5. -- Creating proxy cloud checkin -- +``` diff --git a/docs/readmes/howtos/troubleshooting/user_unable_to_attach.md b/docs/readmes/howtos/troubleshooting/user_unable_to_attach.md new file mode 100644 index 000000000000..9f143d7a95e8 --- /dev/null +++ b/docs/readmes/howtos/troubleshooting/user_unable_to_attach.md @@ -0,0 +1,91 @@ +--- +id: user_unable_to_attach +title: User is unable to attach to Magma +hide_title: true +--- +# User is unable to attach to Magma + +**Description:** This document describe the steps triage issues when a user is unable to attach to the network, AGW is rejecting the attach request. + +**Environment:** AGW and Orc8r deployed. + +**Affected components:** AGW, Orchestrator + +**Prerequisites**: + +- APN configuration has been done in Orc8r +- AGW has been successfully registered in Orc8r +- User has been registered in Orc8r with the correct parameters( Authentication and APN) +- eNB has been successfully integrated with AGW + + +**Triaging steps**: + +1. Run the command sudo mobility_cli.py get_subscriber_table, the output will show the list of subscribers(by IMSI) that are currently attached to the network. If the user is showing in the list, it means the attach request was successfully completed. If the IMSI is not showing there, proceed with the next step +2. Verify in the pcap trace or NMS logs the reason of attach reject. + a. To obtain a pcap trace in AGW you can run the following command `sudo tcpdump -i any sctp -w ` + b. To verify the logs in NMS you can go in NMS to Equipment->Select the AGW-> Logs->Search logs + + +The attach request flow should follow below signal flow, where sctpd, mme, subscriberdb, mobilityd and sessiond are used services in the AGW: + +![Attach flow](assets/lte/attach_flow.png) + + +Below you will find possible causes of why the Attach Request could be rejected by AGW. + + +**Causes related to invalid messages or Unknown messages** + +UE may include in the Attach Request a parameter Magma doesn’t support. Magma will reject the request with the following cause: + +``` +SEMANTICALLY_INCORRECT 95 +INVALID_MANDATORY_INFO 96 +MESSAGE_TYPE_NOT_IMPLEMENTED 97 +MESSAGE_TYPE_NOT_COMPATIBLE 98 +IE_NOT_IMPLEMENTED 99 +CONDITIONAL_IE_ERROR 100 +MESSAGE_NOT_COMPATIBLE 101 +PROTOCOL_ERROR 111 +``` + +Suggested Solution: + +1. Identify which parameter the UE is sending in the Attach Request and compare with the ones Magma support. You can find which parameters magma is currently supported from the code. https://github.com/magma/magma/blob/master/lte/gateway/c/oai/tasks/nas/emm/msg/AttachRequest.c + + Note: Make sure to select the branch you are currently have in your network(v1.1, v1.2, v1.3 , etc) + +2. Once you identify the parameter, verify with the UE/CPE vendor if you can disable it. If you can't disable, file a feature request. + + +**Misconfiguration of APN** + +Using the pcap trace, verify the APN configured in NMS/Orc8r matches the APN sent by UE. +- You will find the APN sent by UE in the “PDN connectivity request”( Attach Request) or in the “ESM information transfer” +- Make sure the APN matches the one configured in Orc8r for this user + + + +**Cause related to subscription options** +If the user is not correctly provisioned in the NMS/Orc8r, the attach request will be rejected with the following causes: + +``` +EMM_CAUSE_IMEI_NOT_ACCEPTED 5 +EMM_CAUSE_EPS_NOT_ALLOWED 7 +EMM_CAUSE_BOTH_NOT_ALLOWED 8 +EMM_CAUSE_PLMN_NOT_ALLOWED 11 +EMM_CAUSE_TA_NOT_ALLOWED 12 +EMM_CAUSE_ROAMING_NOT_ALLOWED 13 +EMM_CAUSE_EPS_NOT_ALLOWED_IN_PLMN 14 +EMM_CAUSE_NO_SUITABLE_CELLS 15 +EMM_CAUSE_CSG_NOT_AUTHORIZED 25 +EMM_CAUSE_NOT_AUTHORIZED_IN_PLMN 35 +EMM_CAUSE_NO_EPS_BEARER_CTX_ACTIVE 40 +``` + +Suggested Solution: + +1. Double check with the Auth Key and OPC are correctly set for this user +2. Verify the Auth Key and OPC values with the SIM vendor +3. If issue still persists, please file github issues or ask in our support channels https://www.magmacore.org/community/ From 1a4b2b309470f5178a931199fced08e1fe722b86 Mon Sep 17 00:00:00 2001 From: Ulas Kozat Date: Mon, 5 Apr 2021 17:44:26 -0700 Subject: [PATCH 27/91] Bug fix in parsing cause type (#5921) Signed-off-by: Ulas Kozat --- lte/gateway/c/oai/tasks/s1ap/s1ap_mme_handlers.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/lte/gateway/c/oai/tasks/s1ap/s1ap_mme_handlers.c b/lte/gateway/c/oai/tasks/s1ap/s1ap_mme_handlers.c index d9aca03b216a..fcf34bb5639b 100644 --- a/lte/gateway/c/oai/tasks/s1ap/s1ap_mme_handlers.c +++ b/lte/gateway/c/oai/tasks/s1ap/s1ap_mme_handlers.c @@ -1823,10 +1823,12 @@ int s1ap_mme_handle_ue_context_modification_failure( S1AP_FIND_PROTOCOLIE_BY_ID( S1ap_UEContextModificationFailureIEs_t, ie, container, S1ap_ProtocolIE_ID_id_Cause, true); - if (!ie) { + if (ie) { + cause_type = ie->value.choice.Cause.present; + } else { OAILOG_FUNC_RETURN(LOG_S1AP, RETURNerror); } - cause_value = ie->value.choice.Cause.choice.radioNetwork; + switch (cause_type) { case S1ap_Cause_PR_radioNetwork: cause_value = ie->value.choice.Cause.choice.radioNetwork; From 50b35a7bf02a46de68ff5698ffaad094a59aea8e Mon Sep 17 00:00:00 2001 From: Youssef EL MASMOUDI Date: Tue, 6 Apr 2021 09:53:35 +0800 Subject: [PATCH 28/91] [orc8r][NetworkProbe] Support NetworkProbe APIs in swagger (#4962) * [orc8r][NetworkProbe] Support NetworkProbe APIs in swagger Signed-off-by: YOUSSEF EL MASMOUDI * [orc8r][NetworkProbe] add unit tests Signed-off-by: YOUSSEF EL MASMOUDI * [orc8r][NetworkProbe] move swagger definitions to a separate service Signed-off-by: YOUSSEF EL MASMOUDI * [orc8r][NetworkProbe] update correlation field Signed-off-by: YOUSSEF EL MASMOUDI * [orc8r][NetworkProbe] fix nits and change correlation_id type to uint64 Signed-off-by: YOUSSEF EL MASMOUDI * Fix review comments Signed-off-by: YOUSSEF EL MASMOUDI --- lte/cloud/configs/nprobe.yml | 11 + lte/cloud/configs/service_registry.yml | 13 + lte/cloud/go/lte/const.go | 24 +- lte/cloud/go/serdes/serdes.go | 4 +- lte/cloud/go/services/nprobe/doc.go | 17 + lte/cloud/go/services/nprobe/nprobe/main.go | 46 ++ .../nprobe/obsidian/handlers/handlers.go | 292 ++++++++ .../nprobe/obsidian/handlers/handlers_test.go | 662 ++++++++++++++++++ .../nprobe/obsidian/models/conversion.go | 47 ++ .../go/services/nprobe/obsidian/models/gen.go | 15 + ...rk_probe_destination_details_swaggergen.go | 118 ++++ ...network_probe_destination_id_swaggergen.go | 19 + .../network_probe_destination_swaggergen.go | 93 +++ .../network_probe_task_details_swaggergen.go | 218 ++++++ .../network_probe_task_id_swaggergen.go | 19 + .../models/network_probe_task_swaggergen.go | 93 +++ .../services/nprobe/obsidian/models/serdes.go | 28 + .../nprobe/obsidian/models/swagger.v1.yml | 294 ++++++++ .../nprobe/obsidian/models/validate.go | 26 + .../cloud/docker/controller/supervisord.conf | 8 + .../cloud/go/obsidian/swagger/v1/swagger.yml | 249 +++++++ 21 files changed, 2284 insertions(+), 12 deletions(-) create mode 100644 lte/cloud/configs/nprobe.yml create mode 100644 lte/cloud/go/services/nprobe/doc.go create mode 100644 lte/cloud/go/services/nprobe/nprobe/main.go create mode 100644 lte/cloud/go/services/nprobe/obsidian/handlers/handlers.go create mode 100644 lte/cloud/go/services/nprobe/obsidian/handlers/handlers_test.go create mode 100644 lte/cloud/go/services/nprobe/obsidian/models/conversion.go create mode 100644 lte/cloud/go/services/nprobe/obsidian/models/gen.go create mode 100644 lte/cloud/go/services/nprobe/obsidian/models/network_probe_destination_details_swaggergen.go create mode 100644 lte/cloud/go/services/nprobe/obsidian/models/network_probe_destination_id_swaggergen.go create mode 100644 lte/cloud/go/services/nprobe/obsidian/models/network_probe_destination_swaggergen.go create mode 100644 lte/cloud/go/services/nprobe/obsidian/models/network_probe_task_details_swaggergen.go create mode 100644 lte/cloud/go/services/nprobe/obsidian/models/network_probe_task_id_swaggergen.go create mode 100644 lte/cloud/go/services/nprobe/obsidian/models/network_probe_task_swaggergen.go create mode 100644 lte/cloud/go/services/nprobe/obsidian/models/serdes.go create mode 100644 lte/cloud/go/services/nprobe/obsidian/models/swagger.v1.yml create mode 100644 lte/cloud/go/services/nprobe/obsidian/models/validate.go diff --git a/lte/cloud/configs/nprobe.yml b/lte/cloud/configs/nprobe.yml new file mode 100644 index 000000000000..3878c7114b5f --- /dev/null +++ b/lte/cloud/configs/nprobe.yml @@ -0,0 +1,11 @@ +--- +# Copyright 2020 The Magma Authors. + +# This source code is licensed under the BSD-style license found in the +# LICENSE file in the root directory of this source tree. + +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. diff --git a/lte/cloud/configs/service_registry.yml b/lte/cloud/configs/service_registry.yml index 770fa16a90ba..4923c9cd4e0e 100644 --- a/lte/cloud/configs/service_registry.yml +++ b/lte/cloud/configs/service_registry.yml @@ -84,3 +84,16 @@ services: annotations: orc8r.io/obsidian_handlers_path_prefixes: > /magma/v1/lte/:network_id/sms, + + nprobe: + host: "localhost" + port: 9666 + echo_port: 10088 + proxy_type: "clientcert" + labels: + orc8r.io/obsidian_handlers: "true" + orc8r.io/swagger_spec: "true" + annotations: + orc8r.io/obsidian_handlers_path_prefixes: > + /magma/v1/lte/:network_id/network_probe/tasks, + /magma/v1/lte/:network_id/network_probe/destinations, diff --git a/lte/cloud/go/lte/const.go b/lte/cloud/go/lte/const.go index 0ed681c9c36e..486992791c62 100644 --- a/lte/cloud/go/lte/const.go +++ b/lte/cloud/go/lte/const.go @@ -63,17 +63,19 @@ const ( NetworkSubscriberConfigType = "network_subscriber_config" // APNEntityType etc. are configurator network entity types. - APNEntityType = "apn" - APNPolicyProfileEntityType = "apn_policy_profile" - APNResourceEntityType = "apn_resource" - BaseNameEntityType = "base_name" - CellularEnodebEntityType = "cellular_enodeb" - CellularGatewayEntityType = "cellular_gateway" - CellularGatewayPoolEntityType = "cellular_gateway_pool" - PolicyQoSProfileEntityType = "policy_qos_profile" - PolicyRuleEntityType = "policy" - RatingGroupEntityType = "rating_group" - SubscriberEntityType = "subscriber" + APNEntityType = "apn" + APNPolicyProfileEntityType = "apn_policy_profile" + APNResourceEntityType = "apn_resource" + BaseNameEntityType = "base_name" + CellularEnodebEntityType = "cellular_enodeb" + CellularGatewayEntityType = "cellular_gateway" + CellularGatewayPoolEntityType = "cellular_gateway_pool" + PolicyQoSProfileEntityType = "policy_qos_profile" + PolicyRuleEntityType = "policy" + RatingGroupEntityType = "rating_group" + SubscriberEntityType = "subscriber" + NetworkProbeTaskEntityType = "network_probe_task" + NetworkProbeDestinationEntityType = "network_probe_destination" // ApnRuleMappingsStreamName etc. are streamer stream names. ApnRuleMappingsStreamName = "apn_rule_mappings" diff --git a/lte/cloud/go/serdes/serdes.go b/lte/cloud/go/serdes/serdes.go index f150a27d59d0..33e9c5a323bc 100644 --- a/lte/cloud/go/serdes/serdes.go +++ b/lte/cloud/go/serdes/serdes.go @@ -16,6 +16,7 @@ package serdes import ( "magma/lte/cloud/go/lte" lte_models "magma/lte/cloud/go/services/lte/obsidian/models" + nprobe_models "magma/lte/cloud/go/services/nprobe/obsidian/models" policydb_models "magma/lte/cloud/go/services/policydb/obsidian/models" subscriberdb_models "magma/lte/cloud/go/services/subscriberdb/obsidian/models" "magma/orc8r/cloud/go/serde" @@ -34,7 +35,8 @@ var ( Entity = serdes.Entity. MustMerge(lte_models.EntitySerdes). MustMerge(subscriberdb_models.EntitySerdes). - MustMerge(policydb_models.EntitySerdes) + MustMerge(policydb_models.EntitySerdes). + MustMerge(nprobe_models.EntitySerdes) // State contains the full set of state serdes used in the LTE module State = serdes.State.MustMerge(serde.NewRegistry( state.NewStateSerde(lte.EnodebStateType, <e_models.EnodebState{}), diff --git a/lte/cloud/go/services/nprobe/doc.go b/lte/cloud/go/services/nprobe/doc.go new file mode 100644 index 000000000000..dd4ce740a54e --- /dev/null +++ b/lte/cloud/go/services/nprobe/doc.go @@ -0,0 +1,17 @@ +/* +Copyright 2020 The Magma Authors. + +This source code is licensed under the BSD-style license found in the +LICENSE file in the root directory of this source tree. + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +// Package nprobe provides the network probe service. +package nprobe + +const ServiceName = "nprobe" diff --git a/lte/cloud/go/services/nprobe/nprobe/main.go b/lte/cloud/go/services/nprobe/nprobe/main.go new file mode 100644 index 000000000000..3918973cde7c --- /dev/null +++ b/lte/cloud/go/services/nprobe/nprobe/main.go @@ -0,0 +1,46 @@ +/* +Copyright 2020 The Magma Authors. + +This source code is licensed under the BSD-style license found in the +LICENSE file in the root directory of this source tree. + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package main + +import ( + "magma/lte/cloud/go/lte" + "magma/lte/cloud/go/services/nprobe" + "magma/lte/cloud/go/services/nprobe/obsidian/handlers" + + "magma/orc8r/cloud/go/obsidian" + "magma/orc8r/cloud/go/obsidian/swagger" + "magma/orc8r/cloud/go/obsidian/swagger/protos" + "magma/orc8r/cloud/go/service" + + "github.com/golang/glog" +) + +func main() { + // Create service + srv, err := service.NewOrchestratorService(lte.ModuleName, nprobe.ServiceName) + if err != nil { + glog.Fatalf("Error creating service: %v", err) + } + + // Attach handlers + obsidian.AttachHandlers(srv.EchoServer, handlers.GetHandlers()) + protos.RegisterSwaggerSpecServer(srv.GrpcServer, swagger.NewSpecServicerFromFile(nprobe.ServiceName)) + + // Run service + err = srv.Run() + if err != nil { + glog.Fatalf("Error while running service and echo server: %v", err) + } + +} diff --git a/lte/cloud/go/services/nprobe/obsidian/handlers/handlers.go b/lte/cloud/go/services/nprobe/obsidian/handlers/handlers.go new file mode 100644 index 000000000000..01093ef5b745 --- /dev/null +++ b/lte/cloud/go/services/nprobe/obsidian/handlers/handlers.go @@ -0,0 +1,292 @@ +/* + * Copyright 2020 The Magma Authors. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package handlers + +import ( + "math/rand" + "net/http" + "time" + + "magma/lte/cloud/go/lte" + "magma/lte/cloud/go/serdes" + "magma/lte/cloud/go/services/lte/obsidian/handlers" + "magma/lte/cloud/go/services/nprobe/obsidian/models" + + "magma/orc8r/cloud/go/obsidian" + "magma/orc8r/cloud/go/services/configurator" + merrors "magma/orc8r/lib/go/errors" + + strfmt "github.com/go-openapi/strfmt" + "github.com/labstack/echo" + "github.com/pkg/errors" +) + +const ( + NetworkProbePath = handlers.ManageNetworkPath + obsidian.UrlSep + "network_probe" + + NetworkProbeTasksPath = NetworkProbePath + obsidian.UrlSep + "tasks" + NetworkProbeDestinationsPath = NetworkProbePath + obsidian.UrlSep + "destinations" + + NetworkProbeTaskDetailsPath = NetworkProbeTasksPath + obsidian.UrlSep + ":task_id" + NetworkProbeDestinationDetailsPath = NetworkProbeDestinationsPath + obsidian.UrlSep + ":destination_id" +) + +func GetHandlers() []obsidian.Handler { + ret := []obsidian.Handler{ + {Path: NetworkProbeTasksPath, Methods: obsidian.GET, HandlerFunc: listNetworkProbeTasks}, + {Path: NetworkProbeTasksPath, Methods: obsidian.POST, HandlerFunc: createNetworkProbeTask}, + {Path: NetworkProbeTaskDetailsPath, Methods: obsidian.GET, HandlerFunc: getNetworkProbeTask}, + {Path: NetworkProbeTaskDetailsPath, Methods: obsidian.PUT, HandlerFunc: updateNetworkProbeTask}, + {Path: NetworkProbeTaskDetailsPath, Methods: obsidian.DELETE, HandlerFunc: deleteNetworkProbeTask}, + + {Path: NetworkProbeDestinationsPath, Methods: obsidian.GET, HandlerFunc: listNetworkProbeDestinations}, + {Path: NetworkProbeDestinationsPath, Methods: obsidian.POST, HandlerFunc: createNetworkProbeDestination}, + {Path: NetworkProbeDestinationDetailsPath, Methods: obsidian.GET, HandlerFunc: getNetworkProbeDestination}, + {Path: NetworkProbeDestinationDetailsPath, Methods: obsidian.PUT, HandlerFunc: updateNetworkProbeDestination}, + {Path: NetworkProbeDestinationDetailsPath, Methods: obsidian.DELETE, HandlerFunc: deleteNetworkProbeDestination}, + } + return ret +} + +func listNetworkProbeTasks(c echo.Context) error { + networkID, nerr := obsidian.GetNetworkId(c) + if nerr != nil { + return nerr + } + + ents, _, err := configurator.LoadAllEntitiesOfType( + networkID, lte.NetworkProbeTaskEntityType, + configurator.EntityLoadCriteria{LoadConfig: true}, + serdes.Entity, + ) + if err == merrors.ErrNotFound { + return echo.ErrNotFound + } + if err != nil { + return obsidian.HttpError(errors.Wrap(err, "failed to load existing NetworkProbeTasks"), http.StatusInternalServerError) + } + + ret := make(map[string]*models.NetworkProbeTask, len(ents)) + for _, ent := range ents { + ret[ent.Key] = (&models.NetworkProbeTask{}).FromBackendModels(ent) + } + return c.JSON(http.StatusOK, ret) +} + +func createNetworkProbeTask(c echo.Context) error { + networkID, nerr := obsidian.GetNetworkId(c) + if nerr != nil { + return nerr + } + + payload := &models.NetworkProbeTask{} + if err := c.Bind(payload); err != nil { + return obsidian.HttpError(err, http.StatusBadRequest) + } + if err := payload.ValidateModel(); err != nil { + return obsidian.HttpError(err, http.StatusBadRequest) + } + + // generate random correlation ID if not provided + if payload.TaskDetails.CorrelationID == 0 { + payload.TaskDetails.CorrelationID = rand.Uint64() + } + + payload.TaskDetails.Timestamp = strfmt.DateTime(time.Now().UTC()) + _, err := configurator.CreateEntity( + networkID, + configurator.NetworkEntity{ + Type: lte.NetworkProbeTaskEntityType, + Key: string(payload.TaskID), + Config: payload.TaskDetails, + }, + serdes.Entity, + ) + if err != nil { + return obsidian.HttpError(err, http.StatusInternalServerError) + } + + return c.NoContent(http.StatusCreated) +} + +func getNetworkProbeTask(c echo.Context) error { + paramNames := []string{"network_id", "task_id"} + values, nerr := obsidian.GetParamValues(c, paramNames...) + if nerr != nil { + return nerr + } + + networkID, taskID := values[0], values[1] + ent, err := configurator.LoadEntity(networkID, + lte.NetworkProbeTaskEntityType, + taskID, + configurator.EntityLoadCriteria{LoadConfig: true}, + serdes.Entity) + if err == merrors.ErrNotFound { + return echo.ErrNotFound + } + if err != nil { + return obsidian.HttpError(err, http.StatusInternalServerError) + } + + ret := (&models.NetworkProbeTask{}).FromBackendModels(ent) + return c.JSON(http.StatusOK, ret) +} + +func updateNetworkProbeTask(c echo.Context) error { + networkID, nerr := obsidian.GetNetworkId(c) + if nerr != nil { + return nerr + } + + payload := &models.NetworkProbeTask{} + if err := c.Bind(payload); err != nil { + return obsidian.HttpError(err, http.StatusBadRequest) + } + if err := payload.ValidateModel(); err != nil { + return obsidian.HttpError(err, http.StatusBadRequest) + } + + _, err := configurator.UpdateEntity(networkID, payload.ToEntityUpdateCriteria(), serdes.Entity) + if err != nil { + return obsidian.HttpError(err, http.StatusInternalServerError) + } + return c.NoContent(http.StatusNoContent) +} + +func deleteNetworkProbeTask(c echo.Context) error { + paramNames := []string{"network_id", "task_id"} + values, nerr := obsidian.GetParamValues(c, paramNames...) + if nerr != nil { + return nerr + } + + networkID, taskID := values[0], values[1] + err := configurator.DeleteEntity(networkID, lte.NetworkProbeTaskEntityType, taskID) + if err != nil { + return obsidian.HttpError(err, http.StatusInternalServerError) + } + return c.NoContent(http.StatusNoContent) +} + +func listNetworkProbeDestinations(c echo.Context) error { + networkID, nerr := obsidian.GetNetworkId(c) + if nerr != nil { + return nerr + } + + ents, _, err := configurator.LoadAllEntitiesOfType( + networkID, lte.NetworkProbeDestinationEntityType, + configurator.EntityLoadCriteria{LoadConfig: true}, + serdes.Entity, + ) + if err != nil { + return obsidian.HttpError(err, http.StatusInternalServerError) + } + + ret := make(map[string]*models.NetworkProbeDestination, len(ents)) + for _, ent := range ents { + ret[ent.Key] = (&models.NetworkProbeDestination{}).FromBackendModels(ent) + } + return c.JSON(http.StatusOK, ret) +} + +func createNetworkProbeDestination(c echo.Context) error { + networkID, nerr := obsidian.GetNetworkId(c) + if nerr != nil { + return nerr + } + + payload := &models.NetworkProbeDestination{} + if err := c.Bind(payload); err != nil { + return obsidian.HttpError(err, http.StatusBadRequest) + } + if err := payload.ValidateModel(); err != nil { + return obsidian.HttpError(err, http.StatusBadRequest) + } + + _, err := configurator.CreateEntity( + networkID, + configurator.NetworkEntity{ + Type: lte.NetworkProbeDestinationEntityType, + Key: string(payload.DestinationID), + Config: payload.DestinationDetails, + }, + serdes.Entity, + ) + if err != nil { + return obsidian.HttpError(err, http.StatusInternalServerError) + } + return c.NoContent(http.StatusCreated) +} + +func getNetworkProbeDestination(c echo.Context) error { + paramNames := []string{"network_id", "destination_id"} + values, nerr := obsidian.GetParamValues(c, paramNames...) + if nerr != nil { + return nerr + } + + networkID, destinationID := values[0], values[1] + ent, err := configurator.LoadEntity(networkID, + lte.NetworkProbeDestinationEntityType, + destinationID, + configurator.EntityLoadCriteria{LoadConfig: true}, + serdes.Entity) + if err == merrors.ErrNotFound { + return echo.ErrNotFound + } + if err != nil { + return obsidian.HttpError(err, http.StatusInternalServerError) + } + + ret := (&models.NetworkProbeDestination{}).FromBackendModels(ent) + return c.JSON(http.StatusOK, ret) +} + +func updateNetworkProbeDestination(c echo.Context) error { + networkID, nerr := obsidian.GetNetworkId(c) + if nerr != nil { + return nerr + } + + payload := &models.NetworkProbeDestination{} + if err := c.Bind(payload); err != nil { + return obsidian.HttpError(err, http.StatusBadRequest) + } + if err := payload.ValidateModel(); err != nil { + return obsidian.HttpError(err, http.StatusBadRequest) + } + + _, err := configurator.UpdateEntity(networkID, payload.ToEntityUpdateCriteria(), serdes.Entity) + if err != nil { + return obsidian.HttpError(err, http.StatusInternalServerError) + } + return c.NoContent(http.StatusNoContent) +} + +func deleteNetworkProbeDestination(c echo.Context) error { + paramNames := []string{"network_id", "destination_id"} + values, nerr := obsidian.GetParamValues(c, paramNames...) + if nerr != nil { + return nerr + } + + networkID, destinationID := values[0], values[1] + err := configurator.DeleteEntity(networkID, lte.NetworkProbeDestinationEntityType, destinationID) + if err != nil { + return obsidian.HttpError(err, http.StatusInternalServerError) + } + return c.NoContent(http.StatusNoContent) +} diff --git a/lte/cloud/go/services/nprobe/obsidian/handlers/handlers_test.go b/lte/cloud/go/services/nprobe/obsidian/handlers/handlers_test.go new file mode 100644 index 000000000000..af96ce504d2e --- /dev/null +++ b/lte/cloud/go/services/nprobe/obsidian/handlers/handlers_test.go @@ -0,0 +1,662 @@ +/* + * Copyright 2020 The Magma Authors. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package handlers_test + +import ( + "testing" + "time" + + "magma/lte/cloud/go/lte" + "magma/lte/cloud/go/serdes" + "magma/lte/cloud/go/services/nprobe/obsidian/handlers" + "magma/lte/cloud/go/services/nprobe/obsidian/models" + "magma/orc8r/cloud/go/obsidian" + "magma/orc8r/cloud/go/obsidian/tests" + "magma/orc8r/cloud/go/services/configurator" + configuratorTestInit "magma/orc8r/cloud/go/services/configurator/test_init" + + "github.com/go-openapi/strfmt" + "github.com/labstack/echo" + "github.com/stretchr/testify/assert" +) + +func init() { + //_ = flag.Set("alsologtostderr", "true") // uncomment to view logs during test +} + +func TestCreateNetworkProbeTask(t *testing.T) { + configuratorTestInit.StartTestService(t) + err := configurator.CreateNetwork(configurator.Network{ID: "n1"}, serdes.Network) + assert.NoError(t, err) + + e := echo.New() + testURLRoot := "/magma/v1/lte/:network_id/network_probe/tasks" + handlers := handlers.GetHandlers() + createNetworkProbeTask := tests.GetHandlerByPathAndMethod(t, handlers, testURLRoot, obsidian.POST).HandlerFunc + + payload := &models.NetworkProbeTask{ + TaskID: "test", + TaskDetails: &models.NetworkProbeTaskDetails{ + TargetID: "test", + TargetType: "imsi", + DeliveryType: "all", + CorrelationID: 8674665223082154000, + Timestamp: strfmt.DateTime(time.Now().UTC()), + }, + } + + tc := tests.Test{ + Method: "POST", + URL: testURLRoot, + Payload: payload, + Handler: createNetworkProbeTask, + ParamNames: []string{"network_id"}, + ParamValues: []string{"n1"}, + ExpectedStatus: 201, + } + tests.RunUnitTest(t, e, tc) + + actual, err := configurator.LoadEntity("n1", lte.NetworkProbeTaskEntityType, "test", configurator.FullEntityLoadCriteria(), serdes.Entity) + assert.NoError(t, err) + expected := configurator.NetworkEntity{ + NetworkID: "n1", + Type: lte.NetworkProbeTaskEntityType, + Key: "test", + Config: payload.TaskDetails, + GraphID: "2", + } + + expected_task := expected.Config.(*models.NetworkProbeTaskDetails) + actual_task := actual.Config.(*models.NetworkProbeTaskDetails) + + assert.Equal(t, expected_task.TargetID, actual_task.TargetID) + assert.Equal(t, expected_task.TargetType, actual_task.TargetType) + assert.Equal(t, expected_task.DeliveryType, actual_task.DeliveryType) + assert.Equal(t, expected_task.CorrelationID, actual_task.CorrelationID) +} + +func TestListNetworkProbeTasks(t *testing.T) { + configuratorTestInit.StartTestService(t) + err := configurator.CreateNetwork(configurator.Network{ID: "n1"}, serdes.Network) + assert.NoError(t, err) + + e := echo.New() + testURLRoot := "/magma/v1/lte/:network_id/network_probe/tasks" + handlers := handlers.GetHandlers() + listNetworkProbeTasks := tests.GetHandlerByPathAndMethod(t, handlers, testURLRoot, obsidian.GET).HandlerFunc + + tc := tests.Test{ + Method: "GET", + URL: testURLRoot, + Handler: listNetworkProbeTasks, + ParamNames: []string{"network_id"}, + ParamValues: []string{"n1"}, + ExpectedStatus: 200, + ExpectedResult: tests.JSONMarshaler(map[string]*models.NetworkProbeTask{}), + } + tests.RunUnitTest(t, e, tc) + + _, err = configurator.CreateEntities( + "n1", + []configurator.NetworkEntity{ + { + Key: "IMSI1234", + Type: lte.NetworkProbeTaskEntityType, + Config: &models.NetworkProbeTaskDetails{ + TargetID: "IMSI1234", + TargetType: "imsi", + DeliveryType: "events_only", + CorrelationID: 8674665223082154000, + }, + }, + { + Key: "IMSI1235", + Type: lte.NetworkProbeTaskEntityType, + Config: &models.NetworkProbeTaskDetails{ + TargetID: "IMSI1235", + TargetType: "imsi", + DeliveryType: "all", + CorrelationID: 8674665223082154099, + }, + }, + }, + serdes.Entity, + ) + assert.NoError(t, err) + + tc = tests.Test{ + Method: "GET", + URL: testURLRoot, + Handler: listNetworkProbeTasks, + ParamNames: []string{"network_id"}, + ParamValues: []string{"n1"}, + ExpectedStatus: 200, + ExpectedResult: tests.JSONMarshaler(map[string]*models.NetworkProbeTask{ + "IMSI1234": { + TaskID: "IMSI1234", + TaskDetails: &models.NetworkProbeTaskDetails{ + TargetID: "IMSI1234", + TargetType: "imsi", + DeliveryType: "events_only", + CorrelationID: 8674665223082154000, + }, + }, + "IMSI1235": { + TaskID: "IMSI1235", + TaskDetails: &models.NetworkProbeTaskDetails{ + TargetID: "IMSI1235", + TargetType: "imsi", + DeliveryType: "all", + CorrelationID: 8674665223082154099, + }, + }, + }), + } + tests.RunUnitTest(t, e, tc) +} + +func TestGetNetworkProbeTask(t *testing.T) { + configuratorTestInit.StartTestService(t) + err := configurator.CreateNetwork(configurator.Network{ID: "n1"}, serdes.Network) + assert.NoError(t, err) + + e := echo.New() + testURLRoot := "/magma/v1/lte/:network_id/network_probe/tasks/:task_id" + handlers := handlers.GetHandlers() + getNetworkProbeTask := tests.GetHandlerByPathAndMethod(t, handlers, testURLRoot, obsidian.GET).HandlerFunc + + tc := tests.Test{ + Method: "GET", + URL: testURLRoot, + Handler: getNetworkProbeTask, + ParamNames: []string{"network_id", "task_id"}, + ParamValues: []string{"n1", "IMSI1234"}, + ExpectedStatus: 404, + ExpectedError: "Not Found", + } + tests.RunUnitTest(t, e, tc) + + _, err = configurator.CreateEntity( + "n1", + configurator.NetworkEntity{ + Key: "IMSI1234", + Type: lte.NetworkProbeTaskEntityType, + Config: &models.NetworkProbeTaskDetails{ + TargetID: "IMSI1234", + TargetType: "imsi", + DeliveryType: "events_only", + CorrelationID: 8674665223082154000, + }, + }, + serdes.Entity, + ) + assert.NoError(t, err) + + tc = tests.Test{ + Method: "GET", + URL: testURLRoot, + Handler: getNetworkProbeTask, + ParamNames: []string{"network_id", "task_id"}, + ParamValues: []string{"n1", "IMSI1234"}, + ExpectedStatus: 200, + ExpectedResult: &models.NetworkProbeTask{ + TaskID: "IMSI1234", + TaskDetails: &models.NetworkProbeTaskDetails{ + TargetID: "IMSI1234", + TargetType: "imsi", + DeliveryType: "events_only", + CorrelationID: 8674665223082154000, + }, + }, + } + tests.RunUnitTest(t, e, tc) +} + +func TestUpdateNetworkProbeTask(t *testing.T) { + configuratorTestInit.StartTestService(t) + err := configurator.CreateNetwork(configurator.Network{ID: "n1"}, serdes.Network) + assert.NoError(t, err) + + e := echo.New() + testURLRoot := "/magma/v1/lte/:network_id/network_probe/tasks/:task_id" + handlers := handlers.GetHandlers() + updateNetworkProbeTask := tests.GetHandlerByPathAndMethod(t, handlers, testURLRoot, obsidian.PUT).HandlerFunc + + // 404 + payload := &models.NetworkProbeTask{ + TaskID: "IMSI1234", + TaskDetails: &models.NetworkProbeTaskDetails{ + TargetID: "IMSI1234", + TargetType: "imsi", + DeliveryType: "events_only", + CorrelationID: 8674665223082154000, + }, + } + + tc := tests.Test{ + Method: "PUT", + URL: testURLRoot, + Handler: updateNetworkProbeTask, + Payload: payload, + ParamNames: []string{"network_id", "task_id"}, + ParamValues: []string{"n1", "IMSI1234"}, + ExpectedStatus: 500, + ExpectedError: "failed to load entity being updated: expected to load 1 ent for update, got 0", + } + tests.RunUnitTest(t, e, tc) + + // Add the NetworkProbeTask + _, err = configurator.CreateEntity( + "n1", + configurator.NetworkEntity{ + Key: "IMSI1234", + Type: lte.NetworkProbeTaskEntityType, + Config: &models.NetworkProbeTaskDetails{ + TargetID: "IMSI1234", + TargetType: "imsi", + DeliveryType: "events_only", + CorrelationID: 8674665223082154000, + }, + }, + serdes.Entity, + ) + assert.NoError(t, err) + + tc = tests.Test{ + Method: "PUT", + URL: testURLRoot, + Handler: updateNetworkProbeTask, + Payload: payload, + ParamNames: []string{"network_id", "task_id"}, + ParamValues: []string{"n1", "IMSI1234"}, + ExpectedStatus: 204, + } + tests.RunUnitTest(t, e, tc) + + actual, err := configurator.LoadEntity("n1", lte.NetworkProbeTaskEntityType, "IMSI1234", configurator.FullEntityLoadCriteria(), serdes.Entity) + assert.NoError(t, err) + expected := configurator.NetworkEntity{ + NetworkID: "n1", + Type: lte.NetworkProbeTaskEntityType, + Key: "IMSI1234", + Config: payload.TaskDetails, + GraphID: "2", + Version: 1, + } + assert.Equal(t, expected, actual) +} + +func TestDeleteNetworkProbeTask(t *testing.T) { + configuratorTestInit.StartTestService(t) + err := configurator.CreateNetwork(configurator.Network{ID: "n1"}, serdes.Network) + assert.NoError(t, err) + + e := echo.New() + testURLRoot := "/magma/v1/lte/:network_id/network_probe/tasks/:task_id" + handlers := handlers.GetHandlers() + deleteNetworkProbeTask := tests.GetHandlerByPathAndMethod(t, handlers, testURLRoot, obsidian.DELETE).HandlerFunc + + _, err = configurator.CreateEntities( + "n1", + []configurator.NetworkEntity{ + { + Key: "IMSI1234", + Type: lte.NetworkProbeTaskEntityType, + Config: &models.NetworkProbeTaskDetails{ + TargetID: "IMSI1234", + TargetType: "imsi", + DeliveryType: "events_only", + CorrelationID: 8674665223082154000, + }, + }, + { + Key: "IMSI1235", + Type: lte.NetworkProbeTaskEntityType, + Config: &models.NetworkProbeTaskDetails{ + TargetID: "IMSI1235", + TargetType: "imsi", + DeliveryType: "all", + CorrelationID: 8674665223082154099, + }, + }, + }, + serdes.Entity, + ) + assert.NoError(t, err) + + tc := tests.Test{ + Method: "DELETE", + URL: testURLRoot, + Handler: deleteNetworkProbeTask, + ParamNames: []string{"network_id", "task_id"}, + ParamValues: []string{"n1", "IMSI1234"}, + ExpectedStatus: 204, + } + tests.RunUnitTest(t, e, tc) + + actual, _, err := configurator.LoadAllEntitiesOfType("n1", lte.NetworkProbeTaskEntityType, configurator.FullEntityLoadCriteria(), serdes.Entity) + assert.NoError(t, err) + assert.Equal(t, 1, len(actual)) + expected := configurator.NetworkEntity{ + NetworkID: "n1", + Type: lte.NetworkProbeTaskEntityType, + Key: "IMSI1235", + Config: &models.NetworkProbeTaskDetails{ + TargetID: "IMSI1235", + TargetType: "imsi", + DeliveryType: "all", + CorrelationID: 8674665223082154099, + }, + GraphID: "4", + Version: 0, + } + assert.Equal(t, expected, actual[0]) +} + +func TestCreateNetworkProbeDestination(t *testing.T) { + configuratorTestInit.StartTestService(t) + err := configurator.CreateNetwork(configurator.Network{ID: "n1"}, serdes.Network) + assert.NoError(t, err) + + e := echo.New() + testURLRoot := "/magma/v1/lte/:network_id/network_probe/destinations" + handlers := handlers.GetHandlers() + createNetworkProbeDestination := tests.GetHandlerByPathAndMethod(t, handlers, testURLRoot, obsidian.POST).HandlerFunc + + payload := &models.NetworkProbeDestination{ + DestinationID: "test", + DestinationDetails: &models.NetworkProbeDestinationDetails{ + DeliveryAddress: "127.0.0.1:4000", + DeliveryType: "all", + }, + } + + tc := tests.Test{ + Method: "POST", + URL: testURLRoot, + Payload: payload, + Handler: createNetworkProbeDestination, + ParamNames: []string{"network_id"}, + ParamValues: []string{"n1"}, + ExpectedStatus: 201, + } + tests.RunUnitTest(t, e, tc) + + actual, err := configurator.LoadEntity("n1", lte.NetworkProbeDestinationEntityType, "test", configurator.FullEntityLoadCriteria(), serdes.Entity) + assert.NoError(t, err) + expected := configurator.NetworkEntity{ + NetworkID: "n1", + Type: lte.NetworkProbeDestinationEntityType, + Key: "test", + Config: payload.DestinationDetails, + GraphID: "2", + } + assert.Equal(t, expected, actual) +} + +func TestListNetworkProbeDestinations(t *testing.T) { + configuratorTestInit.StartTestService(t) + err := configurator.CreateNetwork(configurator.Network{ID: "n1"}, serdes.Network) + assert.NoError(t, err) + + e := echo.New() + testURLRoot := "/magma/v1/lte/:network_id/network_probe/destinations" + handlers := handlers.GetHandlers() + listNetworkProbeDestinations := tests.GetHandlerByPathAndMethod(t, handlers, testURLRoot, obsidian.GET).HandlerFunc + + tc := tests.Test{ + Method: "GET", + URL: testURLRoot, + Handler: listNetworkProbeDestinations, + ParamNames: []string{"network_id"}, + ParamValues: []string{"n1"}, + ExpectedStatus: 200, + ExpectedResult: tests.JSONMarshaler(map[string]*models.NetworkProbeDestination{}), + } + tests.RunUnitTest(t, e, tc) + + _, err = configurator.CreateEntities( + "n1", + []configurator.NetworkEntity{ + { + Key: "1111-2222-3333", + Type: lte.NetworkProbeDestinationEntityType, + Config: &models.NetworkProbeDestinationDetails{ + DeliveryAddress: "127.0.0.1:4000", + DeliveryType: "all", + }, + }, + { + Key: "2222-3333-4444", + Type: lte.NetworkProbeDestinationEntityType, + Config: &models.NetworkProbeDestinationDetails{ + DeliveryAddress: "127.0.0.1:4001", + DeliveryType: "all", + }, + }, + }, + serdes.Entity, + ) + assert.NoError(t, err) + + tc = tests.Test{ + Method: "GET", + URL: testURLRoot, + Handler: listNetworkProbeDestinations, + ParamNames: []string{"network_id"}, + ParamValues: []string{"n1"}, + ExpectedStatus: 200, + ExpectedResult: tests.JSONMarshaler(map[string]*models.NetworkProbeDestination{ + "1111-2222-3333": { + DestinationID: "1111-2222-3333", + DestinationDetails: &models.NetworkProbeDestinationDetails{ + DeliveryAddress: "127.0.0.1:4000", + DeliveryType: "all", + }, + }, + "2222-3333-4444": { + DestinationID: "2222-3333-4444", + DestinationDetails: &models.NetworkProbeDestinationDetails{ + DeliveryAddress: "127.0.0.1:4001", + DeliveryType: "all", + }, + }, + }), + } + tests.RunUnitTest(t, e, tc) +} + +func TestGetNetworkProbeDestination(t *testing.T) { + configuratorTestInit.StartTestService(t) + err := configurator.CreateNetwork(configurator.Network{ID: "n1"}, serdes.Network) + assert.NoError(t, err) + + e := echo.New() + testURLRoot := "/magma/v1/lte/:network_id/network_probe/destinations/:destination_id" + handlers := handlers.GetHandlers() + getNetworkProbeDestination := tests.GetHandlerByPathAndMethod(t, handlers, testURLRoot, obsidian.GET).HandlerFunc + + tc := tests.Test{ + Method: "GET", + URL: testURLRoot, + Handler: getNetworkProbeDestination, + ParamNames: []string{"network_id", "destination_id"}, + ParamValues: []string{"n1", "1111-2222-3333"}, + ExpectedStatus: 404, + ExpectedError: "Not Found", + } + tests.RunUnitTest(t, e, tc) + + _, err = configurator.CreateEntity( + "n1", + configurator.NetworkEntity{ + Key: "1111-2222-3333", + Type: lte.NetworkProbeDestinationEntityType, + Config: &models.NetworkProbeDestinationDetails{ + DeliveryAddress: "127.0.0.1:4000", + DeliveryType: "all", + }, + }, + serdes.Entity, + ) + assert.NoError(t, err) + + tc = tests.Test{ + Method: "GET", + URL: testURLRoot, + Handler: getNetworkProbeDestination, + ParamNames: []string{"network_id", "destination_id"}, + ParamValues: []string{"n1", "1111-2222-3333"}, + ExpectedStatus: 200, + ExpectedResult: &models.NetworkProbeDestination{ + DestinationID: "1111-2222-3333", + DestinationDetails: &models.NetworkProbeDestinationDetails{ + DeliveryAddress: "127.0.0.1:4000", + DeliveryType: "all", + }, + }, + } + tests.RunUnitTest(t, e, tc) +} + +func TestUpdateNetworkProbeDestination(t *testing.T) { + configuratorTestInit.StartTestService(t) + err := configurator.CreateNetwork(configurator.Network{ID: "n1"}, serdes.Network) + assert.NoError(t, err) + + e := echo.New() + testURLRoot := "/magma/v1/lte/:network_id/network_probe/destinations/:destination_id" + handlers := handlers.GetHandlers() + updateNetworkProbeDestination := tests.GetHandlerByPathAndMethod(t, handlers, testURLRoot, obsidian.PUT).HandlerFunc + + // 404 + payload := &models.NetworkProbeDestination{ + DestinationID: "1111-2222-3333", + DestinationDetails: &models.NetworkProbeDestinationDetails{ + DeliveryAddress: "127.0.0.1:4000", + DeliveryType: "all", + }, + } + + tc := tests.Test{ + Method: "PUT", + URL: testURLRoot, + Handler: updateNetworkProbeDestination, + Payload: payload, + ParamNames: []string{"network_id", "destination_id"}, + ParamValues: []string{"n1", "1111-2222-3333"}, + ExpectedStatus: 500, + ExpectedError: "failed to load entity being updated: expected to load 1 ent for update, got 0", + } + tests.RunUnitTest(t, e, tc) + + // Add the NetworkProbeDestination + _, err = configurator.CreateEntity( + "n1", + configurator.NetworkEntity{ + Key: "1111-2222-3333", + Type: lte.NetworkProbeDestinationEntityType, + Config: &models.NetworkProbeDestinationDetails{ + DeliveryAddress: "127.0.0.1:4000", + DeliveryType: "all", + }, + }, + serdes.Entity, + ) + assert.NoError(t, err) + + tc = tests.Test{ + Method: "PUT", + URL: testURLRoot, + Handler: updateNetworkProbeDestination, + Payload: payload, + ParamNames: []string{"network_id", "destination_id"}, + ParamValues: []string{"n1", "1111-2222-3333"}, + ExpectedStatus: 204, + } + tests.RunUnitTest(t, e, tc) + + actual, err := configurator.LoadEntity("n1", lte.NetworkProbeDestinationEntityType, "1111-2222-3333", configurator.FullEntityLoadCriteria(), serdes.Entity) + assert.NoError(t, err) + expected := configurator.NetworkEntity{ + NetworkID: "n1", + Type: lte.NetworkProbeDestinationEntityType, + Key: "1111-2222-3333", + Config: payload.DestinationDetails, + GraphID: "2", + Version: 1, + } + assert.Equal(t, expected, actual) +} + +func TestDeleteNetworkProbeDestination(t *testing.T) { + configuratorTestInit.StartTestService(t) + err := configurator.CreateNetwork(configurator.Network{ID: "n1"}, serdes.Network) + assert.NoError(t, err) + + e := echo.New() + testURLRoot := "/magma/v1/lte/:network_id/network_probe/destinations/:destination_id" + handlers := handlers.GetHandlers() + deleteNetworkProbeDestination := tests.GetHandlerByPathAndMethod(t, handlers, testURLRoot, obsidian.DELETE).HandlerFunc + + _, err = configurator.CreateEntities( + "n1", + []configurator.NetworkEntity{ + { + Key: "1111-2222-3333", + Type: lte.NetworkProbeDestinationEntityType, + Config: &models.NetworkProbeDestinationDetails{ + DeliveryAddress: "127.0.0.1:4000", + DeliveryType: "events_only", + }, + }, + { + Key: "2222-3333-4444", + Type: lte.NetworkProbeDestinationEntityType, + Config: &models.NetworkProbeDestinationDetails{ + DeliveryAddress: "127.0.0.1:4001", + DeliveryType: "all", + }, + }, + }, + serdes.Entity, + ) + assert.NoError(t, err) + + tc := tests.Test{ + Method: "DELETE", + URL: testURLRoot, + Handler: deleteNetworkProbeDestination, + ParamNames: []string{"network_id", "destination_id"}, + ParamValues: []string{"n1", "1111-2222-3333"}, + ExpectedStatus: 204, + } + tests.RunUnitTest(t, e, tc) + + actual, _, err := configurator.LoadAllEntitiesOfType("n1", lte.NetworkProbeDestinationEntityType, configurator.FullEntityLoadCriteria(), serdes.Entity) + assert.NoError(t, err) + assert.Equal(t, 1, len(actual)) + expected := configurator.NetworkEntity{ + NetworkID: "n1", + Type: lte.NetworkProbeDestinationEntityType, + Key: "2222-3333-4444", + Config: &models.NetworkProbeDestinationDetails{ + DeliveryAddress: "127.0.0.1:4001", + DeliveryType: "all", + }, + GraphID: "4", + Version: 0, + } + assert.Equal(t, expected, actual[0]) +} diff --git a/lte/cloud/go/services/nprobe/obsidian/models/conversion.go b/lte/cloud/go/services/nprobe/obsidian/models/conversion.go new file mode 100644 index 000000000000..344ee1629224 --- /dev/null +++ b/lte/cloud/go/services/nprobe/obsidian/models/conversion.go @@ -0,0 +1,47 @@ +/* + * Copyright 2020 The Magma Authors. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package models + +import ( + "magma/lte/cloud/go/lte" + "magma/orc8r/cloud/go/services/configurator" +) + +func (m *NetworkProbeTask) ToEntityUpdateCriteria() configurator.EntityUpdateCriteria { + return configurator.EntityUpdateCriteria{ + Type: lte.NetworkProbeTaskEntityType, + Key: string(m.TaskID), + NewConfig: m.TaskDetails, + } +} + +func (m *NetworkProbeTask) FromBackendModels(ent configurator.NetworkEntity) *NetworkProbeTask { + m.TaskID = NetworkProbeTaskID(ent.Key) + m.TaskDetails = ent.Config.(*NetworkProbeTaskDetails) + return m +} + +func (m *NetworkProbeDestination) ToEntityUpdateCriteria() configurator.EntityUpdateCriteria { + return configurator.EntityUpdateCriteria{ + Type: lte.NetworkProbeDestinationEntityType, + Key: string(m.DestinationID), + NewConfig: m.DestinationDetails, + } +} + +func (m *NetworkProbeDestination) FromBackendModels(ent configurator.NetworkEntity) *NetworkProbeDestination { + m.DestinationID = NetworkProbeDestinationID(ent.Key) + m.DestinationDetails = ent.Config.(*NetworkProbeDestinationDetails) + return m +} diff --git a/lte/cloud/go/services/nprobe/obsidian/models/gen.go b/lte/cloud/go/services/nprobe/obsidian/models/gen.go new file mode 100644 index 000000000000..e9a3b7bb83c1 --- /dev/null +++ b/lte/cloud/go/services/nprobe/obsidian/models/gen.go @@ -0,0 +1,15 @@ +/* + * Copyright 2020 The Magma Authors. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +//go:generate swaggergen --target=swagger.v1.yml --root=$MAGMA_ROOT --config=$SWAGGER_V1_CONFIG +package models diff --git a/lte/cloud/go/services/nprobe/obsidian/models/network_probe_destination_details_swaggergen.go b/lte/cloud/go/services/nprobe/obsidian/models/network_probe_destination_details_swaggergen.go new file mode 100644 index 000000000000..94187efc1e08 --- /dev/null +++ b/lte/cloud/go/services/nprobe/obsidian/models/network_probe_destination_details_swaggergen.go @@ -0,0 +1,118 @@ +// Code generated by go-swagger; DO NOT EDIT. + +package models + +// This file was generated by the swagger tool. +// Editing this file might prove futile when you re-run the swagger generate command + +import ( + "encoding/json" + + strfmt "github.com/go-openapi/strfmt" + + "github.com/go-openapi/errors" + "github.com/go-openapi/swag" + "github.com/go-openapi/validate" +) + +// NetworkProbeDestinationDetails network probe destination details +// swagger:model network_probe_destination_details +type NetworkProbeDestinationDetails struct { + + // delivery address + // Required: true + DeliveryAddress string `json:"delivery_address"` + + // delivery type + // Required: true + // Enum: [all events_only] + DeliveryType string `json:"delivery_type"` +} + +// Validate validates this network probe destination details +func (m *NetworkProbeDestinationDetails) Validate(formats strfmt.Registry) error { + var res []error + + if err := m.validateDeliveryAddress(formats); err != nil { + res = append(res, err) + } + + if err := m.validateDeliveryType(formats); err != nil { + res = append(res, err) + } + + if len(res) > 0 { + return errors.CompositeValidationError(res...) + } + return nil +} + +func (m *NetworkProbeDestinationDetails) validateDeliveryAddress(formats strfmt.Registry) error { + + if err := validate.RequiredString("delivery_address", "body", string(m.DeliveryAddress)); err != nil { + return err + } + + return nil +} + +var networkProbeDestinationDetailsTypeDeliveryTypePropEnum []interface{} + +func init() { + var res []string + if err := json.Unmarshal([]byte(`["all","events_only"]`), &res); err != nil { + panic(err) + } + for _, v := range res { + networkProbeDestinationDetailsTypeDeliveryTypePropEnum = append(networkProbeDestinationDetailsTypeDeliveryTypePropEnum, v) + } +} + +const ( + + // NetworkProbeDestinationDetailsDeliveryTypeAll captures enum value "all" + NetworkProbeDestinationDetailsDeliveryTypeAll string = "all" + + // NetworkProbeDestinationDetailsDeliveryTypeEventsOnly captures enum value "events_only" + NetworkProbeDestinationDetailsDeliveryTypeEventsOnly string = "events_only" +) + +// prop value enum +func (m *NetworkProbeDestinationDetails) validateDeliveryTypeEnum(path, location string, value string) error { + if err := validate.Enum(path, location, value, networkProbeDestinationDetailsTypeDeliveryTypePropEnum); err != nil { + return err + } + return nil +} + +func (m *NetworkProbeDestinationDetails) validateDeliveryType(formats strfmt.Registry) error { + + if err := validate.RequiredString("delivery_type", "body", string(m.DeliveryType)); err != nil { + return err + } + + // value enum + if err := m.validateDeliveryTypeEnum("delivery_type", "body", m.DeliveryType); err != nil { + return err + } + + return nil +} + +// MarshalBinary interface implementation +func (m *NetworkProbeDestinationDetails) MarshalBinary() ([]byte, error) { + if m == nil { + return nil, nil + } + return swag.WriteJSON(m) +} + +// UnmarshalBinary interface implementation +func (m *NetworkProbeDestinationDetails) UnmarshalBinary(b []byte) error { + var res NetworkProbeDestinationDetails + if err := swag.ReadJSON(b, &res); err != nil { + return err + } + *m = res + return nil +} diff --git a/lte/cloud/go/services/nprobe/obsidian/models/network_probe_destination_id_swaggergen.go b/lte/cloud/go/services/nprobe/obsidian/models/network_probe_destination_id_swaggergen.go new file mode 100644 index 000000000000..559e050fa422 --- /dev/null +++ b/lte/cloud/go/services/nprobe/obsidian/models/network_probe_destination_id_swaggergen.go @@ -0,0 +1,19 @@ +// Code generated by go-swagger; DO NOT EDIT. + +package models + +// This file was generated by the swagger tool. +// Editing this file might prove futile when you re-run the swagger generate command + +import ( + strfmt "github.com/go-openapi/strfmt" +) + +// NetworkProbeDestinationID network probe destination id +// swagger:model network_probe_destination_id +type NetworkProbeDestinationID string + +// Validate validates this network probe destination id +func (m NetworkProbeDestinationID) Validate(formats strfmt.Registry) error { + return nil +} diff --git a/lte/cloud/go/services/nprobe/obsidian/models/network_probe_destination_swaggergen.go b/lte/cloud/go/services/nprobe/obsidian/models/network_probe_destination_swaggergen.go new file mode 100644 index 000000000000..1d9870525907 --- /dev/null +++ b/lte/cloud/go/services/nprobe/obsidian/models/network_probe_destination_swaggergen.go @@ -0,0 +1,93 @@ +// Code generated by go-swagger; DO NOT EDIT. + +package models + +// This file was generated by the swagger tool. +// Editing this file might prove futile when you re-run the swagger generate command + +import ( + strfmt "github.com/go-openapi/strfmt" + + "github.com/go-openapi/errors" + "github.com/go-openapi/swag" + "github.com/go-openapi/validate" +) + +// NetworkProbeDestination Network Probe Destination +// swagger:model network_probe_destination +type NetworkProbeDestination struct { + + // destination details + // Required: true + DestinationDetails *NetworkProbeDestinationDetails `json:"destination_details"` + + // destination id + // Required: true + DestinationID NetworkProbeDestinationID `json:"destination_id"` +} + +// Validate validates this network probe destination +func (m *NetworkProbeDestination) Validate(formats strfmt.Registry) error { + var res []error + + if err := m.validateDestinationDetails(formats); err != nil { + res = append(res, err) + } + + if err := m.validateDestinationID(formats); err != nil { + res = append(res, err) + } + + if len(res) > 0 { + return errors.CompositeValidationError(res...) + } + return nil +} + +func (m *NetworkProbeDestination) validateDestinationDetails(formats strfmt.Registry) error { + + if err := validate.Required("destination_details", "body", m.DestinationDetails); err != nil { + return err + } + + if m.DestinationDetails != nil { + if err := m.DestinationDetails.Validate(formats); err != nil { + if ve, ok := err.(*errors.Validation); ok { + return ve.ValidateName("destination_details") + } + return err + } + } + + return nil +} + +func (m *NetworkProbeDestination) validateDestinationID(formats strfmt.Registry) error { + + if err := m.DestinationID.Validate(formats); err != nil { + if ve, ok := err.(*errors.Validation); ok { + return ve.ValidateName("destination_id") + } + return err + } + + return nil +} + +// MarshalBinary interface implementation +func (m *NetworkProbeDestination) MarshalBinary() ([]byte, error) { + if m == nil { + return nil, nil + } + return swag.WriteJSON(m) +} + +// UnmarshalBinary interface implementation +func (m *NetworkProbeDestination) UnmarshalBinary(b []byte) error { + var res NetworkProbeDestination + if err := swag.ReadJSON(b, &res); err != nil { + return err + } + *m = res + return nil +} diff --git a/lte/cloud/go/services/nprobe/obsidian/models/network_probe_task_details_swaggergen.go b/lte/cloud/go/services/nprobe/obsidian/models/network_probe_task_details_swaggergen.go new file mode 100644 index 000000000000..c385e0d789cf --- /dev/null +++ b/lte/cloud/go/services/nprobe/obsidian/models/network_probe_task_details_swaggergen.go @@ -0,0 +1,218 @@ +// Code generated by go-swagger; DO NOT EDIT. + +package models + +// This file was generated by the swagger tool. +// Editing this file might prove futile when you re-run the swagger generate command + +import ( + "encoding/json" + + strfmt "github.com/go-openapi/strfmt" + + "github.com/go-openapi/errors" + "github.com/go-openapi/swag" + "github.com/go-openapi/validate" +) + +// NetworkProbeTaskDetails network probe task details +// swagger:model network_probe_task_details +type NetworkProbeTaskDetails struct { + + // correlation id + CorrelationID uint64 `json:"correlation_id,omitempty"` + + // delivery type + // Required: true + // Enum: [all events_only] + DeliveryType string `json:"delivery_type"` + + // the duration in seconds after which the task will expire. + // Minimum: 0 + Duration *int64 `json:"duration,omitempty"` + + // target id + // Required: true + TargetID string `json:"target_id"` + + // target type + // Required: true + // Enum: [imsi imei msisdn] + TargetType string `json:"target_type"` + + // The timestamp in ISO 8601 format + // Format: date-time + Timestamp strfmt.DateTime `json:"timestamp,omitempty"` +} + +// Validate validates this network probe task details +func (m *NetworkProbeTaskDetails) Validate(formats strfmt.Registry) error { + var res []error + + if err := m.validateDeliveryType(formats); err != nil { + res = append(res, err) + } + + if err := m.validateDuration(formats); err != nil { + res = append(res, err) + } + + if err := m.validateTargetID(formats); err != nil { + res = append(res, err) + } + + if err := m.validateTargetType(formats); err != nil { + res = append(res, err) + } + + if err := m.validateTimestamp(formats); err != nil { + res = append(res, err) + } + + if len(res) > 0 { + return errors.CompositeValidationError(res...) + } + return nil +} + +var networkProbeTaskDetailsTypeDeliveryTypePropEnum []interface{} + +func init() { + var res []string + if err := json.Unmarshal([]byte(`["all","events_only"]`), &res); err != nil { + panic(err) + } + for _, v := range res { + networkProbeTaskDetailsTypeDeliveryTypePropEnum = append(networkProbeTaskDetailsTypeDeliveryTypePropEnum, v) + } +} + +const ( + + // NetworkProbeTaskDetailsDeliveryTypeAll captures enum value "all" + NetworkProbeTaskDetailsDeliveryTypeAll string = "all" + + // NetworkProbeTaskDetailsDeliveryTypeEventsOnly captures enum value "events_only" + NetworkProbeTaskDetailsDeliveryTypeEventsOnly string = "events_only" +) + +// prop value enum +func (m *NetworkProbeTaskDetails) validateDeliveryTypeEnum(path, location string, value string) error { + if err := validate.Enum(path, location, value, networkProbeTaskDetailsTypeDeliveryTypePropEnum); err != nil { + return err + } + return nil +} + +func (m *NetworkProbeTaskDetails) validateDeliveryType(formats strfmt.Registry) error { + + if err := validate.RequiredString("delivery_type", "body", string(m.DeliveryType)); err != nil { + return err + } + + // value enum + if err := m.validateDeliveryTypeEnum("delivery_type", "body", m.DeliveryType); err != nil { + return err + } + + return nil +} + +func (m *NetworkProbeTaskDetails) validateDuration(formats strfmt.Registry) error { + + if swag.IsZero(m.Duration) { // not required + return nil + } + + if err := validate.MinimumInt("duration", "body", int64(*m.Duration), 0, false); err != nil { + return err + } + + return nil +} + +func (m *NetworkProbeTaskDetails) validateTargetID(formats strfmt.Registry) error { + + if err := validate.RequiredString("target_id", "body", string(m.TargetID)); err != nil { + return err + } + + return nil +} + +var networkProbeTaskDetailsTypeTargetTypePropEnum []interface{} + +func init() { + var res []string + if err := json.Unmarshal([]byte(`["imsi","imei","msisdn"]`), &res); err != nil { + panic(err) + } + for _, v := range res { + networkProbeTaskDetailsTypeTargetTypePropEnum = append(networkProbeTaskDetailsTypeTargetTypePropEnum, v) + } +} + +const ( + + // NetworkProbeTaskDetailsTargetTypeImsi captures enum value "imsi" + NetworkProbeTaskDetailsTargetTypeImsi string = "imsi" + + // NetworkProbeTaskDetailsTargetTypeImei captures enum value "imei" + NetworkProbeTaskDetailsTargetTypeImei string = "imei" + + // NetworkProbeTaskDetailsTargetTypeMsisdn captures enum value "msisdn" + NetworkProbeTaskDetailsTargetTypeMsisdn string = "msisdn" +) + +// prop value enum +func (m *NetworkProbeTaskDetails) validateTargetTypeEnum(path, location string, value string) error { + if err := validate.Enum(path, location, value, networkProbeTaskDetailsTypeTargetTypePropEnum); err != nil { + return err + } + return nil +} + +func (m *NetworkProbeTaskDetails) validateTargetType(formats strfmt.Registry) error { + + if err := validate.RequiredString("target_type", "body", string(m.TargetType)); err != nil { + return err + } + + // value enum + if err := m.validateTargetTypeEnum("target_type", "body", m.TargetType); err != nil { + return err + } + + return nil +} + +func (m *NetworkProbeTaskDetails) validateTimestamp(formats strfmt.Registry) error { + + if swag.IsZero(m.Timestamp) { // not required + return nil + } + + if err := validate.FormatOf("timestamp", "body", "date-time", m.Timestamp.String(), formats); err != nil { + return err + } + + return nil +} + +// MarshalBinary interface implementation +func (m *NetworkProbeTaskDetails) MarshalBinary() ([]byte, error) { + if m == nil { + return nil, nil + } + return swag.WriteJSON(m) +} + +// UnmarshalBinary interface implementation +func (m *NetworkProbeTaskDetails) UnmarshalBinary(b []byte) error { + var res NetworkProbeTaskDetails + if err := swag.ReadJSON(b, &res); err != nil { + return err + } + *m = res + return nil +} diff --git a/lte/cloud/go/services/nprobe/obsidian/models/network_probe_task_id_swaggergen.go b/lte/cloud/go/services/nprobe/obsidian/models/network_probe_task_id_swaggergen.go new file mode 100644 index 000000000000..1e96e4d9d1b7 --- /dev/null +++ b/lte/cloud/go/services/nprobe/obsidian/models/network_probe_task_id_swaggergen.go @@ -0,0 +1,19 @@ +// Code generated by go-swagger; DO NOT EDIT. + +package models + +// This file was generated by the swagger tool. +// Editing this file might prove futile when you re-run the swagger generate command + +import ( + strfmt "github.com/go-openapi/strfmt" +) + +// NetworkProbeTaskID network probe task id +// swagger:model network_probe_task_id +type NetworkProbeTaskID string + +// Validate validates this network probe task id +func (m NetworkProbeTaskID) Validate(formats strfmt.Registry) error { + return nil +} diff --git a/lte/cloud/go/services/nprobe/obsidian/models/network_probe_task_swaggergen.go b/lte/cloud/go/services/nprobe/obsidian/models/network_probe_task_swaggergen.go new file mode 100644 index 000000000000..fc8e59027ff8 --- /dev/null +++ b/lte/cloud/go/services/nprobe/obsidian/models/network_probe_task_swaggergen.go @@ -0,0 +1,93 @@ +// Code generated by go-swagger; DO NOT EDIT. + +package models + +// This file was generated by the swagger tool. +// Editing this file might prove futile when you re-run the swagger generate command + +import ( + strfmt "github.com/go-openapi/strfmt" + + "github.com/go-openapi/errors" + "github.com/go-openapi/swag" + "github.com/go-openapi/validate" +) + +// NetworkProbeTask Network Probe Task +// swagger:model network_probe_task +type NetworkProbeTask struct { + + // task details + // Required: true + TaskDetails *NetworkProbeTaskDetails `json:"task_details"` + + // task id + // Required: true + TaskID NetworkProbeTaskID `json:"task_id"` +} + +// Validate validates this network probe task +func (m *NetworkProbeTask) Validate(formats strfmt.Registry) error { + var res []error + + if err := m.validateTaskDetails(formats); err != nil { + res = append(res, err) + } + + if err := m.validateTaskID(formats); err != nil { + res = append(res, err) + } + + if len(res) > 0 { + return errors.CompositeValidationError(res...) + } + return nil +} + +func (m *NetworkProbeTask) validateTaskDetails(formats strfmt.Registry) error { + + if err := validate.Required("task_details", "body", m.TaskDetails); err != nil { + return err + } + + if m.TaskDetails != nil { + if err := m.TaskDetails.Validate(formats); err != nil { + if ve, ok := err.(*errors.Validation); ok { + return ve.ValidateName("task_details") + } + return err + } + } + + return nil +} + +func (m *NetworkProbeTask) validateTaskID(formats strfmt.Registry) error { + + if err := m.TaskID.Validate(formats); err != nil { + if ve, ok := err.(*errors.Validation); ok { + return ve.ValidateName("task_id") + } + return err + } + + return nil +} + +// MarshalBinary interface implementation +func (m *NetworkProbeTask) MarshalBinary() ([]byte, error) { + if m == nil { + return nil, nil + } + return swag.WriteJSON(m) +} + +// UnmarshalBinary interface implementation +func (m *NetworkProbeTask) UnmarshalBinary(b []byte) error { + var res NetworkProbeTask + if err := swag.ReadJSON(b, &res); err != nil { + return err + } + *m = res + return nil +} diff --git a/lte/cloud/go/services/nprobe/obsidian/models/serdes.go b/lte/cloud/go/services/nprobe/obsidian/models/serdes.go new file mode 100644 index 000000000000..ae7219c1214c --- /dev/null +++ b/lte/cloud/go/services/nprobe/obsidian/models/serdes.go @@ -0,0 +1,28 @@ +/* + Copyright 2020 The Magma Authors. + + This source code is licensed under the BSD-style license found in the + LICENSE file in the root directory of this source tree. + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + +package models + +import ( + "magma/lte/cloud/go/lte" + "magma/orc8r/cloud/go/serde" + "magma/orc8r/cloud/go/services/configurator" +) + +var ( + // EntitySerdes contains the package's configurator network entity serdes + EntitySerdes = serde.NewRegistry( + configurator.NewNetworkEntityConfigSerde(lte.NetworkProbeTaskEntityType, &NetworkProbeTaskDetails{}), + configurator.NewNetworkEntityConfigSerde(lte.NetworkProbeDestinationEntityType, &NetworkProbeDestinationDetails{}), + ) +) diff --git a/lte/cloud/go/services/nprobe/obsidian/models/swagger.v1.yml b/lte/cloud/go/services/nprobe/obsidian/models/swagger.v1.yml new file mode 100644 index 000000000000..956dc9654b08 --- /dev/null +++ b/lte/cloud/go/services/nprobe/obsidian/models/swagger.v1.yml @@ -0,0 +1,294 @@ +--- +swagger: '2.0' + +magma-gen-meta: + go-package: magma/lte/cloud/go/services/nprobe/obsidian/models + dependencies: + - 'orc8r/cloud/go/models/swagger-common.yml' + - 'orc8r/cloud/go/services/orchestrator/obsidian/models/swagger.v1.yml' + temp-gen-filename: lte-nprobe-swagger.yml + output-dir: lte/cloud/go/services/nprobe/obsidian + types: + - go-struct-name: NetworkProbeTaskID + filename: network_probe_task_id_swaggergen.go + - go-struct-name: NetworkProbeTaskDetails + filename: network_probe_task_details_swaggergen.go + - go-struct-name: NetworkProbeTask + filename: network_probe_task_swaggergen.go + - go-struct-name: NetworkProbeDestinationID + filename: network_probe_destination_id_swaggergen.go + - go-struct-name: NetworkProbeDestinationDetails + filename: network_probe_destination_details_swaggergen.go + - go-struct-name: NetworkProbeDestination + filename: network_probe_destination_swaggergen.go + +info: + title: LTE Network Probes Management + description: LTE REST APIs + version: 1.0.0 + +basePath: /magma/v1 + +paths: + /lte/{network_id}/network_probe/tasks: + get: + summary: List NetworkProbeTask in the network + tags: + - Network Probes + parameters: + - $ref: './orc8r-swagger-common.yml#/parameters/network_id' + responses: + '200': + description: Provisioned NetworkProbeTasks + schema: + $ref: '#/definitions/network_probe_task' + default: + $ref: './orc8r-swagger-common.yml#/responses/UnexpectedError' + post: + summary: Add a new NetworkProbeTask to the network + tags: + - Network Probes + parameters: + - $ref: './orc8r-swagger-common.yml#/parameters/network_id' + - name: network_probe_task + in: body + required: true + schema: + $ref: '#/definitions/network_probe_task' + responses: + '201': + description: Success + default: + $ref: './orc8r-swagger-common.yml#/responses/UnexpectedError' + + /lte/{network_id}/network_probe/tasks/{task_id}: + get: + summary: Retrieve the NetworkProbeTask info + tags: + - Network Probes + parameters: + - $ref: './orc8r-swagger-common.yml#/parameters/network_id' + - $ref: '#/parameters/task_id' + responses: + '200': + description: NetworkProbeTask Info + schema: + $ref: '#/definitions/network_probe_task' + default: + $ref: './orc8r-swagger-common.yml#/responses/UnexpectedError' + put: + summary: Update an existing NetworkProbeTask in the network + tags: + - Network Probes + parameters: + - $ref: './orc8r-swagger-common.yml#/parameters/network_id' + - $ref: '#/parameters/task_id' + - in: body + name: network_probe_task + description: New NetworkProbeTask configuration + required: true + schema: + $ref: '#/definitions/network_probe_task' + responses: + '204': + description: Success + default: + $ref: './orc8r-swagger-common.yml#/responses/UnexpectedError' + delete: + summary: Remove an NetworkProbeTask from the network + tags: + - Network Probes + parameters: + - $ref: './orc8r-swagger-common.yml#/parameters/network_id' + - $ref: '#/parameters/task_id' + responses: + '204': + description: Success + default: + $ref: './orc8r-swagger-common.yml#/responses/UnexpectedError' + + /lte/{network_id}/network_probe/destinations: + get: + summary: List NetworkProbe Destinations in the network + tags: + - Network Probes + parameters: + - $ref: './orc8r-swagger-common.yml#/parameters/network_id' + responses: + '200': + description: Provisioned NetworkProbe Destinations + schema: + $ref: '#/definitions/network_probe_destination' + default: + $ref: './orc8r-swagger-common.yml#/responses/UnexpectedError' + post: + summary: Add a new NetworkProbeDestination to the network + tags: + - Network Probes + parameters: + - $ref: './orc8r-swagger-common.yml#/parameters/network_id' + - name: network_probe_destination + in: body + required: true + schema: + $ref: '#/definitions/network_probe_destination' + responses: + '201': + description: Success + default: + $ref: './orc8r-swagger-common.yml#/responses/UnexpectedError' + + /lte/{network_id}/network_probe/destinations/{destination_id}: + get: + summary: Retrieve a NetworkProbe Destination + tags: + - Network Probes + parameters: + - $ref: './orc8r-swagger-common.yml#/parameters/network_id' + - $ref: '#/parameters/destination_id' + responses: + '200': + description: NetworkProbeDestination Info + schema: + $ref: '#/definitions/network_probe_destination' + default: + $ref: './orc8r-swagger-common.yml#/responses/UnexpectedError' + put: + summary: Update an existing NetworkProbe Destination in the network + tags: + - Network Probes + parameters: + - $ref: './orc8r-swagger-common.yml#/parameters/network_id' + - $ref: '#/parameters/destination_id' + - in: body + name: network_probe_destination + description: New NetworkProbeDestination configuration + required: true + schema: + $ref: '#/definitions/network_probe_destination' + responses: + '204': + description: Success + default: + $ref: './orc8r-swagger-common.yml#/responses/UnexpectedError' + delete: + summary: Remove a NetworkProbe Destination from the network + tags: + - Network Probes + parameters: + - $ref: './orc8r-swagger-common.yml#/parameters/network_id' + - $ref: '#/parameters/destination_id' + responses: + '204': + description: Success + default: + $ref: './orc8r-swagger-common.yml#/responses/UnexpectedError' + +parameters: + task_id: + in: path + name: task_id + description: Network Probe Task ID + required: true + type: string + + destination_id: + in: path + name: destination_id + description: Network Probe Destination ID + required: true + type: string + +definitions: + network_probe_task: + description: Network Probe Task + type: object + required: + - task_id + - task_details + properties: + task_id: + $ref: "#/definitions/network_probe_task_id" + task_details: + $ref: '#/definitions/network_probe_task_details' + + network_probe_task_id: + type: string + x-nullable: false + example: 'imsi1023001' + + network_probe_task_details: + type: object + required: + - target_id + - target_type + - delivery_type + properties: + target_id: + type: string + x-nullable: false + target_type: + type: string + x-nullable: false + enum: + - 'imsi' + - 'imei' + - 'msisdn' + example: 'imsi' + delivery_type: + type: string + x-nullable: false + enum: + - 'all' + - 'events_only' + example: 'events_only' + correlation_id: + type: integer + format: uint64 + example: 605394647632969700 + duration: + type: integer + default: 0 + minimum: 0 + example: 300 + description: the duration in seconds after which the task will expire. + timestamp: + type: string + format: date-time + example: 2020-03-11T00:36:59.65Z + description: The timestamp in ISO 8601 format + + network_probe_destination: + description: Network Probe Destination + type: object + required: + - destination_id + - destination_details + properties: + destination_id: + $ref: '#/definitions/network_probe_destination_id' + destination_details: + $ref: '#/definitions/network_probe_destination_details' + + network_probe_destination_id: + type: string + x-nullable: false + example: 'xxxx-yyyy-zzzz' + + network_probe_destination_details: + type: object + required: + - delivery_type + - delivery_address + properties: + delivery_type: + type: string + x-nullable: false + enum: + - 'all' + - 'events_only' + example: 'events_only' + delivery_address: + type: string + x-nullable: false + example: '127.0.0.1:4040' diff --git a/lte/cloud/go/services/nprobe/obsidian/models/validate.go b/lte/cloud/go/services/nprobe/obsidian/models/validate.go new file mode 100644 index 000000000000..5358471cff6c --- /dev/null +++ b/lte/cloud/go/services/nprobe/obsidian/models/validate.go @@ -0,0 +1,26 @@ +/* + * Copyright 2020 The Magma Authors. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package models + +import ( + strfmt "github.com/go-openapi/strfmt" +) + +func (m *NetworkProbeTask) ValidateModel() error { + return m.Validate(strfmt.Default) +} + +func (m *NetworkProbeDestination) ValidateModel() error { + return m.Validate(strfmt.Default) +} diff --git a/orc8r/cloud/docker/controller/supervisord.conf b/orc8r/cloud/docker/controller/supervisord.conf index c9e9fbda3c59..f22bf3bd4320 100644 --- a/orc8r/cloud/docker/controller/supervisord.conf +++ b/orc8r/cloud/docker/controller/supervisord.conf @@ -269,3 +269,11 @@ stdout_logfile=NONE stderr_logfile=NONE stdout_events_enabled=true stderr_events_enabled=true + +[program:nprobe] +command=/usr/bin/envdir /var/opt/magma/envdir /var/opt/magma/bin/nprobe -run_echo_server=true -logtostderr=true -v=0 +autorestart=true +stdout_logfile=NONE +stderr_logfile=NONE +stdout_events_enabled=true +stderr_events_enabled=true diff --git a/orc8r/cloud/go/obsidian/swagger/v1/swagger.yml b/orc8r/cloud/go/obsidian/swagger/v1/swagger.yml index a1368c7a713f..8287d991e4fd 100644 --- a/orc8r/cloud/go/obsidian/swagger/v1/swagger.yml +++ b/orc8r/cloud/go/obsidian/swagger/v1/swagger.yml @@ -3203,6 +3203,156 @@ paths: summary: Update the name of an LTE network tags: - LTE Networks + /lte/{network_id}/network_probe/destinations: + get: + parameters: + - $ref: '#/parameters/network_id' + responses: + "200": + description: Provisioned NetworkProbe Destinations + schema: + $ref: '#/definitions/network_probe_destination' + default: + $ref: '#/responses/UnexpectedError' + summary: List NetworkProbe Destinations in the network + tags: + - Network Probes + post: + parameters: + - $ref: '#/parameters/network_id' + - in: body + name: network_probe_destination + required: true + schema: + $ref: '#/definitions/network_probe_destination' + responses: + "201": + description: Success + default: + $ref: '#/responses/UnexpectedError' + summary: Add a new NetworkProbeDestination to the network + tags: + - Network Probes + /lte/{network_id}/network_probe/destinations/{destination_id}: + delete: + parameters: + - $ref: '#/parameters/network_id' + - $ref: '#/parameters/destination_id' + responses: + "204": + description: Success + default: + $ref: '#/responses/UnexpectedError' + summary: Remove a NetworkProbe Destination from the network + tags: + - Network Probes + get: + parameters: + - $ref: '#/parameters/network_id' + - $ref: '#/parameters/destination_id' + responses: + "200": + description: NetworkProbeDestination Info + schema: + $ref: '#/definitions/network_probe_destination' + default: + $ref: '#/responses/UnexpectedError' + summary: Retrieve a NetworkProbe Destination + tags: + - Network Probes + put: + parameters: + - $ref: '#/parameters/network_id' + - $ref: '#/parameters/destination_id' + - description: New NetworkProbeDestination configuration + in: body + name: network_probe_destination + required: true + schema: + $ref: '#/definitions/network_probe_destination' + responses: + "204": + description: Success + default: + $ref: '#/responses/UnexpectedError' + summary: Update an existing NetworkProbe Destination in the network + tags: + - Network Probes + /lte/{network_id}/network_probe/tasks: + get: + parameters: + - $ref: '#/parameters/network_id' + responses: + "200": + description: Provisioned NetworkProbeTasks + schema: + $ref: '#/definitions/network_probe_task' + default: + $ref: '#/responses/UnexpectedError' + summary: List NetworkProbeTask in the network + tags: + - Network Probes + post: + parameters: + - $ref: '#/parameters/network_id' + - in: body + name: network_probe_task + required: true + schema: + $ref: '#/definitions/network_probe_task' + responses: + "201": + description: Success + default: + $ref: '#/responses/UnexpectedError' + summary: Add a new NetworkProbeTask to the network + tags: + - Network Probes + /lte/{network_id}/network_probe/tasks/{task_id}: + delete: + parameters: + - $ref: '#/parameters/network_id' + - $ref: '#/parameters/task_id' + responses: + "204": + description: Success + default: + $ref: '#/responses/UnexpectedError' + summary: Remove an NetworkProbeTask from the network + tags: + - Network Probes + get: + parameters: + - $ref: '#/parameters/network_id' + - $ref: '#/parameters/task_id' + responses: + "200": + description: NetworkProbeTask Info + schema: + $ref: '#/definitions/network_probe_task' + default: + $ref: '#/responses/UnexpectedError' + summary: Retrieve the NetworkProbeTask info + tags: + - Network Probes + put: + parameters: + - $ref: '#/parameters/network_id' + - $ref: '#/parameters/task_id' + - description: New NetworkProbeTask configuration + in: body + name: network_probe_task + required: true + schema: + $ref: '#/definitions/network_probe_task' + responses: + "204": + description: Success + default: + $ref: '#/responses/UnexpectedError' + summary: Update an existing NetworkProbeTask in the network + tags: + - Network Probes /lte/{network_id}/policy_qos_profiles: get: parameters: @@ -6543,6 +6693,12 @@ parameters: name: channel_id required: true type: string + destination_id: + description: Network Probe Destination ID + in: path + name: destination_id + required: true + type: string dns_domain: description: DNS record domain in: path @@ -6657,6 +6813,12 @@ parameters: name: subscriber_id required: true type: string + task_id: + description: Network Probe Task ID + in: path + name: task_id + required: true + type: string tenant_id: description: Tenant ID in: path @@ -9928,6 +10090,93 @@ definitions: minLength: 1 type: string x-nullable: false + network_probe_destination: + description: Network Probe Destination + properties: + destination_details: + $ref: '#/definitions/network_probe_destination_details' + destination_id: + $ref: '#/definitions/network_probe_destination_id' + required: + - destination_id + - destination_details + type: object + network_probe_destination_details: + properties: + delivery_address: + example: 127.0.0.1:4040 + type: string + x-nullable: false + delivery_type: + enum: + - all + - events_only + example: events_only + type: string + x-nullable: false + required: + - delivery_type + - delivery_address + type: object + network_probe_destination_id: + example: xxxx-yyyy-zzzz + type: string + x-nullable: false + network_probe_task: + description: Network Probe Task + properties: + task_details: + $ref: '#/definitions/network_probe_task_details' + task_id: + $ref: '#/definitions/network_probe_task_id' + required: + - task_id + - task_details + type: object + network_probe_task_details: + properties: + correlation_id: + example: 605394647632969700 + format: uint64 + type: integer + delivery_type: + enum: + - all + - events_only + example: events_only + type: string + x-nullable: false + duration: + default: 0 + description: the duration in seconds after which the task will expire. + example: 300 + minimum: 0 + type: integer + target_id: + type: string + x-nullable: false + target_type: + enum: + - imsi + - imei + - msisdn + example: imsi + type: string + x-nullable: false + timestamp: + description: The timestamp in ISO 8601 format + example: "2020-03-11T00:36:59.65Z" + format: date-time + type: string + required: + - target_id + - target_type + - delivery_type + type: object + network_probe_task_id: + example: imsi1023001 + type: string + x-nullable: false network_ran_configs: description: RAN (radio access network) cellular configuration for a network minLength: 1 From 8a3e663411ffeed6176c86f52b2110dc4e9ad88f Mon Sep 17 00:00:00 2001 From: Ulas Kozat Date: Mon, 5 Apr 2021 19:21:59 -0700 Subject: [PATCH 29/91] Fix bug on referenced value assignment (#5927) Signed-off-by: Ulas Kozat --- lte/gateway/c/oai/tasks/s1ap/s1ap_mme_nas_procedures.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lte/gateway/c/oai/tasks/s1ap/s1ap_mme_nas_procedures.c b/lte/gateway/c/oai/tasks/s1ap/s1ap_mme_nas_procedures.c index 494c9b66d8ef..e8d3745a9c25 100644 --- a/lte/gateway/c/oai/tasks/s1ap/s1ap_mme_nas_procedures.c +++ b/lte/gateway/c/oai/tasks/s1ap/s1ap_mme_nas_procedures.c @@ -520,7 +520,7 @@ int s1ap_generate_downlink_nas_transport( if (hashtable_uint64_ts_insert( imsi_map->mme_ue_id_imsi_htbl, (const hash_key_t) ue_id, imsi64) == HASH_TABLE_SAME_KEY_VALUE_EXISTS) { - is_state_same = true; + *is_state_same = true; } S1ap_DownlinkNASTransport_IEs_t* ie = NULL; From 37303025e720e7c67aeee46a8a7e55d757e71694 Mon Sep 17 00:00:00 2001 From: Hunter Gatewood Date: Tue, 6 Apr 2021 05:22:09 -0700 Subject: [PATCH 30/91] [codeowners] Bare-metal proposal (#5857) Signed-off-by: Hunter Gatewood --- CODEOWNERS | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/CODEOWNERS b/CODEOWNERS index 90dc6527c8b2..15c3ba51e1be 100644 --- a/CODEOWNERS +++ b/CODEOWNERS @@ -3,16 +3,17 @@ circleci/ @tmdzk @mattymo @119Vik ci-scripts/ @rdefosse @tmdzk @mattymo @119Vik -*/cloud/ @magma/orc8r-approvers +*/cloud/ @magma/orc8r-approvers .golangci.yml @magma/orc8r-approvers -orc8r/ @magma/orc8r-approvers +orc8r/ @magma/orc8r-approvers orc8r/tools/packer/ @tmdzk @mattymo @119Vik orc8r/cloud/deploy/bare-metal/ @tmdzk @mattymo @119Vik +orc8r/cloud/deploy/bare-metal-ansible/ @mattymo @119Vik orc8r/gateway/c/ @themarwhal @ardzoht -feg/ @magma/feg-approvers +feg/ @magma/feg-approvers cwf/ @themarwhal @mpgermano @uri200 @emakeev cwf/gateway/Vagrantfile @mattymo @119Vik @tmdzk From bfa832a0e7eb7910bfc5a1d0c6355d861d1c5af2 Mon Sep 17 00:00:00 2001 From: Timothee Dzik Date: Tue, 6 Apr 2021 09:29:20 -0400 Subject: [PATCH 31/91] Revert changes to push to new registry (#5943) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Timothée Dzik --- .circleci/config.yml | 54 ++++++++++++++++++++++---------------------- 1 file changed, 27 insertions(+), 27 deletions(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index 32b179177272..ca937349a2ef 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -566,13 +566,13 @@ jobs: project: orc8r images: "nginx|controller" tag-latest: true - - tag-push-docker: - project: orc8r - images: "nginx|controller" - tag-latest: true - registry: $JFROG_DOCKER_ORC8R_REGISTRY - username: $JFROG_USERNAME - password: $JFROG_PASSWORD + # - tag-push-docker: + # project: orc8r + # images: "nginx|controller" + # tag-latest: true + # registry: $JFROG_DOCKER_ORC8R_REGISTRY + # username: $JFROG_USERNAME + # password: $JFROG_PASSWORD - persist-githash-version: file_prefix: orc8r - notify-magma: @@ -672,12 +672,12 @@ jobs: project: feg images: "gateway_go|gateway_python" registry: $DOCKER_FEG_REGISTRY - - tag-push-docker: - project: feg - images: "gateway_go|gateway_python" - registry: $JFROG_DOCKER_FEG_REGISTRY - username: $JFROG_USERNAME - password: $JFROG_PASSWORD + # - tag-push-docker: + # project: feg + # images: "gateway_go|gateway_python" + # registry: $JFROG_DOCKER_FEG_REGISTRY + # username: $JFROG_USERNAME + # password: $JFROG_PASSWORD - persist-githash-version: file_prefix: feg - notify-magma: @@ -754,14 +754,14 @@ jobs: tag: <> registry: $DOCKER_MAGMA_REGISTRY tag-latest: <> - - tag-push-docker: - project: cwf - images: <> - tag: <> - tag-latest: <> - registry: $JFROG_DOCKER_CWF_REGISTRY - username: $JFROG_USERNAME - password: $JFROG_PASSWORD + # - tag-push-docker: + # project: cwf + # images: <> + # tag: <> + # tag-latest: <> + # registry: $JFROG_DOCKER_CWF_REGISTRY + # username: $JFROG_USERNAME + # password: $JFROG_PASSWORD - persist-githash-version: file_prefix: cwag - notify-magma: @@ -992,12 +992,12 @@ jobs: project: cwf images: "operator" registry: $DOCKER_MAGMA_REGISTRY - - tag-push-docker: - project: cwf - images: "operator" - registry: $JFROG_DOCKER_CWF_REGISTRY - username: $JFROG_USERNAME - password: $JFROG_PASSWORD + # - tag-push-docker: + # project: cwf + # images: "operator" + # registry: $JFROG_DOCKER_CWF_REGISTRY + # username: $JFROG_USERNAME + # password: $JFROG_PASSWORD - persist-githash-version: file_prefix: cwf_operator - notify-magma: From 9f86fe649d41deebbb38f75b5d7a492cbf6f27ab Mon Sep 17 00:00:00 2001 From: Michael Germano Date: Tue, 6 Apr 2021 12:24:09 -0700 Subject: [PATCH 32/91] Fix bootstrapping for on-prem orc8r deployments (#5923) Signed-off-by: Michael Germano --- orc8r/cloud/docker/nginx/templates/nginx.conf.j2 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/orc8r/cloud/docker/nginx/templates/nginx.conf.j2 b/orc8r/cloud/docker/nginx/templates/nginx.conf.j2 index 34c03858bfbc..1e0f90afba1b 100644 --- a/orc8r/cloud/docker/nginx/templates/nginx.conf.j2 +++ b/orc8r/cloud/docker/nginx/templates/nginx.conf.j2 @@ -133,7 +133,7 @@ http { if ($srv ~* "(\w+)[_](\w+)") { set $srv "$1-$2"; } - grpc_pass grpc://orc8r-$srv.{{ backend }}:9180; + grpc_pass grpc://orc8r-bootstrapper.{{ backend }}:9180; {%- else %} grpc_pass grpc://{{ backend }}:$open_srvport; {%- endif %} From c0086df5b76412a2ab809591badcdd2ebac80127 Mon Sep 17 00:00:00 2001 From: Hunter Gatewood Date: Tue, 6 Apr 2021 13:08:33 -0700 Subject: [PATCH 33/91] [codeowners] Fix script to pull codeowners (#5856) Signed-off-by: Hunter Gatewood --- scripts/codeowners.bash | 31 +++++++++++++++++++++++++------ 1 file changed, 25 insertions(+), 6 deletions(-) diff --git a/scripts/codeowners.bash b/scripts/codeowners.bash index 1e61b55db1d2..00717224f02c 100755 --- a/scripts/codeowners.bash +++ b/scripts/codeowners.bash @@ -12,16 +12,35 @@ # codeowners.bash prints information on the current set of Magma codeowners. -codeowners=$(curl --silent --show-error https://raw.githubusercontent.com/magma/magma/master/CODEOWNERS \ - | egrep --only-matching '@\S+' \ - | grep --invert-match '@magma/' \ - | sort \ - | uniq +function usage() { + echo ${1} + exit 1 +} + +token=${GITHUB_TOKEN} +username=${1} + +[[ ${token} == '' ]] && usage 'GITHUB_TOKEN environment variable must be set to a GitHub personal access token' +[[ ${username} == '' ]] && usage 'Usage: codeowners.bash YOUR_GITHUB_USERNAME' + +codeowners=$(curl --silent --show-error -u ${username}:${token} \ + -H "Accept: application/vnd.github.v3+json" \ + https://api.github.com/organizations/66266171/team/4477370/members \ + | jq '.[].login' \ + | tr -d '"' \ + | sort ) n_codeowners=$(echo ${codeowners} | wc -w) n_majority=$(python3 -c "import math; print(math.floor((${n_codeowners}/2)+1))") -echo ${codeowners} +echo '' +echo 'This script pulls the set of codeowners as defined by the magma/magma-maintainers GitHub team.' +echo 'That team is expected to be kept manually updated to reflect the magma/magma CODEOWNERS file.' +echo 'To double-check, consider looking through' +echo ' - CODEOWNERS file: https://github.com/magma/magma/blob/master/CODEOWNERS' +echo ' - All Magma teams: https://github.com/orgs/magma/teams' +echo '' +echo "${codeowners}" echo '' echo ${n_codeowners} total codeowners echo ${n_majority} constitutes majority From d629b9fd099e4afa4d38e56eed2d6f6301429295 Mon Sep 17 00:00:00 2001 From: Mykola Yurchenko Date: Tue, 6 Apr 2021 16:12:55 -0400 Subject: [PATCH 34/91] [pipelined]Use Rule Version Provided by SessionD (#5689) * [pipelined]Use Rule Version Provided by SessionD Signed-off-by: Nick Yurchenko * Pa Signed-off-by: Nick Yurchenko * Update Test cases Signed-off-by: Nick Yurchenko * Fix cleanup Signed-off-by: Nick Yurchenko * Fix leftover usage Signed-off-by: Nick Yurchenko * Fix restart recovery versioning Signed-off-by: Nick Yurchenko * Fix lint Signed-off-by: Nick Yurchenko * Fix typo Signed-off-by: Nick Yurchenko * Fix var name Signed-off-by: Nick Yurchenko * Fix lint Signed-off-by: Nick Yurchenko --- .../services/aaa/test/pipelined/main.go | 8 +- lte/cloud/go/protos/pipelined.pb.go | 442 +++++++++--------- .../python/magma/pipelined/app/enforcement.py | 17 +- .../magma/pipelined/app/enforcement_stats.py | 10 +- lte/gateway/python/magma/pipelined/app/gy.py | 17 +- .../magma/pipelined/app/policy_mixin.py | 47 +- .../python/magma/pipelined/rpc_servicer.py | 106 ++--- .../python/magma/pipelined/rule_mappers.py | 45 +- .../python/magma/pipelined/service_manager.py | 1 - .../magma/pipelined/tests/app/subscriber.py | 20 +- ...eTest.test_subscriber_ipv6_policy.snapshot | 2 +- ...tTableTest.test_subscriber_policy.snapshot | 2 +- ...Test.test_subscriber_two_policies.snapshot | 2 +- ...t.test_subscriber_redirect_policy.snapshot | 16 +- ...t.test_subscriber_restrict_policy.snapshot | 2 +- ...st.test_subscriber_policy_with_he.snapshot | 2 +- ...t.RedirectTest.test_ipv4_redirect.snapshot | 14 +- ...ct.RedirectTest.test_url_redirect.snapshot | 18 +- ...tResilienceTest.test_url_redirect.snapshot | 18 +- .../magma/pipelined/tests/test_enforcement.py | 54 ++- .../pipelined/tests/test_enforcement_stats.py | 114 +++-- .../python/magma/pipelined/tests/test_gy.py | 27 +- .../python/magma/pipelined/tests/test_he.py | 8 +- .../magma/pipelined/tests/test_redirect.py | 39 +- .../tests/test_restart_resilience.py | 69 ++- .../pipelined/tests/test_rule_mappers.py | 29 +- lte/protos/pipelined.proto | 4 +- 27 files changed, 608 insertions(+), 525 deletions(-) diff --git a/feg/gateway/services/aaa/test/pipelined/main.go b/feg/gateway/services/aaa/test/pipelined/main.go index 7d0f7437cae8..56d23162b7ef 100644 --- a/feg/gateway/services/aaa/test/pipelined/main.go +++ b/feg/gateway/services/aaa/test/pipelined/main.go @@ -60,11 +60,11 @@ func (c *DummyPipelined) ActivateFlows( ctx context.Context, req *protos.ActivateFlowsRequest) (res *protos.ActivateFlowsResult, err error) { res = &protos.ActivateFlowsResult{ - DynamicRuleResults: []*protos.RuleModResult{}, + PolicyResults: []*protos.RuleModResult{}, } - for _, dynRule := range req.GetDynamicRules() { - res.DynamicRuleResults = append(res.DynamicRuleResults, &protos.RuleModResult{ - RuleId: dynRule.GetId(), + for _, policyRule := range req.GetPolicies() { + res.PolicyResults = append(res.PolicyResults, &protos.RuleModResult{ + RuleId: policyRule.GetRule().GetId(), Result: protos.RuleModResult_SUCCESS, }) } diff --git a/lte/cloud/go/protos/pipelined.pb.go b/lte/cloud/go/protos/pipelined.pb.go index 38cb9138bfda..9a4015e135e6 100644 --- a/lte/cloud/go/protos/pipelined.pb.go +++ b/lte/cloud/go/protos/pipelined.pb.go @@ -1077,6 +1077,7 @@ func (m *DeactivateFlowsRequest) GetPolicies() []*VersionedPolicyID { type RuleModResult struct { RuleId string `protobuf:"bytes,1,opt,name=rule_id,json=ruleId,proto3" json:"rule_id,omitempty"` Result RuleModResult_Result `protobuf:"varint,2,opt,name=result,proto3,enum=magma.lte.RuleModResult_Result" json:"result,omitempty"` + Version uint64 `protobuf:"varint,3,opt,name=version,proto3" json:"version,omitempty"` XXX_NoUnkeyedLiteral struct{} `json:"-"` XXX_unrecognized []byte `json:"-"` XXX_sizecache int32 `json:"-"` @@ -1121,8 +1122,15 @@ func (m *RuleModResult) GetResult() RuleModResult_Result { return RuleModResult_SUCCESS } +func (m *RuleModResult) GetVersion() uint64 { + if m != nil { + return m.Version + } + return 0 +} + type ActivateFlowsResult struct { - DynamicRuleResults []*RuleModResult `protobuf:"bytes,2,rep,name=dynamic_rule_results,json=dynamicRuleResults,proto3" json:"dynamic_rule_results,omitempty"` + PolicyResults []*RuleModResult `protobuf:"bytes,2,rep,name=policy_results,json=policyResults,proto3" json:"policy_results,omitempty"` XXX_NoUnkeyedLiteral struct{} `json:"-"` XXX_unrecognized []byte `json:"-"` XXX_sizecache int32 `json:"-"` @@ -1153,9 +1161,9 @@ func (m *ActivateFlowsResult) XXX_DiscardUnknown() { var xxx_messageInfo_ActivateFlowsResult proto.InternalMessageInfo -func (m *ActivateFlowsResult) GetDynamicRuleResults() []*RuleModResult { +func (m *ActivateFlowsResult) GetPolicyResults() []*RuleModResult { if m != nil { - return m.DynamicRuleResults + return m.PolicyResults } return nil } @@ -2722,221 +2730,221 @@ func init() { func init() { proto.RegisterFile("lte/protos/pipelined.proto", fileDescriptor_e17e923ef6f5752e) } var fileDescriptor_e17e923ef6f5752e = []byte{ - // 3412 bytes of a gzipped FileDescriptorProto + // 3423 bytes of a gzipped FileDescriptorProto 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xb4, 0x3a, 0x4d, 0x73, 0x1b, 0x47, - 0x76, 0xc4, 0x07, 0xf1, 0xf1, 0x00, 0x90, 0xa3, 0x96, 0x44, 0x81, 0x94, 0x25, 0x53, 0x63, 0x3b, - 0xa6, 0x9c, 0x2c, 0x95, 0x70, 0x55, 0xf2, 0xae, 0x9d, 0xca, 0xee, 0x08, 0x33, 0xa0, 0xc6, 0x0b, - 0x02, 0x50, 0x0f, 0x40, 0xcb, 0x39, 0xa4, 0x6b, 0x38, 0xdd, 0xa0, 0x27, 0x06, 0x66, 0x46, 0x3d, - 0x03, 0xc9, 0x4a, 0xa5, 0x72, 0xc8, 0x21, 0x87, 0x9c, 0x72, 0x4e, 0x52, 0x95, 0xdf, 0x90, 0x63, - 0xfe, 0x40, 0x2e, 0x49, 0x55, 0x2e, 0xb9, 0x65, 0xf3, 0x07, 0x52, 0x95, 0xaa, 0x1c, 0xf2, 0x03, - 0x52, 0xdd, 0x3d, 0x03, 0x0e, 0x40, 0x50, 0x94, 0x6d, 0xe5, 0x84, 0xee, 0xd7, 0xaf, 0x5f, 0xbf, - 0x7e, 0xdf, 0xaf, 0x07, 0xb0, 0x37, 0x4d, 0xd8, 0xa3, 0x88, 0x87, 0x49, 0x18, 0x3f, 0x8a, 0xfc, - 0x88, 0x4d, 0xfd, 0x80, 0xd1, 0x43, 0x09, 0x40, 0xf5, 0x99, 0x7b, 0x3e, 0x73, 0x0f, 0xa7, 0x09, - 0xdb, 0xdb, 0x0d, 0xb9, 0xf7, 0x0b, 0x9e, 0x21, 0x7a, 0xe1, 0x6c, 0x16, 0x06, 0x0a, 0x6b, 0x6f, - 0x37, 0x4f, 0x21, 0x9c, 0xfa, 0xde, 0x1b, 0x7a, 0x96, 0x2e, 0xed, 0xe7, 0x96, 0x62, 0x16, 0xc7, - 0x7e, 0x18, 0x90, 0x99, 0x1b, 0xb8, 0xe7, 0x8c, 0xa7, 0x18, 0xf7, 0xf2, 0x18, 0xf3, 0xb3, 0xd8, - 0xe3, 0xfe, 0x19, 0xe3, 0x19, 0x01, 0xfd, 0x9f, 0x0a, 0x70, 0xc3, 0x61, 0xc9, 0x3c, 0xea, 0x4e, - 0xc3, 0xd7, 0x31, 0x66, 0x2f, 0xe7, 0x2c, 0x4e, 0xd0, 0x97, 0x50, 0xe3, 0x6a, 0x18, 0xb7, 0x0b, - 0xfb, 0xa5, 0x83, 0xc6, 0xd1, 0x87, 0x87, 0x0b, 0x56, 0x0f, 0x0d, 0x2f, 0xf1, 0x5f, 0xb9, 0x09, - 0xcb, 0x6f, 0xc1, 0x8b, 0x0d, 0xe8, 0x16, 0x6c, 0xb2, 0x28, 0xf4, 0xbe, 0x6d, 0x17, 0xf7, 0x0b, - 0x07, 0x65, 0xac, 0x26, 0xe8, 0x39, 0xb4, 0x5e, 0xce, 0xc3, 0xc4, 0x25, 0xf3, 0x88, 0xba, 0x09, - 0x8b, 0xdb, 0xa5, 0xfd, 0xc2, 0x41, 0xe3, 0xe8, 0xf7, 0x72, 0x74, 0xc7, 0x72, 0xc5, 0x59, 0x30, - 0xf9, 0x5c, 0xe0, 0x3b, 0x89, 0x9b, 0xb0, 0xec, 0x90, 0xa6, 0x24, 0xa1, 0xf0, 0x62, 0xfd, 0x77, - 0xe1, 0xa6, 0x64, 0xdd, 0x64, 0x13, 0x77, 0x3e, 0x4d, 0x32, 0xe6, 0x17, 0xe7, 0x17, 0x72, 0xe7, - 0xeb, 0x67, 0xe9, 0x3d, 0xc7, 0xd6, 0x89, 0xeb, 0x65, 0xa8, 0x9f, 0x5f, 0xba, 0xe7, 0xdd, 0x3c, - 0x3f, 0x02, 0x55, 0x5c, 0xf2, 0x1d, 0xef, 0xa8, 0x9f, 0x03, 0x92, 0x67, 0x0c, 0xa5, 0x92, 0xfe, - 0xff, 0x84, 0xa9, 0xff, 0x79, 0x7a, 0x19, 0x29, 0xa1, 0xec, 0x9c, 0x4b, 0x12, 0x2e, 0xfc, 0x54, - 0x09, 0x5f, 0x71, 0xfa, 0x5f, 0x15, 0x40, 0xcb, 0xdb, 0x4c, 0x3c, 0x9f, 0x26, 0xe8, 0x0b, 0xa8, - 0x70, 0x39, 0x92, 0xc7, 0x6e, 0x1d, 0xe9, 0xb9, 0x63, 0x57, 0x91, 0x0f, 0xd5, 0x0f, 0x4e, 0x77, - 0xe8, 0x4f, 0xa0, 0x92, 0x52, 0x69, 0x40, 0xd5, 0x19, 0x77, 0x3a, 0x96, 0xe3, 0x68, 0x1b, 0x62, - 0xd2, 0x35, 0xec, 0xde, 0x18, 0x5b, 0x5a, 0x01, 0x21, 0xd8, 0x1a, 0x8c, 0x47, 0xa6, 0x31, 0xb2, - 0x4c, 0x62, 0x0d, 0x07, 0x9d, 0x67, 0x5a, 0x51, 0x7f, 0x05, 0x37, 0x52, 0xbe, 0x07, 0xdc, 0x3f, - 0xf7, 0x83, 0xd1, 0x9b, 0x88, 0xa1, 0x2f, 0xa1, 0x9c, 0xbc, 0x89, 0x58, 0xca, 0xc6, 0xa7, 0x39, - 0x36, 0x2e, 0xe1, 0x1e, 0x5e, 0x0c, 0xb1, 0xdc, 0xa4, 0x7f, 0x0c, 0x90, 0x23, 0x55, 0x81, 0xe2, - 0xf1, 0x0b, 0x6d, 0x43, 0xfe, 0x7e, 0xa3, 0x15, 0xc4, 0x6f, 0xff, 0xb1, 0x56, 0xd4, 0x4f, 0x61, - 0xfb, 0x94, 0x71, 0xe1, 0x6c, 0x8c, 0x2a, 0x5d, 0xa3, 0x87, 0x50, 0xe6, 0xf3, 0x29, 0x4b, 0x65, - 0x7e, 0x3b, 0x77, 0x6a, 0x6a, 0x0c, 0xf3, 0x29, 0xc3, 0x12, 0x05, 0xb5, 0xa1, 0xfa, 0x4a, 0xed, - 0x96, 0x62, 0x6d, 0xe1, 0x6c, 0xaa, 0xff, 0xb6, 0x04, 0xb7, 0xd6, 0xd9, 0x03, 0x7a, 0x08, 0xa5, - 0xd8, 0xa7, 0x29, 0xf1, 0x3b, 0x79, 0xc9, 0x2e, 0x54, 0x69, 0x9b, 0x58, 0xe0, 0xa0, 0x3b, 0x50, - 0xf5, 0x23, 0xe2, 0x52, 0xca, 0x25, 0xf5, 0x3a, 0xae, 0xf8, 0x91, 0x41, 0x29, 0x47, 0x5f, 0x40, - 0x8b, 0xbe, 0x09, 0xdc, 0x99, 0xef, 0x11, 0xc1, 0x46, 0xdc, 0x2e, 0x4b, 0x5b, 0xbc, 0x82, 0xd5, - 0x66, 0x8a, 0x2b, 0x26, 0x31, 0xea, 0xc0, 0x56, 0x6a, 0x91, 0x24, 0x94, 0xe2, 0x69, 0x6f, 0x4a, - 0x56, 0x3e, 0x78, 0x9b, 0x74, 0x71, 0x8b, 0xe7, 0x41, 0xe8, 0x8f, 0xa0, 0xe6, 0x46, 0x01, 0x71, - 0x67, 0x67, 0xbc, 0x5d, 0x91, 0xdb, 0x3f, 0xca, 0xfb, 0xc1, 0xf9, 0x39, 0x67, 0xe7, 0x6e, 0xc2, - 0xe8, 0x89, 0xfb, 0xbd, 0x3f, 0x9b, 0xcf, 0x9e, 0xfa, 0x09, 0x17, 0x86, 0x59, 0x75, 0xa3, 0xc0, - 0x98, 0x9d, 0x71, 0x74, 0x17, 0xea, 0x7e, 0xf4, 0xea, 0x89, 0xba, 0x5b, 0x75, 0xbf, 0x70, 0xd0, - 0xc4, 0x35, 0x01, 0x90, 0xb7, 0xdb, 0x81, 0xca, 0x2c, 0xf6, 0x63, 0x1a, 0xb4, 0x6b, 0x72, 0x25, - 0x9d, 0xa1, 0x8f, 0xa0, 0x35, 0x8f, 0xa6, 0x7e, 0xf0, 0x1d, 0x49, 0xe6, 0x41, 0xc0, 0xa6, 0xed, - 0xba, 0x14, 0x79, 0x53, 0x01, 0x47, 0x12, 0x86, 0x3e, 0x85, 0x6d, 0x1a, 0xbe, 0x0e, 0xf2, 0x68, - 0x20, 0xd1, 0xb6, 0x32, 0x70, 0x8a, 0xf8, 0x04, 0x6a, 0x32, 0x00, 0xfb, 0x2c, 0x6e, 0x37, 0xa4, - 0xf8, 0xf6, 0x72, 0x57, 0x58, 0xb1, 0x09, 0xbc, 0xc0, 0xfd, 0xaa, 0x5c, 0x2b, 0x69, 0x65, 0xbd, - 0x0b, 0x37, 0x56, 0x50, 0x6c, 0x53, 0xe8, 0x4b, 0xa8, 0x83, 0xa4, 0xea, 0xad, 0xe3, 0x8a, 0x98, - 0xda, 0xf4, 0x2d, 0x66, 0xf2, 0xf7, 0x25, 0xd8, 0x31, 0x99, 0xfb, 0x13, 0x0d, 0x65, 0x17, 0x6a, - 0xe9, 0xc1, 0x71, 0xbb, 0xb8, 0x5f, 0x3a, 0xa8, 0xe3, 0xaa, 0x3a, 0x79, 0x9d, 0xba, 0x4b, 0x3f, - 0x5c, 0xdd, 0x39, 0x43, 0x2c, 0x2f, 0x19, 0xe2, 0x92, 0x1e, 0x37, 0x57, 0xf4, 0xf8, 0x4b, 0xd8, - 0xe5, 0x6c, 0x16, 0xbe, 0x62, 0x84, 0xaa, 0xa8, 0x4e, 0x28, 0x0f, 0x23, 0x32, 0x11, 0x97, 0x94, - 0x56, 0x53, 0xc3, 0x3b, 0x0a, 0x21, 0x8d, 0xfa, 0x26, 0x0f, 0x55, 0x68, 0xb9, 0xac, 0xea, 0xea, - 0xbb, 0xa9, 0xba, 0xb6, 0x56, 0xd5, 0xbf, 0xc8, 0xa9, 0xba, 0x2e, 0x55, 0xfd, 0xc1, 0xd5, 0xaa, - 0xb6, 0xcd, 0x0b, 0x65, 0xeb, 0x7f, 0x5b, 0x80, 0x96, 0x70, 0x9b, 0x93, 0x90, 0xa6, 0x51, 0xed, - 0x4a, 0x1d, 0x7f, 0xbe, 0x08, 0x9a, 0x45, 0x19, 0xad, 0xf2, 0x89, 0x61, 0x89, 0xc4, 0x6a, 0xc4, - 0xfc, 0x7c, 0x7d, 0xc4, 0xbc, 0x09, 0xdb, 0x43, 0x03, 0x8f, 0x6c, 0xa3, 0x47, 0x32, 0x60, 0x21, - 0x1f, 0x46, 0x8b, 0xfa, 0x39, 0xdc, 0x5c, 0x89, 0x30, 0x92, 0xca, 0x57, 0x70, 0x2b, 0x1f, 0x1c, - 0x88, 0x3a, 0x46, 0x19, 0x46, 0xe3, 0xa8, 0x7d, 0x15, 0x5b, 0x18, 0xe5, 0xc2, 0x84, 0x02, 0x09, - 0x63, 0x2f, 0x68, 0x45, 0xfd, 0x6f, 0x0a, 0x70, 0xfb, 0x92, 0x91, 0xca, 0xb3, 0x7e, 0xb5, 0x92, - 0x29, 0xf2, 0x21, 0x7a, 0xed, 0x8e, 0xf7, 0x95, 0x2e, 0xfe, 0xbb, 0x08, 0x8d, 0x5c, 0x3a, 0x47, - 0x9f, 0xc1, 0xe6, 0xcc, 0x4d, 0xd2, 0x42, 0xa1, 0x71, 0x74, 0x2b, 0xc7, 0x87, 0x40, 0x3b, 0x11, - 0x6b, 0x58, 0xa1, 0x08, 0x6f, 0x71, 0xa3, 0x88, 0x04, 0xee, 0x8c, 0xa5, 0x71, 0xb5, 0xea, 0x46, - 0x51, 0xdf, 0x9d, 0x31, 0xb1, 0x74, 0xf6, 0x26, 0x61, 0x31, 0xe1, 0xdf, 0x4b, 0x3f, 0x29, 0xe3, - 0xaa, 0x9c, 0xe3, 0xef, 0xd1, 0x03, 0x68, 0xc6, 0x8c, 0xbf, 0xf2, 0x3d, 0x46, 0x64, 0x4e, 0x52, - 0x8e, 0xd0, 0x48, 0x61, 0x32, 0xc7, 0xdc, 0x81, 0x6a, 0xcc, 0x3d, 0x32, 0x73, 0x3d, 0xe9, 0x0b, - 0x75, 0x5c, 0x89, 0xb9, 0x77, 0xe2, 0x7a, 0x62, 0x81, 0xc6, 0x89, 0x5c, 0xa8, 0xa8, 0x05, 0x1a, - 0x27, 0x62, 0xe1, 0x09, 0x6c, 0xc6, 0x22, 0x65, 0x4b, 0xfb, 0xde, 0x3a, 0xda, 0x5f, 0x61, 0x3b, - 0xbd, 0x9d, 0x1c, 0xab, 0xd4, 0xae, 0xd0, 0xf5, 0x10, 0xea, 0x0b, 0x18, 0xd2, 0xa0, 0xd9, 0xed, - 0x0d, 0xbe, 0x26, 0x1d, 0x6c, 0x09, 0x19, 0x69, 0x1b, 0xe8, 0x43, 0xb8, 0x2b, 0x21, 0x99, 0x01, - 0x75, 0x7a, 0x86, 0xe3, 0xd8, 0x5d, 0xbb, 0x63, 0x8c, 0xec, 0x41, 0x5f, 0x2b, 0xa0, 0x7b, 0xb0, - 0x2b, 0x11, 0xba, 0x76, 0xff, 0xf2, 0x72, 0x71, 0x41, 0xd1, 0x7a, 0x31, 0xb4, 0xb1, 0x65, 0x6a, - 0x25, 0xfd, 0x2f, 0xa0, 0xa9, 0x18, 0x8a, 0xa3, 0x30, 0x88, 0x19, 0x7a, 0xb2, 0xa2, 0xf8, 0xfb, - 0x97, 0x38, 0x57, 0x88, 0xef, 0x4b, 0xdf, 0xff, 0x56, 0x00, 0x6d, 0xb5, 0x86, 0xfb, 0x81, 0x11, - 0x72, 0xe6, 0x7a, 0xf9, 0x5c, 0x5a, 0x9d, 0xb9, 0xde, 0x4a, 0xba, 0x29, 0x29, 0xdd, 0xa4, 0xe9, - 0xe6, 0x3e, 0x34, 0xdc, 0x88, 0x2c, 0x76, 0x29, 0x7d, 0xd7, 0xdd, 0xe8, 0x24, 0xdd, 0x77, 0x07, - 0xaa, 0x6e, 0x6a, 0x45, 0xa9, 0xb6, 0x5d, 0x65, 0x44, 0x1f, 0xc3, 0x56, 0x44, 0x23, 0x12, 0x27, - 0x2e, 0x4f, 0x48, 0xe2, 0xcf, 0x98, 0x54, 0x7a, 0x19, 0x37, 0x23, 0x1a, 0x39, 0x02, 0x38, 0xf2, - 0x67, 0x4c, 0xff, 0x8f, 0x02, 0xdc, 0x5e, 0xa9, 0xde, 0x54, 0xa9, 0xf6, 0x9e, 0xae, 0xd5, 0x85, - 0x86, 0x2a, 0x1e, 0x95, 0xb9, 0x96, 0xa4, 0x9a, 0x3e, 0x59, 0x4b, 0x2d, 0x77, 0xf8, 0xa1, 0x0c, - 0xff, 0xa0, 0x76, 0x8a, 0xb1, 0xfe, 0x18, 0xca, 0xd2, 0xb8, 0xb7, 0xa1, 0x71, 0x6a, 0xf4, 0x6c, - 0x93, 0x3c, 0x1f, 0x0f, 0x46, 0x86, 0xb6, 0x81, 0x9a, 0x50, 0xeb, 0x0f, 0xd2, 0x59, 0x01, 0xb5, - 0xa0, 0x3e, 0xb2, 0xf0, 0x89, 0xdd, 0x37, 0x46, 0x22, 0x36, 0x11, 0x78, 0x70, 0x6d, 0x81, 0x8a, - 0xbe, 0x80, 0xea, 0x45, 0x7d, 0x2b, 0x82, 0xd3, 0xfe, 0x75, 0xec, 0xe1, 0x6c, 0x83, 0xce, 0x61, - 0x7b, 0xe4, 0x9e, 0x4d, 0x99, 0x11, 0xc7, 0xfe, 0x79, 0x30, 0x63, 0x41, 0xb2, 0xe4, 0xd7, 0x85, - 0x65, 0xbf, 0xbe, 0x07, 0x30, 0x73, 0xfd, 0x80, 0x24, 0x62, 0x4b, 0x5a, 0x01, 0xd7, 0x05, 0x44, - 0xd2, 0x40, 0x9f, 0xc0, 0x56, 0xec, 0x71, 0x11, 0x1c, 0x14, 0x86, 0xe8, 0x68, 0x4a, 0x07, 0x65, - 0xdc, 0x4a, 0xa1, 0x12, 0x2b, 0xd6, 0xff, 0x04, 0x6e, 0x1a, 0xd3, 0xe9, 0xca, 0xb1, 0x31, 0x3a, - 0x86, 0x1b, 0x72, 0x17, 0x71, 0x2f, 0x80, 0xe9, 0x85, 0xf2, 0x25, 0xc5, 0xca, 0x3e, 0xac, 0x25, - 0x2b, 0x84, 0xf4, 0x2f, 0x45, 0x13, 0xc4, 0x7d, 0x77, 0xea, 0xff, 0x19, 0xa3, 0xf8, 0xcd, 0x7c, - 0xe8, 0x7a, 0xdf, 0xb1, 0x04, 0x69, 0x50, 0x8a, 0xbe, 0x53, 0x8e, 0xd6, 0xc4, 0x62, 0x88, 0x10, - 0x94, 0xfd, 0x59, 0xec, 0xa7, 0x2a, 0x97, 0x63, 0xfd, 0x10, 0x6e, 0x28, 0x7c, 0x91, 0x45, 0xe5, - 0x59, 0xb6, 0xb4, 0x0f, 0xc5, 0x5a, 0x6a, 0x4f, 0x9b, 0xb8, 0x9a, 0xa8, 0x25, 0xfd, 0x7f, 0x0b, - 0x50, 0xef, 0xc6, 0x33, 0x22, 0x03, 0x0a, 0xfa, 0x79, 0x16, 0x88, 0x94, 0x3b, 0xdf, 0xcb, 0xbb, - 0x73, 0x86, 0x24, 0x46, 0x4b, 0x51, 0xe8, 0x1f, 0x0b, 0x50, 0xcb, 0x60, 0xc2, 0x6b, 0x1d, 0xcb, - 0x71, 0xec, 0x41, 0x9f, 0x18, 0x9d, 0x91, 0x7d, 0x6a, 0x69, 0x1b, 0x68, 0x07, 0x50, 0x06, 0x5b, - 0x18, 0x87, 0xa9, 0x95, 0xd1, 0x03, 0xb8, 0xb7, 0x0a, 0x17, 0x63, 0xa7, 0xf3, 0xcc, 0x32, 0xc7, - 0x3d, 0xcb, 0xd4, 0x36, 0xd1, 0x2d, 0xd0, 0x32, 0x14, 0x6c, 0xf5, 0x2c, 0xc3, 0xb1, 0x4c, 0xad, - 0x22, 0x6c, 0x4e, 0x46, 0x39, 0xbb, 0x7f, 0xac, 0x55, 0x45, 0xd4, 0xc8, 0x62, 0x5e, 0x0d, 0x01, - 0x54, 0xd2, 0x73, 0xeb, 0x02, 0xcd, 0xee, 0xa7, 0x33, 0x10, 0x68, 0x29, 0x09, 0xad, 0xa1, 0xff, - 0x65, 0x01, 0xc0, 0xa1, 0x93, 0xae, 0x3f, 0x4d, 0x18, 0x8f, 0xd1, 0x43, 0x28, 0x4e, 0x32, 0x57, - 0xdb, 0x5d, 0x89, 0x61, 0x26, 0x13, 0x06, 0x18, 0x25, 0x21, 0xc7, 0xc5, 0x09, 0x15, 0x6a, 0x48, - 0x12, 0x4f, 0xca, 0xbc, 0x89, 0xc5, 0x50, 0x40, 0xe2, 0xc8, 0x97, 0xae, 0xd5, 0xc4, 0x62, 0x88, - 0xb6, 0xa0, 0x38, 0x99, 0xca, 0x50, 0xd1, 0xc4, 0xc5, 0xc9, 0x14, 0xdd, 0x86, 0x4a, 0x4c, 0x27, - 0x42, 0xfa, 0x9b, 0xb2, 0x32, 0xd9, 0x8c, 0xe9, 0xc4, 0xa6, 0xfa, 0x9f, 0xc2, 0xd6, 0xf2, 0x01, - 0xe8, 0x67, 0xcb, 0xf9, 0xeb, 0xce, 0xba, 0xfc, 0xd5, 0x67, 0xaf, 0xb3, 0x14, 0xf6, 0x10, 0x2a, - 0x22, 0xb9, 0xa6, 0xf5, 0xe4, 0xd6, 0xd1, 0x8d, 0x95, 0x2e, 0x34, 0x0c, 0x70, 0x8a, 0xa0, 0xff, - 0x43, 0x41, 0x85, 0xee, 0x8c, 0x84, 0xb0, 0x09, 0x3f, 0x7a, 0xf5, 0x98, 0xc4, 0xdc, 0xcb, 0xdc, - 0x44, 0xcc, 0x1d, 0xee, 0x2d, 0x96, 0x68, 0x9c, 0x64, 0xe1, 0x44, 0xcc, 0xcd, 0x38, 0x11, 0x15, - 0x99, 0x7c, 0x65, 0xf0, 0xc2, 0xe9, 0x45, 0x40, 0xa9, 0xe3, 0x66, 0x06, 0x94, 0x31, 0x62, 0x17, - 0x6a, 0x22, 0xcf, 0x45, 0x21, 0x4f, 0xa4, 0x10, 0x5a, 0x58, 0xe4, 0xbd, 0x61, 0xc8, 0xa5, 0x73, - 0x8a, 0xdc, 0x28, 0x97, 0x94, 0x2c, 0x44, 0xae, 0x14, 0x4b, 0xfa, 0xbf, 0x14, 0xa0, 0x89, 0x19, - 0xf5, 0x39, 0xf3, 0x12, 0x3b, 0x98, 0x84, 0xe8, 0x2b, 0x68, 0x72, 0x46, 0x45, 0x54, 0x23, 0xb9, - 0xf6, 0xef, 0x60, 0xa9, 0x62, 0xbd, 0x40, 0x5f, 0x4c, 0x44, 0xd8, 0x53, 0xe1, 0x8b, 0x33, 0x6a, - 0x50, 0x2a, 0x59, 0xfa, 0x1d, 0xd8, 0x16, 0xb4, 0x44, 0x9a, 0x66, 0x3c, 0x1f, 0x28, 0x5b, 0x9c, - 0x51, 0x47, 0x42, 0xc5, 0x3e, 0xfd, 0x18, 0xb4, 0x55, 0x3a, 0xa8, 0x06, 0x65, 0x7b, 0x78, 0xfa, - 0x58, 0xdb, 0x48, 0x47, 0x4f, 0xb4, 0x02, 0xaa, 0x42, 0x69, 0x8c, 0x7b, 0x5a, 0x51, 0xd8, 0x9b, - 0x63, 0x0f, 0xc7, 0xd8, 0xd6, 0x4a, 0x62, 0x2c, 0x10, 0x4f, 0x9f, 0x68, 0x65, 0x51, 0x95, 0x0d, - 0xe6, 0x09, 0xe3, 0xcf, 0x98, 0x4b, 0x19, 0xef, 0x70, 0xe6, 0x0a, 0x35, 0x08, 0x4b, 0x08, 0x49, - 0xc2, 0x52, 0x3f, 0x6c, 0xe1, 0xcd, 0x70, 0xc4, 0x7c, 0x8a, 0xf6, 0xa1, 0x79, 0x1e, 0x9c, 0x11, - 0x29, 0x75, 0x77, 0xc1, 0x1b, 0x9c, 0x07, 0x67, 0x76, 0xf4, 0xea, 0xb1, 0xa1, 0xd2, 0x8c, 0x10, - 0x1a, 0x09, 0x42, 0x29, 0xf2, 0x16, 0xae, 0x88, 0x69, 0x3f, 0xd4, 0xff, 0x55, 0x78, 0xdf, 0x6b, - 0x3a, 0x74, 0xb9, 0x3b, 0x13, 0x01, 0x8e, 0x8a, 0x1a, 0xdf, 0x9f, 0xb8, 0x1e, 0x4b, 0x8f, 0xa8, - 0x0b, 0x88, 0x2d, 0x00, 0xa2, 0x78, 0x09, 0x58, 0x42, 0xfc, 0x20, 0x4e, 0xdc, 0xc0, 0xcb, 0xca, - 0x9e, 0x46, 0xc0, 0x12, 0x3b, 0x05, 0xa1, 0x3f, 0x04, 0x21, 0x11, 0x29, 0x00, 0xe2, 0x07, 0x93, - 0x30, 0xed, 0x13, 0xee, 0x5c, 0x21, 0x75, 0xdc, 0xe4, 0x79, 0x95, 0xfd, 0x1a, 0x9a, 0xe1, 0x3c, - 0xe1, 0xe4, 0x5b, 0xe6, 0x52, 0xe2, 0xa9, 0x6c, 0xd9, 0x58, 0xaa, 0x0a, 0xd6, 0x08, 0x05, 0x83, - 0xd8, 0x23, 0x60, 0x1d, 0xae, 0x3f, 0x84, 0x9a, 0x39, 0x8f, 0xde, 0xe5, 0x36, 0x22, 0x74, 0x95, - 0x86, 0xa6, 0x2d, 0x6c, 0x52, 0xd8, 0x94, 0x1f, 0x24, 0x8c, 0xe7, 0x30, 0x9b, 0x31, 0xf7, 0xec, - 0x0c, 0x26, 0x24, 0x3c, 0x0d, 0x3d, 0x77, 0x4a, 0x26, 0x4a, 0xfc, 0xaa, 0x01, 0x03, 0x09, 0xeb, - 0x4a, 0x1d, 0xac, 0x0a, 0xa7, 0x74, 0x59, 0x38, 0x7b, 0x50, 0x9f, 0x33, 0x22, 0x7b, 0xa0, 0xac, - 0x12, 0xa8, 0xce, 0x99, 0x1d, 0x09, 0x05, 0xb5, 0xa1, 0x96, 0x70, 0xc2, 0xa2, 0xcc, 0xcb, 0x9b, - 0xb8, 0x92, 0x70, 0x2b, 0xb2, 0x29, 0x7a, 0x02, 0x0d, 0xe1, 0xfd, 0x13, 0x15, 0x6b, 0xd2, 0x46, - 0x39, 0xdf, 0xa4, 0x5f, 0x04, 0x22, 0x0c, 0xf1, 0x45, 0x50, 0xba, 0x0d, 0x15, 0x91, 0xc8, 0x7c, - 0x2a, 0xcb, 0xc2, 0x3a, 0xde, 0x74, 0xa3, 0xc8, 0xa6, 0xfa, 0x6f, 0x4b, 0xd0, 0x70, 0x58, 0x72, - 0xcc, 0xc3, 0x79, 0x34, 0x34, 0xb1, 0x40, 0x8b, 0x28, 0x27, 0x17, 0x26, 0x15, 0x51, 0x6e, 0x53, - 0xf4, 0x21, 0x34, 0x04, 0x38, 0xdf, 0x70, 0x6e, 0x62, 0x88, 0x28, 0x4f, 0x1b, 0x1d, 0x74, 0x1f, - 0x20, 0xe2, 0xcc, 0x63, 0x94, 0x65, 0xb7, 0x6d, 0xe1, 0x1c, 0x04, 0xfd, 0x3e, 0xd4, 0x05, 0x01, - 0x95, 0x0f, 0xca, 0xd2, 0xf7, 0x6e, 0xe6, 0x5f, 0x16, 0x28, 0x57, 0x59, 0xa0, 0x16, 0xa5, 0x23, - 0xb4, 0x0f, 0xa5, 0x88, 0xfa, 0xe9, 0x43, 0xc2, 0x56, 0x1e, 0xd7, 0xb4, 0xb1, 0x58, 0x42, 0x0f, - 0xa0, 0x15, 0x92, 0x6f, 0x89, 0x68, 0xf7, 0x08, 0x65, 0xb1, 0xaa, 0x83, 0x5b, 0x18, 0xc2, 0x67, - 0x98, 0xcd, 0x42, 0x11, 0x08, 0xd1, 0x01, 0x68, 0xb2, 0x61, 0x60, 0x24, 0x12, 0x1e, 0x2b, 0x9f, - 0x60, 0xd4, 0xfd, 0xb7, 0x14, 0x7c, 0xc8, 0x19, 0x15, 0xcd, 0x09, 0x7a, 0x0c, 0x10, 0xb3, 0x84, - 0x9c, 0x73, 0x32, 0x71, 0xb9, 0xec, 0xf9, 0x1a, 0x47, 0x3b, 0xcb, 0x6f, 0x54, 0x52, 0x48, 0x5d, - 0x03, 0xe3, 0x5a, 0x2c, 0x26, 0x5d, 0x97, 0xa3, 0xe7, 0x70, 0x93, 0x2e, 0x5a, 0x12, 0xd9, 0x85, - 0x12, 0xce, 0x5e, 0xca, 0x47, 0x84, 0xc6, 0xd1, 0x83, 0xb7, 0x35, 0x2e, 0xea, 0x39, 0xed, 0x06, - 0x5d, 0x82, 0x63, 0xf6, 0x12, 0xfd, 0x06, 0x6e, 0x5c, 0x26, 0x08, 0x92, 0xe0, 0xb5, 0xef, 0x82, - 0xdb, 0x2b, 0xc4, 0xf4, 0x7f, 0x2e, 0x5c, 0xa8, 0xb7, 0x6b, 0x48, 0xf5, 0x4e, 0xdc, 0xbc, 0x7a, - 0x27, 0xae, 0x50, 0xef, 0xaf, 0xe1, 0xa6, 0x00, 0xab, 0xe8, 0x4e, 0x92, 0x90, 0xb8, 0x51, 0x34, - 0x7d, 0x23, 0xbb, 0xbb, 0xb5, 0x79, 0x40, 0x9b, 0xb8, 0x5c, 0x0d, 0x47, 0xa1, 0x21, 0x50, 0xd1, - 0x21, 0xd4, 0x26, 0xaf, 0x29, 0x89, 0x5c, 0x3e, 0x4b, 0x9d, 0x3c, 0xaf, 0xde, 0x2c, 0xa4, 0xe0, - 0xea, 0x44, 0x8e, 0x66, 0x02, 0x9f, 0xce, 0x05, 0xba, 0x3b, 0x4b, 0xfd, 0x3a, 0x8f, 0x9f, 0x39, - 0x2d, 0xae, 0xd2, 0xb9, 0x1c, 0xe8, 0x7f, 0x5d, 0x04, 0x70, 0xd4, 0x03, 0xb6, 0xc3, 0x64, 0xe6, - 0xb8, 0x78, 0xac, 0xbe, 0xe8, 0x9b, 0x9b, 0x17, 0x40, 0x9b, 0xbe, 0x83, 0x97, 0x7e, 0x0a, 0xdb, - 0xd9, 0xab, 0x78, 0x66, 0xda, 0xca, 0x74, 0xb7, 0x52, 0x70, 0x66, 0xde, 0x9f, 0x41, 0x35, 0x08, - 0xa9, 0x2c, 0x79, 0x14, 0xb7, 0x79, 0xa1, 0xf4, 0x43, 0xca, 0x6c, 0x13, 0x57, 0x04, 0x86, 0x4d, - 0x45, 0xdb, 0xa8, 0xcc, 0x7c, 0xf3, 0x72, 0xdb, 0x98, 0x95, 0x3d, 0x69, 0xb5, 0x93, 0xb3, 0xba, - 0x88, 0xf2, 0x76, 0x45, 0xd6, 0x77, 0xeb, 0xac, 0x6e, 0x68, 0x66, 0x56, 0x37, 0xa4, 0x5c, 0xff, - 0xcf, 0x02, 0xec, 0x8c, 0x87, 0xdd, 0x54, 0x1e, 0x9d, 0x30, 0x48, 0xd8, 0xf7, 0x89, 0xf2, 0x9a, - 0x3f, 0x00, 0xf0, 0xdc, 0x79, 0xcc, 0x54, 0xb8, 0x55, 0x89, 0x1f, 0xe5, 0x08, 0x76, 0xc4, 0xa2, - 0x6d, 0xe1, 0xba, 0xc4, 0x92, 0x61, 0xd6, 0x02, 0x2d, 0x13, 0x42, 0x1c, 0xb8, 0x51, 0xfc, 0x6d, - 0xa8, 0x12, 0xf5, 0x72, 0xa5, 0x79, 0x71, 0x9e, 0x72, 0xd4, 0x4c, 0x70, 0x4e, 0xba, 0x05, 0xd9, - 0xb0, 0x3d, 0x71, 0xfd, 0xe9, 0x9c, 0x33, 0x92, 0x3d, 0x66, 0x94, 0x2e, 0xb9, 0x41, 0x57, 0x61, - 0x08, 0x8f, 0x13, 0x67, 0xf3, 0x99, 0x8a, 0xd9, 0xad, 0x49, 0x0e, 0x4e, 0xf5, 0xbf, 0x2b, 0x43, - 0x35, 0x65, 0x14, 0xfd, 0x12, 0x6a, 0xe9, 0x85, 0xd8, 0x9a, 0xb6, 0x30, 0xc5, 0x52, 0xbf, 0xa7, - 0xee, 0x74, 0xce, 0x62, 0x5c, 0x55, 0x57, 0x63, 0xfa, 0xbf, 0x97, 0xa0, 0x91, 0x5b, 0x10, 0x15, - 0x1c, 0xb6, 0x1c, 0x0b, 0x9f, 0xca, 0x7e, 0xf6, 0x16, 0x68, 0xd8, 0x7a, 0x3e, 0xb6, 0x9c, 0x11, - 0x31, 0x3a, 0x1d, 0x6b, 0x28, 0x2a, 0xbe, 0x02, 0xba, 0x0f, 0x7b, 0x19, 0x14, 0x5b, 0x5f, 0x59, - 0x1d, 0xd1, 0x2f, 0xf6, 0x07, 0x04, 0x5b, 0x86, 0x23, 0xbb, 0xd8, 0x7b, 0xb0, 0x9b, 0x95, 0x90, - 0x9d, 0x41, 0x7f, 0x64, 0xbd, 0x18, 0x91, 0xfe, 0x60, 0x44, 0xba, 0x83, 0x71, 0xdf, 0xd4, 0x4a, - 0xa8, 0x0d, 0xb7, 0x4e, 0x8c, 0xbe, 0x69, 0x8c, 0x06, 0xf8, 0x1b, 0x62, 0x5b, 0xe4, 0xc4, 0x76, - 0x1c, 0x51, 0x57, 0x96, 0xd1, 0x1e, 0xec, 0x74, 0x06, 0x7d, 0xd3, 0x16, 0x45, 0xa9, 0xd1, 0xcb, - 0xaf, 0x6d, 0x8a, 0x32, 0xd7, 0xee, 0xab, 0x46, 0xa8, 0x67, 0xf5, 0x8f, 0x47, 0xcf, 0xb4, 0x8a, - 0xc0, 0x5f, 0xa2, 0x64, 0xf7, 0x3b, 0x03, 0x8c, 0xad, 0xce, 0x48, 0xab, 0x0a, 0x26, 0x32, 0xfc, - 0xee, 0x00, 0x7f, 0x6d, 0x60, 0xd3, 0xee, 0x1f, 0x93, 0xe1, 0xa0, 0x67, 0x77, 0xbe, 0xd1, 0x6a, - 0xe8, 0x63, 0xd8, 0x5f, 0x2c, 0x93, 0x91, 0x65, 0x9b, 0xc4, 0xe8, 0xf5, 0x06, 0xaa, 0x11, 0x27, - 0x83, 0xa1, 0xec, 0xc7, 0xeb, 0xe8, 0x23, 0xf8, 0xb0, 0x3f, 0x20, 0x96, 0x33, 0x32, 0x9e, 0xf6, - 0x6c, 0xe7, 0x99, 0x65, 0x92, 0x61, 0xb7, 0x33, 0x24, 0x86, 0xe3, 0x0c, 0x3a, 0xb6, 0x6a, 0xda, - 0x01, 0x3d, 0x84, 0x4f, 0xf0, 0xb8, 0x67, 0xa9, 0x67, 0x00, 0xb9, 0x1d, 0x93, 0x93, 0x81, 0xb9, - 0xe8, 0xeb, 0x49, 0xd6, 0x61, 0x37, 0xd0, 0x07, 0xd0, 0x96, 0x04, 0xac, 0xfe, 0x48, 0x70, 0x2c, - 0xe5, 0x73, 0x6c, 0x39, 0x92, 0x50, 0x53, 0x5c, 0x47, 0x8a, 0xd1, 0x19, 0x8c, 0x71, 0xc7, 0x72, - 0x88, 0x71, 0x6a, 0xd8, 0x3d, 0xe3, 0x69, 0xcf, 0xd2, 0x5a, 0x68, 0x17, 0x6e, 0x0b, 0xad, 0xd8, - 0x1d, 0x4b, 0xca, 0xd2, 0x19, 0x0f, 0x87, 0x03, 0x2c, 0xd4, 0xb1, 0x25, 0x1b, 0x80, 0x6f, 0x9c, - 0x91, 0x75, 0xb2, 0x38, 0x68, 0x5b, 0x7f, 0x0a, 0x3b, 0xeb, 0xcd, 0x08, 0x1d, 0x88, 0x94, 0xc1, - 0xd3, 0x36, 0x29, 0xef, 0x46, 0x83, 0xc9, 0x84, 0x05, 0xd4, 0x0f, 0xce, 0x6d, 0x4b, 0xa4, 0x0e, - 0xae, 0xff, 0x57, 0x01, 0x1a, 0x39, 0xa0, 0x48, 0x5f, 0x3e, 0x65, 0x41, 0xe2, 0x4f, 0x7c, 0xc6, - 0xd3, 0xd8, 0x98, 0x83, 0x5c, 0xfd, 0xd8, 0x8a, 0xbe, 0x86, 0xf6, 0xcb, 0x30, 0x26, 0x4c, 0xb0, - 0xe1, 0xb1, 0xe5, 0xd7, 0xb1, 0xd2, 0xa5, 0x82, 0x65, 0xcd, 0xdb, 0x1a, 0xbe, 0xfd, 0x32, 0x8c, - 0x2d, 0xb5, 0x3d, 0xf7, 0x4c, 0x86, 0x9e, 0xc1, 0x36, 0x65, 0x53, 0xf2, 0x92, 0xf1, 0x05, 0x3d, - 0x15, 0x7a, 0xf6, 0xaf, 0x7b, 0x0f, 0xc3, 0x2d, 0xca, 0xa6, 0xcf, 0x19, 0x4f, 0x29, 0x7d, 0xf6, - 0x05, 0x54, 0x54, 0xb0, 0x16, 0x25, 0xa7, 0x89, 0x07, 0x43, 0x55, 0x7c, 0x0a, 0xd3, 0xd1, 0x0a, - 0x62, 0xf4, 0x74, 0xdc, 0xed, 0x6a, 0x45, 0x31, 0xea, 0x0f, 0x3a, 0x43, 0xad, 0x24, 0xf1, 0xc6, - 0xc3, 0x9e, 0x56, 0xfe, 0xec, 0x67, 0x50, 0xcb, 0x72, 0xb3, 0xe8, 0x79, 0xec, 0xbe, 0x33, 0x32, - 0x7a, 0x3d, 0x6d, 0x43, 0x94, 0xa7, 0xd8, 0x3a, 0x19, 0x9c, 0x5a, 0x8a, 0x84, 0x6d, 0xf6, 0x2c, - 0xad, 0x78, 0xf4, 0x3f, 0x75, 0xa8, 0x0f, 0xb3, 0x8f, 0x98, 0xe8, 0x05, 0xdc, 0xc9, 0x7f, 0x80, - 0x13, 0x81, 0x8a, 0x87, 0xd3, 0xa9, 0x28, 0x47, 0xee, 0xaf, 0x7e, 0xfe, 0x59, 0xfe, 0x48, 0xb7, - 0x77, 0xf7, 0x2d, 0x9f, 0x87, 0xf4, 0x0d, 0xd4, 0x83, 0x2d, 0x87, 0x25, 0xce, 0x49, 0x16, 0x93, - 0x62, 0xb4, 0x54, 0x02, 0x2d, 0x12, 0xc5, 0xde, 0x83, 0xb5, 0x21, 0x2c, 0x1f, 0x32, 0xf5, 0x0d, - 0x34, 0x4c, 0xbf, 0x57, 0xa9, 0xc7, 0x5a, 0xf5, 0x5a, 0x7c, 0x6f, 0x95, 0x81, 0xa5, 0x8f, 0x76, - 0xd7, 0xf1, 0x87, 0xa1, 0xb5, 0xa4, 0x6a, 0x74, 0x5d, 0xea, 0xde, 0xbb, 0xc6, 0x4a, 0xf4, 0x0d, - 0xf4, 0x02, 0xb6, 0x57, 0xd4, 0x8d, 0xae, 0xaf, 0x30, 0xf6, 0xae, 0xb5, 0x16, 0x7d, 0x03, 0x19, - 0xb0, 0x75, 0xcc, 0x12, 0x75, 0xc1, 0x71, 0xec, 0x9e, 0x33, 0x94, 0xa5, 0x37, 0xf9, 0xd1, 0xf9, - 0xf0, 0x34, 0xf4, 0xe9, 0xde, 0xde, 0xca, 0x23, 0x2f, 0x66, 0x5e, 0xc8, 0xa9, 0x7c, 0x14, 0xd0, - 0x37, 0xd0, 0xaf, 0x00, 0x64, 0x05, 0x2e, 0x29, 0xa3, 0x9d, 0xf5, 0x6f, 0x8e, 0x7b, 0x77, 0xae, - 0x78, 0xd1, 0x53, 0x04, 0xb0, 0x7c, 0xb7, 0xff, 0xb1, 0x04, 0x4c, 0xd8, 0x56, 0xef, 0x39, 0xd9, - 0x23, 0x66, 0xfc, 0x63, 0xa8, 0xf4, 0x61, 0xfb, 0xe2, 0xcb, 0xa9, 0x12, 0xf2, 0x07, 0xab, 0xaa, - 0xce, 0x7f, 0x55, 0xbd, 0xce, 0x10, 0x18, 0xec, 0x5d, 0xfd, 0x66, 0x85, 0x7e, 0xd0, 0xb7, 0xd7, - 0x77, 0x61, 0x7b, 0xf1, 0x9c, 0xb9, 0x86, 0xed, 0xfc, 0x97, 0xed, 0xeb, 0xd8, 0xee, 0x42, 0xd3, - 0xa0, 0x74, 0x41, 0x0d, 0xbd, 0xed, 0xb3, 0xf7, 0xdb, 0xf8, 0xb2, 0x85, 0xcd, 0x4e, 0x59, 0xc2, - 0xde, 0x0b, 0x29, 0x25, 0x22, 0x7b, 0xd8, 0xb5, 0x5f, 0xfc, 0x24, 0x52, 0xbf, 0x81, 0x9d, 0x63, - 0x96, 0xac, 0x7b, 0x76, 0x5b, 0x63, 0xf7, 0x4b, 0x8e, 0x79, 0x79, 0xcb, 0xd3, 0xbb, 0x7f, 0xbc, - 0x2b, 0x11, 0x1e, 0x4d, 0x13, 0xf6, 0xc8, 0x9b, 0x86, 0x73, 0xfa, 0xe8, 0x3c, 0x4c, 0xff, 0x51, - 0x71, 0x56, 0x91, 0xbf, 0x3f, 0xff, 0xbf, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, 0x19, 0xd6, - 0xe5, 0x21, 0x00, 0x00, + 0x76, 0xc4, 0x07, 0xf1, 0xf1, 0xf0, 0xc1, 0x51, 0x4b, 0xa2, 0x40, 0xca, 0x92, 0xa9, 0xb1, 0x1d, + 0x53, 0x4e, 0x96, 0x4a, 0xb8, 0x2a, 0x7a, 0xd7, 0x4e, 0x65, 0x77, 0x04, 0x0c, 0xa8, 0xf1, 0x82, + 0x00, 0xd4, 0x03, 0xd0, 0x72, 0x2a, 0x95, 0xae, 0xe1, 0x74, 0x83, 0x9e, 0x18, 0x98, 0x19, 0xf5, + 0x0c, 0x28, 0x2b, 0x95, 0xca, 0x21, 0x87, 0x1c, 0x72, 0xca, 0x3d, 0xa9, 0xca, 0x6f, 0x48, 0x55, + 0x2e, 0xf9, 0x03, 0xb9, 0x24, 0x55, 0xb9, 0xe4, 0x96, 0xcd, 0x1f, 0x48, 0x55, 0xaa, 0x72, 0xc8, + 0x0f, 0x48, 0x75, 0xf7, 0x0c, 0x38, 0x00, 0x41, 0x51, 0x5e, 0x6b, 0x4f, 0xe8, 0x7e, 0xfd, 0xfa, + 0xf5, 0xeb, 0xf7, 0xfd, 0x7a, 0x00, 0xbb, 0xd3, 0x98, 0x3d, 0x09, 0x79, 0x10, 0x07, 0xd1, 0x93, + 0xd0, 0x0b, 0xd9, 0xd4, 0xf3, 0x19, 0x3d, 0x90, 0x00, 0x54, 0x9d, 0x39, 0xe7, 0x33, 0xe7, 0x60, + 0x1a, 0xb3, 0xdd, 0x9d, 0x80, 0xbb, 0x3f, 0xe3, 0x29, 0xa2, 0x1b, 0xcc, 0x66, 0x81, 0xaf, 0xb0, + 0x76, 0x77, 0xb2, 0x14, 0x82, 0xa9, 0xe7, 0xbe, 0xa1, 0x67, 0xc9, 0xd2, 0x5e, 0x66, 0x29, 0x62, + 0x51, 0xe4, 0x05, 0x3e, 0x99, 0x39, 0xbe, 0x73, 0xce, 0x78, 0x82, 0xf1, 0x20, 0x8b, 0x31, 0x3f, + 0x8b, 0x5c, 0xee, 0x9d, 0x31, 0x9e, 0x12, 0xd0, 0xff, 0x39, 0x07, 0xb7, 0x6c, 0x16, 0xcf, 0xc3, + 0xee, 0x34, 0x78, 0x1d, 0x61, 0xf6, 0x6a, 0xce, 0xa2, 0x18, 0x7d, 0x09, 0x15, 0xae, 0x86, 0x51, + 0x2b, 0xb7, 0x57, 0xd8, 0xaf, 0x1d, 0x7e, 0x78, 0xb0, 0x60, 0xf5, 0xc0, 0x70, 0x63, 0xef, 0xc2, + 0x89, 0x59, 0x76, 0x0b, 0x5e, 0x6c, 0x40, 0x77, 0x60, 0x93, 0x85, 0x81, 0xfb, 0x6d, 0x2b, 0xbf, + 0x97, 0xdb, 0x2f, 0x62, 0x35, 0x41, 0x2f, 0xa0, 0xf1, 0x6a, 0x1e, 0xc4, 0x0e, 0x99, 0x87, 0xd4, + 0x89, 0x59, 0xd4, 0x2a, 0xec, 0xe5, 0xf6, 0x6b, 0x87, 0xbf, 0x97, 0xa1, 0x3b, 0x96, 0x2b, 0xf6, + 0x82, 0xc9, 0x17, 0x02, 0xdf, 0x8e, 0x9d, 0x98, 0xa5, 0x87, 0xd4, 0x25, 0x09, 0x85, 0x17, 0xe9, + 0xbf, 0x0b, 0xb7, 0x25, 0xeb, 0x1d, 0x36, 0x71, 0xe6, 0xd3, 0x38, 0x65, 0x7e, 0x71, 0x7e, 0x2e, + 0x73, 0xbe, 0x7e, 0x96, 0xdc, 0x73, 0x6c, 0x9e, 0x38, 0x6e, 0x8a, 0xfa, 0xf9, 0x95, 0x7b, 0xde, + 0xcf, 0xf2, 0x23, 0x50, 0xc5, 0x25, 0xdf, 0xf1, 0x8e, 0xfa, 0x39, 0x20, 0x79, 0xc6, 0x50, 0x2a, + 0xe9, 0xb7, 0x27, 0x4c, 0xfd, 0x2f, 0x92, 0xcb, 0x48, 0x09, 0xa5, 0xe7, 0x5c, 0x91, 0x70, 0xee, + 0xc7, 0x4a, 0xf8, 0x9a, 0xd3, 0xff, 0x3a, 0x07, 0x5a, 0xd6, 0x66, 0xa2, 0xf9, 0x34, 0x46, 0x5f, + 0x40, 0x89, 0xcb, 0x91, 0x3c, 0xb6, 0x79, 0xa8, 0x67, 0x8e, 0x5d, 0x45, 0x3e, 0x50, 0x3f, 0x38, + 0xd9, 0xa1, 0x1f, 0x41, 0x29, 0xa1, 0x52, 0x83, 0xb2, 0x3d, 0x6e, 0xb7, 0x4d, 0xdb, 0xd6, 0x36, + 0xc4, 0xa4, 0x6b, 0x58, 0xbd, 0x31, 0x36, 0xb5, 0x1c, 0x42, 0xd0, 0x1c, 0x8c, 0x47, 0x1d, 0x63, + 0x64, 0x76, 0x88, 0x39, 0x1c, 0xb4, 0x9f, 0x6b, 0x79, 0xfd, 0x02, 0x6e, 0x25, 0x7c, 0x0f, 0xb8, + 0x77, 0xee, 0xf9, 0xa3, 0x37, 0x21, 0x43, 0x5f, 0x42, 0x31, 0x7e, 0x13, 0xb2, 0x84, 0x8d, 0x4f, + 0x33, 0x6c, 0x5c, 0xc1, 0x3d, 0xb8, 0x1c, 0x62, 0xb9, 0x49, 0xff, 0x18, 0x20, 0x43, 0xaa, 0x04, + 0xf9, 0xe3, 0x97, 0xda, 0x86, 0xfc, 0xfd, 0x46, 0xcb, 0x89, 0xdf, 0xfe, 0x53, 0x2d, 0xaf, 0x9f, + 0xc2, 0xd6, 0x29, 0xe3, 0xc2, 0xd9, 0x18, 0x55, 0xba, 0x46, 0x8f, 0xa1, 0xc8, 0xe7, 0x53, 0x96, + 0xc8, 0xfc, 0x6e, 0xe6, 0xd4, 0xc4, 0x18, 0xe6, 0x53, 0x86, 0x25, 0x0a, 0x6a, 0x41, 0xf9, 0x42, + 0xed, 0x96, 0x62, 0x6d, 0xe0, 0x74, 0xaa, 0xff, 0xba, 0x00, 0x77, 0xd6, 0xd9, 0x03, 0x7a, 0x0c, + 0x85, 0xc8, 0xa3, 0x09, 0xf1, 0x7b, 0x59, 0xc9, 0x2e, 0x54, 0x69, 0x75, 0xb0, 0xc0, 0x41, 0xf7, + 0xa0, 0xec, 0x85, 0xc4, 0xa1, 0x94, 0x4b, 0xea, 0x55, 0x5c, 0xf2, 0x42, 0x83, 0x52, 0x8e, 0xbe, + 0x80, 0x06, 0x7d, 0xe3, 0x3b, 0x33, 0xcf, 0x25, 0x82, 0x8d, 0xa8, 0x55, 0x94, 0xb6, 0x78, 0x0d, + 0xab, 0xf5, 0x04, 0x57, 0x4c, 0x22, 0xd4, 0x86, 0x66, 0x62, 0x91, 0x24, 0x90, 0xe2, 0x69, 0x6d, + 0x4a, 0x56, 0x3e, 0x78, 0x9b, 0x74, 0x71, 0x83, 0x67, 0x41, 0xe8, 0x8f, 0xa0, 0xe2, 0x84, 0x3e, + 0x71, 0x66, 0x67, 0xbc, 0x55, 0x92, 0xdb, 0x3f, 0xca, 0xfa, 0xc1, 0xf9, 0x39, 0x67, 0xe7, 0x4e, + 0xcc, 0xe8, 0x89, 0xf3, 0xbd, 0x37, 0x9b, 0xcf, 0x9e, 0x79, 0x31, 0x17, 0x86, 0x59, 0x76, 0x42, + 0xdf, 0x98, 0x9d, 0x71, 0x74, 0x1f, 0xaa, 0x5e, 0x78, 0x71, 0xa4, 0xee, 0x56, 0xde, 0xcb, 0xed, + 0xd7, 0x71, 0x45, 0x00, 0xe4, 0xed, 0xb6, 0xa1, 0x34, 0x8b, 0xbc, 0x88, 0xfa, 0xad, 0x8a, 0x5c, + 0x49, 0x66, 0xe8, 0x23, 0x68, 0xcc, 0xc3, 0xa9, 0xe7, 0x7f, 0x47, 0xe2, 0xb9, 0xef, 0xb3, 0x69, + 0xab, 0x2a, 0x45, 0x5e, 0x57, 0xc0, 0x91, 0x84, 0xa1, 0x4f, 0x61, 0x8b, 0x06, 0xaf, 0xfd, 0x2c, + 0x1a, 0x48, 0xb4, 0x66, 0x0a, 0x4e, 0x10, 0x8f, 0xa0, 0x22, 0x03, 0xb0, 0xc7, 0xa2, 0x56, 0x4d, + 0x8a, 0x6f, 0x37, 0x73, 0x85, 0x15, 0x9b, 0xc0, 0x0b, 0xdc, 0xaf, 0x8a, 0x95, 0x82, 0x56, 0xd4, + 0xbb, 0x70, 0x6b, 0x05, 0xc5, 0xea, 0x08, 0x7d, 0x09, 0x75, 0x90, 0x44, 0xbd, 0x55, 0x5c, 0x12, + 0x53, 0x8b, 0xbe, 0xc5, 0x4c, 0xfe, 0xbe, 0x00, 0xdb, 0x1d, 0xe6, 0xfc, 0x48, 0x43, 0xd9, 0x81, + 0x4a, 0x72, 0x70, 0xd4, 0xca, 0xef, 0x15, 0xf6, 0xab, 0xb8, 0xac, 0x4e, 0x5e, 0xa7, 0xee, 0xc2, + 0x0f, 0x57, 0x77, 0xc6, 0x10, 0x8b, 0x4b, 0x86, 0xb8, 0xa4, 0xc7, 0xcd, 0x15, 0x3d, 0xfe, 0x1c, + 0x76, 0x38, 0x9b, 0x05, 0x17, 0x8c, 0x50, 0x15, 0xd5, 0x09, 0xe5, 0x41, 0x48, 0x26, 0xe2, 0x92, + 0xd2, 0x6a, 0x2a, 0x78, 0x5b, 0x21, 0x24, 0x51, 0xbf, 0xc3, 0x03, 0x15, 0x5a, 0xae, 0xaa, 0xba, + 0xfc, 0x6e, 0xaa, 0xae, 0xac, 0x55, 0xf5, 0xcf, 0x32, 0xaa, 0xae, 0x4a, 0x55, 0x7f, 0x70, 0xbd, + 0xaa, 0xad, 0xce, 0xa5, 0xb2, 0xf5, 0x7f, 0xca, 0x41, 0x43, 0xb8, 0xcd, 0x49, 0x40, 0x93, 0xa8, + 0x76, 0xad, 0x8e, 0x3f, 0x5f, 0x04, 0xcd, 0xbc, 0x8c, 0x56, 0xd9, 0xc4, 0xb0, 0x44, 0x62, 0x25, + 0x62, 0x66, 0x8d, 0xa3, 0x20, 0x43, 0xf3, 0xc2, 0x38, 0x3e, 0x5f, 0x1f, 0x4b, 0x6f, 0xc3, 0xd6, + 0xd0, 0xc0, 0x23, 0xcb, 0xe8, 0x91, 0x14, 0x98, 0xcb, 0x06, 0xd8, 0xbc, 0xfe, 0x27, 0x70, 0x7b, + 0x25, 0xf6, 0x48, 0x2a, 0xbf, 0x80, 0xa6, 0xaa, 0x39, 0x88, 0x3a, 0x5a, 0x19, 0x4b, 0xed, 0xb0, + 0x75, 0x1d, 0xab, 0xb8, 0x11, 0x26, 0xe9, 0x4f, 0xa2, 0x7f, 0x55, 0xac, 0xe4, 0xb4, 0xbc, 0xfe, + 0xb7, 0x39, 0xb8, 0x7b, 0xc5, 0x66, 0x93, 0x03, 0x96, 0x13, 0x47, 0x36, 0x62, 0xaf, 0xdd, 0xf1, + 0xbe, 0xb2, 0xc7, 0xff, 0xe4, 0xa1, 0x96, 0xc9, 0xee, 0xe8, 0x33, 0xd8, 0x9c, 0x39, 0x71, 0x52, + 0x37, 0xd4, 0x0e, 0xef, 0x64, 0xf8, 0x10, 0x68, 0x27, 0x62, 0x0d, 0x2b, 0x14, 0xe1, 0x3c, 0x4e, + 0x18, 0x12, 0xdf, 0x99, 0xb1, 0x24, 0xcc, 0x96, 0x9d, 0x30, 0xec, 0x3b, 0x33, 0x26, 0x96, 0xce, + 0xde, 0xc4, 0x2c, 0x22, 0xfc, 0xfb, 0x54, 0x37, 0x72, 0x8e, 0xbf, 0x47, 0x8f, 0xa0, 0x1e, 0x31, + 0x7e, 0xe1, 0xb9, 0x8c, 0xc8, 0x14, 0xa5, 0xfc, 0xa2, 0x96, 0xc0, 0x64, 0xca, 0xb9, 0x07, 0xe5, + 0x88, 0xbb, 0x64, 0xe6, 0xb8, 0xd2, 0x35, 0xaa, 0xb8, 0x14, 0x71, 0xf7, 0xc4, 0x71, 0xc5, 0x02, + 0x8d, 0x62, 0xb9, 0x50, 0x52, 0x0b, 0x34, 0x8a, 0xc5, 0xc2, 0x11, 0x6c, 0x46, 0x22, 0x83, 0x4b, + 0x73, 0x6f, 0x1e, 0xee, 0xad, 0xb0, 0x9d, 0xdc, 0x4e, 0x8e, 0x55, 0xa6, 0x57, 0xe8, 0x7a, 0x00, + 0xd5, 0x05, 0x0c, 0x69, 0x50, 0xef, 0xf6, 0x06, 0x5f, 0x93, 0x36, 0x36, 0x85, 0x8c, 0xb4, 0x0d, + 0xf4, 0x21, 0xdc, 0x97, 0x90, 0xd4, 0x6a, 0xda, 0x3d, 0xc3, 0xb6, 0xad, 0xae, 0xd5, 0x36, 0x46, + 0xd6, 0xa0, 0xaf, 0xe5, 0xd0, 0x03, 0xd8, 0x91, 0x08, 0x5d, 0xab, 0x7f, 0x75, 0x39, 0xbf, 0xa0, + 0x68, 0xbe, 0x1c, 0x5a, 0xd8, 0xec, 0x68, 0x05, 0xfd, 0x2f, 0xa1, 0xae, 0x18, 0x8a, 0xc2, 0xc0, + 0x8f, 0x18, 0x3a, 0x5a, 0x51, 0xfc, 0xc3, 0x2b, 0x9c, 0x2b, 0xc4, 0xf7, 0xa5, 0xef, 0x7f, 0xcf, + 0x81, 0xb6, 0x5a, 0xd2, 0xfd, 0xc0, 0x80, 0x39, 0x73, 0xdc, 0x6c, 0x6a, 0x2d, 0xcf, 0x1c, 0x77, + 0x25, 0xfb, 0x14, 0x94, 0x6e, 0x92, 0xec, 0xf3, 0x10, 0x6a, 0x4e, 0x48, 0x16, 0xbb, 0x94, 0xbe, + 0xab, 0x4e, 0x78, 0x92, 0xec, 0xbb, 0x07, 0x65, 0x27, 0xb1, 0xa2, 0x44, 0xdb, 0x8e, 0x32, 0xa2, + 0x8f, 0xa1, 0x19, 0xd2, 0x90, 0x44, 0xb1, 0xc3, 0x63, 0x12, 0x7b, 0x33, 0x26, 0x95, 0x5e, 0xc4, + 0xf5, 0x90, 0x86, 0xb6, 0x00, 0x8e, 0xbc, 0x19, 0xd3, 0xff, 0x33, 0x07, 0x77, 0x57, 0x8a, 0x39, + 0x55, 0xb9, 0xbd, 0xa7, 0x6b, 0x75, 0xa1, 0xa6, 0x6a, 0x49, 0x65, 0xae, 0x05, 0xa9, 0xa6, 0x4f, + 0xd6, 0x52, 0xcb, 0x1c, 0x7e, 0x20, 0xb3, 0x01, 0xa8, 0x9d, 0x62, 0xac, 0x3f, 0x85, 0xa2, 0x34, + 0xee, 0x2d, 0xa8, 0x9d, 0x1a, 0x3d, 0xab, 0x43, 0x5e, 0x8c, 0x07, 0x23, 0x43, 0xdb, 0x40, 0x75, + 0xa8, 0xf4, 0x07, 0xc9, 0x2c, 0x87, 0x1a, 0x50, 0x1d, 0x99, 0xf8, 0xc4, 0xea, 0x1b, 0x23, 0x11, + 0x90, 0x08, 0x3c, 0xba, 0xb1, 0x5e, 0x45, 0x5f, 0x40, 0xf9, 0xb2, 0xdc, 0x15, 0x71, 0x69, 0xef, + 0x26, 0xf6, 0x70, 0xba, 0x41, 0xe7, 0xb0, 0x35, 0x72, 0xce, 0xa6, 0xcc, 0x88, 0x22, 0xef, 0xdc, + 0x9f, 0x31, 0x3f, 0x5e, 0xf2, 0xeb, 0xdc, 0xb2, 0x5f, 0x3f, 0x00, 0x98, 0x39, 0x9e, 0x4f, 0x62, + 0xb1, 0x25, 0x29, 0x88, 0xab, 0x02, 0x22, 0x69, 0xa0, 0x4f, 0xa0, 0x19, 0xb9, 0x5c, 0x04, 0x07, + 0x85, 0x21, 0x1a, 0x9c, 0xc2, 0x7e, 0x11, 0x37, 0x12, 0xa8, 0xc4, 0x8a, 0xf4, 0x3f, 0x85, 0xdb, + 0xc6, 0x74, 0xba, 0x72, 0x6c, 0x84, 0x8e, 0xe1, 0x96, 0xdc, 0x45, 0x9c, 0x4b, 0x60, 0x72, 0xa1, + 0x6c, 0x85, 0xb1, 0xb2, 0x0f, 0x6b, 0xf1, 0x0a, 0x21, 0xfd, 0x4b, 0xd1, 0x13, 0x71, 0xcf, 0x99, + 0x7a, 0x7f, 0xce, 0x28, 0x7e, 0x33, 0x1f, 0x3a, 0xee, 0x77, 0x2c, 0x46, 0x1a, 0x14, 0xc2, 0xef, + 0x94, 0xa3, 0xd5, 0xb1, 0x18, 0x22, 0x04, 0x45, 0x6f, 0x16, 0x79, 0x89, 0xca, 0xe5, 0x58, 0x3f, + 0x80, 0x5b, 0x0a, 0x5f, 0x24, 0x55, 0x79, 0x96, 0x25, 0xed, 0x43, 0xb1, 0x96, 0xd8, 0xd3, 0x26, + 0x2e, 0xc7, 0x6a, 0x49, 0xff, 0xbf, 0x1c, 0x54, 0xbb, 0xd1, 0x8c, 0xc8, 0x80, 0x82, 0x7e, 0x9a, + 0x06, 0x22, 0xe5, 0xce, 0x0f, 0xb2, 0xee, 0x9c, 0x22, 0x89, 0xd1, 0x52, 0x14, 0xfa, 0xc7, 0x1c, + 0x54, 0x52, 0x98, 0xf0, 0x5a, 0xdb, 0xb4, 0x6d, 0x6b, 0xd0, 0x27, 0x46, 0x7b, 0x64, 0x9d, 0x9a, + 0xda, 0x06, 0xda, 0x06, 0x94, 0xc2, 0x16, 0xc6, 0xd1, 0xd1, 0x8a, 0xe8, 0x11, 0x3c, 0x58, 0x85, + 0x8b, 0xb1, 0xdd, 0x7e, 0x6e, 0x76, 0xc6, 0x3d, 0xb3, 0xa3, 0x6d, 0xa2, 0x3b, 0xa0, 0xa5, 0x28, + 0xd8, 0xec, 0x99, 0x86, 0x6d, 0x76, 0xb4, 0x92, 0xb0, 0x39, 0x19, 0xe5, 0xac, 0xfe, 0xb1, 0x56, + 0x16, 0x51, 0x23, 0x8d, 0x79, 0x15, 0x04, 0x50, 0x4a, 0xce, 0xad, 0x0a, 0x34, 0xab, 0x9f, 0xcc, + 0x40, 0xa0, 0x25, 0x24, 0xb4, 0x9a, 0xfe, 0x57, 0x39, 0x00, 0x9b, 0x4e, 0xba, 0xde, 0x34, 0x66, + 0x3c, 0x42, 0x8f, 0x21, 0x3f, 0x49, 0x5d, 0x6d, 0x67, 0x25, 0x86, 0x75, 0x98, 0x30, 0xc0, 0x30, + 0x0e, 0x38, 0xce, 0x4f, 0xa8, 0x50, 0x43, 0x1c, 0xbb, 0x52, 0xe6, 0x75, 0x2c, 0x86, 0x02, 0x12, + 0x85, 0x9e, 0x74, 0xad, 0x3a, 0x16, 0x43, 0xd4, 0x84, 0xfc, 0x64, 0x2a, 0x43, 0x45, 0x1d, 0xe7, + 0x27, 0x53, 0x74, 0x17, 0x4a, 0x11, 0x9d, 0x08, 0xe9, 0x6f, 0xca, 0x42, 0x65, 0x33, 0xa2, 0x13, + 0x8b, 0xea, 0x7f, 0x06, 0xcd, 0xe5, 0x03, 0xd0, 0x4f, 0x96, 0xf3, 0xd7, 0xbd, 0x75, 0xf9, 0xab, + 0xcf, 0x5e, 0xa7, 0x29, 0xec, 0x31, 0x94, 0x44, 0x72, 0x4d, 0xca, 0xcb, 0xe6, 0xe1, 0xad, 0x95, + 0xa6, 0x34, 0xf0, 0x71, 0x82, 0xa0, 0xff, 0x43, 0x4e, 0x85, 0xee, 0x94, 0x84, 0xb0, 0x09, 0x2f, + 0xbc, 0x78, 0x4a, 0x22, 0xee, 0xa6, 0x6e, 0x22, 0xe6, 0x36, 0x77, 0x17, 0x4b, 0x34, 0x8a, 0xd3, + 0x70, 0x22, 0xe6, 0x9d, 0x28, 0x16, 0x05, 0x9a, 0x7c, 0x74, 0x70, 0x83, 0xe9, 0x65, 0x40, 0xa9, + 0xe2, 0x7a, 0x0a, 0x94, 0x31, 0x62, 0x07, 0x2a, 0x22, 0xcf, 0x85, 0x01, 0x8f, 0xa5, 0x10, 0x1a, + 0x58, 0xe4, 0xbd, 0x61, 0xc0, 0xa5, 0x73, 0x8a, 0xdc, 0x28, 0x97, 0x94, 0x2c, 0x44, 0xae, 0x14, + 0x4b, 0xfa, 0xbf, 0xe6, 0xa0, 0x8e, 0x19, 0xf5, 0x38, 0x73, 0x63, 0xcb, 0x9f, 0x04, 0xe8, 0x2b, + 0xa8, 0x73, 0x46, 0x45, 0x54, 0x23, 0x99, 0x6e, 0x70, 0x7f, 0xa9, 0x80, 0xbd, 0x44, 0x5f, 0x4c, + 0x44, 0xd8, 0x53, 0xe1, 0x8b, 0x33, 0x6a, 0x50, 0x2a, 0x59, 0xfa, 0x1d, 0xd8, 0x12, 0xb4, 0x44, + 0x9a, 0x66, 0x3c, 0x1b, 0x28, 0x1b, 0x9c, 0x51, 0x5b, 0x42, 0xc5, 0x3e, 0xfd, 0x18, 0xb4, 0x55, + 0x3a, 0xa8, 0x02, 0x45, 0x6b, 0x78, 0xfa, 0x54, 0xdb, 0x48, 0x46, 0x47, 0x5a, 0x0e, 0x95, 0xa1, + 0x30, 0xc6, 0x3d, 0x2d, 0x2f, 0xec, 0xcd, 0xb6, 0x86, 0x63, 0x6c, 0x69, 0x05, 0x31, 0x16, 0x88, + 0xa7, 0x47, 0x5a, 0x51, 0x3f, 0x87, 0xdb, 0x83, 0x79, 0xcc, 0xf8, 0x73, 0xe6, 0x50, 0xc6, 0xdb, + 0x9c, 0x39, 0x42, 0x0d, 0xc2, 0x12, 0x02, 0x12, 0xb3, 0xc4, 0x0f, 0x1b, 0x78, 0x33, 0x18, 0x31, + 0x8f, 0xa2, 0x3d, 0xa8, 0x9f, 0xfb, 0x67, 0x44, 0x4a, 0xdd, 0x59, 0xf0, 0x06, 0xe7, 0xfe, 0x99, + 0x15, 0x5e, 0x3c, 0x35, 0x54, 0x9a, 0x11, 0x42, 0x23, 0x7e, 0x20, 0x45, 0xde, 0xc0, 0x25, 0x31, + 0xed, 0x07, 0xfa, 0xbf, 0x09, 0xef, 0x7b, 0x4d, 0x87, 0x0e, 0x77, 0x66, 0x22, 0xc0, 0x51, 0x51, + 0xf2, 0x7b, 0x13, 0xc7, 0x65, 0xc9, 0x11, 0x55, 0x01, 0xb1, 0x04, 0x40, 0x14, 0x2f, 0x3e, 0x8b, + 0x89, 0xe7, 0x47, 0xb1, 0xe3, 0xbb, 0x69, 0xd9, 0x53, 0xf3, 0x59, 0x6c, 0x25, 0x20, 0xf4, 0x87, + 0x20, 0x24, 0x22, 0x05, 0x40, 0x3c, 0x7f, 0x12, 0x24, 0x6d, 0xc3, 0xbd, 0x6b, 0xa4, 0x8e, 0xeb, + 0x3c, 0xab, 0xb2, 0x5f, 0x42, 0x3d, 0x98, 0xc7, 0x9c, 0x7c, 0xcb, 0x1c, 0x4a, 0x5c, 0x95, 0x2d, + 0x6b, 0x4b, 0x55, 0xc1, 0x1a, 0xa1, 0x60, 0x10, 0x7b, 0x04, 0xac, 0xcd, 0xf5, 0xc7, 0x50, 0xe9, + 0xcc, 0xc3, 0x77, 0xb9, 0x8d, 0x08, 0x5d, 0x85, 0x61, 0xc7, 0x12, 0x36, 0x29, 0x6c, 0xca, 0xf3, + 0x63, 0xc6, 0x33, 0x98, 0xf5, 0x88, 0xbb, 0x56, 0x0a, 0x13, 0x12, 0x9e, 0x06, 0xae, 0x33, 0x25, + 0x13, 0x25, 0x7e, 0xd5, 0x8f, 0x81, 0x84, 0x75, 0xa5, 0x0e, 0x56, 0x85, 0x53, 0xb8, 0x2a, 0x9c, + 0x5d, 0xa8, 0xce, 0x19, 0x91, 0x2d, 0x51, 0x5a, 0x09, 0x94, 0xe7, 0xcc, 0x0a, 0x85, 0x82, 0x5a, + 0x50, 0x89, 0x39, 0x61, 0x61, 0xea, 0xe5, 0x75, 0x5c, 0x8a, 0xb9, 0x19, 0x5a, 0x14, 0x1d, 0x41, + 0x4d, 0x78, 0xff, 0x44, 0xc5, 0x9a, 0xa4, 0x6f, 0xce, 0xf6, 0xec, 0x97, 0x81, 0x08, 0x43, 0x74, + 0x19, 0x94, 0xee, 0x42, 0x49, 0x24, 0x32, 0x8f, 0xca, 0xb2, 0xb0, 0x8a, 0x37, 0x9d, 0x30, 0xb4, + 0xa8, 0xfe, 0xeb, 0x02, 0xd4, 0x6c, 0x16, 0x1f, 0xf3, 0x60, 0x1e, 0x0e, 0x3b, 0x58, 0xa0, 0x85, + 0x94, 0x93, 0x4b, 0x93, 0x0a, 0x29, 0xb7, 0x28, 0xfa, 0x10, 0x6a, 0x02, 0x9c, 0xed, 0x3f, 0x37, + 0x31, 0x84, 0x94, 0x27, 0x7d, 0x0f, 0x7a, 0x08, 0x10, 0x72, 0xe6, 0x32, 0xca, 0xd2, 0xdb, 0x36, + 0x70, 0x06, 0x82, 0x7e, 0x1f, 0xaa, 0x82, 0x80, 0xca, 0x07, 0x45, 0xe9, 0x7b, 0xb7, 0xb3, 0x0f, + 0x0d, 0x94, 0xab, 0x2c, 0x50, 0x09, 0x93, 0x11, 0xda, 0x83, 0x42, 0x48, 0xbd, 0xe4, 0x5d, 0xa1, + 0x99, 0xc5, 0xed, 0x58, 0x58, 0x2c, 0xa1, 0x47, 0xd0, 0x08, 0xc8, 0xb7, 0x44, 0x74, 0x7f, 0x84, + 0xb2, 0x48, 0xd5, 0xc1, 0x0d, 0x0c, 0xc1, 0x73, 0xcc, 0x66, 0x81, 0x08, 0x84, 0x68, 0x1f, 0x34, + 0xd9, 0x30, 0x30, 0x12, 0x0a, 0x8f, 0x95, 0x2f, 0x32, 0xea, 0xfe, 0x4d, 0x05, 0x1f, 0x72, 0x46, + 0x45, 0xb7, 0x82, 0x9e, 0x02, 0x44, 0x2c, 0x26, 0xe7, 0x9c, 0x4c, 0x1c, 0x2e, 0x5b, 0xc0, 0xda, + 0xe1, 0xf6, 0xf2, 0x93, 0x95, 0x14, 0x52, 0xd7, 0xc0, 0xb8, 0x12, 0x89, 0x49, 0xd7, 0xe1, 0xe8, + 0x05, 0xdc, 0xa6, 0x8b, 0x96, 0x44, 0x36, 0xa5, 0x84, 0xb3, 0x57, 0xf2, 0x4d, 0xa1, 0x76, 0xf8, + 0xe8, 0x6d, 0x8d, 0x8b, 0x7a, 0x5d, 0xbb, 0x45, 0x97, 0xe0, 0x98, 0xbd, 0x42, 0xbf, 0x82, 0x5b, + 0x57, 0x09, 0x82, 0x24, 0x78, 0xe3, 0x33, 0xe1, 0xd6, 0x0a, 0x31, 0xfd, 0x5f, 0x72, 0x97, 0xea, + 0xed, 0x1a, 0x52, 0xbd, 0x13, 0x27, 0xab, 0xde, 0x89, 0x23, 0xd4, 0xfb, 0x4b, 0xb8, 0x2d, 0xc0, + 0x2a, 0xba, 0x93, 0x38, 0x20, 0x4e, 0x18, 0x4e, 0xdf, 0xc8, 0xc6, 0x6e, 0x6d, 0x1e, 0xd0, 0x26, + 0x0e, 0x57, 0xc3, 0x51, 0x60, 0x08, 0x54, 0x74, 0x00, 0x95, 0xc9, 0x6b, 0x4a, 0x42, 0x87, 0xcf, + 0x12, 0x27, 0xcf, 0xaa, 0x37, 0x0d, 0x29, 0xb8, 0x3c, 0x91, 0xa3, 0x99, 0xc0, 0xa7, 0x73, 0x81, + 0xee, 0xcc, 0x12, 0xbf, 0xce, 0xe2, 0xa7, 0x4e, 0x8b, 0xcb, 0x74, 0x2e, 0x07, 0xfa, 0xdf, 0xe4, + 0x01, 0x6c, 0xf5, 0x9e, 0x6d, 0x33, 0x99, 0x39, 0x2e, 0xdf, 0xae, 0x2f, 0xdb, 0xe8, 0xfa, 0x25, + 0xd0, 0xa2, 0xef, 0xe0, 0xa5, 0x9f, 0xc2, 0x56, 0xfa, 0x48, 0x9e, 0xed, 0x9e, 0x1b, 0xb8, 0x99, + 0x80, 0x53, 0xf3, 0xfe, 0x0c, 0xca, 0x7e, 0x40, 0x65, 0xc9, 0xa3, 0xb8, 0xcd, 0x0a, 0xa5, 0x1f, + 0x50, 0x66, 0x75, 0x70, 0x49, 0x60, 0x58, 0x54, 0xb4, 0x8d, 0xca, 0xcc, 0x37, 0xaf, 0xb6, 0x8d, + 0x69, 0xd9, 0x93, 0x54, 0x3b, 0x19, 0xab, 0x0b, 0x29, 0x6f, 0x95, 0x64, 0x7d, 0xb7, 0xce, 0xea, + 0x86, 0x9d, 0xd4, 0xea, 0x86, 0x94, 0xeb, 0xff, 0x95, 0x83, 0xed, 0xf1, 0xb0, 0x9b, 0xc8, 0xa3, + 0x1d, 0xf8, 0x31, 0xfb, 0x3e, 0x56, 0x5e, 0xf3, 0x07, 0x00, 0xae, 0x33, 0x8f, 0x98, 0x0a, 0xb7, + 0x2a, 0xf1, 0xa3, 0x0c, 0xc1, 0xb6, 0x58, 0xb4, 0x4c, 0x5c, 0x95, 0x58, 0x32, 0xcc, 0x9a, 0xa0, + 0xa5, 0x42, 0x88, 0x7c, 0x27, 0x8c, 0xbe, 0x0d, 0x54, 0xa2, 0x5e, 0xae, 0x34, 0x2f, 0xcf, 0x53, + 0x8e, 0x9a, 0x0a, 0xce, 0x4e, 0xb6, 0x20, 0x0b, 0xb6, 0x26, 0x8e, 0x37, 0x9d, 0x73, 0x46, 0xd2, + 0xb7, 0x8d, 0xc2, 0x15, 0x37, 0xe8, 0x2a, 0x0c, 0xe1, 0x71, 0xe2, 0x6c, 0x3e, 0x53, 0x31, 0xbb, + 0x31, 0xc9, 0xc0, 0xa9, 0xfe, 0x77, 0x45, 0x28, 0x27, 0x8c, 0xa2, 0x9f, 0x43, 0x25, 0xb9, 0x10, + 0x5b, 0xd3, 0x16, 0x26, 0x58, 0xea, 0xf7, 0xd4, 0x99, 0xce, 0x59, 0x84, 0xcb, 0xea, 0x6a, 0x4c, + 0xff, 0x8f, 0x02, 0xd4, 0x32, 0x0b, 0xa2, 0x82, 0xc3, 0xa6, 0x6d, 0xe2, 0x53, 0xd9, 0xcf, 0xde, + 0x01, 0x0d, 0x9b, 0x2f, 0xc6, 0xa6, 0x3d, 0x22, 0x46, 0xbb, 0x6d, 0x0e, 0x45, 0xc5, 0x97, 0x43, + 0x0f, 0x61, 0x37, 0x85, 0x62, 0xf3, 0x2b, 0xb3, 0x2d, 0xfa, 0xc5, 0xfe, 0x80, 0x60, 0xd3, 0xb0, + 0x65, 0x17, 0xfb, 0x00, 0x76, 0xd2, 0x12, 0xb2, 0x3d, 0xe8, 0x8f, 0xcc, 0x97, 0x23, 0xd2, 0x1f, + 0x8c, 0x48, 0x77, 0x30, 0xee, 0x77, 0xb4, 0x02, 0x6a, 0xc1, 0x9d, 0x13, 0xa3, 0xdf, 0x31, 0x46, + 0x03, 0xfc, 0x0d, 0xb1, 0x4c, 0x72, 0x62, 0xd9, 0xb6, 0xa8, 0x2b, 0x8b, 0x68, 0x17, 0xb6, 0xdb, + 0x83, 0x7e, 0xc7, 0x12, 0x45, 0xa9, 0xd1, 0xcb, 0xae, 0x6d, 0x8a, 0x32, 0xd7, 0xea, 0xab, 0x46, + 0xa8, 0x67, 0xf6, 0x8f, 0x47, 0xcf, 0xb5, 0x92, 0xc0, 0x5f, 0xa2, 0x64, 0xf5, 0xdb, 0x03, 0x8c, + 0xcd, 0xf6, 0x48, 0x2b, 0x0b, 0x26, 0x52, 0xfc, 0xee, 0x00, 0x7f, 0x6d, 0xe0, 0x8e, 0xd5, 0x3f, + 0x26, 0xc3, 0x41, 0xcf, 0x6a, 0x7f, 0xa3, 0x55, 0xd0, 0xc7, 0xb0, 0xb7, 0x58, 0x26, 0x23, 0xd3, + 0xea, 0x10, 0xa3, 0xd7, 0x1b, 0xa8, 0x46, 0x9c, 0x0c, 0x86, 0xb2, 0x1f, 0xaf, 0xa2, 0x8f, 0xe0, + 0xc3, 0xfe, 0x80, 0x98, 0xf6, 0xc8, 0x78, 0xd6, 0xb3, 0xec, 0xe7, 0x66, 0x87, 0x0c, 0xbb, 0xed, + 0x21, 0x31, 0x6c, 0x7b, 0xd0, 0xb6, 0x54, 0xd3, 0x0e, 0xe8, 0x31, 0x7c, 0x82, 0xc7, 0x3d, 0x53, + 0x3d, 0x03, 0xc8, 0xed, 0x98, 0x9c, 0x0c, 0x3a, 0x8b, 0xbe, 0x9e, 0xa4, 0x1d, 0x76, 0x0d, 0x7d, + 0x00, 0x2d, 0x49, 0xc0, 0xec, 0x8f, 0x04, 0xc7, 0x52, 0x3e, 0xc7, 0xa6, 0x2d, 0x09, 0xd5, 0xc5, + 0x75, 0xa4, 0x18, 0xed, 0xc1, 0x18, 0xb7, 0x4d, 0x9b, 0x18, 0xa7, 0x86, 0xd5, 0x33, 0x9e, 0xf5, + 0x4c, 0xad, 0x81, 0x76, 0xe0, 0xae, 0xd0, 0x8a, 0xd5, 0x36, 0xa5, 0x2c, 0xed, 0xf1, 0x70, 0x38, + 0xc0, 0x42, 0x1d, 0x4d, 0xd9, 0x00, 0x7c, 0x63, 0x8f, 0xcc, 0x93, 0xc5, 0x41, 0x5b, 0xfa, 0x33, + 0xd8, 0x5e, 0x6f, 0x46, 0x68, 0x5f, 0xa4, 0x0c, 0x9e, 0xb4, 0x49, 0x59, 0x37, 0x1a, 0x4c, 0x26, + 0xcc, 0xa7, 0x9e, 0x7f, 0x6e, 0x99, 0x22, 0x75, 0x70, 0xfd, 0xbf, 0x73, 0x50, 0xcb, 0x00, 0x45, + 0xfa, 0xf2, 0x28, 0xf3, 0x63, 0x6f, 0xe2, 0x31, 0x9e, 0xc4, 0xc6, 0x0c, 0xe4, 0xfa, 0xb7, 0x57, + 0xf4, 0x35, 0xb4, 0x5e, 0x05, 0x11, 0x61, 0x82, 0x0d, 0x37, 0x31, 0xfd, 0xf4, 0x61, 0xac, 0x70, + 0xa5, 0x60, 0x59, 0xf3, 0xa0, 0x86, 0xef, 0xbe, 0x0a, 0x22, 0x53, 0x6d, 0x97, 0x2f, 0xed, 0x6a, + 0x33, 0x7a, 0x0e, 0x5b, 0x94, 0x4d, 0xc9, 0x2b, 0xc6, 0x17, 0xf4, 0x54, 0xe8, 0xd9, 0xbb, 0xe9, + 0x3d, 0x0c, 0x37, 0x28, 0x9b, 0xbe, 0x60, 0x3c, 0xa1, 0xf4, 0xd9, 0x17, 0x50, 0x52, 0xc1, 0x5a, + 0x94, 0x9c, 0x1d, 0x3c, 0x18, 0xaa, 0xe2, 0x53, 0x98, 0x8e, 0x96, 0x13, 0xa3, 0x67, 0xe3, 0x6e, + 0x57, 0xcb, 0x8b, 0x51, 0x7f, 0xd0, 0x1e, 0x6a, 0x05, 0x89, 0x37, 0x1e, 0xf6, 0xb4, 0xe2, 0x67, + 0x3f, 0x81, 0x4a, 0x9a, 0x9b, 0x45, 0xcf, 0x63, 0xf5, 0xed, 0x91, 0xd1, 0xeb, 0x69, 0x1b, 0xa2, + 0x3c, 0xc5, 0xe6, 0xc9, 0xe0, 0xd4, 0x54, 0x24, 0xac, 0x4e, 0xcf, 0xd4, 0xf2, 0x87, 0xff, 0x5b, + 0x85, 0xea, 0x30, 0xfd, 0xa6, 0x89, 0x5e, 0xc2, 0xbd, 0xec, 0xf7, 0x38, 0x11, 0xa8, 0x78, 0x30, + 0x9d, 0x8a, 0x72, 0xe4, 0xe1, 0xea, 0xd7, 0xa0, 0xe5, 0x6f, 0x76, 0xbb, 0xf7, 0xdf, 0xf2, 0xb5, + 0x48, 0xdf, 0x40, 0x3d, 0x68, 0xda, 0x2c, 0xb6, 0x4f, 0xd2, 0x98, 0x14, 0xa1, 0xa5, 0x12, 0x68, + 0x91, 0x28, 0x76, 0x1f, 0xad, 0x0d, 0x61, 0xd9, 0x90, 0xa9, 0x6f, 0xa0, 0x61, 0xf2, 0xf9, 0x4a, + 0xbd, 0xdd, 0xaa, 0xc7, 0xe3, 0x07, 0xab, 0x0c, 0x2c, 0x7d, 0xc3, 0xbb, 0x89, 0x3f, 0x0c, 0x8d, + 0x25, 0x55, 0xa3, 0x9b, 0x52, 0xf7, 0xee, 0x0d, 0x56, 0xa2, 0x6f, 0xa0, 0x97, 0xb0, 0xb5, 0xa2, + 0x6e, 0x74, 0x73, 0x85, 0xb1, 0x7b, 0xa3, 0xb5, 0xe8, 0x1b, 0xc8, 0x80, 0xe6, 0x31, 0x8b, 0xd5, + 0x05, 0xc7, 0x91, 0x73, 0xce, 0x50, 0x9a, 0xde, 0xe4, 0x37, 0xe8, 0x83, 0xd3, 0xc0, 0xa3, 0xbb, + 0xbb, 0x2b, 0xef, 0xbb, 0x98, 0xb9, 0x01, 0xa7, 0xf2, 0x51, 0x40, 0xdf, 0x40, 0xbf, 0x00, 0x90, + 0x15, 0xb8, 0xa4, 0x8c, 0xb6, 0xd7, 0xbf, 0x39, 0xee, 0xde, 0xbb, 0xe6, 0x45, 0x4f, 0x11, 0xc0, + 0xf2, 0x19, 0xff, 0x37, 0x25, 0xd0, 0x81, 0x2d, 0xf5, 0x9e, 0x93, 0x3e, 0x62, 0x46, 0xbf, 0x09, + 0x95, 0x3e, 0x6c, 0x5d, 0x7e, 0x48, 0x55, 0x42, 0xfe, 0x60, 0x55, 0xd5, 0xd9, 0x8f, 0xac, 0x37, + 0x19, 0x02, 0x83, 0xdd, 0xeb, 0xdf, 0xac, 0xd0, 0x0f, 0xfa, 0x14, 0xfb, 0x2e, 0x6c, 0x2f, 0x9e, + 0x33, 0xd7, 0xb0, 0x9d, 0xfd, 0xd0, 0x7d, 0x13, 0xdb, 0x5d, 0xa8, 0x1b, 0x94, 0x2e, 0xa8, 0xa1, + 0xb7, 0x7d, 0x05, 0x7f, 0x1b, 0x5f, 0x96, 0xb0, 0xd9, 0x29, 0x8b, 0xd9, 0x7b, 0x21, 0xa5, 0x44, + 0x64, 0x0d, 0xbb, 0xd6, 0xcb, 0x1f, 0x45, 0xea, 0x57, 0xb0, 0x7d, 0xcc, 0xe2, 0x75, 0xcf, 0x6e, + 0x6b, 0xec, 0x7e, 0xc9, 0x31, 0xaf, 0x6e, 0x79, 0x76, 0xff, 0x8f, 0x77, 0x24, 0xc2, 0x93, 0x69, + 0xcc, 0x9e, 0xb8, 0xd3, 0x60, 0x4e, 0x9f, 0x9c, 0x07, 0xc9, 0x1f, 0x2c, 0xce, 0x4a, 0xf2, 0xf7, + 0xa7, 0xff, 0x1f, 0x00, 0x00, 0xff, 0xff, 0x3e, 0xd5, 0x68, 0x75, 0xf4, 0x21, 0x00, 0x00, } // Reference imports to suppress errors if they are not otherwise used. diff --git a/lte/gateway/python/magma/pipelined/app/enforcement.py b/lte/gateway/python/magma/pipelined/app/enforcement.py index fe001a61e94e..360539b2e05d 100644 --- a/lte/gateway/python/magma/pipelined/app/enforcement.py +++ b/lte/gateway/python/magma/pipelined/app/enforcement.py @@ -151,7 +151,7 @@ def _get_default_flow_msgs(self, datapath) -> DefaultMsgsMap: return {self.tbl_num: [msg]} - def _get_rule_match_flow_msgs(self, imsi, msisdn: bytes, uplink_tunnel: int, ip_addr, apn_ambr, rule): + def _get_rule_match_flow_msgs(self, imsi, msisdn: bytes, uplink_tunnel: int, ip_addr, apn_ambr, rule, version): """ Get flow msgs to get stats for a particular rule. Flows will match on IMSI, cookie (the rule num), in/out direction @@ -169,8 +169,6 @@ def _get_rule_match_flow_msgs(self, imsi, msisdn: bytes, uplink_tunnel: int, ip_ flow_adds = [] for flow in rule.flow_list: try: - version = self._session_rule_version_mapper.get_version(imsi, ip_addr, - rule.id) flow_adds.extend(self._get_classify_rule_flow_msgs( imsi, msisdn, uplink_tunnel, ip_addr, apn_ambr, flow, rule_num, priority, rule.qos, rule.hard_timeout, rule.id, rule.app_name, @@ -184,7 +182,7 @@ def _get_rule_match_flow_msgs(self, imsi, msisdn: bytes, uplink_tunnel: int, ip_ raise err return flow_adds - def _install_flow_for_rule(self, imsi, msisdn: bytes, uplink_tunnel: int, ip_addr, apn_ambr, rule): + def _install_flow_for_rule(self, imsi, msisdn: bytes, uplink_tunnel: int, ip_addr, apn_ambr, rule, version): """ Install a flow to get stats for a particular rule. Flows will match on IMSI, cookie (the rule num), in/out direction @@ -197,7 +195,7 @@ def _install_flow_for_rule(self, imsi, msisdn: bytes, uplink_tunnel: int, ip_add rule (PolicyRule): policy rule proto """ if rule.redirect.support == rule.redirect.ENABLED: - return self._install_redirect_flow(imsi, ip_addr, rule) + return self._install_redirect_flow(imsi, ip_addr, rule, version) if not rule.flow_list: self.logger.error('The flow list for imsi %s, rule.id - %s' @@ -206,7 +204,7 @@ def _install_flow_for_rule(self, imsi, msisdn: bytes, uplink_tunnel: int, ip_add flow_adds = [] try: - flow_adds = self._get_rule_match_flow_msgs(imsi, msisdn, uplink_tunnel, ip_addr, apn_ambr, rule) + flow_adds = self._get_rule_match_flow_msgs(imsi, msisdn, uplink_tunnel, ip_addr, apn_ambr, rule, version) except FlowMatchError: return RuleModResult.FAILURE @@ -218,18 +216,15 @@ def _install_flow_for_rule(self, imsi, msisdn: bytes, uplink_tunnel: int, ip_add return RuleModResult.FAILURE return self._wait_for_rule_responses(imsi, ip_addr, rule, chan) - def _install_redirect_flow(self, imsi, ip_addr, rule): + def _install_redirect_flow(self, imsi, ip_addr, rule, version): rule_num = self._rule_mapper.get_or_create_rule_num(rule.id) - rule_version = self._session_rule_version_mapper.get_version(imsi, - ip_addr, - rule.id) priority = Utils.get_of_priority(rule.priority) redirect_request = RedirectionManager.RedirectRequest( imsi=imsi, ip_addr=ip_addr.address.decode('utf-8'), rule=rule, rule_num=rule_num, - rule_version=rule_version, + rule_version=version, priority=priority) try: self._redirect_manager.setup_lte_redirect( diff --git a/lte/gateway/python/magma/pipelined/app/enforcement_stats.py b/lte/gateway/python/magma/pipelined/app/enforcement_stats.py index 5f8d505318b8..f69948ccf894 100644 --- a/lte/gateway/python/magma/pipelined/app/enforcement_stats.py +++ b/lte/gateway/python/magma/pipelined/app/enforcement_stats.py @@ -143,7 +143,7 @@ def cleanup_on_disconnect(self, datapath): if self._clean_restart: self.delete_all_flows(datapath) - def _install_flow_for_rule(self, imsi, msisdn: bytes, uplink_tunnel: int, ip_addr, apn_ambr, rule): + def _install_flow_for_rule(self, imsi, msisdn: bytes, uplink_tunnel: int, ip_addr, apn_ambr, rule, version): """ Install a flow to get stats for a particular rule. Flows will match on IMSI, cookie (the rule num), in/out direction @@ -161,7 +161,7 @@ def fail(err): rule.id, imsi, err) return RuleModResult.FAILURE - msgs = self._get_rule_match_flow_msgs(imsi, msisdn, uplink_tunnel, ip_addr, apn_ambr, rule) + msgs = self._get_rule_match_flow_msgs(imsi, msisdn, uplink_tunnel, ip_addr, apn_ambr, rule, version) try: chan = self._msg_hub.send(msgs, self._datapath) @@ -188,13 +188,11 @@ def _handle_error(self, ev): self._msg_hub.handle_error(ev) # pylint: disable=protected-access,unused-argument - def _get_rule_match_flow_msgs(self, imsi, _, __, ip_addr, ambr, rule): + def _get_rule_match_flow_msgs(self, imsi, _, __, ip_addr, ambr, rule, version): """ Returns flow add messages used for rule matching. """ rule_num = self._rule_mapper.get_or_create_rule_num(rule.id) - version = self._session_rule_version_mapper.get_version(imsi, ip_addr, - rule.id) self.logger.debug( 'Installing flow for %s with rule num %s (version %s)', imsi, rule_num, version) @@ -270,7 +268,7 @@ def _get_default_flow_msgs_for_subscriber(self, imsi, ip_addr): flows.get_add_drop_flow_msg(self._datapath, self.tbl_num, match_out, priority=Utils.DROP_PRIORITY)] - def _install_redirect_flow(self, imsi, ip_addr, rule): + def _install_redirect_flow(self, imsi, ip_addr, rule, version): pass def _install_default_flow_for_subscriber(self, imsi, ip_addr): diff --git a/lte/gateway/python/magma/pipelined/app/gy.py b/lte/gateway/python/magma/pipelined/app/gy.py index f424486c3ee2..dbe6c8364a1c 100644 --- a/lte/gateway/python/magma/pipelined/app/gy.py +++ b/lte/gateway/python/magma/pipelined/app/gy.py @@ -160,7 +160,7 @@ def _deactivate_flow_for_rule(self, imsi, ip_addr, rule_id): self._qos_mgr.remove_subscriber_qos(imsi, num) self._remove_he_flows(ip_addr, rule_id) - def _install_flow_for_rule(self, imsi, msisdn:bytes, uplink_tunnel: int, ip_addr, apn_ambr, rule): + def _install_flow_for_rule(self, imsi, msisdn:bytes, uplink_tunnel: int, ip_addr, apn_ambr, rule, version): """ Install a flow to get stats for a particular rule. Flows will match on IMSI, cookie (the rule num), in/out direction @@ -172,7 +172,7 @@ def _install_flow_for_rule(self, imsi, msisdn:bytes, uplink_tunnel: int, ip_addr rule (PolicyRule): policy rule proto """ if rule.redirect.support == rule.redirect.ENABLED: - self._install_redirect_flow(imsi, ip_addr, rule) + self._install_redirect_flow(imsi, ip_addr, rule, version) return RuleModResult.SUCCESS if not rule.flow_list: @@ -182,7 +182,7 @@ def _install_flow_for_rule(self, imsi, msisdn:bytes, uplink_tunnel: int, ip_addr flow_adds = [] try: - flow_adds = self._get_rule_match_flow_msgs(imsi, msisdn, uplink_tunnel, ip_addr, apn_ambr, rule) + flow_adds = self._get_rule_match_flow_msgs(imsi, msisdn, uplink_tunnel, ip_addr, apn_ambr, rule, version) except FlowMatchError: return RuleModResult.FAILURE @@ -215,11 +215,8 @@ def _install_default_flows(self, datapath): priority=flows.MINIMUM_PRIORITY, resubmit_table=self.next_main_table) - def _install_redirect_flow(self, imsi, ip_addr, rule): + def _install_redirect_flow(self, imsi, ip_addr, rule, version): rule_num = self._rule_mapper.get_or_create_rule_num(rule.id) - rule_version = self._session_rule_version_mapper.get_version(imsi, - ip_addr, - rule.id) # CWF generates an internal IP for redirection so ip_addr is not needed if self._setup_type == 'CWF': ip_addr_str = None @@ -233,7 +230,7 @@ def _install_redirect_flow(self, imsi, ip_addr, rule): ip_addr=ip_addr_str, rule=rule, rule_num=rule_num, - rule_version=rule_version, + rule_version=version, priority=priority) try: if self._setup_type == 'CWF': @@ -267,7 +264,7 @@ def _get_default_flow_msgs(self, datapath) -> DefaultMsgsMap: return {self.tbl_num: [msg]} - def _get_rule_match_flow_msgs(self, imsi, msisdn: bytes, uplink_tunnel: int, ip_addr, apn_ambr, rule): + def _get_rule_match_flow_msgs(self, imsi, msisdn: bytes, uplink_tunnel: int, ip_addr, apn_ambr, rule, version): """ Get flow msgs to get stats for a particular rule. Flows will match on IMSI, cookie (the rule num), in/out direction @@ -285,8 +282,6 @@ def _get_rule_match_flow_msgs(self, imsi, msisdn: bytes, uplink_tunnel: int, ip_ flow_adds = [] for flow in rule.flow_list: try: - version = self._session_rule_version_mapper.get_version(imsi, ip_addr, - rule.id) flow_adds.extend(self._get_classify_rule_flow_msgs( imsi, msisdn, uplink_tunnel, ip_addr, apn_ambr, flow, rule_num, priority, rule.qos, rule.hard_timeout, rule.id, rule.app_name, diff --git a/lte/gateway/python/magma/pipelined/app/policy_mixin.py b/lte/gateway/python/magma/pipelined/app/policy_mixin.py index 16734b89da85..bd63c8360703 100644 --- a/lte/gateway/python/magma/pipelined/app/policy_mixin.py +++ b/lte/gateway/python/magma/pipelined/app/policy_mixin.py @@ -55,7 +55,7 @@ def __init__(self, *args, **kwargs): self.proxy_controller_fut = None self.proxy_controller = None - def activate_rules(self, imsi, msisdn: bytes, uplink_tunnel: int, ip_addr, apn_ambr, dynamic_rules): + def activate_rules(self, imsi, msisdn: bytes, uplink_tunnel: int, ip_addr, apn_ambr, policies): """ Activate the flows for a subscriber based on the rules stored in Redis. During activation, a default flow may be installed for the subscriber. @@ -65,25 +65,26 @@ def activate_rules(self, imsi, msisdn: bytes, uplink_tunnel: int, ip_addr, apn_a msisdn (bytes): subscriber MSISDN uplink_tunnel(int): Tunnel ID of the subscriber session. ip_addr (string): subscriber session ipv4 address - dynamic_rules (PolicyRule []): list of dynamic rules to activate + policies (VersionedPolicies []): list of versioned policies to activate """ if self._datapath is None: self.logger.error('Datapath not initialized for adding flows') return ActivateFlowsResult( - dynamic_rule_results=[RuleModResult( - rule_id=rule.id, + policy_results=[RuleModResult( + rule_id=policy.rule.id, + version=policy.version, result=RuleModResult.FAILURE, - ) for rule in dynamic_rules], + ) for policy in policies], ) - dyn_results = [] - for rule in dynamic_rules: - res = self._install_flow_for_rule(imsi, msisdn, uplink_tunnel, ip_addr, apn_ambr, rule) - dyn_results.append(RuleModResult(rule_id=rule.id, result=res)) + policy_results = [] + for policy in policies: + res = self._install_flow_for_rule(imsi, msisdn, uplink_tunnel, ip_addr, apn_ambr, policy.rule, policy.version) + policy_results.append(RuleModResult(rule_id=policy.rule.id, version=policy.version, result=res)) # Install a base flow for when no rule is matched. self._install_default_flow_for_subscriber(imsi, ip_addr) return ActivateFlowsResult( - dynamic_rule_results=dyn_results, + policy_results=policy_results, ) def _remove_he_flows(self, ip_addr: IPAddress, rule_id: str = "", @@ -257,7 +258,7 @@ def _get_ue_specific_flow_msgs(self, requests: List[ActivateFlowsRequest]): imsi = add_flow_req.sid.id ip_addr = convert_ipv4_str_to_ip_proto(add_flow_req.ip_addr) apn_ambr = add_flow_req.apn_ambr - dynamic_rules = add_flow_req.dynamic_rules + policies = add_flow_req.policies msisdn = add_flow_req.msisdn uplink_tunnel = add_flow_req.uplink_tunnel @@ -265,14 +266,18 @@ def _get_ue_specific_flow_msgs(self, requests: List[ActivateFlowsRequest]): if msgs: msg_list.extend(msgs) - for rule in dynamic_rules: + for policy in policies: + # As the versions are managed by sessiond, save state here + self._service_manager.session_rule_version_mapper.save_version( + imsi, ip_addr, policy.rule.id, policy.version) try: - if rule.redirect.support == rule.redirect.ENABLED: + if policy.rule.redirect.support == policy.rule.redirect.ENABLED: continue - flow_adds = self._get_rule_match_flow_msgs(imsi, msisdn, uplink_tunnel, ip_addr, apn_ambr, rule) + flow_adds = self._get_rule_match_flow_msgs(imsi, msisdn, uplink_tunnel, ip_addr, apn_ambr, policy.rule, policy.version) msg_list.extend(flow_adds) except FlowMatchError: - self.logger.error("Failed to verify rule_id: %s", rule.id) + self.logger.error("Failed to verify rule_id: %s", + policy.rule.id) return {self.tbl_num: msg_list} @@ -280,11 +285,11 @@ def _process_redirection_rules(self, requests): for add_flow_req in requests: imsi = add_flow_req.sid.id ip_addr = convert_ipv4_str_to_ip_proto(add_flow_req.ip_addr) - dynamic_rules = add_flow_req.dynamic_rules + policies = add_flow_req.policies - for rule in dynamic_rules: - if rule.redirect.support == rule.redirect.ENABLED: - self._install_redirect_flow(imsi, ip_addr, rule) + for policy in policies: + if policy.rule.redirect.support == policy.rule.redirect.ENABLED: + self._install_redirect_flow(imsi, ip_addr, policy.rule, policy.version) def finish_init(self, requests): # For now just reinsert redirection rules, this is a bit of a hack but @@ -300,7 +305,7 @@ def finish_init(self, requests): @abstractmethod - def _install_flow_for_rule(self, imsi, msisdn: bytes, uplink_tunnel: int, ip_addr, apn_ambr, rule): + def _install_flow_for_rule(self, imsi, msisdn: bytes, uplink_tunnel: int, ip_addr, apn_ambr, rule, version): """ Install a flow given a rule. Subclass should implement this. @@ -323,7 +328,7 @@ def _install_default_flow_for_subscriber(self, imsi, ip_addr): raise NotImplementedError @abstractmethod - def _install_redirect_flow(self, imsi, ip_addr, rule): + def _install_redirect_flow(self, imsi, ip_addr, rule, version): """ Install a redirection flow for the subscriber. Subclass should implement this. diff --git a/lte/gateway/python/magma/pipelined/rpc_servicer.py b/lte/gateway/python/magma/pipelined/rpc_servicer.py index 3b25c81cd5cb..563be7495d7f 100644 --- a/lte/gateway/python/magma/pipelined/rpc_servicer.py +++ b/lte/gateway/python/magma/pipelined/rpc_servicer.py @@ -14,7 +14,6 @@ import logging import concurrent.futures import queue -from functools import partial from concurrent.futures import Future from typing import List, Tuple from collections import OrderedDict @@ -39,8 +38,8 @@ PdrState, UPFSessionContextState, OffendingIE, + VersionedPolicy, CauseIE) -from lte.protos.policydb_pb2 import PolicyRule from lte.protos.mobilityd_pb2 import IPAddress from lte.protos.subscriberdb_pb2 import AggregatedMaximumBitrate from lte.protos.session_manager_pb2 import RuleRecordTable @@ -214,27 +213,25 @@ def _update_version(self, request: ActivateFlowsRequest, ipv4: IPAddress): """ Update version for a given subscriber and rule. """ - for rule in request.dynamic_rules: - self._service_manager.session_rule_version_mapper.update_version( - request.sid.id, ipv4, rule.id) + for policy in request.policies: + self._service_manager.session_rule_version_mapper.save_version( + request.sid.id, ipv4, policy.rule.id, policy.version) def _remove_version(self, request: DeactivateFlowsRequest, ip_address: str): - def cleanup_redis(imsi, ip_address, rule_id, version): + def cleanup_dict(imsi, ip_address, rule_id, version): self._service_manager.session_rule_version_mapper \ .remove(imsi, ip_address, rule_id, version) + if not request.policies: + self._service_manager.session_rule_version_mapper\ + .update_all_ue_versions(request.sid.id, ip_address) + return - for rule_id in request.rule_ids: + for policy in request.policies: self._service_manager.session_rule_version_mapper \ - .update_version(request.sid.id, ip_address, - rule_id) - version = self._service_manager.session_rule_version_mapper \ - .get_version(request.sid.id, ip_address, rule_id) - - # Give it sometime to cleanup enf stats - self._loop.call_later( - self._service_config['enforcement']['poll_interval'] * 2, - partial(cleanup_redis, request.sid.id, ip_address, rule_id, - version)) + .save_version(request.sid.id, ip_address, + policy.rule_id, policy.version) + cleanup_dict(request.sid.id, ip_address, policy.rule_id, + policy.version) def _activate_flows(self, request: ActivateFlowsRequest, fut: 'Future[ActivateFlowsResult]' @@ -251,7 +248,7 @@ def _activate_flows(self, request: ActivateFlowsRequest, ret_ipv4 = self._install_flows_gx(request, ipv4) else: ret_ipv4 = self._install_flows_gy(request, ipv4) - ret.dynamic_rule_results.extend(ret_ipv4.dynamic_rule_results) + ret.policy_results.extend(ret_ipv4.policy_results) if request.ipv6_addr: ipv6 = convert_ipv6_bytes_to_ip_proto(request.ipv6_addr) self._update_ipv6_prefix_store(request.ipv6_addr) @@ -259,7 +256,7 @@ def _activate_flows(self, request: ActivateFlowsRequest, ret_ipv6 = self._install_flows_gx(request, ipv6) else: ret_ipv6 = self._install_flows_gy(request, ipv6) - ret.dynamic_rule_results.extend(ret_ipv6.dynamic_rule_results) + ret.policy_results.extend(ret_ipv6.policy_results) if request.uplink_tunnel and request.downlink_tunnel: self._update_tunnel_map_store(request.uplink_tunnel, request.downlink_tunnel) @@ -281,21 +278,21 @@ def _install_flows_gx(self, request: ActivateFlowsRequest, # Install rules in enforcement stats enforcement_stats_res = self._activate_rules_in_enforcement_stats( request.sid.id, request.msisdn, request.uplink_tunnel, ip_address, request.apn_ambr, - request.dynamic_rules) + request.policies) - failed_dynamic_rule_results = \ + failed_policies_results = \ _retrieve_failed_results(enforcement_stats_res) # Do not install any rules that failed to install in enforcement_stats. - dynamic_rules = \ - _filter_failed_dynamic_rules(request, failed_dynamic_rule_results) + policies = \ + _filter_failed_policies(request, failed_policies_results) enforcement_res = self._activate_rules_in_enforcement( request.sid.id, request.msisdn, request.uplink_tunnel, ip_address, request.apn_ambr, - dynamic_rules) + policies) # Include the failed rules from enforcement_stats in the response. - enforcement_res.dynamic_rule_results.extend( - failed_dynamic_rule_results) + enforcement_res.policy_results.extend( + failed_policies_results) return enforcement_res def _install_flows_gy(self, request: ActivateFlowsRequest, @@ -313,20 +310,20 @@ def _install_flows_gy(self, request: ActivateFlowsRequest, # Install rules in enforcement stats enforcement_stats_res = self._activate_rules_in_enforcement_stats( request.sid.id, request.msisdn, request.uplink_tunnel, ip_address, request.apn_ambr, - request.dynamic_rules) + request.policies) - failed_dynamic_rule_results = \ + failed_policies_results = \ _retrieve_failed_results(enforcement_stats_res) # Do not install any rules that failed to install in enforcement_stats. - dynamic_rules = \ - _filter_failed_dynamic_rules(request, failed_dynamic_rule_results) + policies = \ + _filter_failed_policies(request, failed_policies_results) gy_res = self._activate_rules_in_gy(request.sid.id, request.msisdn, request.uplink_tunnel, ip_address, request.apn_ambr, - dynamic_rules) + policies) # Include the failed rules from enforcement_stats in the response. - gy_res.dynamic_rule_results.extend(failed_dynamic_rule_results) + gy_res.policy_results.extend(failed_policies_results) return gy_res def _activate_rules_in_enforcement_stats(self, imsi: str, @@ -334,14 +331,14 @@ def _activate_rules_in_enforcement_stats(self, imsi: str, uplink_tunnel: int, ip_addr: IPAddress, apn_ambr: AggregatedMaximumBitrate, - dynamic_rules: List[PolicyRule] + policies: List[VersionedPolicy] ) -> ActivateFlowsResult: if not self._service_manager.is_app_enabled( EnforcementStatsController.APP_NAME): return ActivateFlowsResult() enforcement_stats_res = self._enforcement_stats.activate_rules( - imsi, msisdn, uplink_tunnel, ip_addr, apn_ambr, dynamic_rules) + imsi, msisdn, uplink_tunnel, ip_addr, apn_ambr, policies) _report_enforcement_stats_failures(enforcement_stats_res, imsi) return enforcement_stats_res @@ -349,12 +346,12 @@ def _activate_rules_in_enforcement(self, imsi: str, msisdn: bytes, uplink_tunnel: int, ip_addr: IPAddress, apn_ambr: AggregatedMaximumBitrate, - dynamic_rules: List[PolicyRule] + policies: List[VersionedPolicy] ) -> ActivateFlowsResult: # TODO: this will crash pipelined if called with both static rules # and dynamic rules at the same time enforcement_res = self._enforcer_app.activate_rules( - imsi, msisdn, uplink_tunnel, ip_addr, apn_ambr, dynamic_rules) + imsi, msisdn, uplink_tunnel, ip_addr, apn_ambr, policies) # TODO ?? Should the enforcement failure be reported per imsi session _report_enforcement_failures(enforcement_res, imsi) return enforcement_res @@ -363,11 +360,11 @@ def _activate_rules_in_gy(self, imsi: str, msisdn: bytes, uplink_tunnel: int, ip_addr: IPAddress, apn_ambr: AggregatedMaximumBitrate, - dynamic_rules: List[PolicyRule] + policies: List[VersionedPolicy] ) -> ActivateFlowsResult: gy_res = self._gy_app.activate_rules(imsi, msisdn, uplink_tunnel, ip_addr, apn_ambr, - dynamic_rules) + policies) # TODO: add metrics return gy_res @@ -415,13 +412,7 @@ def _deactivate_flows(self, request): def _deactivate_flows_gx(self, request, ip_address: IPAddress): logging.debug('Deactivating GX flows for %s', request.sid.id) - if request.rule_ids: - self._remove_version(request, ip_address) - else: - # TODO cleanup redis? - # If no rule ids are given, all flows are deactivated - self._service_manager.session_rule_version_mapper.update_version( - request.sid.id, ip_address) + self._remove_version(request, ip_address) if request.remove_default_drop_flows: self._enforcement_stats.deactivate_default_flow(request.sid.id, ip_address) @@ -431,8 +422,7 @@ def _deactivate_flows_gx(self, request, ip_address: IPAddress): def _deactivate_flows_gy(self, request, ip_address: IPAddress): logging.debug('Deactivating GY flows for %s', request.sid.id) # Only deactivate requested rules here to not affect GX - if request.rule_ids: - self._remove_version(request, ip_address) + self._remove_version(request, ip_address) self._gy_app.deactivate_rules(request.sid.id, ip_address, request.rule_ids) @@ -801,24 +791,24 @@ def _ng_tunnel_update(self, pdr_entry: PDRRuleEntry, subscriber_id: str) -> bool def _retrieve_failed_results(activate_flow_result: ActivateFlowsResult ) -> Tuple[List[RuleModResult], List[RuleModResult]]: - failed_dynamic_rule_results = \ + failed_policies_results = \ [result for result in - activate_flow_result.dynamic_rule_results if + activate_flow_result.policy_results if result.result == RuleModResult.FAILURE] - return failed_dynamic_rule_results + return failed_policies_results -def _filter_failed_dynamic_rules(request: ActivateFlowsRequest, - failed_results: List[RuleModResult] - ) -> List[PolicyRule]: - failed_dynamic_rule_ids = [result.rule_id for result in failed_results] - return [rule for rule in request.dynamic_rules if - rule.id not in failed_dynamic_rule_ids] +def _filter_failed_policies(request: ActivateFlowsRequest, + failed_results: List[RuleModResult] + ) -> List[VersionedPolicy]: + failed_policies = [result.rule_id for result in failed_results] + return [policy for policy in request.policies if + policy.rule.id not in failed_policies] def _report_enforcement_failures(activate_flow_result: ActivateFlowsResult, imsi: str): - for result in activate_flow_result.dynamic_rule_results: + for result in activate_flow_result.policy_results: if result.result == RuleModResult.SUCCESS: continue ENFORCEMENT_RULE_INSTALL_FAIL.labels(rule_id=result.rule_id, @@ -828,7 +818,7 @@ def _report_enforcement_failures(activate_flow_result: ActivateFlowsResult, def _report_enforcement_stats_failures( activate_flow_result: ActivateFlowsResult, imsi: str): - for result in activate_flow_result.dynamic_rule_results: + for result in activate_flow_result.policy_results: if result.result == RuleModResult.SUCCESS: continue ENFORCEMENT_STATS_RULE_INSTALL_FAIL.labels(rule_id=result.rule_id, diff --git a/lte/gateway/python/magma/pipelined/rule_mappers.py b/lte/gateway/python/magma/pipelined/rule_mappers.py index 203c3a194549..038a65ddd6de 100644 --- a/lte/gateway/python/magma/pipelined/rule_mappers.py +++ b/lte/gateway/python/magma/pipelined/rule_mappers.py @@ -13,7 +13,6 @@ import json import threading from collections import namedtuple -from typing import Optional from lte.protos.mobilityd_pb2 import IPAddress from magma.pipelined.imsi import encode_imsi @@ -84,25 +83,16 @@ class SessionRuleToVersionMapper: multiple threads. """ - VERSION_LIMIT = 0xFFFFFFFF # 32 bit unsigned int limit (inclusive) - def __init__(self): self._version_by_imsi_and_rule = {} self._lock = threading.Lock() # write lock - def setup_redis(self): - self._version_by_imsi_and_rule = RuleVersionDict() - - def _update_version_unsafe(self, imsi: str, ip_addr: str, rule_id: str): + def _save_version_unsafe(self, imsi: str, ip_addr: str, rule_id: str, + version): key = self._get_json_key(encode_imsi(imsi), ip_addr, rule_id) - version = self._version_by_imsi_and_rule.get(key) - if not version: - version = 0 - self._version_by_imsi_and_rule[key] = \ - (version % self.VERSION_LIMIT) + 1 - - def update_version(self, imsi: str, ip_addr: IPAddress, - rule_id: Optional[str] = None): + self._version_by_imsi_and_rule[key] = version + + def update_all_ue_versions(self, imsi: str, ip_addr: IPAddress): """ Increment the version number for a given subscriber and rule. If the rule id is not specified, then all rules for the subscriber will be @@ -114,13 +104,24 @@ def update_version(self, imsi: str, ip_addr: IPAddress, else: ip_addr_str = ip_addr.address.decode('utf-8').strip() with self._lock: - if rule_id is None: - for k, v in self._version_by_imsi_and_rule.items(): - _, imsi, ip_addr_str, _ = SubscriberRuleKey(*json.loads(k)) - if imsi == encoded_imsi and ip_addr_str == ip_addr_str: - self._version_by_imsi_and_rule[k] = v + 1 - else: - self._update_version_unsafe(imsi, ip_addr_str, rule_id) + for k, v in self._version_by_imsi_and_rule.items(): + _, imsi, ip_addr_str, _ = SubscriberRuleKey(*json.loads(k)) + if imsi == encoded_imsi and ip_addr_str == ip_addr_str: + self._version_by_imsi_and_rule[k] = v + 1 + + def save_version(self, imsi: str, ip_addr: IPAddress, + rule_id: [str], version: int): + """ + Increment the version number for a given subscriber and rule. If the + rule id is not specified, then all rules for the subscriber will be + incremented. + """ + if ip_addr is None or ip_addr.address is None: + ip_addr_str = "" + else: + ip_addr_str = ip_addr.address.decode('utf-8').strip() + with self._lock: + self._save_version_unsafe(imsi, ip_addr_str, rule_id, version) def get_version(self, imsi: str, ip_addr: IPAddress, rule_id: str) -> int: """ diff --git a/lte/gateway/python/magma/pipelined/service_manager.py b/lte/gateway/python/magma/pipelined/service_manager.py index d18e3c17b61d..2823fb6a6d66 100644 --- a/lte/gateway/python/magma/pipelined/service_manager.py +++ b/lte/gateway/python/magma/pipelined/service_manager.py @@ -533,7 +533,6 @@ def load(self): logging.warning("Pipelined waiting for redis...") time.sleep(1) self.rule_id_mapper.setup_redis() - self.session_rule_version_mapper.setup_redis() self.interface_to_prefix_mapper.setup_redis() self.tunnel_id_mapper.setup_redis() diff --git a/lte/gateway/python/magma/pipelined/tests/app/subscriber.py b/lte/gateway/python/magma/pipelined/tests/app/subscriber.py index 88c517d1ce53..79e40f5c533a 100644 --- a/lte/gateway/python/magma/pipelined/tests/app/subscriber.py +++ b/lte/gateway/python/magma/pipelined/tests/app/subscriber.py @@ -55,7 +55,7 @@ class SubscriberContext(abc.ABC): """ @abc.abstractmethod - def add_dynamic_rule(self, policy_rule): + def add_policy(self, policy): """ Adds new dynamic rule to subcriber Args: @@ -104,18 +104,18 @@ class RyuRPCSubscriberContext(SubscriberContext): def __init__(self, imsi, ip, pipelined_stub, table_id=5): self.cfg = SubContextConfig(imsi, ip, default_ambr_config, table_id) - self._dynamic_rules = [] + self._policies = [] self._pipelined_stub = pipelined_stub - def add_dynamic_rule(self, policy_rule): - self._dynamic_rules.append(policy_rule) + def add_policy(self, policy): + self._policies.append(policy) return self def _activate_subscriber_rules(self): try_grpc_call_with_retries( lambda: self._pipelined_stub.ActivateFlows( ActivateFlowsRequest(sid=SIDUtils.to_pb(self.cfg.imsi), - dynamic_rules=self._dynamic_rules)) + policies=self._policies)) ) def _deactivate_subscriber_rules(self): @@ -134,13 +134,13 @@ class RyuDirectSubscriberContext(SubscriberContext): def __init__(self, imsi, ip, enforcement_controller, table_id=5, enforcement_stats_controller=None, nuke_flows_on_exit=True): self.cfg = SubContextConfig(imsi, ip, default_ambr_config, table_id) - self._dynamic_rules = [] + self._policies = [] self._ec = enforcement_controller self._esc = enforcement_stats_controller self._nuke_flows_on_exit = nuke_flows_on_exit - def add_dynamic_rule(self, policy_rule): - self._dynamic_rules.append(policy_rule) + def add_policy(self, policy): + self._policies.append(policy) return self def _activate_subscriber_rules(self): @@ -152,7 +152,7 @@ def activate_flows(): uplink_tunnel=None, ip_addr=ip_addr, apn_ambr=default_ambr_config, - dynamic_rules=self._dynamic_rules) + policies=self._policies) if self._esc: self._esc.activate_rules( imsi=self.cfg.imsi, @@ -160,7 +160,7 @@ def activate_flows(): uplink_tunnel=None, ip_addr=ip_addr, apn_ambr=default_ambr_config, - dynamic_rules=self._dynamic_rules) + policies=self._policies) hub.joinall([hub.spawn(activate_flows)]) def _deactivate_subscriber_rules(self): diff --git a/lte/gateway/python/magma/pipelined/tests/snapshots/test_enforcement.EnforcementTableTest.test_subscriber_ipv6_policy.snapshot b/lte/gateway/python/magma/pipelined/tests/snapshots/test_enforcement.EnforcementTableTest.test_subscriber_ipv6_policy.snapshot index 1b23d9d968e5..53eac34b8e9e 100644 --- a/lte/gateway/python/magma/pipelined/tests/snapshots/test_enforcement.EnforcementTableTest.test_subscriber_ipv6_policy.snapshot +++ b/lte/gateway/python/magma/pipelined/tests/snapshots/test_enforcement.EnforcementTableTest.test_subscriber_ipv6_policy.snapshot @@ -2,5 +2,5 @@ cookie=0x0, table=mme(main_table), n_packets=0, n_bytes=0, priority=65535,ipv6,ipv6_dst=de34:431d:1bc:: actions=load:0x48c2739fd9c3->OXM_OF_METADATA[],load:0x10->NXM_NX_REG1[],resubmit(,enforcement(main_table)) cookie=0x0, table=proxy(main_table), n_packets=0, n_bytes=0, priority=1,in_port=16 actions=drop cookie=0x0, table=proxy(main_table), n_packets=0, n_bytes=0, priority=0 actions=resubmit(,middle(main_table)),set_field:0->reg0,set_field:0->reg3 - cookie=0x2, table=enforcement(main_table), n_packets=1, n_bytes=54, priority=65533,ipv6,reg1=0x1,metadata=0x48c2739fd9c3,ipv6_src=de34:431d:1bc::,ipv6_dst=f333:432::dbca actions=note:b'simple_match',set_field:0x2->reg2,set_field:0->reg4,resubmit(,enforcement_stats(main_table)),resubmit(,egress(main_table)) + cookie=0x2, table=enforcement(main_table), n_packets=1, n_bytes=54, priority=65533,ipv6,reg1=0x1,metadata=0x48c2739fd9c3,ipv6_src=de34:431d:1bc::,ipv6_dst=f333:432::dbca actions=note:b'simple_match',set_field:0x2->reg2,set_field:0x1->reg4,resubmit(,enforcement_stats(main_table)),resubmit(,egress(main_table)) cookie=0xfffffffffffffffe, table=enforcement(main_table), n_packets=0, n_bytes=0, priority=0 actions=resubmit(,enforcement_stats(main_table)),set_field:0->reg0,set_field:0->reg3 \ No newline at end of file diff --git a/lte/gateway/python/magma/pipelined/tests/snapshots/test_enforcement.EnforcementTableTest.test_subscriber_policy.snapshot b/lte/gateway/python/magma/pipelined/tests/snapshots/test_enforcement.EnforcementTableTest.test_subscriber_policy.snapshot index 94c8bb3642b4..ed1494ce9fab 100644 --- a/lte/gateway/python/magma/pipelined/tests/snapshots/test_enforcement.EnforcementTableTest.test_subscriber_policy.snapshot +++ b/lte/gateway/python/magma/pipelined/tests/snapshots/test_enforcement.EnforcementTableTest.test_subscriber_policy.snapshot @@ -2,5 +2,5 @@ cookie=0x0, table=mme(main_table), n_packets=0, n_bytes=0, priority=65535,ip,nw_dst=192.168.128.74 actions=load:0x48c2739fd9c3->OXM_OF_METADATA[],load:0x10->NXM_NX_REG1[],resubmit(,enforcement(main_table)) cookie=0x0, table=proxy(main_table), n_packets=0, n_bytes=0, priority=1,in_port=16 actions=drop cookie=0x0, table=proxy(main_table), n_packets=0, n_bytes=0, priority=0 actions=resubmit(,middle(main_table)),set_field:0->reg0,set_field:0->reg3 - cookie=0x2, table=enforcement(main_table), n_packets=256, n_bytes=8704, priority=65533,ip,reg1=0x1,metadata=0x48c2739fd9c3,nw_src=192.168.128.74,nw_dst=45.10.0.0/24 actions=note:b'simple_match',set_field:0x2->reg2,set_field:0->reg4,resubmit(,enforcement_stats(main_table)),resubmit(,egress(main_table)) + cookie=0x2, table=enforcement(main_table), n_packets=256, n_bytes=8704, priority=65533,ip,reg1=0x1,metadata=0x48c2739fd9c3,nw_src=192.168.128.74,nw_dst=45.10.0.0/24 actions=note:b'simple_match',set_field:0x2->reg2,set_field:0x1->reg4,resubmit(,enforcement_stats(main_table)),resubmit(,egress(main_table)) cookie=0xfffffffffffffffe, table=enforcement(main_table), n_packets=3840, n_bytes=130560, priority=0 actions=resubmit(,enforcement_stats(main_table)),set_field:0->reg0,set_field:0->reg3 \ No newline at end of file diff --git a/lte/gateway/python/magma/pipelined/tests/snapshots/test_enforcement.EnforcementTableTest.test_subscriber_two_policies.snapshot b/lte/gateway/python/magma/pipelined/tests/snapshots/test_enforcement.EnforcementTableTest.test_subscriber_two_policies.snapshot index c112914c5c89..b40ee1377e4e 100644 --- a/lte/gateway/python/magma/pipelined/tests/snapshots/test_enforcement.EnforcementTableTest.test_subscriber_two_policies.snapshot +++ b/lte/gateway/python/magma/pipelined/tests/snapshots/test_enforcement.EnforcementTableTest.test_subscriber_two_policies.snapshot @@ -3,5 +3,5 @@ cookie=0x0, table=proxy(main_table), n_packets=0, n_bytes=0, priority=1,in_port=16 actions=drop cookie=0x0, table=proxy(main_table), n_packets=0, n_bytes=0, priority=0 actions=resubmit(,middle(main_table)),set_field:0->reg0,set_field:0->reg3 cookie=0x3, table=enforcement(main_table), n_packets=42, n_bytes=1428, priority=65533,ip,reg1=0x10,metadata=0x5f04fb434e009,nw_src=15.0.0.0/24,nw_dst=192.168.128.74 actions=note:b'match',set_field:0x2->reg3,resubmit(,enforcement_stats(main_table)) - cookie=0x4, table=enforcement(main_table), n_packets=0, n_bytes=0, priority=65533,tcp,reg1=0x1,metadata=0x5f04fb434e009,nw_src=192.168.128.74 actions=note:b'no_match',set_field:0x4->reg2,set_field:0->reg4,resubmit(,enforcement_stats(main_table)),resubmit(,egress(main_table)) + cookie=0x4, table=enforcement(main_table), n_packets=0, n_bytes=0, priority=65533,tcp,reg1=0x1,metadata=0x5f04fb434e009,nw_src=192.168.128.74 actions=note:b'no_match',set_field:0x4->reg2,set_field:0x1->reg4,resubmit(,enforcement_stats(main_table)),resubmit(,egress(main_table)) cookie=0xfffffffffffffffe, table=enforcement(main_table), n_packets=0, n_bytes=0, priority=0 actions=resubmit(,enforcement_stats(main_table)),set_field:0->reg0,set_field:0->reg3 \ No newline at end of file diff --git a/lte/gateway/python/magma/pipelined/tests/snapshots/test_gy.GYTableTest.test_subscriber_redirect_policy.snapshot b/lte/gateway/python/magma/pipelined/tests/snapshots/test_gy.GYTableTest.test_subscriber_redirect_policy.snapshot index f8815f55eec6..cd1c883140b8 100644 --- a/lte/gateway/python/magma/pipelined/tests/snapshots/test_gy.GYTableTest.test_subscriber_redirect_policy.snapshot +++ b/lte/gateway/python/magma/pipelined/tests/snapshots/test_gy.GYTableTest.test_subscriber_redirect_policy.snapshot @@ -4,18 +4,18 @@ cookie=0x1, table=gy(main_table), priority=13,tcp,reg1=0x1,metadata=0x48c2739fd9c3,vlan_tci=0x1000/0x1000,tp_dst=80 actions=learn(table=204,priority=12,cookie=0x1,eth_type=0x800,nw_proto=6,ip_src=192.168.128.1,ip_dst=192.168.0.1,NXM_OF_TCP_DST[]=NXM_OF_TCP_SRC[],tcp_src=8080,load:NXM_OF_ETH_SRC[]->NXM_OF_ETH_DST[],load:0x48c2739fd9c3->OXM_OF_METADATA[],load:NXM_OF_IP_SRC[]->NXM_OF_IP_DST[],load:NXM_OF_IP_DST[]->NXM_OF_IP_SRC[],load:NXM_OF_TCP_DST[]->NXM_OF_TCP_SRC[]),mod_nw_src:192.168.0.1,mod_nw_dst:192.168.128.1,mod_dl_dst:00:11:22:33:44:55,mod_tp_dst:8080,strip_vlan,LOCAL cookie=0x1, table=gy(main_table), priority=13,tcp,reg1=0x1,metadata=0x48c2739fd9c3,vlan_tci=0x1000/0x1000,tp_dst=8080 actions=learn(table=204,priority=12,cookie=0x1,eth_type=0x800,nw_proto=6,ip_src=192.168.128.1,ip_dst=192.168.0.1,NXM_OF_TCP_DST[]=NXM_OF_TCP_SRC[],tcp_src=8080,load:NXM_OF_ETH_SRC[]->NXM_OF_ETH_DST[],load:0x48c2739fd9c3->OXM_OF_METADATA[],load:NXM_OF_IP_SRC[]->NXM_OF_IP_DST[],load:NXM_OF_IP_DST[]->NXM_OF_IP_SRC[],load:NXM_OF_TCP_DST[]->NXM_OF_TCP_SRC[]),mod_nw_src:192.168.0.1,mod_nw_dst:192.168.128.1,mod_dl_dst:00:11:22:33:44:55,mod_tp_dst:8080,strip_vlan,LOCAL cookie=0x1, table=gy(main_table), priority=13,tcp,reg1=0x1,metadata=0x48c2739fd9c3,vlan_tci=0x1000/0x1000,tp_dst=8008 actions=learn(table=204,priority=12,cookie=0x1,eth_type=0x800,nw_proto=6,ip_src=192.168.128.1,ip_dst=192.168.0.1,NXM_OF_TCP_DST[]=NXM_OF_TCP_SRC[],tcp_src=8080,load:NXM_OF_ETH_SRC[]->NXM_OF_ETH_DST[],load:0x48c2739fd9c3->OXM_OF_METADATA[],load:NXM_OF_IP_SRC[]->NXM_OF_IP_DST[],load:NXM_OF_IP_DST[]->NXM_OF_IP_SRC[],load:NXM_OF_TCP_DST[]->NXM_OF_TCP_SRC[]),mod_nw_src:192.168.0.1,mod_nw_dst:192.168.128.1,mod_dl_dst:00:11:22:33:44:55,mod_tp_dst:8080,strip_vlan,LOCAL - cookie=0x1, table=gy(main_table), priority=3,udp,reg1=0x1,metadata=0x48c2739fd9c3,tp_dst=53 actions=note:b'redir_test',set_field:0x1->reg2,set_field:0->reg4,resubmit(,egress(main_table)),resubmit(,enforcement_stats(main_table)),set_field:0->reg0,set_field:0->reg3 - cookie=0x1, table=gy(main_table), priority=3,tcp,reg1=0x1,metadata=0x48c2739fd9c3,tp_dst=53 actions=note:b'redir_test',set_field:0x1->reg2,set_field:0->reg4,resubmit(,egress(main_table)),resubmit(,enforcement_stats(main_table)),set_field:0->reg0,set_field:0->reg3 + cookie=0x1, table=gy(main_table), priority=3,udp,reg1=0x1,metadata=0x48c2739fd9c3,tp_dst=53 actions=note:b'redir_test',set_field:0x1->reg2,set_field:0x1->reg4,resubmit(,egress(main_table)),resubmit(,enforcement_stats(main_table)),set_field:0->reg0,set_field:0->reg3 + cookie=0x1, table=gy(main_table), priority=3,tcp,reg1=0x1,metadata=0x48c2739fd9c3,tp_dst=53 actions=note:b'redir_test',set_field:0x1->reg2,set_field:0x1->reg4,resubmit(,egress(main_table)),resubmit(,enforcement_stats(main_table)),set_field:0->reg0,set_field:0->reg3 cookie=0x1, table=gy(main_table), priority=12,tcp,reg1=0x1,metadata=0x48c2739fd9c3,tp_dst=80 actions=learn(table=204,priority=12,cookie=0x1,eth_type=0x800,nw_proto=6,ip_src=192.168.128.1,ip_dst=192.168.0.1,NXM_OF_TCP_DST[]=NXM_OF_TCP_SRC[],tcp_src=8080,load:NXM_OF_ETH_SRC[]->NXM_OF_ETH_DST[],load:0x48c2739fd9c3->OXM_OF_METADATA[],load:NXM_OF_IP_SRC[]->NXM_OF_IP_DST[],load:NXM_OF_IP_DST[]->NXM_OF_IP_SRC[],load:NXM_OF_TCP_DST[]->NXM_OF_TCP_SRC[]),mod_nw_src:192.168.0.1,mod_nw_dst:192.168.128.1,mod_dl_dst:00:11:22:33:44:55,mod_tp_dst:8080,LOCAL cookie=0x1, table=gy(main_table), priority=12,tcp,reg1=0x1,metadata=0x48c2739fd9c3,tp_dst=8080 actions=learn(table=204,priority=12,cookie=0x1,eth_type=0x800,nw_proto=6,ip_src=192.168.128.1,ip_dst=192.168.0.1,NXM_OF_TCP_DST[]=NXM_OF_TCP_SRC[],tcp_src=8080,load:NXM_OF_ETH_SRC[]->NXM_OF_ETH_DST[],load:0x48c2739fd9c3->OXM_OF_METADATA[],load:NXM_OF_IP_SRC[]->NXM_OF_IP_DST[],load:NXM_OF_IP_DST[]->NXM_OF_IP_SRC[],load:NXM_OF_TCP_DST[]->NXM_OF_TCP_SRC[]),mod_nw_src:192.168.0.1,mod_nw_dst:192.168.128.1,mod_dl_dst:00:11:22:33:44:55,mod_tp_dst:8080,LOCAL cookie=0x1, table=gy(main_table), priority=12,tcp,reg1=0x1,metadata=0x48c2739fd9c3,tp_dst=8008 actions=learn(table=204,priority=12,cookie=0x1,eth_type=0x800,nw_proto=6,ip_src=192.168.128.1,ip_dst=192.168.0.1,NXM_OF_TCP_DST[]=NXM_OF_TCP_SRC[],tcp_src=8080,load:NXM_OF_ETH_SRC[]->NXM_OF_ETH_DST[],load:0x48c2739fd9c3->OXM_OF_METADATA[],load:NXM_OF_IP_SRC[]->NXM_OF_IP_DST[],load:NXM_OF_IP_DST[]->NXM_OF_IP_SRC[],load:NXM_OF_TCP_DST[]->NXM_OF_TCP_SRC[]),mod_nw_src:192.168.0.1,mod_nw_dst:192.168.128.1,mod_dl_dst:00:11:22:33:44:55,mod_tp_dst:8080,LOCAL cookie=0x1, table=gy(main_table), priority=10,tcp,reg1=0x10,metadata=0x48c2739fd9c3,in_port=LOCAL actions=resubmit(,egress(main_table)),set_field:0->reg0,set_field:0->reg3 - cookie=0x1, table=gy(main_table), priority=4,ip,reg1=0x1,metadata=0x48c2739fd9c3,nw_dst=185.128.101.5 actions=note:b'redir_test',set_field:0x1->reg2,set_field:0->reg4,resubmit(,egress(main_table)),resubmit(,enforcement_stats(main_table)),set_field:0->reg0,set_field:0->reg3 - cookie=0x1, table=gy(main_table), priority=4,ip,reg1=0x1,metadata=0x48c2739fd9c3,nw_dst=185.128.121.4 actions=note:b'redir_test',set_field:0x1->reg2,set_field:0->reg4,resubmit(,egress(main_table)),resubmit(,enforcement_stats(main_table)),set_field:0->reg0,set_field:0->reg3 - cookie=0x1, table=gy(main_table), priority=4,ip,reg1=0x10,metadata=0x48c2739fd9c3,nw_src=185.128.101.5 actions=note:b'redir_test',set_field:0x1->reg2,set_field:0->reg4,resubmit(,egress(main_table)),resubmit(,enforcement_stats(main_table)),set_field:0->reg0,set_field:0->reg3 - cookie=0x1, table=gy(main_table), priority=4,ip,reg1=0x10,metadata=0x48c2739fd9c3,nw_src=185.128.121.4 actions=note:b'redir_test',set_field:0x1->reg2,set_field:0->reg4,resubmit(,egress(main_table)),resubmit(,enforcement_stats(main_table)),set_field:0->reg0,set_field:0->reg3 - cookie=0x1, table=gy(main_table), priority=3,udp,reg1=0x10,metadata=0x48c2739fd9c3,tp_src=53 actions=note:b'redir_test',set_field:0x1->reg2,set_field:0->reg4,resubmit(,egress(main_table)),resubmit(,enforcement_stats(main_table)),set_field:0->reg0,set_field:0->reg3 - cookie=0x1, table=gy(main_table), priority=3,tcp,reg1=0x10,metadata=0x48c2739fd9c3,tp_src=53 actions=note:b'redir_test',set_field:0x1->reg2,set_field:0->reg4,resubmit(,egress(main_table)),resubmit(,enforcement_stats(main_table)),set_field:0->reg0,set_field:0->reg3 + cookie=0x1, table=gy(main_table), priority=4,ip,reg1=0x1,metadata=0x48c2739fd9c3,nw_dst=185.128.101.5 actions=note:b'redir_test',set_field:0x1->reg2,set_field:0x1->reg4,resubmit(,egress(main_table)),resubmit(,enforcement_stats(main_table)),set_field:0->reg0,set_field:0->reg3 + cookie=0x1, table=gy(main_table), priority=4,ip,reg1=0x1,metadata=0x48c2739fd9c3,nw_dst=185.128.121.4 actions=note:b'redir_test',set_field:0x1->reg2,set_field:0x1->reg4,resubmit(,egress(main_table)),resubmit(,enforcement_stats(main_table)),set_field:0->reg0,set_field:0->reg3 + cookie=0x1, table=gy(main_table), priority=4,ip,reg1=0x10,metadata=0x48c2739fd9c3,nw_src=185.128.101.5 actions=note:b'redir_test',set_field:0x1->reg2,set_field:0x1->reg4,resubmit(,egress(main_table)),resubmit(,enforcement_stats(main_table)),set_field:0->reg0,set_field:0->reg3 + cookie=0x1, table=gy(main_table), priority=4,ip,reg1=0x10,metadata=0x48c2739fd9c3,nw_src=185.128.121.4 actions=note:b'redir_test',set_field:0x1->reg2,set_field:0x1->reg4,resubmit(,egress(main_table)),resubmit(,enforcement_stats(main_table)),set_field:0->reg0,set_field:0->reg3 + cookie=0x1, table=gy(main_table), priority=3,udp,reg1=0x10,metadata=0x48c2739fd9c3,tp_src=53 actions=note:b'redir_test',set_field:0x1->reg2,set_field:0x1->reg4,resubmit(,egress(main_table)),resubmit(,enforcement_stats(main_table)),set_field:0->reg0,set_field:0->reg3 + cookie=0x1, table=gy(main_table), priority=3,tcp,reg1=0x10,metadata=0x48c2739fd9c3,tp_src=53 actions=note:b'redir_test',set_field:0x1->reg2,set_field:0x1->reg4,resubmit(,egress(main_table)),resubmit(,enforcement_stats(main_table)),set_field:0->reg0,set_field:0->reg3 cookie=0x1, table=gy(main_table), priority=1,metadata=0x48c2739fd9c3 actions=drop table=gy(main_table), priority=0 actions=resubmit(,enforcement(main_table)),set_field:0->reg0,set_field:0->reg3 cookie=0x1, table=204, priority=12,tcp,nw_src=192.168.128.1,nw_dst=192.168.0.1,tp_src=8080,tp_dst=42132 actions=load:0x1201020aabb->NXM_OF_ETH_DST[],load:0x48c2739fd9c3->OXM_OF_METADATA[],load:0xc0a8804a->NXM_OF_IP_DST[],load:0x972a297a->NXM_OF_IP_SRC[],load:0x50->NXM_OF_TCP_SRC[] \ No newline at end of file diff --git a/lte/gateway/python/magma/pipelined/tests/snapshots/test_gy.GYTableTest.test_subscriber_restrict_policy.snapshot b/lte/gateway/python/magma/pipelined/tests/snapshots/test_gy.GYTableTest.test_subscriber_restrict_policy.snapshot index b303821d29a0..25ca0293fec0 100644 --- a/lte/gateway/python/magma/pipelined/tests/snapshots/test_gy.GYTableTest.test_subscriber_restrict_policy.snapshot +++ b/lte/gateway/python/magma/pipelined/tests/snapshots/test_gy.GYTableTest.test_subscriber_restrict_policy.snapshot @@ -1,6 +1,6 @@ priority=65535,ip,nw_src=192.168.128.74 actions=load:0x48c2739fd9c3->OXM_OF_METADATA[],load:0x1->NXM_NX_REG1[],resubmit(,gy(main_table)) priority=65535,ip,nw_dst=192.168.128.74 actions=load:0x48c2739fd9c3->OXM_OF_METADATA[],load:0x10->NXM_NX_REG1[],resubmit(,gy(main_table)) priority=10,in_port=LOCAL actions=resubmit(,204),resubmit(,ingress(main_table)),set_field:0->reg0,set_field:0->reg3 - cookie=0x2, table=gy(main_table), priority=65533,ip,reg1=0x1,metadata=0x48c2739fd9c3,nw_src=192.168.128.74,nw_dst=8.8.8.0/24 actions=note:b'restrict_match',set_field:0x2->reg2,set_field:0->reg4,resubmit(,enforcement_stats(main_table)),resubmit(,egress(main_table)) + cookie=0x2, table=gy(main_table), priority=65533,ip,reg1=0x1,metadata=0x48c2739fd9c3,nw_src=192.168.128.74,nw_dst=8.8.8.0/24 actions=note:b'restrict_match',set_field:0x2->reg2,set_field:0x1->reg4,resubmit(,enforcement_stats(main_table)),resubmit(,egress(main_table)) table=gy(main_table), priority=0 actions=resubmit(,enforcement(main_table)),set_field:0->reg0,set_field:0->reg3 cookie=0x1, table=204, priority=12,tcp,nw_src=192.168.128.1,nw_dst=192.168.0.1,tp_src=8080,tp_dst=42132 actions=load:0x1201020aabb->NXM_OF_ETH_DST[],load:0x48c2739fd9c3->OXM_OF_METADATA[],load:0xc0a8804a->NXM_OF_IP_DST[],load:0x972a297a->NXM_OF_IP_SRC[],load:0x50->NXM_OF_TCP_SRC[] \ No newline at end of file diff --git a/lte/gateway/python/magma/pipelined/tests/snapshots/test_he.EnforcementTableHeTest.test_subscriber_policy_with_he.snapshot b/lte/gateway/python/magma/pipelined/tests/snapshots/test_he.EnforcementTableHeTest.test_subscriber_policy_with_he.snapshot index 982e36fd9577..3bf52189b62b 100644 --- a/lte/gateway/python/magma/pipelined/tests/snapshots/test_he.EnforcementTableHeTest.test_subscriber_policy_with_he.snapshot +++ b/lte/gateway/python/magma/pipelined/tests/snapshots/test_he.EnforcementTableHeTest.test_subscriber_policy_with_he.snapshot @@ -8,5 +8,5 @@ cookie=0x1, table=proxy(main_table), n_packets=0, n_bytes=0, priority=10,tcp,reg10=0,nw_src=192.168.128.74,nw_dst=45.10.0.0/24,tp_dst=80 actions=set_field:0x1->reg1,set_field:0x1->reg6,set_field:0x1->reg10,resubmit(,middle(main_table)) cookie=0x0, table=proxy(main_table), n_packets=0, n_bytes=0, priority=1,in_port=16 actions=drop cookie=0x0, table=proxy(main_table), n_packets=0, n_bytes=0, priority=0 actions=resubmit(,middle(main_table)),set_field:0->reg0,set_field:0->reg3 - cookie=0x1, table=enforcement(main_table), n_packets=0, n_bytes=0, priority=65533,ip,reg1=0x1,metadata=0x48c2739fd9c3,nw_src=192.168.128.74,nw_dst=45.10.0.0/24 actions=note:b'simple_match',set_field:0x1->reg2,set_field:0->reg4,resubmit(,enforcement_stats(main_table)),resubmit(,egress(main_table)) + cookie=0x1, table=enforcement(main_table), n_packets=0, n_bytes=0, priority=65533,ip,reg1=0x1,metadata=0x48c2739fd9c3,nw_src=192.168.128.74,nw_dst=45.10.0.0/24 actions=note:b'simple_match',set_field:0x1->reg2,set_field:0x1->reg4,resubmit(,enforcement_stats(main_table)),resubmit(,egress(main_table)) cookie=0xfffffffffffffffe, table=enforcement(main_table), n_packets=0, n_bytes=0, priority=0 actions=resubmit(,enforcement_stats(main_table)),set_field:0->reg0,set_field:0->reg3 \ No newline at end of file diff --git a/lte/gateway/python/magma/pipelined/tests/snapshots/test_redirect.RedirectTest.test_ipv4_redirect.snapshot b/lte/gateway/python/magma/pipelined/tests/snapshots/test_redirect.RedirectTest.test_ipv4_redirect.snapshot index 6ef198cab2d4..d4c71644ff86 100644 --- a/lte/gateway/python/magma/pipelined/tests/snapshots/test_redirect.RedirectTest.test_ipv4_redirect.snapshot +++ b/lte/gateway/python/magma/pipelined/tests/snapshots/test_redirect.RedirectTest.test_ipv4_redirect.snapshot @@ -1,14 +1,14 @@ cookie=0x0, table=mme(main_table), n_packets=1, n_bytes=54, priority=65535,ip,nw_src=192.168.128.74 actions=load:0x574fbdf0d9c3->OXM_OF_METADATA[],load:0x1->NXM_NX_REG1[],resubmit(,enforcement(main_table)) cookie=0x0, table=mme(main_table), n_packets=0, n_bytes=0, priority=65535,ip,nw_dst=192.168.128.74 actions=load:0x574fbdf0d9c3->OXM_OF_METADATA[],load:0x10->NXM_NX_REG1[],resubmit(,enforcement(main_table)) - cookie=0x1, table=enforcement(main_table), n_packets=0, n_bytes=0, priority=65533,ip,reg1=0x1,metadata=0x574fbdf0d9c3,nw_src=192.168.128.74,nw_dst=54.12.31.42 actions=note:b'redir_ip_test',set_field:0x1->reg2,set_field:0->reg4,resubmit(,egress(main_table)),resubmit(,enforcement_stats(main_table)),set_field:0->reg0,set_field:0->reg3 - cookie=0x1, table=enforcement(main_table), n_packets=0, n_bytes=0, priority=65533,ip,reg1=0x10,metadata=0x574fbdf0d9c3,nw_src=54.12.31.42,nw_dst=192.168.128.74 actions=note:b'redir_ip_test',set_field:0x1->reg2,set_field:0->reg4,resubmit(,egress(main_table)),resubmit(,enforcement_stats(main_table)),set_field:0->reg0,set_field:0->reg3 - cookie=0x1, table=enforcement(main_table), n_packets=0, n_bytes=0, priority=65532,udp,reg1=0x10,metadata=0x574fbdf0d9c3,tp_src=53 actions=note:b'redir_ip_test',set_field:0x1->reg2,set_field:0->reg4,resubmit(,egress(main_table)),resubmit(,enforcement_stats(main_table)),set_field:0->reg0,set_field:0->reg3 - cookie=0x1, table=enforcement(main_table), n_packets=0, n_bytes=0, priority=65532,tcp,reg1=0x10,metadata=0x574fbdf0d9c3,tp_src=53 actions=note:b'redir_ip_test',set_field:0x1->reg2,set_field:0->reg4,resubmit(,egress(main_table)),resubmit(,enforcement_stats(main_table)),set_field:0->reg0,set_field:0->reg3 - cookie=0x1, table=enforcement(main_table), n_packets=0, n_bytes=0, priority=65532,udp,reg1=0x1,metadata=0x574fbdf0d9c3,tp_dst=53 actions=note:b'redir_ip_test',set_field:0x1->reg2,set_field:0->reg4,resubmit(,egress(main_table)),resubmit(,enforcement_stats(main_table)),set_field:0->reg0,set_field:0->reg3 - cookie=0x1, table=enforcement(main_table), n_packets=0, n_bytes=0, priority=65532,tcp,reg1=0x1,metadata=0x574fbdf0d9c3,tp_dst=53 actions=note:b'redir_ip_test',set_field:0x1->reg2,set_field:0->reg4,resubmit(,egress(main_table)),resubmit(,enforcement_stats(main_table)),set_field:0->reg0,set_field:0->reg3 + cookie=0x1, table=enforcement(main_table), n_packets=0, n_bytes=0, priority=65533,ip,reg1=0x1,metadata=0x574fbdf0d9c3,nw_src=192.168.128.74,nw_dst=54.12.31.42 actions=note:b'redir_ip_test',set_field:0x1->reg2,set_field:0x1->reg4,resubmit(,egress(main_table)),resubmit(,enforcement_stats(main_table)),set_field:0->reg0,set_field:0->reg3 + cookie=0x1, table=enforcement(main_table), n_packets=0, n_bytes=0, priority=65533,ip,reg1=0x10,metadata=0x574fbdf0d9c3,nw_src=54.12.31.42,nw_dst=192.168.128.74 actions=note:b'redir_ip_test',set_field:0x1->reg2,set_field:0x1->reg4,resubmit(,egress(main_table)),resubmit(,enforcement_stats(main_table)),set_field:0->reg0,set_field:0->reg3 + cookie=0x1, table=enforcement(main_table), n_packets=0, n_bytes=0, priority=65532,udp,reg1=0x10,metadata=0x574fbdf0d9c3,tp_src=53 actions=note:b'redir_ip_test',set_field:0x1->reg2,set_field:0x1->reg4,resubmit(,egress(main_table)),resubmit(,enforcement_stats(main_table)),set_field:0->reg0,set_field:0->reg3 + cookie=0x1, table=enforcement(main_table), n_packets=0, n_bytes=0, priority=65532,tcp,reg1=0x10,metadata=0x574fbdf0d9c3,tp_src=53 actions=note:b'redir_ip_test',set_field:0x1->reg2,set_field:0x1->reg4,resubmit(,egress(main_table)),resubmit(,enforcement_stats(main_table)),set_field:0->reg0,set_field:0->reg3 + cookie=0x1, table=enforcement(main_table), n_packets=0, n_bytes=0, priority=65532,udp,reg1=0x1,metadata=0x574fbdf0d9c3,tp_dst=53 actions=note:b'redir_ip_test',set_field:0x1->reg2,set_field:0x1->reg4,resubmit(,egress(main_table)),resubmit(,enforcement_stats(main_table)),set_field:0->reg0,set_field:0->reg3 + cookie=0x1, table=enforcement(main_table), n_packets=0, n_bytes=0, priority=65532,tcp,reg1=0x1,metadata=0x574fbdf0d9c3,tp_dst=53 actions=note:b'redir_ip_test',set_field:0x1->reg2,set_field:0x1->reg4,resubmit(,egress(main_table)),resubmit(,enforcement_stats(main_table)),set_field:0->reg0,set_field:0->reg3 cookie=0x1, table=enforcement(main_table), n_packets=1, n_bytes=54, priority=65532,ip,reg0=0,reg1=0x1,metadata=0x574fbdf0d9c3,nw_src=192.168.128.74 actions=note:b'redir_ip_test',resubmit(,enforcement(scratch_table_0)) cookie=0x1, table=enforcement(main_table), n_packets=1, n_bytes=54, priority=65532,ip,reg0=0x1,reg1=0x1,metadata=0x574fbdf0d9c3,nw_src=192.168.128.74 actions=note:b'redir_ip_test',resubmit(,egress(main_table)),resubmit(,enforcement_stats(main_table)),set_field:0->reg0,set_field:0->reg3 cookie=0x1, table=enforcement(main_table), n_packets=0, n_bytes=0, priority=65532,tcp,reg1=0x10,metadata=0x574fbdf0d9c3,nw_src=192.168.128.1,nw_dst=192.168.128.74,tp_src=8080,tp_dst=42132 actions=load:0x972a297a->NXM_OF_IP_SRC[],load:0x50->NXM_OF_TCP_SRC[],LOCAL cookie=0xfffffffffffffffe, table=enforcement(main_table), n_packets=0, n_bytes=0, priority=0 actions=resubmit(,enforcement_stats(main_table)),set_field:0->reg0,set_field:0->reg3 - cookie=0x1, table=enforcement(scratch_table_0), n_packets=1, n_bytes=54, priority=65532,tcp,reg1=0x1,metadata=0x574fbdf0d9c3,tp_dst=80 actions=learn(table=enforcement(main_table),priority=65532,cookie=0x1,eth_type=0x800,nw_proto=6,reg1=0x10,ip_src=192.168.128.1,NXM_OF_IP_DST[]=NXM_OF_IP_SRC[],NXM_OF_TCP_DST[]=NXM_OF_TCP_SRC[],tcp_src=8080,metadata=0x574fbdf0d9c3,load:NXM_OF_IP_DST[]->NXM_OF_IP_SRC[],load:0x50->NXM_OF_TCP_SRC[],output:OXM_OF_IN_PORT[0..15]),set_field:0x1->reg0,mod_nw_dst:192.168.128.1,mod_tp_dst:8080,note:b'redir_ip_test',set_field:0x1->reg2,set_field:0->reg4,resubmit(,enforcement(main_table)) + cookie=0x1, table=enforcement(scratch_table_0), n_packets=1, n_bytes=54, priority=65532,tcp,reg1=0x1,metadata=0x574fbdf0d9c3,tp_dst=80 actions=learn(table=enforcement(main_table),priority=65532,cookie=0x1,eth_type=0x800,nw_proto=6,reg1=0x10,ip_src=192.168.128.1,NXM_OF_IP_DST[]=NXM_OF_IP_SRC[],NXM_OF_TCP_DST[]=NXM_OF_TCP_SRC[],tcp_src=8080,metadata=0x574fbdf0d9c3,load:NXM_OF_IP_DST[]->NXM_OF_IP_SRC[],load:0x50->NXM_OF_TCP_SRC[],output:OXM_OF_IN_PORT[0..15]),set_field:0x1->reg0,mod_nw_dst:192.168.128.1,mod_tp_dst:8080,note:b'redir_ip_test',set_field:0x1->reg2,set_field:0x1->reg4,resubmit(,enforcement(main_table)) cookie=0x1, table=enforcement(scratch_table_0), n_packets=0, n_bytes=0, priority=1,metadata=0x574fbdf0d9c3 actions=drop \ No newline at end of file diff --git a/lte/gateway/python/magma/pipelined/tests/snapshots/test_redirect.RedirectTest.test_url_redirect.snapshot b/lte/gateway/python/magma/pipelined/tests/snapshots/test_redirect.RedirectTest.test_url_redirect.snapshot index 17084747e472..1c2f348c1e35 100644 --- a/lte/gateway/python/magma/pipelined/tests/snapshots/test_redirect.RedirectTest.test_url_redirect.snapshot +++ b/lte/gateway/python/magma/pipelined/tests/snapshots/test_redirect.RedirectTest.test_url_redirect.snapshot @@ -1,16 +1,16 @@ cookie=0x0, table=mme(main_table), n_packets=1, n_bytes=54, priority=65535,ip,nw_src=192.168.128.74 actions=load:0x48c2739fd9c3->OXM_OF_METADATA[],load:0x1->NXM_NX_REG1[],resubmit(,enforcement(main_table)) cookie=0x0, table=mme(main_table), n_packets=0, n_bytes=0, priority=65535,ip,nw_dst=192.168.128.74 actions=load:0x48c2739fd9c3->OXM_OF_METADATA[],load:0x10->NXM_NX_REG1[],resubmit(,enforcement(main_table)) - cookie=0x2, table=enforcement(main_table), n_packets=0, n_bytes=0, priority=65533,ip,reg1=0x1,metadata=0x48c2739fd9c3,nw_src=192.168.128.74,nw_dst=185.128.101.5 actions=note:b'redir_test',set_field:0x2->reg2,set_field:0->reg4,resubmit(,egress(main_table)),resubmit(,enforcement_stats(main_table)),set_field:0->reg0,set_field:0->reg3 - cookie=0x2, table=enforcement(main_table), n_packets=0, n_bytes=0, priority=65533,ip,reg1=0x10,metadata=0x48c2739fd9c3,nw_src=185.128.101.5,nw_dst=192.168.128.74 actions=note:b'redir_test',set_field:0x2->reg2,set_field:0->reg4,resubmit(,egress(main_table)),resubmit(,enforcement_stats(main_table)),set_field:0->reg0,set_field:0->reg3 - cookie=0x2, table=enforcement(main_table), n_packets=0, n_bytes=0, priority=65533,ip,reg1=0x1,metadata=0x48c2739fd9c3,nw_src=192.168.128.74,nw_dst=185.128.121.4 actions=note:b'redir_test',set_field:0x2->reg2,set_field:0->reg4,resubmit(,egress(main_table)),resubmit(,enforcement_stats(main_table)),set_field:0->reg0,set_field:0->reg3 - cookie=0x2, table=enforcement(main_table), n_packets=0, n_bytes=0, priority=65533,ip,reg1=0x10,metadata=0x48c2739fd9c3,nw_src=185.128.121.4,nw_dst=192.168.128.74 actions=note:b'redir_test',set_field:0x2->reg2,set_field:0->reg4,resubmit(,egress(main_table)),resubmit(,enforcement_stats(main_table)),set_field:0->reg0,set_field:0->reg3 - cookie=0x2, table=enforcement(main_table), n_packets=0, n_bytes=0, priority=65532,udp,reg1=0x10,metadata=0x48c2739fd9c3,tp_src=53 actions=note:b'redir_test',set_field:0x2->reg2,set_field:0->reg4,resubmit(,egress(main_table)),resubmit(,enforcement_stats(main_table)),set_field:0->reg0,set_field:0->reg3 - cookie=0x2, table=enforcement(main_table), n_packets=0, n_bytes=0, priority=65532,tcp,reg1=0x10,metadata=0x48c2739fd9c3,tp_src=53 actions=note:b'redir_test',set_field:0x2->reg2,set_field:0->reg4,resubmit(,egress(main_table)),resubmit(,enforcement_stats(main_table)),set_field:0->reg0,set_field:0->reg3 - cookie=0x2, table=enforcement(main_table), n_packets=0, n_bytes=0, priority=65532,udp,reg1=0x1,metadata=0x48c2739fd9c3,tp_dst=53 actions=note:b'redir_test',set_field:0x2->reg2,set_field:0->reg4,resubmit(,egress(main_table)),resubmit(,enforcement_stats(main_table)),set_field:0->reg0,set_field:0->reg3 - cookie=0x2, table=enforcement(main_table), n_packets=0, n_bytes=0, priority=65532,tcp,reg1=0x1,metadata=0x48c2739fd9c3,tp_dst=53 actions=note:b'redir_test',set_field:0x2->reg2,set_field:0->reg4,resubmit(,egress(main_table)),resubmit(,enforcement_stats(main_table)),set_field:0->reg0,set_field:0->reg3 + cookie=0x2, table=enforcement(main_table), n_packets=0, n_bytes=0, priority=65533,ip,reg1=0x1,metadata=0x48c2739fd9c3,nw_src=192.168.128.74,nw_dst=185.128.101.5 actions=note:b'redir_test',set_field:0x2->reg2,set_field:0x1->reg4,resubmit(,egress(main_table)),resubmit(,enforcement_stats(main_table)),set_field:0->reg0,set_field:0->reg3 + cookie=0x2, table=enforcement(main_table), n_packets=0, n_bytes=0, priority=65533,ip,reg1=0x10,metadata=0x48c2739fd9c3,nw_src=185.128.101.5,nw_dst=192.168.128.74 actions=note:b'redir_test',set_field:0x2->reg2,set_field:0x1->reg4,resubmit(,egress(main_table)),resubmit(,enforcement_stats(main_table)),set_field:0->reg0,set_field:0->reg3 + cookie=0x2, table=enforcement(main_table), n_packets=0, n_bytes=0, priority=65533,ip,reg1=0x1,metadata=0x48c2739fd9c3,nw_src=192.168.128.74,nw_dst=185.128.121.4 actions=note:b'redir_test',set_field:0x2->reg2,set_field:0x1->reg4,resubmit(,egress(main_table)),resubmit(,enforcement_stats(main_table)),set_field:0->reg0,set_field:0->reg3 + cookie=0x2, table=enforcement(main_table), n_packets=0, n_bytes=0, priority=65533,ip,reg1=0x10,metadata=0x48c2739fd9c3,nw_src=185.128.121.4,nw_dst=192.168.128.74 actions=note:b'redir_test',set_field:0x2->reg2,set_field:0x1->reg4,resubmit(,egress(main_table)),resubmit(,enforcement_stats(main_table)),set_field:0->reg0,set_field:0->reg3 + cookie=0x2, table=enforcement(main_table), n_packets=0, n_bytes=0, priority=65532,udp,reg1=0x10,metadata=0x48c2739fd9c3,tp_src=53 actions=note:b'redir_test',set_field:0x2->reg2,set_field:0x1->reg4,resubmit(,egress(main_table)),resubmit(,enforcement_stats(main_table)),set_field:0->reg0,set_field:0->reg3 + cookie=0x2, table=enforcement(main_table), n_packets=0, n_bytes=0, priority=65532,tcp,reg1=0x10,metadata=0x48c2739fd9c3,tp_src=53 actions=note:b'redir_test',set_field:0x2->reg2,set_field:0x1->reg4,resubmit(,egress(main_table)),resubmit(,enforcement_stats(main_table)),set_field:0->reg0,set_field:0->reg3 + cookie=0x2, table=enforcement(main_table), n_packets=0, n_bytes=0, priority=65532,udp,reg1=0x1,metadata=0x48c2739fd9c3,tp_dst=53 actions=note:b'redir_test',set_field:0x2->reg2,set_field:0x1->reg4,resubmit(,egress(main_table)),resubmit(,enforcement_stats(main_table)),set_field:0->reg0,set_field:0->reg3 + cookie=0x2, table=enforcement(main_table), n_packets=0, n_bytes=0, priority=65532,tcp,reg1=0x1,metadata=0x48c2739fd9c3,tp_dst=53 actions=note:b'redir_test',set_field:0x2->reg2,set_field:0x1->reg4,resubmit(,egress(main_table)),resubmit(,enforcement_stats(main_table)),set_field:0->reg0,set_field:0->reg3 cookie=0x2, table=enforcement(main_table), n_packets=1, n_bytes=54, priority=65532,ip,reg0=0,reg1=0x1,metadata=0x48c2739fd9c3,nw_src=192.168.128.74 actions=note:b'redir_test',resubmit(,enforcement(scratch_table_0)) cookie=0x2, table=enforcement(main_table), n_packets=1, n_bytes=54, priority=65532,ip,reg0=0x1,reg1=0x1,metadata=0x48c2739fd9c3,nw_src=192.168.128.74 actions=note:b'redir_test',resubmit(,egress(main_table)),resubmit(,enforcement_stats(main_table)),set_field:0->reg0,set_field:0->reg3 cookie=0x2, table=enforcement(main_table), n_packets=0, n_bytes=0, priority=65532,tcp,reg1=0x10,metadata=0x48c2739fd9c3,nw_src=192.168.128.1,nw_dst=192.168.128.74,tp_src=8080,tp_dst=42132 actions=load:0x972a297a->NXM_OF_IP_SRC[],load:0x50->NXM_OF_TCP_SRC[],LOCAL cookie=0xfffffffffffffffe, table=enforcement(main_table), n_packets=0, n_bytes=0, priority=0 actions=resubmit(,enforcement_stats(main_table)),set_field:0->reg0,set_field:0->reg3 - cookie=0x2, table=enforcement(scratch_table_0), n_packets=1, n_bytes=54, priority=65532,tcp,reg1=0x1,metadata=0x48c2739fd9c3,tp_dst=80 actions=learn(table=enforcement(main_table),priority=65532,cookie=0x2,eth_type=0x800,nw_proto=6,reg1=0x10,ip_src=192.168.128.1,NXM_OF_IP_DST[]=NXM_OF_IP_SRC[],NXM_OF_TCP_DST[]=NXM_OF_TCP_SRC[],tcp_src=8080,metadata=0x48c2739fd9c3,load:NXM_OF_IP_DST[]->NXM_OF_IP_SRC[],load:0x50->NXM_OF_TCP_SRC[],output:OXM_OF_IN_PORT[0..15]),set_field:0x1->reg0,mod_nw_dst:192.168.128.1,mod_tp_dst:8080,note:b'redir_test',set_field:0x2->reg2,set_field:0->reg4,resubmit(,enforcement(main_table)) + cookie=0x2, table=enforcement(scratch_table_0), n_packets=1, n_bytes=54, priority=65532,tcp,reg1=0x1,metadata=0x48c2739fd9c3,tp_dst=80 actions=learn(table=enforcement(main_table),priority=65532,cookie=0x2,eth_type=0x800,nw_proto=6,reg1=0x10,ip_src=192.168.128.1,NXM_OF_IP_DST[]=NXM_OF_IP_SRC[],NXM_OF_TCP_DST[]=NXM_OF_TCP_SRC[],tcp_src=8080,metadata=0x48c2739fd9c3,load:NXM_OF_IP_DST[]->NXM_OF_IP_SRC[],load:0x50->NXM_OF_TCP_SRC[],output:OXM_OF_IN_PORT[0..15]),set_field:0x1->reg0,mod_nw_dst:192.168.128.1,mod_tp_dst:8080,note:b'redir_test',set_field:0x2->reg2,set_field:0x1->reg4,resubmit(,enforcement(main_table)) cookie=0x2, table=enforcement(scratch_table_0), n_packets=0, n_bytes=0, priority=1,metadata=0x48c2739fd9c3 actions=drop \ No newline at end of file diff --git a/lte/gateway/python/magma/pipelined/tests/snapshots/test_restart_resilience.RestartResilienceTest.test_url_redirect.snapshot b/lte/gateway/python/magma/pipelined/tests/snapshots/test_restart_resilience.RestartResilienceTest.test_url_redirect.snapshot index 35e8ee29cf40..717f763d333b 100644 --- a/lte/gateway/python/magma/pipelined/tests/snapshots/test_restart_resilience.RestartResilienceTest.test_url_redirect.snapshot +++ b/lte/gateway/python/magma/pipelined/tests/snapshots/test_restart_resilience.RestartResilienceTest.test_url_redirect.snapshot @@ -1,13 +1,13 @@ cookie=0x0, table=mme(main_table), n_packets=1, n_bytes=54, priority=65535,ip,nw_src=192.168.128.74 actions=load:0x48c2739fd9c3->OXM_OF_METADATA[],load:0x1->NXM_NX_REG1[],resubmit(,enforcement(main_table)) cookie=0x0, table=mme(main_table), n_packets=0, n_bytes=0, priority=65535,ip,nw_dst=192.168.128.74 actions=load:0x48c2739fd9c3->OXM_OF_METADATA[],load:0x10->NXM_NX_REG1[],resubmit(,enforcement(main_table)) - cookie=0x6, table=enforcement(main_table), n_packets=0, n_bytes=0, priority=65533,ip,reg1=0x1,metadata=0x48c2739fd9c3,nw_src=192.168.128.74,nw_dst=185.128.101.5 actions=note:b'redir_test',set_field:0x6->reg2,set_field:0->reg4,resubmit(,egress(main_table)),resubmit(,enforcement_stats(main_table)),set_field:0->reg0,set_field:0->reg3 - cookie=0x6, table=enforcement(main_table), n_packets=0, n_bytes=0, priority=65533,ip,reg1=0x10,metadata=0x48c2739fd9c3,nw_src=185.128.101.5,nw_dst=192.168.128.74 actions=note:b'redir_test',set_field:0x6->reg2,set_field:0->reg4,resubmit(,egress(main_table)),resubmit(,enforcement_stats(main_table)),set_field:0->reg0,set_field:0->reg3 - cookie=0x6, table=enforcement(main_table), n_packets=0, n_bytes=0, priority=65533,ip,reg1=0x1,metadata=0x48c2739fd9c3,nw_src=192.168.128.74,nw_dst=185.128.121.4 actions=note:b'redir_test',set_field:0x6->reg2,set_field:0->reg4,resubmit(,egress(main_table)),resubmit(,enforcement_stats(main_table)),set_field:0->reg0,set_field:0->reg3 - cookie=0x6, table=enforcement(main_table), n_packets=0, n_bytes=0, priority=65533,ip,reg1=0x10,metadata=0x48c2739fd9c3,nw_src=185.128.121.4,nw_dst=192.168.128.74 actions=note:b'redir_test',set_field:0x6->reg2,set_field:0->reg4,resubmit(,egress(main_table)),resubmit(,enforcement_stats(main_table)),set_field:0->reg0,set_field:0->reg3 - cookie=0x6, table=enforcement(main_table), n_packets=0, n_bytes=0, priority=65532,udp,reg1=0x10,metadata=0x48c2739fd9c3,tp_src=53 actions=note:b'redir_test',set_field:0x6->reg2,set_field:0->reg4,resubmit(,egress(main_table)),resubmit(,enforcement_stats(main_table)),set_field:0->reg0,set_field:0->reg3 - cookie=0x6, table=enforcement(main_table), n_packets=0, n_bytes=0, priority=65532,tcp,reg1=0x10,metadata=0x48c2739fd9c3,tp_src=53 actions=note:b'redir_test',set_field:0x6->reg2,set_field:0->reg4,resubmit(,egress(main_table)),resubmit(,enforcement_stats(main_table)),set_field:0->reg0,set_field:0->reg3 - cookie=0x6, table=enforcement(main_table), n_packets=0, n_bytes=0, priority=65532,udp,reg1=0x1,metadata=0x48c2739fd9c3,tp_dst=53 actions=note:b'redir_test',set_field:0x6->reg2,set_field:0->reg4,resubmit(,egress(main_table)),resubmit(,enforcement_stats(main_table)),set_field:0->reg0,set_field:0->reg3 - cookie=0x6, table=enforcement(main_table), n_packets=0, n_bytes=0, priority=65532,tcp,reg1=0x1,metadata=0x48c2739fd9c3,tp_dst=53 actions=note:b'redir_test',set_field:0x6->reg2,set_field:0->reg4,resubmit(,egress(main_table)),resubmit(,enforcement_stats(main_table)),set_field:0->reg0,set_field:0->reg3 + cookie=0x6, table=enforcement(main_table), n_packets=0, n_bytes=0, priority=65533,ip,reg1=0x1,metadata=0x48c2739fd9c3,nw_src=192.168.128.74,nw_dst=185.128.101.5 actions=note:b'redir_test',set_field:0x6->reg2,set_field:0x1->reg4,resubmit(,egress(main_table)),resubmit(,enforcement_stats(main_table)),set_field:0->reg0,set_field:0->reg3 + cookie=0x6, table=enforcement(main_table), n_packets=0, n_bytes=0, priority=65533,ip,reg1=0x10,metadata=0x48c2739fd9c3,nw_src=185.128.101.5,nw_dst=192.168.128.74 actions=note:b'redir_test',set_field:0x6->reg2,set_field:0x1->reg4,resubmit(,egress(main_table)),resubmit(,enforcement_stats(main_table)),set_field:0->reg0,set_field:0->reg3 + cookie=0x6, table=enforcement(main_table), n_packets=0, n_bytes=0, priority=65533,ip,reg1=0x1,metadata=0x48c2739fd9c3,nw_src=192.168.128.74,nw_dst=185.128.121.4 actions=note:b'redir_test',set_field:0x6->reg2,set_field:0x1->reg4,resubmit(,egress(main_table)),resubmit(,enforcement_stats(main_table)),set_field:0->reg0,set_field:0->reg3 + cookie=0x6, table=enforcement(main_table), n_packets=0, n_bytes=0, priority=65533,ip,reg1=0x10,metadata=0x48c2739fd9c3,nw_src=185.128.121.4,nw_dst=192.168.128.74 actions=note:b'redir_test',set_field:0x6->reg2,set_field:0x1->reg4,resubmit(,egress(main_table)),resubmit(,enforcement_stats(main_table)),set_field:0->reg0,set_field:0->reg3 + cookie=0x6, table=enforcement(main_table), n_packets=0, n_bytes=0, priority=65532,udp,reg1=0x10,metadata=0x48c2739fd9c3,tp_src=53 actions=note:b'redir_test',set_field:0x6->reg2,set_field:0x1->reg4,resubmit(,egress(main_table)),resubmit(,enforcement_stats(main_table)),set_field:0->reg0,set_field:0->reg3 + cookie=0x6, table=enforcement(main_table), n_packets=0, n_bytes=0, priority=65532,tcp,reg1=0x10,metadata=0x48c2739fd9c3,tp_src=53 actions=note:b'redir_test',set_field:0x6->reg2,set_field:0x1->reg4,resubmit(,egress(main_table)),resubmit(,enforcement_stats(main_table)),set_field:0->reg0,set_field:0->reg3 + cookie=0x6, table=enforcement(main_table), n_packets=0, n_bytes=0, priority=65532,udp,reg1=0x1,metadata=0x48c2739fd9c3,tp_dst=53 actions=note:b'redir_test',set_field:0x6->reg2,set_field:0x1->reg4,resubmit(,egress(main_table)),resubmit(,enforcement_stats(main_table)),set_field:0->reg0,set_field:0->reg3 + cookie=0x6, table=enforcement(main_table), n_packets=0, n_bytes=0, priority=65532,tcp,reg1=0x1,metadata=0x48c2739fd9c3,tp_dst=53 actions=note:b'redir_test',set_field:0x6->reg2,set_field:0x1->reg4,resubmit(,egress(main_table)),resubmit(,enforcement_stats(main_table)),set_field:0->reg0,set_field:0->reg3 cookie=0x6, table=enforcement(main_table), n_packets=1, n_bytes=54, priority=65532,ip,reg0=0,reg1=0x1,metadata=0x48c2739fd9c3,nw_src=192.168.128.74 actions=note:b'redir_test',resubmit(,enforcement(scratch_table_0)) cookie=0x6, table=enforcement(main_table), n_packets=1, n_bytes=54, priority=65532,ip,reg0=0x1,reg1=0x1,metadata=0x48c2739fd9c3,nw_src=192.168.128.74 actions=note:b'redir_test',resubmit(,egress(main_table)),resubmit(,enforcement_stats(main_table)),set_field:0->reg0,set_field:0->reg3 cookie=0x6, table=enforcement(main_table), n_packets=0, n_bytes=0, priority=65532,tcp,reg1=0x10,metadata=0x48c2739fd9c3,nw_src=192.168.128.1,nw_dst=192.168.128.74,tp_src=8080,tp_dst=42132 actions=load:0x972a297a->NXM_OF_IP_SRC[],load:0x50->NXM_OF_TCP_SRC[],LOCAL @@ -15,5 +15,5 @@ cookie=0x0, table=enforcement_stats(main_table), n_packets=0, n_bytes=0, priority=1,ip,reg1=0x10,reg2=0,reg4=0,metadata=0x48c2739fd9c3,nw_dst=192.168.128.74 actions=drop cookie=0x0, table=enforcement_stats(main_table), n_packets=0, n_bytes=0, priority=1,ip,reg1=0x1,reg2=0,reg4=0,metadata=0x48c2739fd9c3,nw_src=192.168.128.74 actions=drop cookie=0xfffffffffffffffe, table=enforcement_stats(main_table), n_packets=1, n_bytes=54, priority=0 actions=drop - cookie=0x6, table=enforcement(scratch_table_0), n_packets=1, n_bytes=54, priority=65532,tcp,reg1=0x1,metadata=0x48c2739fd9c3,tp_dst=80 actions=learn(table=enforcement(main_table),priority=65532,cookie=0x6,eth_type=0x800,nw_proto=6,reg1=0x10,ip_src=192.168.128.1,NXM_OF_IP_DST[]=NXM_OF_IP_SRC[],NXM_OF_TCP_DST[]=NXM_OF_TCP_SRC[],tcp_src=8080,metadata=0x48c2739fd9c3,load:NXM_OF_IP_DST[]->NXM_OF_IP_SRC[],load:0x50->NXM_OF_TCP_SRC[],output:OXM_OF_IN_PORT[0..15]),set_field:0x1->reg0,mod_nw_dst:192.168.128.1,mod_tp_dst:8080,note:b'redir_test',set_field:0x6->reg2,set_field:0->reg4,resubmit(,enforcement(main_table)) + cookie=0x6, table=enforcement(scratch_table_0), n_packets=1, n_bytes=54, priority=65532,tcp,reg1=0x1,metadata=0x48c2739fd9c3,tp_dst=80 actions=learn(table=enforcement(main_table),priority=65532,cookie=0x6,eth_type=0x800,nw_proto=6,reg1=0x10,ip_src=192.168.128.1,NXM_OF_IP_DST[]=NXM_OF_IP_SRC[],NXM_OF_TCP_DST[]=NXM_OF_TCP_SRC[],tcp_src=8080,metadata=0x48c2739fd9c3,load:NXM_OF_IP_DST[]->NXM_OF_IP_SRC[],load:0x50->NXM_OF_TCP_SRC[],output:OXM_OF_IN_PORT[0..15]),set_field:0x1->reg0,mod_nw_dst:192.168.128.1,mod_tp_dst:8080,note:b'redir_test',set_field:0x6->reg2,set_field:0x1->reg4,resubmit(,enforcement(main_table)) cookie=0x6, table=enforcement(scratch_table_0), n_packets=0, n_bytes=0, priority=1,metadata=0x48c2739fd9c3 actions=drop \ No newline at end of file diff --git a/lte/gateway/python/magma/pipelined/tests/test_enforcement.py b/lte/gateway/python/magma/pipelined/tests/test_enforcement.py index 1e5cfb4b6311..2539ff8066c0 100644 --- a/lte/gateway/python/magma/pipelined/tests/test_enforcement.py +++ b/lte/gateway/python/magma/pipelined/tests/test_enforcement.py @@ -20,6 +20,7 @@ from lte.protos.mconfig.mconfigs_pb2 import PipelineD from lte.protos.policydb_pb2 import FlowDescription, FlowMatch, PolicyRule,\ HeaderEnrichment +from lte.protos.pipelined_pb2 import VersionedPolicy from magma.pipelined.app.enforcement import EnforcementController from lte.protos.mobilityd_pb2 import IPAddress from magma.pipelined.bridge_util import BridgeTools @@ -148,7 +149,10 @@ def test_subscriber_policy(self): action=FlowDescription.PERMIT) ] policies = [ - PolicyRule(id='simple_match', priority=2, flow_list=flow_list1) + VersionedPolicy( + rule=PolicyRule(id='simple_match', priority=2,flow_list=flow_list1), + version=1, + ), ] pkts_matched = 256 pkts_sent = 4096 @@ -156,7 +160,7 @@ def test_subscriber_policy(self): # ============================ Subscriber ============================ sub_context = RyuDirectSubscriberContext( imsi, sub_ip, self.enforcement_controller, self._tbl_num - ).add_dynamic_rule(policies[0]) + ).add_policy(policies[0]) isolator = RyuDirectTableIsolator( RyuForwardFlowArgsBuilder.from_subscriber(sub_context.cfg) .build_requests(), @@ -206,13 +210,16 @@ def test_subscriber_ipv6_policy(self): action=FlowDescription.PERMIT) ] policies = [ - PolicyRule(id='simple_match', priority=2, flow_list=flow_list1) + VersionedPolicy( + rule=PolicyRule(id='simple_match', priority=2, flow_list=flow_list1), + version=1, + ), ] # ============================ Subscriber ============================ sub_context = RyuDirectSubscriberContext( imsi, sub_ip, self.enforcement_controller, self._tbl_num - ).add_dynamic_rule(policies[0]) + ).add_policy(policies[0]) isolator = RyuDirectTableIsolator( RyuForwardFlowArgsBuilder.from_subscriber(sub_context.cfg) .build_requests(), @@ -246,10 +253,14 @@ def test_invalid_subscriber(self): ip_src=convert_ipv4_str_to_ip_proto('9999.0.0.0/24')), action=FlowDescription.DENY )] - policy = PolicyRule(id='invalid', priority=2, flow_list=flow_list) + policy = \ + VersionedPolicy( + rule=PolicyRule(id='invalid', priority=2, flow_list=flow_list), + version=1, + ) invalid_sub_context = RyuDirectSubscriberContext( imsi, sub_ip, self.enforcement_controller, - self._tbl_num).add_dynamic_rule(policy) + self._tbl_num).add_policy(policy) isolator = RyuDirectTableIsolator( RyuForwardFlowArgsBuilder.from_subscriber(invalid_sub_context.cfg) .build_requests(), @@ -287,9 +298,16 @@ def test_subscriber_two_policies(self): match=FlowMatch(ip_proto=6, direction=FlowMatch.UPLINK), action=FlowDescription.PERMIT) ] + policies = [ - PolicyRule(id='match', priority=2, flow_list=flow_list1), - PolicyRule(id='no_match', priority=2, flow_list=flow_list2) + VersionedPolicy( + rule=PolicyRule(id='match', priority=2, flow_list=flow_list1), + version=1, + ), + VersionedPolicy( + rule=PolicyRule(id='no_match', priority=2, flow_list=flow_list2), + version=1, + ), ] pkts_sent = 42 @@ -297,8 +315,8 @@ def test_subscriber_two_policies(self): sub_context = RyuDirectSubscriberContext(imsi, sub_ip, self.enforcement_controller, self._tbl_num) \ - .add_dynamic_rule(policies[0])\ - .add_dynamic_rule(policies[1]) + .add_policy(policies[0])\ + .add_policy(policies[1]) isolator = RyuDirectTableIsolator( RyuForwardFlowArgsBuilder.from_subscriber(sub_context.cfg) .build_requests(), @@ -350,13 +368,16 @@ def test_two_subscribers(self): action=FlowDescription.DENY) ] - policy = PolicyRule(id='t', priority=2, flow_list=ip_match) - + policy = \ + VersionedPolicy( + rule=PolicyRule(id='t', priority=2, flow_list=ip_match), + version=1, + ) # =========================== Subscriber 1 =========================== sub_context1 = RyuDirectSubscriberContext( 'IMSI208950001111111', '192.168.128.5', self.enforcement_controller, self._tbl_num - ).add_dynamic_rule(policy) + ).add_policy(policy) isolator1 = RyuDirectTableIsolator( RyuForwardFlowArgsBuilder.from_subscriber(sub_context1.cfg) .build_requests(), @@ -380,8 +401,11 @@ def test_two_subscribers(self): sub_context2 = RyuDirectSubscriberContext( 'IMSI911500451242001', '192.168.128.100', self.enforcement_controller, self._tbl_num - ).add_dynamic_rule( - PolicyRule(id='qqq', priority=2, flow_list=tcp_match) + ).add_policy( + VersionedPolicy( + rule=PolicyRule(id='qqq', priority=2, flow_list=tcp_match), + version=1, + ) ) isolator2 = RyuDirectTableIsolator( RyuForwardFlowArgsBuilder.from_subscriber(sub_context2.cfg) diff --git a/lte/gateway/python/magma/pipelined/tests/test_enforcement_stats.py b/lte/gateway/python/magma/pipelined/tests/test_enforcement_stats.py index b234d0702b66..eb97fc608b70 100644 --- a/lte/gateway/python/magma/pipelined/tests/test_enforcement_stats.py +++ b/lte/gateway/python/magma/pipelined/tests/test_enforcement_stats.py @@ -16,6 +16,7 @@ from unittest.mock import MagicMock import warnings +from lte.protos.pipelined_pb2 import VersionedPolicy from lte.protos.mconfig.mconfigs_pb2 import PipelineD from lte.protos.policydb_pb2 import FlowDescription, FlowMatch, PolicyRule, \ RedirectInformation @@ -161,22 +162,28 @@ def test_subscriber_policy(self): action=FlowDescription.PERMIT) ] policies = [ - PolicyRule(id='tx_match', priority=3, flow_list=flow_list1), - PolicyRule(id='rx_match', priority=5, flow_list=flow_list2) + VersionedPolicy( + rule=PolicyRule(id='tx_match', priority=3, flow_list=flow_list1), + version=1, + ), + VersionedPolicy( + rule=PolicyRule(id='rx_match', priority=5, flow_list=flow_list2), + version=1, + ) ] enf_stat_name = [imsi + '|tx_match' + '|' + sub_ip, imsi + '|rx_match' + '|' + sub_ip] - self.service_manager.session_rule_version_mapper.update_version( - imsi, convert_ipv4_str_to_ip_proto(sub_ip), 'tx_match') - self.service_manager.session_rule_version_mapper.update_version( - imsi, convert_ipv4_str_to_ip_proto(sub_ip), 'rx_match') + self.service_manager.session_rule_version_mapper.save_version( + imsi, convert_ipv4_str_to_ip_proto(sub_ip), 'tx_match', 1) + self.service_manager.session_rule_version_mapper.save_version( + imsi, convert_ipv4_str_to_ip_proto(sub_ip), 'rx_match', 1) """ Setup subscriber, setup table_isolation to fwd pkts """ sub_context = RyuDirectSubscriberContext( imsi, sub_ip, self.enforcement_controller, self._main_tbl_num, self.enforcement_stats_controller - ).add_dynamic_rule(policies[0]) \ - .add_dynamic_rule(policies[1]) + ).add_policy(policies[0]) \ + .add_policy(policies[1]) isolator = RyuDirectTableIsolator( RyuForwardFlowArgsBuilder.from_subscriber(sub_context.cfg) .build_requests(), @@ -242,23 +249,26 @@ def test_redirect_policy(self): imsi = 'IMSI010000000088888' sub_ip = '192.168.128.74' flow_list = [FlowDescription(match=FlowMatch())] - policy = PolicyRule( - id='redir_test', priority=3, flow_list=flow_list, - redirect=RedirectInformation( - support=1, - address_type=2, - server_address="http://about.sha.ddih.org/" - ) + policy = VersionedPolicy( + rule=PolicyRule( + id='redir_test', priority=3, flow_list=flow_list, + redirect=RedirectInformation( + support=1, + address_type=2, + server_address="http://about.sha.ddih.org/" + ) + ), + version=1, ) stat_name = imsi + '|redir_test' + '|' + sub_ip - self.service_manager.session_rule_version_mapper.update_version( - imsi, convert_ipv4_str_to_ip_proto(sub_ip), 'redir_test') + self.service_manager.session_rule_version_mapper.save_version( + imsi, convert_ipv4_str_to_ip_proto(sub_ip), 'redir_test', 1) """ Setup subscriber, setup table_isolation to fwd pkts """ sub_context = RyuDirectSubscriberContext( imsi, sub_ip, self.enforcement_controller, self._main_tbl_num, self.enforcement_stats_controller - ).add_dynamic_rule(policy) + ).add_policy(policy) isolator = RyuDirectTableIsolator( RyuForwardFlowArgsBuilder.from_subscriber(sub_context.cfg) .build_requests(), @@ -312,15 +322,18 @@ def test_rule_install(self): direction=FlowMatch.UPLINK), action=FlowDescription.PERMIT) ] - policy = PolicyRule(id='rule1', priority=3, flow_list=flow_list) - self.service_manager.session_rule_version_mapper.update_version( - imsi, convert_ipv4_str_to_ip_proto(sub_ip), 'rule1') + policy = VersionedPolicy( + rule=PolicyRule(id='rule1', priority=3, flow_list=flow_list), + version=1, + ) + self.service_manager.session_rule_version_mapper.save_version( + imsi, convert_ipv4_str_to_ip_proto(sub_ip), 'rule1', 1) """ Setup subscriber, setup table_isolation to fwd pkts """ sub_context = RyuDirectSubscriberContext( imsi, sub_ip, self.enforcement_controller, self._main_tbl_num, self.enforcement_stats_controller - ).add_dynamic_rule(policy) + ).add_policy(policy) # =========================== Verification =========================== @@ -352,15 +365,18 @@ def test_deny_rule_install(self): direction=FlowMatch.UPLINK), action=FlowDescription.DENY) ] - policy = PolicyRule(id='rule1', priority=3, flow_list=flow_list) - self.service_manager.session_rule_version_mapper.update_version( - imsi, convert_ipv4_str_to_ip_proto(sub_ip), 'rule1') + policy = VersionedPolicy( + rule=PolicyRule(id='rule1', priority=3, flow_list=flow_list), + version=1, + ) + self.service_manager.session_rule_version_mapper.save_version( + imsi, convert_ipv4_str_to_ip_proto(sub_ip), 'rule1', 1) """ Setup subscriber, setup table_isolation to fwd pkts """ sub_context = RyuDirectSubscriberContext( imsi, sub_ip, self.enforcement_controller, self._main_tbl_num, self.enforcement_stats_controller - ).add_dynamic_rule(policy) + ).add_policy(policy) isolator = RyuDirectTableIsolator( RyuForwardFlowArgsBuilder.from_subscriber(sub_context.cfg) @@ -419,15 +435,18 @@ def test_ipv6_rule_install(self): direction=FlowMatch.UPLINK), action=FlowDescription.PERMIT) ] - policy = PolicyRule(id='rule1', priority=3, flow_list=flow_list) - self.service_manager.session_rule_version_mapper.update_version( - imsi, convert_ipv4_str_to_ip_proto(sub_ip), 'rule1') + policy = VersionedPolicy( + rule=PolicyRule(id='rule1', priority=3, flow_list=flow_list), + version=1, + ) + self.service_manager.session_rule_version_mapper.save_version( + imsi, convert_ipv4_str_to_ip_proto(sub_ip), 'rule1', 1) """ Setup subscriber, setup table_isolation to fwd pkts """ sub_context = RyuDirectSubscriberContext( imsi, sub_ip, self.enforcement_controller, self._main_tbl_num, self.enforcement_stats_controller - ).add_dynamic_rule(policy) + ).add_policy(policy) # =========================== Verification =========================== snapshot_verifier = SnapshotVerifier(self, self.BRIDGE, @@ -459,16 +478,19 @@ def test_rule_deactivation(self): direction=FlowMatch.UPLINK), action=FlowDescription.PERMIT) ] - policy = PolicyRule(id='rule1', priority=3, flow_list=flow_list) + policy = VersionedPolicy( + rule=PolicyRule(id='rule1', priority=3, flow_list=flow_list), + version=1, + ) enf_stat_name = imsi + '|rule1' + '|' + sub_ip - self.service_manager.session_rule_version_mapper.update_version( - imsi, convert_ipv4_str_to_ip_proto(sub_ip), 'rule1') + self.service_manager.session_rule_version_mapper.save_version( + imsi, convert_ipv4_str_to_ip_proto(sub_ip), 'rule1', 1) """ Setup subscriber, setup table_isolation to fwd pkts """ sub_context = RyuDirectSubscriberContext( imsi, sub_ip, self.enforcement_controller, self._main_tbl_num, self.enforcement_stats_controller - ).add_dynamic_rule(policy) + ).add_policy(policy) isolator = RyuDirectTableIsolator( RyuForwardFlowArgsBuilder.from_subscriber(sub_context.cfg) .build_requests(), @@ -496,10 +518,10 @@ def test_rule_deactivation(self): self.enforcement_stats_controller._report_usage.reset_mock() pkt_sender.send(packet) self.service_manager.session_rule_version_mapper. \ - update_version(imsi, convert_ipv4_str_to_ip_proto(sub_ip), - 'rule1') + save_version(imsi, convert_ipv4_str_to_ip_proto(sub_ip), + 'rule1', 2) self.enforcement_controller.deactivate_rules( - imsi, convert_ipv4_str_to_ip_proto(sub_ip), [policy.id]) + imsi, convert_ipv4_str_to_ip_proto(sub_ip), [policy.rule.id]) wait_for_enforcement_stats(self.enforcement_stats_controller, [enf_stat_name]) @@ -547,16 +569,19 @@ def test_rule_reactivation(self): direction=FlowMatch.UPLINK), action=FlowDescription.PERMIT) ] - policy = PolicyRule(id='rule1', priority=3, flow_list=flow_list) + policy = VersionedPolicy( + rule=PolicyRule(id='rule1', priority=3, flow_list=flow_list), + version=1, + ) enf_stat_name = imsi + '|rule1' + '|' + sub_ip - self.service_manager.session_rule_version_mapper.update_version( - imsi, convert_ipv4_str_to_ip_proto(sub_ip), 'rule1') + self.service_manager.session_rule_version_mapper.save_version( + imsi, convert_ipv4_str_to_ip_proto(sub_ip), 'rule1', 1) """ Setup subscriber, setup table_isolation to fwd pkts """ sub_context = RyuDirectSubscriberContext( imsi, sub_ip, self.enforcement_controller, self._main_tbl_num, self.enforcement_stats_controller - ).add_dynamic_rule(policy) + ).add_policy(policy) isolator = RyuDirectTableIsolator( RyuForwardFlowArgsBuilder.from_subscriber(sub_context.cfg) .build_requests(), @@ -586,10 +611,11 @@ def test_rule_reactivation(self): self.enforcement_stats_controller._report_usage.reset_mock() self.service_manager.session_rule_version_mapper. \ - update_version(imsi, convert_ipv4_str_to_ip_proto(sub_ip), - 'rule1') + save_version(imsi, convert_ipv4_str_to_ip_proto(sub_ip), + 'rule1', 2) self.enforcement_controller.deactivate_rules( - imsi, convert_ipv4_str_to_ip_proto(sub_ip), [policy.id]) + imsi, convert_ipv4_str_to_ip_proto(sub_ip), [policy.rule.id]) + policy.version=2 self.enforcement_controller.activate_rules( imsi, None, None, convert_ipv4_str_to_ip_proto(sub_ip), None, [policy]) diff --git a/lte/gateway/python/magma/pipelined/tests/test_gy.py b/lte/gateway/python/magma/pipelined/tests/test_gy.py index 1f2600203bee..14ee785f3cb5 100644 --- a/lte/gateway/python/magma/pipelined/tests/test_gy.py +++ b/lte/gateway/python/magma/pipelined/tests/test_gy.py @@ -17,6 +17,7 @@ from unittest.mock import MagicMock from lte.protos.mconfig.mconfigs_pb2 import PipelineD +from lte.protos.pipelined_pb2 import VersionedPolicy from lte.protos.policydb_pb2 import FlowDescription, FlowMatch, PolicyRule, \ RedirectInformation from magma.pipelined.app.gy import GYController @@ -132,19 +133,22 @@ def test_subscriber_redirect_policy(self): "about.sha.ddih.org", lambda: redirect_ips, max_age=42 ) flow_list = [FlowDescription(match=FlowMatch())] - policy = PolicyRule( - id='redir_test', priority=3, flow_list=flow_list, - redirect=RedirectInformation( - support=1, - address_type=2, - server_address="http://about.sha.ddih.org/" - ) + policy = VersionedPolicy( + rule= PolicyRule( + id='redir_test', priority=3, flow_list=flow_list, + redirect=RedirectInformation( + support=1, + address_type=2, + server_address="http://about.sha.ddih.org/" + ) + ), + version=1, ) # ============================ Subscriber ============================ sub_context = RyuDirectSubscriberContext( imsi, sub_ip, self.gy_controller, self._tbl_num - ).add_dynamic_rule(policy) + ).add_policy(policy) isolator = RyuDirectTableIsolator( RyuForwardFlowArgsBuilder.from_subscriber(sub_context.cfg) .build_requests(), @@ -183,7 +187,10 @@ def test_subscriber_restrict_policy(self): action=FlowDescription.PERMIT) ] policies = [ - PolicyRule(id='restrict_match', priority=2, flow_list=flow_list1) + VersionedPolicy( + rule=PolicyRule(id='restrict_match', priority=2, flow_list=flow_list1), + version=1, + ) ] pkts_matched = 256 pkts_sent = 4096 @@ -191,7 +198,7 @@ def test_subscriber_restrict_policy(self): # ============================ Subscriber ============================ sub_context = RyuDirectSubscriberContext( imsi, sub_ip, self.gy_controller, self._tbl_num - ).add_dynamic_rule(policies[0]) + ).add_policy(policies[0]) isolator = RyuDirectTableIsolator( RyuForwardFlowArgsBuilder.from_subscriber(sub_context.cfg) .build_requests(), diff --git a/lte/gateway/python/magma/pipelined/tests/test_he.py b/lte/gateway/python/magma/pipelined/tests/test_he.py index 27c6e659c849..14a36b8cf7d2 100644 --- a/lte/gateway/python/magma/pipelined/tests/test_he.py +++ b/lte/gateway/python/magma/pipelined/tests/test_he.py @@ -23,6 +23,7 @@ from magma.pipelined.app.enforcement import EnforcementController from lte.protos.policydb_pb2 import FlowDescription, FlowMatch, PolicyRule, \ HeaderEnrichment +from lte.protos.pipelined_pb2 import VersionedPolicy from magma.pipelined.bridge_util import BridgeTools from magma.pipelined.tests.app.subscriber import RyuDirectSubscriberContext @@ -489,13 +490,16 @@ def test_subscriber_policy_with_he(self): ] he = HeaderEnrichment(urls=['abc.com']) policies = [ - PolicyRule(id='simple_match', priority=2, flow_list=flow_list1, he=he) + VersionedPolicy( + rule=PolicyRule(id='simple_match', priority=2, flow_list=flow_list1, he=he), + version=1, + ) ] # ============================ Subscriber ============================ sub_context = RyuDirectSubscriberContext( imsi, sub_ip, self.enforcement_controller, self._tbl_num - ).add_dynamic_rule(policies[0]) + ).add_policy(policies[0]) isolator = RyuDirectTableIsolator( RyuForwardFlowArgsBuilder.from_subscriber(sub_context.cfg) diff --git a/lte/gateway/python/magma/pipelined/tests/test_redirect.py b/lte/gateway/python/magma/pipelined/tests/test_redirect.py index a5d6062f26d5..786b7325eb49 100644 --- a/lte/gateway/python/magma/pipelined/tests/test_redirect.py +++ b/lte/gateway/python/magma/pipelined/tests/test_redirect.py @@ -16,6 +16,7 @@ from unittest.mock import MagicMock import warnings +from lte.protos.pipelined_pb2 import VersionedPolicy from lte.protos.mconfig.mconfigs_pb2 import PipelineD from lte.protos.policydb_pb2 import FlowDescription, FlowMatch, PolicyRule, \ RedirectInformation @@ -121,19 +122,22 @@ def test_url_redirect(self): imsi = 'IMSI010000000088888' sub_ip = '192.168.128.74' flow_list = [FlowDescription(match=FlowMatch())] - policy = PolicyRule( - id='redir_test', priority=3, flow_list=flow_list, - redirect=RedirectInformation( - support=1, - address_type=2, - server_address="http://about.sha.ddih.org/" - ) + policy = VersionedPolicy( + rule=PolicyRule( + id='redir_test', priority=3, flow_list=flow_list, + redirect=RedirectInformation( + support=1, + address_type=2, + server_address="http://about.sha.ddih.org/" + ) + ), + version=1, ) # ============================ Subscriber ============================ sub_context = RyuDirectSubscriberContext( imsi, sub_ip, self.enforcement_controller, self._tbl_num - ).add_dynamic_rule(policy) + ).add_policy(policy) isolator = RyuDirectTableIsolator( RyuForwardFlowArgsBuilder.from_subscriber(sub_context.cfg) .build_requests(), @@ -205,19 +209,22 @@ def test_ipv4_redirect(self): imsi = 'IMSI012000000088888' sub_ip = '192.168.128.74' flow_list = [FlowDescription(match=FlowMatch())] - policy = PolicyRule( - id='redir_ip_test', priority=3, flow_list=flow_list, - redirect=RedirectInformation( - support=1, - address_type=0, - server_address=redirect_ip - ) + policy = VersionedPolicy( + rule=PolicyRule( + id='redir_ip_test', priority=3, flow_list=flow_list, + redirect=RedirectInformation( + support=1, + address_type=0, + server_address=redirect_ip + ) + ), + version=1, ) # ============================ Subscriber ============================ sub_context = RyuDirectSubscriberContext( imsi, sub_ip, self.enforcement_controller, self._tbl_num - ).add_dynamic_rule(policy) + ).add_policy(policy) isolator = RyuDirectTableIsolator( RyuForwardFlowArgsBuilder.from_subscriber(sub_context.cfg) .build_requests(), diff --git a/lte/gateway/python/magma/pipelined/tests/test_restart_resilience.py b/lte/gateway/python/magma/pipelined/tests/test_restart_resilience.py index f6d799edcda0..1b8f7cd06ada 100644 --- a/lte/gateway/python/magma/pipelined/tests/test_restart_resilience.py +++ b/lte/gateway/python/magma/pipelined/tests/test_restart_resilience.py @@ -19,7 +19,8 @@ from lte.protos.mconfig.mconfigs_pb2 import PipelineD from lte.protos.policydb_pb2 import FlowDescription, FlowMatch, PolicyRule, \ RedirectInformation -from lte.protos.pipelined_pb2 import ActivateFlowsRequest, SetupFlowsRequest +from lte.protos.pipelined_pb2 import ActivateFlowsRequest, SetupFlowsRequest, \ + VersionedPolicy from magma.subscriberdb.sid import SIDUtils from magma.pipelined.app.enforcement import EnforcementController from magma.pipelined.app.enforcement_stats import EnforcementStatsController @@ -181,35 +182,45 @@ def test_enforcement_restart(self): action=FlowDescription.PERMIT) ] policies1 = [ - PolicyRule(id='sub1_rule_temp', priority=2, flow_list=flow_list1), + VersionedPolicy( + rule=PolicyRule(id='sub1_rule_temp', priority=2, flow_list=flow_list1), + version=1, + ) ] policies2 = [ - PolicyRule(id='sub2_rule_keep', priority=3, flow_list=flow_list2) + VersionedPolicy( + rule=PolicyRule(id='sub2_rule_keep', priority=3, flow_list=flow_list2), + version=1, + ) ] enf_stat_name = [imsi1 + '|sub1_rule_temp' + '|' + sub2_ip, imsi2 + '|sub2_rule_keep' + '|' + sub2_ip] - self.service_manager.session_rule_version_mapper.update_version( - imsi1, convert_ipv4_str_to_ip_proto(sub2_ip), 'sub1_rule_temp') - self.service_manager.session_rule_version_mapper.update_version( - imsi2, convert_ipv4_str_to_ip_proto(sub2_ip), 'sub2_rule_keep') + self.service_manager.session_rule_version_mapper.save_version( + imsi1, convert_ipv4_str_to_ip_proto(sub2_ip), 'sub1_rule_temp', 1) + self.service_manager.session_rule_version_mapper.save_version( + imsi2, convert_ipv4_str_to_ip_proto(sub2_ip), 'sub2_rule_keep', 1) setup_flows_request = SetupFlowsRequest( requests=[ ActivateFlowsRequest( sid=SIDUtils.to_pb(imsi1), ip_addr=sub2_ip, - dynamic_rules=policies1 + policies=policies1 ), ActivateFlowsRequest( sid=SIDUtils.to_pb(imsi2), ip_addr=sub2_ip, - dynamic_rules=policies2 + policies=policies2 ), ], epoch=global_epoch ) + # Simulate clearing the dict + self.service_manager.session_rule_version_mapper\ + ._version_by_imsi_and_rule = {} + fake_controller_setup( enf_controller=self.enforcement_controller, enf_stats_controller=self.enforcement_stats_controller, @@ -242,18 +253,24 @@ def test_enforcement_restart(self): action=FlowDescription.PERMIT) ] policies = [ - PolicyRule(id='sub2_new_rule', priority=2, flow_list=flow_list1), - PolicyRule(id='sub2_rule_keep', priority=3, flow_list=flow_list2) + VersionedPolicy( + rule=PolicyRule(id='sub2_new_rule', priority=2, flow_list=flow_list1), + version=1, + ), + VersionedPolicy( + rule=PolicyRule(id='sub2_rule_keep', priority=3, flow_list=flow_list2), + version=1, + ), ] - self.service_manager.session_rule_version_mapper.update_version( - imsi2, convert_ipv4_str_to_ip_proto(sub2_ip), 'sub2_new_rule') + self.service_manager.session_rule_version_mapper.save_version( + imsi2, convert_ipv4_str_to_ip_proto(sub2_ip), 'sub2_new_rule', 1) enf_stat_name = [imsi2 + '|sub2_new_rule' + '|' + sub2_ip, imsi2 + '|sub2_rule_keep' + '|' + sub2_ip] setup_flows_request = SetupFlowsRequest( requests=[ActivateFlowsRequest( sid=SIDUtils.to_pb(imsi2), ip_addr=sub2_ip, - dynamic_rules=policies + policies=policies, )], epoch=global_epoch ) @@ -323,22 +340,28 @@ def test_enforcement_stats_restart(self): action=FlowDescription.PERMIT) ] policies = [ - PolicyRule(id='tx_match', priority=3, flow_list=flow_list1), - PolicyRule(id='rx_match', priority=5, flow_list=flow_list2) + VersionedPolicy( + rule=PolicyRule(id='tx_match', priority=3, flow_list=flow_list1), + version=1, + ), + VersionedPolicy( + rule=PolicyRule(id='rx_match', priority=5, flow_list=flow_list2), + version=1, + ), ] enf_stat_name = [imsi + '|tx_match' + '|' + sub_ip, imsi + '|rx_match' + '|' + sub_ip] - self.service_manager.session_rule_version_mapper.update_version( - imsi, convert_ipv4_str_to_ip_proto(sub_ip), 'tx_match') - self.service_manager.session_rule_version_mapper.update_version( - imsi, convert_ipv4_str_to_ip_proto(sub_ip), 'rx_match') + self.service_manager.session_rule_version_mapper.save_version( + imsi, convert_ipv4_str_to_ip_proto(sub_ip), 'tx_match', 1) + self.service_manager.session_rule_version_mapper.save_version( + imsi, convert_ipv4_str_to_ip_proto(sub_ip), 'rx_match', 1) """ Setup subscriber, setup table_isolation to fwd pkts """ sub_context = RyuDirectSubscriberContext( imsi, sub_ip, self.enforcement_controller, self._enforcement_tbl_num, self.enforcement_stats_controller, nuke_flows_on_exit=False - ).add_dynamic_rule(policies[0]).add_dynamic_rule(policies[1]) + ).add_policy(policies[0]).add_policy(policies[1]) isolator = RyuDirectTableIsolator( RyuForwardFlowArgsBuilder.from_subscriber(sub_context.cfg) .build_requests(), @@ -393,7 +416,7 @@ def test_enforcement_stats_restart(self): ActivateFlowsRequest( sid=SIDUtils.to_pb(imsi), ip_addr=sub_ip, - dynamic_rules=[policies[0], policies[1]] + policies=[policies[0], policies[1]] ), ], epoch=global_epoch @@ -452,7 +475,7 @@ def test_url_redirect(self): ActivateFlowsRequest( sid=SIDUtils.to_pb(imsi), ip_addr=sub_ip, - dynamic_rules=[policy] + policies=[VersionedPolicy(rule=policy, version=1)] ), ], epoch=global_epoch diff --git a/lte/gateway/python/magma/pipelined/tests/test_rule_mappers.py b/lte/gateway/python/magma/pipelined/tests/test_rule_mappers.py index d19a8ea29fb8..3f0f1ea32add 100644 --- a/lte/gateway/python/magma/pipelined/tests/test_rule_mappers.py +++ b/lte/gateway/python/magma/pipelined/tests/test_rule_mappers.py @@ -34,29 +34,30 @@ def test_session_rule_version_mapper(self): rule_ids = ['rule1', 'rule2'] imsi = 'IMSI12345' ip_addr = '1.2.3.4' - self._session_rule_version_mapper.update_version( - imsi, convert_ipv4_str_to_ip_proto(ip_addr), rule_ids[0]) + self._session_rule_version_mapper.save_version( + imsi, convert_ipv4_str_to_ip_proto(ip_addr), rule_ids[0], 1) self.assertEqual( self._session_rule_version_mapper.get_version( imsi, convert_ipv4_str_to_ip_proto(ip_addr), rule_ids[0]), 1) - self._session_rule_version_mapper.update_version( - imsi, convert_ipv4_str_to_ip_proto(ip_addr), rule_ids[1]) + self._session_rule_version_mapper.save_version( + imsi, convert_ipv4_str_to_ip_proto(ip_addr), rule_ids[1], 1) self.assertEqual( self._session_rule_version_mapper.get_version( imsi, convert_ipv4_str_to_ip_proto(ip_addr), rule_ids[1]), 1) - self._session_rule_version_mapper.update_version( - imsi, convert_ipv4_str_to_ip_proto(ip_addr), rule_ids[0]) + self._session_rule_version_mapper.save_version( + imsi, convert_ipv4_str_to_ip_proto(ip_addr), rule_ids[0], 2) self.assertEqual( self._session_rule_version_mapper.get_version( imsi, convert_ipv4_str_to_ip_proto(ip_addr), rule_ids[0]), 2) # Test updating version for all rules of a subscriber - self._session_rule_version_mapper.update_version(imsi, None) + self._session_rule_version_mapper.update_all_ue_versions(imsi, + convert_ipv4_str_to_ip_proto(ip_addr)) self.assertEqual( self._session_rule_version_mapper.get_version( @@ -70,29 +71,29 @@ def test_session_rule_version_mapper(self): def test_session_rule_version_mapper_cwf(self): rule_ids = ['rule1', 'rule2'] imsi = 'IMSI12345' - self._session_rule_version_mapper.update_version( - imsi, None, rule_ids[0]) + self._session_rule_version_mapper.save_version( + imsi, None, rule_ids[0], 1) self.assertEqual( self._session_rule_version_mapper.get_version( imsi, None, rule_ids[0]), 1) - self._session_rule_version_mapper.update_version( - imsi, None, rule_ids[1]) + self._session_rule_version_mapper.save_version( + imsi, None, rule_ids[1], 1) self.assertEqual( self._session_rule_version_mapper.get_version( imsi, None, rule_ids[1]), 1) - self._session_rule_version_mapper.update_version( - imsi, None, rule_ids[0]) + self._session_rule_version_mapper.save_version( + imsi, None, rule_ids[0], 2) self.assertEqual( self._session_rule_version_mapper.get_version( imsi, None, rule_ids[0]), 2) # Test updating version for all rules of a subscriber - self._session_rule_version_mapper.update_version(imsi, None) + self._session_rule_version_mapper.update_all_ue_versions(imsi, None) self.assertEqual( self._session_rule_version_mapper.get_version( diff --git a/lte/protos/pipelined.proto b/lte/protos/pipelined.proto index c6d95e0615fe..85ce03904574 100644 --- a/lte/protos/pipelined.proto +++ b/lte/protos/pipelined.proto @@ -123,11 +123,11 @@ message RuleModResult { FAILURE = 2; } Result result = 2; + uint64 version = 3; } message ActivateFlowsResult { - repeated RuleModResult dynamic_rule_results = 2; - + repeated RuleModResult policy_results = 2; reserved 1; } From fe8b5904e11390145664970872a579c7fb8d8312 Mon Sep 17 00:00:00 2001 From: Ankit Kumar Aman <50101753+VinashakAnkitAman@users.noreply.github.com> Date: Wed, 7 Apr 2021 01:45:10 +0530 Subject: [PATCH 35/91] Generate failure instead of getting stuck for agw offload test cases (#5931) Signed-off-by: Ankit Kumar Aman --- .../integ_tests/s1aptests/s1ap_utils.py | 36 +++++++++------- .../test_agw_offload_idle_active_ue.py | 42 ++++++++++++------- ...t_agw_offload_mixed_idle_active_multiue.py | 38 ++++++++++------- 3 files changed, 73 insertions(+), 43 deletions(-) diff --git a/lte/gateway/python/integ_tests/s1aptests/s1ap_utils.py b/lte/gateway/python/integ_tests/s1aptests/s1ap_utils.py index 8742377c7f22..1c2c1c7b3896 100644 --- a/lte/gateway/python/integ_tests/s1aptests/s1ap_utils.py +++ b/lte/gateway/python/integ_tests/s1aptests/s1ap_utils.py @@ -1494,11 +1494,13 @@ def send_SetSessionRules(self, imsi, policy_id, flow_list, qos): ) ) + class GTPBridgeUtils: def __init__(self): self.magma_utils = MagmadUtil(None) ret = self.magma_utils.exec_command_output( - "sudo grep ovs_multi_tunnel /etc/magma/spgw.yml") + "sudo grep ovs_multi_tunnel /etc/magma/spgw.yml" + ) if "false" in ret: self.gtp_port_name = "gtp0" else: @@ -1507,42 +1509,48 @@ def __init__(self): def get_gtp_port_no(self) -> Optional[int]: output = self.magma_utils.exec_command_output( - "sudo ovsdb-client dump Interface name ofport") - for line in output.split('\n'): + "sudo ovsdb-client dump Interface name ofport" + ) + for line in output.split("\n"): if self.gtp_port_name in line: port_info = line.split() return port_info[1] def get_proxy_port_no(self) -> Optional[int]: output = self.magma_utils.exec_command_output( - "sudo ovsdb-client dump Interface name ofport") - for line in output.split('\n'): + "sudo ovsdb-client dump Interface name ofport" + ) + for line in output.split("\n"): if self.proxy_port in line: port_info = line.split() return port_info[1] + # RYU rest API is not able dump flows from non zero table. # this adds similar API using `ovs-ofctl` cmd def get_flows(self, table_id) -> []: output = self.magma_utils.exec_command_output( - "sudo ovs-ofctl dump-flows gtp_br0 table={}".format(table_id)) - return output.split('\n') + "sudo ovs-ofctl dump-flows gtp_br0 table={}".format(table_id) + ) + return output.split("\n") + class HaUtil: def __init__(self): - self._ha_stub = HaServiceStub( - get_rpc_channel("spgw_service") - ) + self._ha_stub = HaServiceStub(get_rpc_channel("spgw_service")) def offload_agw(self, imsi, enbID, offloadtype=0): req = StartAgwOffloadRequest( - enb_id = enbID, - enb_offload_type = offloadtype, - imsi = imsi, - ) + enb_id=enbID, + enb_offload_type=offloadtype, + imsi=imsi, + ) try: self._ha_stub.StartAgwOffload(req) except grpc.RpcError as e: print("gRPC failed with %s: %s" % (e.code(), e.details())) + return False + + return True class HeaderEnrichmentUtils: diff --git a/lte/gateway/python/integ_tests/s1aptests/test_agw_offload_idle_active_ue.py b/lte/gateway/python/integ_tests/s1aptests/test_agw_offload_idle_active_ue.py index 7032b65552c4..014a6710bd79 100644 --- a/lte/gateway/python/integ_tests/s1aptests/test_agw_offload_idle_active_ue.py +++ b/lte/gateway/python/integ_tests/s1aptests/test_agw_offload_idle_active_ue.py @@ -18,8 +18,8 @@ from integ_tests.s1aptests import s1ap_wrapper from integ_tests.s1aptests.s1ap_utils import HaUtil -class TestAgwOffloadIdleActiveUe(unittest.TestCase): +class TestAgwOffloadIdleActiveUe(unittest.TestCase): def setUp(self): self._s1ap_wrapper = s1ap_wrapper.TestWrapper() self._ha_util = HaUtil() @@ -30,7 +30,8 @@ def tearDown(self): def test_agw_offload_idle_active_ue(self): """ Basic attach/detach test with a single UE """ # column is a enb parameter, row is a number of enbs - # column description: 1.Cell Id, 2.Tac, 3.EnbType, 4.PLMN Id 5. PLMN length + # column description: + # 1.Cell Id, 2.Tac, 3.EnbType, 4.PLMN Id 5. PLMN length enb_list = [(1, 1, 1, "00101", 5)] self._s1ap_wrapper.multiEnbConfig(len(enb_list), enb_list) @@ -42,21 +43,30 @@ def test_agw_offload_idle_active_ue(self): self._s1ap_wrapper.configUEDevice(num_ues) req = self._s1ap_wrapper.ue_req - print("************************* Running End to End attach for ", - "UE id ", req.ue_id) + print( + "************************* Running End to End attach for ", + "UE id ", + req.ue_id, + ) # Now actually complete the attach self._s1ap_wrapper._s1_util.attach( - req.ue_id, s1ap_types.tfwCmd.UE_END_TO_END_ATTACH_REQUEST, + req.ue_id, + s1ap_types.tfwCmd.UE_END_TO_END_ATTACH_REQUEST, s1ap_types.tfwCmd.UE_ATTACH_ACCEPT_IND, - s1ap_types.ueAttachAccept_t) + s1ap_types.ueAttachAccept_t, + ) # Wait on EMM Information from MME self._s1ap_wrapper._s1_util.receive_emm_info() - print("************************* Offloading UE at state ECM-CONNECTED") + print( + "************************* Offloading UE at state ECM-CONNECTED" + ) # Send offloading request - self._ha_util.offload_agw( - "IMSI" + "".join([str(i) for i in req.imsi]), enb_list[0][0] + self.assertTrue( + self._ha_util.offload_agw( + "IMSI" + "".join([str(i) for i in req.imsi]), enb_list[0][0] + ) ) response = self._s1ap_wrapper.s1_util.get_response() @@ -66,8 +76,10 @@ def test_agw_offload_idle_active_ue(self): print("************************* Offloading UE at state ECM-IDLE") # Send offloading request - self._ha_util.offload_agw( - "IMSI" + "".join([str(i) for i in req.imsi]), enb_list[0][0] + self.assertTrue( + self._ha_util.offload_agw( + "IMSI" + "".join([str(i) for i in req.imsi]), enb_list[0][0] + ) ) response = self._s1ap_wrapper.s1_util.get_response() @@ -105,11 +117,13 @@ def test_agw_offload_idle_active_ue(self): print("************************* SLEEPING for 2 sec") time.sleep(2) - print("************************* Running UE detach for UE id ", - req.ue_id) + print( + "************************* Running UE detach for UE id ", req.ue_id + ) # Now detach the UE self._s1ap_wrapper.s1_util.detach( - req.ue_id, s1ap_types.ueDetachType_t.UE_NORMAL_DETACH.value, True) + req.ue_id, s1ap_types.ueDetachType_t.UE_NORMAL_DETACH.value, True + ) if __name__ == "__main__": diff --git a/lte/gateway/python/integ_tests/s1aptests/test_agw_offload_mixed_idle_active_multiue.py b/lte/gateway/python/integ_tests/s1aptests/test_agw_offload_mixed_idle_active_multiue.py index 140a269ee17d..a1b3a91c88e4 100644 --- a/lte/gateway/python/integ_tests/s1aptests/test_agw_offload_mixed_idle_active_multiue.py +++ b/lte/gateway/python/integ_tests/s1aptests/test_agw_offload_mixed_idle_active_multiue.py @@ -21,8 +21,8 @@ from integ_tests.s1aptests import s1ap_wrapper from integ_tests.s1aptests.s1ap_utils import HaUtil -class TestAgwOffloadMixedIdleActiveMultiUe(unittest.TestCase): +class TestAgwOffloadMixedIdleActiveMultiUe(unittest.TestCase): def setUp(self): self._s1ap_wrapper = s1ap_wrapper.TestWrapper() self._ha_util = HaUtil() @@ -34,7 +34,8 @@ def test_agw_offload_mixed_idle_active_multiue(self): """ Basic attach/detach test with a single UE """ num_ues = 100 # column is a enb parameter, row is a number of enbs - # column description: 1.Cell Id, 2.Tac, 3.EnbType, 4.PLMN Id 5. PLMN length + # column description: + # 1.Cell Id, 2.Tac, 3.EnbType, 4.PLMN Id 5. PLMN length enb_list = [(1, 1, 1, "00101", 5)] self._s1ap_wrapper.multiEnbConfig(len(enb_list), enb_list) @@ -44,13 +45,18 @@ def test_agw_offload_mixed_idle_active_multiue(self): ue_ids = [] for _ in range(num_ues): req = self._s1ap_wrapper.ue_req - print("************************* Running End to End attach for ", - "UE id ", req.ue_id) + print( + "************************* Running End to End attach for ", + "UE id ", + req.ue_id, + ) # Now actually complete the attach self._s1ap_wrapper._s1_util.attach( - req.ue_id, s1ap_types.tfwCmd.UE_END_TO_END_ATTACH_REQUEST, + req.ue_id, + s1ap_types.tfwCmd.UE_END_TO_END_ATTACH_REQUEST, s1ap_types.tfwCmd.UE_ATTACH_ACCEPT_IND, - s1ap_types.ueAttachAccept_t) + s1ap_types.ueAttachAccept_t, + ) # Wait on EMM Information from MME self._s1ap_wrapper._s1_util.receive_emm_info() @@ -58,7 +64,7 @@ def test_agw_offload_mixed_idle_active_multiue(self): # Send UE context release request for half of UEs to move them # to ECM-IDLE state - for i in range(math.floor(num_ues/2)): + for i in range(math.floor(num_ues / 2)): ue_cntxt_rel_req = s1ap_types.ueCntxtRelReq_t() ue_cntxt_rel_req.ue_Id = ue_ids[i] ue_cntxt_rel_req.cause.causeVal = ( @@ -74,7 +80,7 @@ def test_agw_offload_mixed_idle_active_multiue(self): print("************************* Send Offload Request to AGW") # Send offloading request - self._ha_util.offload_agw(None, enb_list[0][0]) + self.assertTrue(self._ha_util.offload_agw(None, enb_list[0][0])) # All UEs should eventually receive Context Release Request # The first half should get it immediately @@ -83,13 +89,15 @@ def test_agw_offload_mixed_idle_active_multiue(self): response = self._s1ap_wrapper.s1_util.get_response() self.assertIn( response.msg_type, - [s1ap_types.tfwCmd.UE_CTX_REL_IND.value, - s1ap_types.tfwCmd.UE_PAGING_IND.value], - 'Not a paging or ue context release message' + [ + s1ap_types.tfwCmd.UE_CTX_REL_IND.value, + s1ap_types.tfwCmd.UE_PAGING_IND.value, + ], + "Not a paging or ue context release message", ) # Send service request as paging response - for i in range(math.floor(num_ues/2)): + for i in range(math.floor(num_ues / 2)): # Send service request to reconnect UE # Auto-release should happen ser_req = s1ap_types.ueserviceReq_t() @@ -127,10 +135,10 @@ def test_agw_offload_mixed_idle_active_multiue(self): # Now detach the UEs normally for ue in ue_ids: - print("************************* Running UE detach for UE id ", - ue) + print("************************* Running UE detach for UE id ", ue) self._s1ap_wrapper.s1_util.detach( - ue, s1ap_types.ueDetachType_t.UE_NORMAL_DETACH.value, True) + ue, s1ap_types.ueDetachType_t.UE_NORMAL_DETACH.value, True + ) if __name__ == "__main__": From 41e07e39ad75c7b43991de6e3d9f0a29529cbd18 Mon Sep 17 00:00:00 2001 From: Andrei Lee Date: Tue, 6 Apr 2021 15:52:24 -0700 Subject: [PATCH 36/91] [ctraced] Fix timeouts, failures and completions update orc8r (#5549) Signed-off-by: Andrei Lee --- .../app/views/tracing/TraceStartDialog.js | 3 +- .../cloud/go/services/ctraced/ctraced/main.go | 16 +- .../ctraced/obsidian/handlers/handlers.go | 6 +- .../ctraced/servicers/trace_servicer.go | 97 +++++++ .../ctraced/servicers/trace_servicer_test.go | 101 +++++++ .../python/magma/ctraced/command_builder.py | 3 + orc8r/gateway/python/magma/ctraced/main.py | 11 +- .../python/magma/ctraced/rpc_servicer.py | 2 + .../python/magma/ctraced/trace_manager.py | 253 ++++++++++++----- orc8r/lib/go/protos/ctraced.pb.go | 260 +++++++++++++++--- orc8r/protos/ctraced.proto | 30 +- 11 files changed, 666 insertions(+), 116 deletions(-) create mode 100644 orc8r/cloud/go/services/ctraced/servicers/trace_servicer.go create mode 100644 orc8r/cloud/go/services/ctraced/servicers/trace_servicer_test.go diff --git a/nms/app/packages/magmalte/app/views/tracing/TraceStartDialog.js b/nms/app/packages/magmalte/app/views/tracing/TraceStartDialog.js index e1d8909dd556..9b6d922a7aca 100644 --- a/nms/app/packages/magmalte/app/views/tracing/TraceStartDialog.js +++ b/nms/app/packages/magmalte/app/views/tracing/TraceStartDialog.js @@ -165,8 +165,9 @@ function CreateTraceDetails(props: Props) { placeholder="Enter Trace Timeout (s)" fullWidth={true} value={traceCfg.timeout} + type="number" onChange={({target}) => { - setTraceCfg({...traceCfg, timeout: target.value}); + setTraceCfg({...traceCfg, timeout: parseInt(target.value)}); }} /> diff --git a/orc8r/cloud/go/services/ctraced/ctraced/main.go b/orc8r/cloud/go/services/ctraced/ctraced/main.go index 38c55418f8d7..f0f912d92ffc 100644 --- a/orc8r/cloud/go/services/ctraced/ctraced/main.go +++ b/orc8r/cloud/go/services/ctraced/ctraced/main.go @@ -22,9 +22,11 @@ import ( "magma/orc8r/cloud/go/service" "magma/orc8r/cloud/go/services/ctraced" "magma/orc8r/cloud/go/services/ctraced/obsidian/handlers" + "magma/orc8r/cloud/go/services/ctraced/servicers" ctraced_storage "magma/orc8r/cloud/go/services/ctraced/storage" "magma/orc8r/cloud/go/sqorc" "magma/orc8r/cloud/go/storage" + "magma/orc8r/lib/go/protos" "github.com/golang/glog" ) @@ -33,29 +35,31 @@ func main() { // Create service srv, err := service.NewOrchestratorService(orc8r.ModuleName, ctraced.ServiceName) if err != nil { - glog.Fatalf("Error creating ctraced service: %s", err) + glog.Fatalf("Error creating ctraced service: %+v", err) } - swagger_protos.RegisterSwaggerSpecServer(srv.GrpcServer, swagger.NewSpecServicerFromFile(ctraced.ServiceName)) - // Init storage db, err := sqorc.Open(storage.SQLDriver, storage.DatabaseSource) if err != nil { - glog.Fatalf("Error opening db connection: %v", err) + glog.Fatalf("Error opening db connection: %+v", err) } fact := blobstore.NewSQLBlobStorageFactory(ctraced.LookupTableBlobstore, db, sqorc.GetSqlBuilder()) err = fact.InitializeFactory() if err != nil { - glog.Fatalf("Error initializing ctraced table: %v", err) + glog.Fatalf("Error initializing ctraced table: %+v", err) } ctracedBlobstore := ctraced_storage.NewCtracedBlobstore(fact) + // Init gRPC servicer + protos.RegisterCallTraceControllerServer(srv.GrpcServer, servicers.NewCallTraceServicer(ctracedBlobstore)) + swagger_protos.RegisterSwaggerSpecServer(srv.GrpcServer, swagger.NewSpecServicerFromFile(ctraced.ServiceName)) + gwClient := handlers.NewGwCtracedClient() obsidian.AttachHandlers(srv.EchoServer, handlers.GetObsidianHandlers(gwClient, ctracedBlobstore)) // Run service err = srv.Run() if err != nil { - glog.Fatalf("Error running ctraced service: %s", err) + glog.Fatalf("Error running ctraced service: %+v", err) } } diff --git a/orc8r/cloud/go/services/ctraced/obsidian/handlers/handlers.go b/orc8r/cloud/go/services/ctraced/obsidian/handlers/handlers.go index c4a93e2c4268..c92df2df5bcf 100644 --- a/orc8r/cloud/go/services/ctraced/obsidian/handlers/handlers.go +++ b/orc8r/cloud/go/services/ctraced/obsidian/handlers/handlers.go @@ -158,7 +158,9 @@ func getUpdateCallTraceHandlerFunc(client GwCtracedClient, storage storage.Ctrac return obsidian.HttpError(errors.New("Error: call trace end already triggered earlier"), http.StatusBadRequest) } - req := &protos.EndTraceRequest{} + req := &protos.EndTraceRequest{ + TraceId: callTraceID, + } resp, err := client.EndCallTrace(networkID, callTrace.Config.GatewayID, req) if err != nil { return err @@ -256,7 +258,9 @@ func getNetworkIDAndCallTraceID(c echo.Context) (string, string, *echo.HTTPError func buildStartTraceRequest(cfg *models.CallTraceConfig) (*protos.StartTraceRequest, error) { req := &protos.StartTraceRequest{ + TraceId: cfg.TraceID, TraceType: protos.StartTraceRequest_ALL, + Timeout: cfg.Timeout, CaptureFilters: cfg.CaptureFilters, DisplayFilters: cfg.DisplayFilters, } diff --git a/orc8r/cloud/go/services/ctraced/servicers/trace_servicer.go b/orc8r/cloud/go/services/ctraced/servicers/trace_servicer.go new file mode 100644 index 000000000000..75f2895b0a1b --- /dev/null +++ b/orc8r/cloud/go/services/ctraced/servicers/trace_servicer.go @@ -0,0 +1,97 @@ +/* +Copyright 2020 The Magma Authors. + +This source code is licensed under the BSD-style license found in the +LICENSE file in the root directory of this source tree. + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package servicers + +import ( + "context" + "fmt" + + "magma/orc8r/cloud/go/orc8r" + "magma/orc8r/cloud/go/serdes" + "magma/orc8r/cloud/go/services/configurator" + "magma/orc8r/cloud/go/services/ctraced/obsidian/models" + "magma/orc8r/cloud/go/services/ctraced/storage" + merrors "magma/orc8r/lib/go/errors" + "magma/orc8r/lib/go/protos" + + "google.golang.org/grpc/codes" + "google.golang.org/grpc/status" +) + +type callTraceServicer struct { + storage storage.CtracedStorage +} + +func NewCallTraceServicer(storage storage.CtracedStorage) protos.CallTraceControllerServer { + return &callTraceServicer{storage: storage} +} + +func (srv *callTraceServicer) ReportEndedCallTrace(ctx context.Context, req *protos.ReportEndedTraceRequest) (*protos.ReportEndedTraceResponse, error) { + networkID, err := getNetworkID(ctx) + if err != nil { + return nil, err + } + callTrace, err := getCallTraceModel(networkID, req.TraceId) + if err != nil { + return nil, err + } + + err = srv.storage.StoreCallTrace(networkID, req.TraceId, req.TraceContent) + if err != nil { + return nil, status.Errorf(codes.Aborted, fmt.Sprintf("failed to save call trace data, network-id: %s, gateway-id: %s, calltrace-id: %s", networkID, callTrace.Config.GatewayID, req.TraceId)) + } + + callTrace.State.CallTraceEnding = req.Success + callTrace.State.CallTraceAvailable = req.Success + + update := configurator.EntityUpdateCriteria{ + Type: orc8r.CallTraceEntityType, + Key: req.TraceId, + NewConfig: callTrace, + } + + _, err = configurator.UpdateEntity(networkID, update, serdes.Entity) + if err != nil { + return nil, status.Errorf(codes.Aborted, fmt.Sprintf("failed to update call trace, network-id: %s, gateway-id: %s, calltrace-id: %s", networkID, callTrace.Config.GatewayID, req.TraceId)) + } + return &protos.ReportEndedTraceResponse{}, nil +} + +func getNetworkID(ctx context.Context) (string, error) { + id, err := protos.GetGatewayIdentity(ctx) + if err != nil { + return "", err + } + return id.GetNetworkId(), nil +} + +func getCallTraceModel(networkID string, callTraceID string) (*models.CallTrace, error) { + ent, err := configurator.LoadEntity( + networkID, orc8r.CallTraceEntityType, callTraceID, + configurator.EntityLoadCriteria{LoadConfig: true}, + serdes.Entity, + ) + if err == merrors.ErrNotFound { + return nil, status.Errorf(codes.InvalidArgument, "Call trace not found") + } + if err != nil { + return nil, status.Errorf(codes.InvalidArgument, "Failed to load call trace") + } + callTrace := &models.CallTrace{} + err = callTrace.FromBackendModels(ent) + if err != nil { + return nil, status.Errorf(codes.Aborted, "Failed to load call trace") + } + return callTrace, nil +} diff --git a/orc8r/cloud/go/services/ctraced/servicers/trace_servicer_test.go b/orc8r/cloud/go/services/ctraced/servicers/trace_servicer_test.go new file mode 100644 index 000000000000..ecd8fc8203df --- /dev/null +++ b/orc8r/cloud/go/services/ctraced/servicers/trace_servicer_test.go @@ -0,0 +1,101 @@ +/* +Copyright 2020 The Magma Authors. + +This source code is licensed under the BSD-style license found in the +LICENSE file in the root directory of this source tree. + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package servicers_test + +import ( + "testing" + + "magma/orc8r/cloud/go/orc8r" + "magma/orc8r/cloud/go/serdes" + "magma/orc8r/cloud/go/services/configurator" + "magma/orc8r/cloud/go/services/configurator/test_init" + models "magma/orc8r/cloud/go/services/ctraced/obsidian/models" + "magma/orc8r/cloud/go/services/ctraced/servicers" + "magma/orc8r/cloud/go/services/ctraced/storage" + deviceTestInit "magma/orc8r/cloud/go/services/device/test_init" + "magma/orc8r/cloud/go/test_utils" + "magma/orc8r/lib/go/protos" + + "github.com/stretchr/testify/assert" + "golang.org/x/net/context" +) + +func TestCallTraceServicer(t *testing.T) { + test_init.StartTestService(t) + deviceTestInit.StartTestService(t) + + testNetworkId := "n1" + testGwHwId := "hw1" + testGwLogicalId := "g1" + + // Initialize network + err := configurator.CreateNetwork(configurator.Network{ID: testNetworkId}, serdes.Network) + assert.NoError(t, err) + + // Create a call trace + testTraceCfg := &models.CallTraceConfig{ + TraceID: "CallTrace1", + GatewayID: "test_gateway_id", + Timeout: 300, + TraceType: models.CallTraceConfigTraceTypeGATEWAY, + } + testTrace := &models.CallTrace{ + Config: testTraceCfg, + State: &models.CallTraceState{ + CallTraceAvailable: false, + CallTraceEnding: false, + }, + } + _, err = configurator.CreateEntity( + testNetworkId, + configurator.NetworkEntity{ + Type: orc8r.CallTraceEntityType, + Key: "CallTrace1", + Config: testTrace, + }, + serdes.Entity, + ) + assert.NoError(t, err) + + // Create an identity and context for sending requests as gateway + id := protos.Identity{} + idgw := protos.Identity_Gateway{HardwareId: testGwHwId, NetworkId: testNetworkId, LogicalId: testGwLogicalId} + id.SetGateway(&idgw) + ctx := id.NewContextWithIdentity(context.Background()) + + fact := test_utils.NewSQLBlobstore(t, "ctraced_trace_servicer_test_blobstore") + blobstore := storage.NewCtracedBlobstore(fact) + srv := servicers.NewCallTraceServicer(blobstore) + + // Missing subscriber ID + req := &protos.ReportEndedTraceRequest{TraceId: "CallTrace0", Success: true, TraceContent: []byte("abcdefghijklmnopqrstuvwxyz\n")} + _, err = srv.ReportEndedCallTrace(ctx, req) + assert.EqualError(t, err, "rpc error: code = InvalidArgument desc = Call trace not found") + + // Successfully ending the call trace + req = &protos.ReportEndedTraceRequest{TraceId: "CallTrace1", Success: true, TraceContent: []byte("abcdefghijklmnopqrstuvwxyz\n")} + _, err = srv.ReportEndedCallTrace(ctx, req) + assert.NoError(t, err) + + // Verify that the call trace has ended + ent, err := configurator.LoadEntity( + testNetworkId, orc8r.CallTraceEntityType, "CallTrace1", + configurator.FullEntityLoadCriteria(), + serdes.Entity, + ) + testCallTrace := (&models.CallTrace{}).FromEntity(ent) + assert.NoError(t, err) + assert.Equal(t, true, testCallTrace.State.CallTraceAvailable) + assert.Equal(t, true, testCallTrace.State.CallTraceEnding) +} diff --git a/orc8r/gateway/python/magma/ctraced/command_builder.py b/orc8r/gateway/python/magma/ctraced/command_builder.py index fcd1d5e834c7..f981eee00d3b 100644 --- a/orc8r/gateway/python/magma/ctraced/command_builder.py +++ b/orc8r/gateway/python/magma/ctraced/command_builder.py @@ -32,6 +32,7 @@ def build_trace_command( self, interfaces: List[str], max_filesize: int, + timeout: int, output_filename: str, capture_filters: str, ) -> List[str]: @@ -79,6 +80,8 @@ def build_trace_command( if max_filesize != -1: command.extend(["-a", "filesize:" + str(max_filesize)]) + command.extend(["-a", "duration:" + str(timeout)]) + # Specify output file command.extend(["-w", output_filename]) diff --git a/orc8r/gateway/python/magma/ctraced/main.py b/orc8r/gateway/python/magma/ctraced/main.py index 32e9d4a8ece2..282949ee2921 100644 --- a/orc8r/gateway/python/magma/ctraced/main.py +++ b/orc8r/gateway/python/magma/ctraced/main.py @@ -13,10 +13,11 @@ from magma.common.service import MagmaService from magma.common.sentry import sentry_init -from orc8r.protos.mconfig.mconfigs_pb2 import CtraceD +from magma.common.service_registry import ServiceRegistry from .rpc_servicer import CtraceDRpcServicer from .trace_manager import TraceManager - +from orc8r.protos.ctraced_pb2_grpc import CallTraceControllerStub +from orc8r.protos.mconfig.mconfigs_pb2 import CtraceD def main(): """ main() for ctraced """ @@ -25,7 +26,11 @@ def main(): # Optionally pipe errors to Sentry sentry_init() - trace_manager = TraceManager(service.config) + orc8r_chan = ServiceRegistry.get_rpc_channel('ctraced', + ServiceRegistry.CLOUD) + ctraced_stub = CallTraceControllerStub(orc8r_chan) + + trace_manager = TraceManager(service.config, ctraced_stub) ctraced_servicer = CtraceDRpcServicer(trace_manager) ctraced_servicer.add_to_server(service.rpc_server) diff --git a/orc8r/gateway/python/magma/ctraced/rpc_servicer.py b/orc8r/gateway/python/magma/ctraced/rpc_servicer.py index 55bd9d36a6a8..53d341e7c5be 100644 --- a/orc8r/gateway/python/magma/ctraced/rpc_servicer.py +++ b/orc8r/gateway/python/magma/ctraced/rpc_servicer.py @@ -39,6 +39,8 @@ def StartCallTrace( _context, ) -> StartTraceResponse: success = self._trace_mgr.start_trace( + request.trace_id, + request.timeout, request.capture_filters, request.display_filters, ) diff --git a/orc8r/gateway/python/magma/ctraced/trace_manager.py b/orc8r/gateway/python/magma/ctraced/trace_manager.py index 5220f1f3c916..0b5d81adef0d 100644 --- a/orc8r/gateway/python/magma/ctraced/trace_manager.py +++ b/orc8r/gateway/python/magma/ctraced/trace_manager.py @@ -12,19 +12,25 @@ """ import errno +import grpc import logging import os import pathlib import subprocess +import threading import time from typing import List from subprocess import SubprocessError from .command_builder import get_trace_builder from collections import namedtuple +from orc8r.protos.ctraced_pb2 import ReportEndedTraceRequest +from orc8r.protos.ctraced_pb2_grpc import CallTraceControllerStub + _TRACE_FILE_NAME = "call_trace" _TRACE_FILE_NAME_POSTPROCESSED = "call_trace_postprocessed" _TRACE_FILE_EXT = "pcapng" +_TRACE_FILE_WRITE_TIMEOUT = 20 # 20 seconds for TShark to write a trace to disk _MAX_FILESIZE = 4000 # ~ 4 MiB for a trace _POSTPROCESSING_TIMEOUT = 10 # 10 seconds for TShark to apply display filters @@ -38,8 +44,10 @@ class TraceManager: Only a single trace can be captured at a time. """ - def __init__(self, config): + def __init__(self, config, ctraced_stub: CallTraceControllerStub): + self._trace_id = "" self._is_active = False # is call trace being captured + self._is_stopping_trace = False # is manual stop initiated self._proc = None self._trace_directory = config.get("trace_directory", "/var/opt/magma/trace") # type: str @@ -57,8 +65,12 @@ def __init__(self, config): self._tool_name = config.get("trace_tool", "tshark") # type: str self._trace_builder = get_trace_builder(self._tool_name) + self._ctraced_stub = ctraced_stub + def start_trace( self, + trace_id: str, + timeout: int, capture_filters: str, display_filters: str, ) -> bool: @@ -83,11 +95,13 @@ def start_trace( "Trace already active") return False + self._trace_id = trace_id self._build_trace_filename() command = self._trace_builder.build_trace_command( self._trace_interfaces, _MAX_FILESIZE, + timeout, self._trace_filename, capture_filters, ) @@ -105,42 +119,126 @@ def end_trace(self) -> EndTraceResult: """ # If trace is active, then stop it if self._is_active: + self._is_stopping_trace = True stopped = self._stop_trace() if not stopped: return EndTraceResult(False, None) - while True: - if self._ensure_trace_file_exists(): - logging.info("TraceManager: Trace file written!") - break - logging.info("TraceManager: Waiting 1s for trace file to be " - "written...") - time.sleep(1) + self._wait_until_trace_file_exists() # Perform postprocessing of capture file with TShark display filters - if len(self._display_filters) > 0: - succeeded = self._postprocess_trace() - if not succeeded: - return EndTraceResult(False, None) - - # Read trace data into bytes - with open(self._trace_filename_postprocessed, "rb") as trace_file: - data = trace_file.read() # type: bytes - else: - # Read trace data into bytes - with open(self._trace_filename, "rb") as trace_file: - data = trace_file.read() # type: bytes - - # Ensure the tmp trace file is deleted - self._ensure_tmp_file_deleted() - self._trace_filename = "" + succeeded = self._conditionally_postprocess_trace() + if not succeeded: + return EndTraceResult(False, None) - self._is_active = False + data = self._get_final_trace_data() # type: bytes + self._cleanup_trace() logging.info("TraceManager: Call trace has ended") # Everything cleaned up, return bytes return EndTraceResult(True, data) + def _execute_start_trace_command(self, command: List[str]) -> bool: + """Executes a command to start a call trace + + Args: + command: Shell command with each token ordered in the list. + example: ["tshark", "-i", "eth0"] would be for "tshark -i eth0" + + Returns: + True if successfully executed command + """ + logging.info("TraceManager: Starting trace with %s, command: [%s]", + self._tool_name, ' '.join(command)) + + self._ensure_trace_directory_exists() + + # TODO(andreilee): Handle edge case where only one instance of the + # process can be running, and may have been started + # by something external as well. + + + # TODO(andreilee): Make sure that a fast failure is detected + def run_tracing_in_thread(on_exit, command: List[str]): + try: + self._proc = subprocess.Popen( + command, + stdout=subprocess.PIPE, + stderr=subprocess.STDOUT) + except SubprocessError as e: + logging.error("TraceManager: Failed to start trace: %s", + str(e)) + + self._is_active = True + logging.info("TraceManager: Successfully started trace with %s", + self._tool_name) + + self._proc.wait() + on_exit() + return + + thread = threading.Thread(target=run_tracing_in_thread, + args=(self._on_trace_exit, command)) + thread.start() + return True + + def _on_trace_exit(self): + if self._is_stopping_trace: + # Manual stop initiated, skip this automatic process + return + logging.info("TraceManager: Call trace timed out") + if self._proc is None: + logging.error("TraceManager: Call trace not running") + self._report_trace_failure() + return + self._proc.poll() + return_code = self._proc.returncode + logging.debug("TraceManager: Tracing process return code: %s", + return_code) + self._dump_trace_logs() + + if return_code != 0: + self._report_trace_failure() + return + + self._wait_until_trace_file_exists() + + # Perform postprocessing of capture file with TShark display filters + succeeded = self._conditionally_postprocess_trace() + if not succeeded: + self._report_trace_failure() + return + + data = self._get_final_trace_data() # type: bytes + + self._cleanup_trace() + logging.info("TraceManager: Reporting call trace timeout") + + self._report_trace_success(data) + + def _report_trace_success(self, data: bytes): + try: + req = ReportEndedTraceRequest( + trace_id=self._trace_id, + success=True, + trace_content=data, + ) + self._ctraced_stub.ReportEndedCallTrace(req) + except grpc.RpcError: + logging.error('Unable to report successful call trace for %s. ', + self._trace_id) + + def _report_trace_failure(self): + try: + req = ReportEndedTraceRequest( + trace_id=self._trace_id, + success=False, + ) + self._ctraced_stub.ReportEndedCallTrace(req) + except grpc.RpcError: + logging.error('Unable to report failed call trace for %s. ', + self._trace_id) + def _stop_trace(self) -> bool: # If the process has ended, then _proc isn't None self._proc.poll() @@ -149,17 +247,10 @@ def _stop_trace(self) -> bool: logging.info("TraceManager: Ending call trace") self._proc.terminate() else: - logging.info("TraceManager: Tracing process return code: %s", - return_code) + logging.debug("TraceManager: Tracing process return code: %s", + return_code) - logging.debug("TraceManager: Trace logs:") - logging.debug("<" * 25) - while True: - line = self._proc.stdout.readline() - if not line: - break - logging.debug("| %s", str(line.rstrip())) - logging.debug(">" * 25) + self._dump_trace_logs() if return_code is not None: self._is_active = False @@ -167,7 +258,11 @@ def _stop_trace(self) -> bool: return True - def _postprocess_trace(self) -> bool: + def _conditionally_postprocess_trace(self) -> bool: + """ Postprocess trace. Return whether succeeded. """ + if len(self._display_filters) == 0: + return True + command = self._trace_builder.build_postprocess_command( self._trace_filename, self._display_filters, @@ -187,6 +282,9 @@ def _postprocess_trace(self) -> bool: return False self._proc.wait() + if not self._proc.stdout: + logging.error("TraceManager: Failed to capture STDOUT of tshark") + return True logging.debug("<" * 25) while True: line = self._proc.stdout.readline() @@ -197,6 +295,23 @@ def _postprocess_trace(self) -> bool: logging.info("TraceManager: Finished postprocess") return True + def _get_final_trace_data(self) -> bytes: + if self._should_postprocess(): + filename = self._trace_filename_postprocessed + else: + filename = self._trace_filename + + with open(filename, "rb") as trace_file: + data = trace_file.read() # type: bytes + return data + + def _cleanup_trace(self): + self._ensure_tmp_file_deleted(self._trace_filename) + self._ensure_tmp_file_deleted(self._trace_filename_postprocessed) + self._is_stopping_trace = False + self._trace_filename = "" + self._is_active = False + def _build_trace_filename(self): # Example filename path: # /var/opt/magma/trace/call_trace_1607358641.pcap @@ -212,51 +327,47 @@ def _build_trace_filename(self): int(time.time()), _TRACE_FILE_EXT) - def _execute_start_trace_command(self, command: List[str]) -> bool: - """Executes a command to start a call trace - - Args: - command: Shell command with each token ordered in the list. - example: ["tshark", "-i", "eth0"] would be for "tshark -i eth0" + def _should_postprocess(self) -> bool: + return len(self._display_filters) > 0 - Returns: - True if successfully executed command - """ - logging.info("TraceManager: Starting trace with %s, command: [%s]", - self._tool_name, ' '.join(command)) - - self._ensure_trace_directory_exists() - - # TODO(andreilee): Handle edge case where only one instance of the - # process can be running, and may have been started - # by something external as well. - try: - self._proc = subprocess.Popen( - command, - stdout=subprocess.PIPE, - stderr=subprocess.STDOUT) - except SubprocessError as e: - logging.error("TraceManager: Failed to start trace: %s", str(e)) - return False - - self._is_active = True - logging.info("TraceManager: Successfully started trace with %s", - self._tool_name) - return True + def _dump_trace_logs(self): + if not self._proc.stdout: + logging.error("TraceManager: Failed to capture trace logs") + return + logging.debug("TraceManager: Trace logs:") + logging.debug("<" * 25) + while True: + line = self._proc.stdout.readline() + if not line: + break + logging.debug("| %s", str(line.rstrip())) + logging.debug(">" * 25) - def _ensure_trace_file_exists(self) -> bool: + def _wait_until_trace_file_exists(self): + time_pending = 0 + while (not self._does_trace_file_exist() and + time_pending < _TRACE_FILE_WRITE_TIMEOUT): + logging.debug("TraceManager: Waiting 1s for trace file to be " + "written...") + time.sleep(1) + time_pending += 1 + if time_pending >= _TRACE_FILE_WRITE_TIMEOUT: + self._report_trace_failure() + return + logging.debug("TraceManager: Trace file written!") + + def _does_trace_file_exist(self) -> bool: return os.path.isfile(self._trace_filename) - def _ensure_tmp_file_deleted(self): + def _ensure_tmp_file_deleted(self, filename: str): """Ensure that tmp trace file is deleted. Uses exception handling rather than a check for file existence to avoid TOCTTOU bug """ try: - os.remove(self._trace_filename) - if os.path.isfile(self._trace_filename_postprocessed): - os.remove(self._trace_filename_postprocessed) + if os.path.isfile(filename): + os.remove(filename) except OSError as e: if e.errno != errno.ENOENT: logging.error("TraceManager: Error when deleting tmp trace " diff --git a/orc8r/lib/go/protos/ctraced.pb.go b/orc8r/lib/go/protos/ctraced.pb.go index c94be056f2cf..ac5d3b852999 100644 --- a/orc8r/lib/go/protos/ctraced.pb.go +++ b/orc8r/lib/go/protos/ctraced.pb.go @@ -114,6 +114,7 @@ func (StartTraceRequest_InterfaceName) EnumDescriptor() ([]byte, []int) { // StartTraceRequest specifies options for a call trace started on a gateway type StartTraceRequest struct { + TraceId string `protobuf:"bytes,8,opt,name=trace_id,json=traceId,proto3" json:"trace_id,omitempty"` TraceType StartTraceRequest_TraceType `protobuf:"varint,1,opt,name=trace_type,json=traceType,proto3,enum=magma.orc8r.StartTraceRequest_TraceType" json:"trace_type,omitempty"` // SPECIFIED ONLY IF trace_type is SUBSCRIBER Imsi string `protobuf:"bytes,2,opt,name=imsi,proto3" json:"imsi,omitempty"` @@ -175,6 +176,13 @@ func (m *StartTraceRequest) XXX_DiscardUnknown() { var xxx_messageInfo_StartTraceRequest proto.InternalMessageInfo +func (m *StartTraceRequest) GetTraceId() string { + if m != nil { + return m.TraceId + } + return "" +} + func (m *StartTraceRequest) GetTraceType() StartTraceRequest_TraceType { if m != nil { return m.TraceType @@ -264,6 +272,7 @@ func (m *StartTraceResponse) GetSuccess() bool { } type EndTraceRequest struct { + TraceId string `protobuf:"bytes,1,opt,name=trace_id,json=traceId,proto3" json:"trace_id,omitempty"` XXX_NoUnkeyedLiteral struct{} `json:"-"` XXX_unrecognized []byte `json:"-"` XXX_sizecache int32 `json:"-"` @@ -294,6 +303,13 @@ func (m *EndTraceRequest) XXX_DiscardUnknown() { var xxx_messageInfo_EndTraceRequest proto.InternalMessageInfo +func (m *EndTraceRequest) GetTraceId() string { + if m != nil { + return m.TraceId + } + return "" +} + type EndTraceResponse struct { Success bool `protobuf:"varint,1,opt,name=success,proto3" json:"success,omitempty"` TraceContent []byte `protobuf:"bytes,2,opt,name=trace_content,json=traceContent,proto3" json:"trace_content,omitempty"` @@ -341,6 +357,92 @@ func (m *EndTraceResponse) GetTraceContent() []byte { return nil } +type ReportEndedTraceRequest struct { + TraceId string `protobuf:"bytes,1,opt,name=trace_id,json=traceId,proto3" json:"trace_id,omitempty"` + Success bool `protobuf:"varint,2,opt,name=success,proto3" json:"success,omitempty"` + TraceContent []byte `protobuf:"bytes,3,opt,name=trace_content,json=traceContent,proto3" json:"trace_content,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` +} + +func (m *ReportEndedTraceRequest) Reset() { *m = ReportEndedTraceRequest{} } +func (m *ReportEndedTraceRequest) String() string { return proto.CompactTextString(m) } +func (*ReportEndedTraceRequest) ProtoMessage() {} +func (*ReportEndedTraceRequest) Descriptor() ([]byte, []int) { + return fileDescriptor_74b70534723ed60d, []int{4} +} + +func (m *ReportEndedTraceRequest) XXX_Unmarshal(b []byte) error { + return xxx_messageInfo_ReportEndedTraceRequest.Unmarshal(m, b) +} +func (m *ReportEndedTraceRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + return xxx_messageInfo_ReportEndedTraceRequest.Marshal(b, m, deterministic) +} +func (m *ReportEndedTraceRequest) XXX_Merge(src proto.Message) { + xxx_messageInfo_ReportEndedTraceRequest.Merge(m, src) +} +func (m *ReportEndedTraceRequest) XXX_Size() int { + return xxx_messageInfo_ReportEndedTraceRequest.Size(m) +} +func (m *ReportEndedTraceRequest) XXX_DiscardUnknown() { + xxx_messageInfo_ReportEndedTraceRequest.DiscardUnknown(m) +} + +var xxx_messageInfo_ReportEndedTraceRequest proto.InternalMessageInfo + +func (m *ReportEndedTraceRequest) GetTraceId() string { + if m != nil { + return m.TraceId + } + return "" +} + +func (m *ReportEndedTraceRequest) GetSuccess() bool { + if m != nil { + return m.Success + } + return false +} + +func (m *ReportEndedTraceRequest) GetTraceContent() []byte { + if m != nil { + return m.TraceContent + } + return nil +} + +type ReportEndedTraceResponse struct { + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` +} + +func (m *ReportEndedTraceResponse) Reset() { *m = ReportEndedTraceResponse{} } +func (m *ReportEndedTraceResponse) String() string { return proto.CompactTextString(m) } +func (*ReportEndedTraceResponse) ProtoMessage() {} +func (*ReportEndedTraceResponse) Descriptor() ([]byte, []int) { + return fileDescriptor_74b70534723ed60d, []int{5} +} + +func (m *ReportEndedTraceResponse) XXX_Unmarshal(b []byte) error { + return xxx_messageInfo_ReportEndedTraceResponse.Unmarshal(m, b) +} +func (m *ReportEndedTraceResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + return xxx_messageInfo_ReportEndedTraceResponse.Marshal(b, m, deterministic) +} +func (m *ReportEndedTraceResponse) XXX_Merge(src proto.Message) { + xxx_messageInfo_ReportEndedTraceResponse.Merge(m, src) +} +func (m *ReportEndedTraceResponse) XXX_Size() int { + return xxx_messageInfo_ReportEndedTraceResponse.Size(m) +} +func (m *ReportEndedTraceResponse) XXX_DiscardUnknown() { + xxx_messageInfo_ReportEndedTraceResponse.DiscardUnknown(m) +} + +var xxx_messageInfo_ReportEndedTraceResponse proto.InternalMessageInfo + func init() { proto.RegisterEnum("magma.orc8r.StartTraceRequest_TraceType", StartTraceRequest_TraceType_name, StartTraceRequest_TraceType_value) proto.RegisterEnum("magma.orc8r.StartTraceRequest_ProtocolName", StartTraceRequest_ProtocolName_name, StartTraceRequest_ProtocolName_value) @@ -349,44 +451,50 @@ func init() { proto.RegisterType((*StartTraceResponse)(nil), "magma.orc8r.StartTraceResponse") proto.RegisterType((*EndTraceRequest)(nil), "magma.orc8r.EndTraceRequest") proto.RegisterType((*EndTraceResponse)(nil), "magma.orc8r.EndTraceResponse") + proto.RegisterType((*ReportEndedTraceRequest)(nil), "magma.orc8r.ReportEndedTraceRequest") + proto.RegisterType((*ReportEndedTraceResponse)(nil), "magma.orc8r.ReportEndedTraceResponse") } func init() { proto.RegisterFile("orc8r/protos/ctraced.proto", fileDescriptor_74b70534723ed60d) } var fileDescriptor_74b70534723ed60d = []byte{ - // 500 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x84, 0x93, 0xdb, 0x8e, 0xda, 0x3c, - 0x10, 0x80, 0x09, 0xf0, 0x73, 0x98, 0x1f, 0x58, 0xaf, 0xaf, 0xb2, 0xf4, 0x84, 0x52, 0xa9, 0xa5, - 0x6a, 0x15, 0xd4, 0xed, 0x4d, 0x6f, 0x21, 0xcd, 0x22, 0x2a, 0x58, 0xa8, 0x93, 0x95, 0xaa, 0xde, - 0xac, 0xb2, 0xc6, 0xbb, 0x8a, 0x94, 0xc4, 0x69, 0x6c, 0x2a, 0xf1, 0x1c, 0x7d, 0x97, 0x3e, 0x5f, - 0xc5, 0x84, 0x70, 0x68, 0xb5, 0xe5, 0x2a, 0x9e, 0xf1, 0x37, 0x5f, 0xec, 0xd1, 0x18, 0xba, 0x32, - 0xe3, 0x1f, 0xb3, 0x41, 0x9a, 0x49, 0x2d, 0xd5, 0x80, 0xeb, 0x2c, 0xe0, 0x62, 0x69, 0x63, 0x48, - 0xff, 0x8f, 0x83, 0x87, 0x38, 0xb0, 0x91, 0xe8, 0x5e, 0x1c, 0x83, 0x32, 0x8e, 0x65, 0x92, 0x73, - 0xd6, 0xcf, 0x2a, 0x9c, 0x7b, 0x3a, 0xc8, 0xb4, 0xbf, 0xa9, 0x66, 0xe2, 0xfb, 0x4a, 0x28, 0x4d, - 0xc7, 0x00, 0x68, 0xbb, 0xd5, 0xeb, 0x54, 0x98, 0x46, 0xcf, 0xe8, 0x77, 0x2e, 0xfb, 0xf6, 0x81, - 0xd2, 0xfe, 0xab, 0xc6, 0xc6, 0xc0, 0x5f, 0xa7, 0x82, 0x35, 0x75, 0xb1, 0xa4, 0x14, 0xaa, 0x61, - 0xac, 0x42, 0xb3, 0xdc, 0x33, 0xfa, 0x4d, 0x86, 0x6b, 0x3a, 0x86, 0x06, 0xfe, 0x9b, 0xcb, 0xc8, - 0xac, 0xa0, 0xfa, 0xed, 0x09, 0xf5, 0x62, 0x8b, 0x5f, 0x07, 0xb1, 0x60, 0xbb, 0x62, 0xfa, 0x19, - 0x9a, 0x61, 0xa2, 0x45, 0x76, 0x1f, 0x70, 0x61, 0x56, 0xd1, 0xf4, 0xee, 0x84, 0x69, 0x52, 0xf0, - 0xa8, 0xda, 0x97, 0x53, 0x13, 0xea, 0x3a, 0x8c, 0x85, 0x5c, 0x69, 0xf3, 0xbf, 0x9e, 0xd1, 0x6f, - 0xb3, 0x22, 0xa4, 0xaf, 0xe1, 0x8c, 0x07, 0xa9, 0x5e, 0x65, 0xe2, 0xf6, 0x3e, 0x8c, 0xb4, 0xc8, - 0x94, 0x59, 0xc3, 0xdb, 0x74, 0xb6, 0xe9, 0xab, 0x3c, 0xbb, 0x01, 0x97, 0xa1, 0x4a, 0xa3, 0x60, - 0xbd, 0x03, 0xeb, 0x39, 0xb8, 0x4d, 0x6f, 0x41, 0x6b, 0x06, 0xcd, 0x5d, 0xb3, 0x68, 0x1d, 0x2a, - 0xc3, 0xe9, 0x94, 0x94, 0x68, 0x07, 0xc0, 0xbb, 0x19, 0x79, 0x0e, 0x9b, 0x8c, 0x5c, 0x46, 0x0c, - 0xda, 0x82, 0xc6, 0x82, 0xcd, 0xfd, 0xb9, 0x33, 0x9f, 0x92, 0x32, 0x6d, 0x43, 0x73, 0x72, 0xed, - 0xbb, 0xec, 0x6a, 0xe8, 0xb8, 0xa4, 0x42, 0x01, 0x6a, 0xce, 0x8d, 0xe7, 0xcf, 0x67, 0xa4, 0x6a, - 0xbd, 0x82, 0xd6, 0x61, 0x83, 0x68, 0x03, 0xaa, 0x9e, 0xe3, 0x2f, 0x48, 0x69, 0xa3, 0xf8, 0x34, - 0x19, 0xce, 0x5c, 0x7f, 0x23, 0xb4, 0xde, 0x40, 0xfb, 0xe8, 0xfa, 0x08, 0xbe, 0x1f, 0x6e, 0xc0, - 0x1a, 0x94, 0xc7, 0x5f, 0x89, 0x81, 0x5f, 0x9f, 0x94, 0x2d, 0x1b, 0xe8, 0x61, 0xef, 0x54, 0x2a, - 0x13, 0x85, 0x3d, 0x52, 0x2b, 0xce, 0x85, 0x52, 0x38, 0x12, 0x0d, 0x56, 0x84, 0xd6, 0x39, 0x9c, - 0xb9, 0xc9, 0xf2, 0xb0, 0xd3, 0xd6, 0x17, 0x20, 0xfb, 0xd4, 0x29, 0x01, 0x7d, 0x09, 0xed, 0x7c, - 0xe0, 0xb8, 0x4c, 0xb4, 0x48, 0x34, 0x0e, 0x4c, 0x8b, 0xb5, 0x30, 0xe9, 0xe4, 0xb9, 0xcb, 0x5f, - 0x06, 0x10, 0x27, 0x88, 0x22, 0x94, 0x7a, 0x22, 0xfb, 0x11, 0x72, 0x41, 0x3d, 0xe8, 0xe0, 0x51, - 0x77, 0x1b, 0xf4, 0xf9, 0xbf, 0x67, 0xa0, 0xfb, 0xe2, 0xd1, 0xfd, 0xfc, 0x98, 0x56, 0x89, 0xce, - 0xa0, 0xe5, 0x26, 0xcb, 0xbd, 0xf2, 0xe9, 0x51, 0xc9, 0x1f, 0x57, 0xed, 0x3e, 0x7b, 0x64, 0xb7, - 0xd0, 0x8d, 0x9e, 0x7c, 0xbb, 0x40, 0x62, 0x90, 0xbf, 0xc3, 0x28, 0xbc, 0x1b, 0x3c, 0xc8, 0xed, - 0x73, 0xbc, 0xab, 0xe1, 0xf7, 0xc3, 0xef, 0x00, 0x00, 0x00, 0xff, 0xff, 0xa6, 0xec, 0xb2, 0x34, - 0xce, 0x03, 0x00, 0x00, + // 576 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x8c, 0x94, 0x5b, 0x8f, 0xd2, 0x40, + 0x14, 0xc7, 0x29, 0x20, 0x94, 0x23, 0xb0, 0x75, 0x34, 0xb1, 0xd4, 0x1b, 0xa9, 0x37, 0x8c, 0x9b, + 0x12, 0xf1, 0xc5, 0x57, 0xa8, 0x5d, 0x82, 0x81, 0x05, 0xa7, 0xdd, 0xc4, 0xf8, 0xb2, 0xe9, 0xb6, + 0xb3, 0x9b, 0x26, 0x6d, 0xa7, 0x4e, 0x07, 0x13, 0x12, 0x3f, 0x93, 0x8f, 0x7e, 0x3e, 0xd3, 0x29, + 0xe5, 0xe2, 0x8a, 0xf8, 0xd4, 0x9e, 0x33, 0xff, 0xf3, 0xfb, 0xf7, 0x9c, 0x9e, 0x0c, 0x68, 0x94, + 0x79, 0x1f, 0x58, 0x3f, 0x61, 0x94, 0xd3, 0xb4, 0xef, 0x71, 0xe6, 0x7a, 0xc4, 0x37, 0x44, 0x88, + 0xee, 0x46, 0xee, 0x4d, 0xe4, 0x1a, 0x42, 0xa1, 0x75, 0xf6, 0x85, 0x34, 0x8a, 0x68, 0x9c, 0xeb, + 0xf4, 0x9f, 0x55, 0xb8, 0x67, 0x73, 0x97, 0x71, 0x27, 0xab, 0xc6, 0xe4, 0xdb, 0x92, 0xa4, 0x1c, + 0x75, 0x40, 0x16, 0xb4, 0xcb, 0xc0, 0x57, 0xe5, 0xae, 0xd4, 0x6b, 0xe0, 0xba, 0x88, 0x27, 0x3e, + 0x1a, 0x03, 0xe4, 0x47, 0x7c, 0x95, 0x10, 0x55, 0xea, 0x4a, 0xbd, 0xf6, 0xa0, 0x67, 0xec, 0xb8, + 0x19, 0xb7, 0x70, 0x86, 0x08, 0x9c, 0x55, 0x42, 0x70, 0x83, 0x17, 0xaf, 0x08, 0x41, 0x35, 0x88, + 0xd2, 0x40, 0x2d, 0x0b, 0xbe, 0x78, 0x47, 0x63, 0x90, 0xc5, 0x67, 0x79, 0x34, 0x54, 0x2b, 0x02, + 0xfd, 0xf6, 0x08, 0x7a, 0xb1, 0x96, 0x9f, 0xbb, 0x11, 0xc1, 0x9b, 0x62, 0xf4, 0x09, 0x1a, 0x41, + 0xcc, 0x09, 0xbb, 0x76, 0x3d, 0xa2, 0x56, 0x05, 0xe9, 0xf4, 0x08, 0x69, 0x52, 0xe8, 0x05, 0x6a, + 0x5b, 0x8e, 0x54, 0xa8, 0xf3, 0x20, 0x22, 0x74, 0xc9, 0xd5, 0x3b, 0x5d, 0xa9, 0xd7, 0xc2, 0x45, + 0x88, 0x5e, 0xc3, 0x89, 0xe7, 0x26, 0x7c, 0xc9, 0xc8, 0xe5, 0x75, 0x10, 0x72, 0xc2, 0x52, 0xb5, + 0x26, 0xba, 0x69, 0xaf, 0xd3, 0x67, 0x79, 0x36, 0x13, 0xfa, 0x41, 0x9a, 0x84, 0xee, 0x6a, 0x23, + 0xac, 0xe7, 0xc2, 0x75, 0x7a, 0x2d, 0xd4, 0x67, 0xd0, 0xd8, 0x0c, 0x0b, 0xd5, 0xa1, 0x32, 0x9c, + 0x4e, 0x95, 0x12, 0x6a, 0x03, 0xd8, 0x17, 0x23, 0xdb, 0xc4, 0x93, 0x91, 0x85, 0x15, 0x09, 0x35, + 0x41, 0x5e, 0xe0, 0xb9, 0x33, 0x37, 0xe7, 0x53, 0xa5, 0x8c, 0x5a, 0xd0, 0x98, 0x9c, 0x3b, 0x16, + 0x3e, 0x1b, 0x9a, 0x96, 0x52, 0x41, 0x00, 0x35, 0xf3, 0xc2, 0x76, 0xe6, 0x33, 0xa5, 0xaa, 0xbf, + 0x82, 0xe6, 0xee, 0x80, 0x90, 0x0c, 0x55, 0xdb, 0x74, 0x16, 0x4a, 0x29, 0x43, 0x7c, 0x9c, 0x0c, + 0x67, 0x96, 0x93, 0x01, 0xf5, 0x37, 0xd0, 0xda, 0x6b, 0x5f, 0x08, 0xdf, 0x0d, 0x33, 0x61, 0x0d, + 0xca, 0xe3, 0x2f, 0x8a, 0x24, 0x9e, 0x8e, 0x52, 0xd6, 0x0d, 0x40, 0xbb, 0xb3, 0x4b, 0x13, 0x1a, + 0xa7, 0x62, 0x46, 0xe9, 0xd2, 0xf3, 0x48, 0x9a, 0x8a, 0x95, 0x90, 0x71, 0x11, 0xea, 0xa7, 0x70, + 0x62, 0xc5, 0xfe, 0xc1, 0xed, 0x92, 0xf6, 0xb6, 0x4b, 0xff, 0x0c, 0xca, 0x56, 0x7d, 0x8c, 0x8d, + 0x9e, 0x43, 0x2b, 0x07, 0x79, 0x34, 0xe6, 0x24, 0xe6, 0x62, 0x97, 0x9a, 0xb8, 0x29, 0x92, 0x66, + 0x9e, 0xd3, 0x53, 0x78, 0x88, 0x49, 0x42, 0x19, 0xb7, 0x62, 0x9f, 0xfc, 0xef, 0x87, 0xec, 0x9a, + 0x96, 0x8f, 0x98, 0x56, 0xfe, 0x62, 0xaa, 0x81, 0x7a, 0xdb, 0x34, 0xef, 0x67, 0xf0, 0x4b, 0x02, + 0xc5, 0x74, 0xc3, 0x50, 0x64, 0x6d, 0xc2, 0xbe, 0x07, 0x1e, 0x41, 0x36, 0xb4, 0xc5, 0x58, 0x37, + 0x07, 0xe8, 0xe9, 0xbf, 0xf7, 0x55, 0x7b, 0x76, 0xf0, 0x3c, 0xf7, 0xd1, 0x4b, 0x68, 0x06, 0x4d, + 0x2b, 0xf6, 0xb7, 0xc8, 0xc7, 0x7b, 0x25, 0x7f, 0xfc, 0x16, 0xed, 0xc9, 0x81, 0xd3, 0x02, 0x37, + 0xf8, 0x01, 0xf7, 0x37, 0xac, 0xac, 0x51, 0x46, 0xc3, 0x90, 0x30, 0x44, 0xe0, 0xc1, 0x4e, 0xaf, + 0x5b, 0xb7, 0x17, 0x7b, 0xbc, 0x03, 0xff, 0x40, 0x7b, 0x79, 0x44, 0x55, 0xb8, 0x8f, 0x1e, 0x7d, + 0xed, 0x08, 0x65, 0x3f, 0xbf, 0xcc, 0xc2, 0xe0, 0xaa, 0x7f, 0x43, 0xd7, 0x77, 0xda, 0x55, 0x4d, + 0x3c, 0xdf, 0xff, 0x0e, 0x00, 0x00, 0xff, 0xff, 0x99, 0x9c, 0x1a, 0xed, 0x13, 0x05, 0x00, 0x00, } // Reference imports to suppress errors if they are not otherwise used. @@ -401,7 +509,12 @@ const _ = grpc.SupportPackageIsVersion6 // // For semantics around ctx use and closing/ending streaming RPCs, please refer to https://godoc.org/google.golang.org/grpc#ClientConn.NewStream. type CallTraceServiceClient interface { + // Start a call trace on the gateway with the specified options. + // Only a single call trace can be active on a gateway at a time. + // StartCallTrace(ctx context.Context, in *StartTraceRequest, opts ...grpc.CallOption) (*StartTraceResponse, error) + // End the call trace that is currently active on the gateway. + // EndCallTrace(ctx context.Context, in *EndTraceRequest, opts ...grpc.CallOption) (*EndTraceResponse, error) } @@ -433,7 +546,12 @@ func (c *callTraceServiceClient) EndCallTrace(ctx context.Context, in *EndTraceR // CallTraceServiceServer is the server API for CallTraceService service. type CallTraceServiceServer interface { + // Start a call trace on the gateway with the specified options. + // Only a single call trace can be active on a gateway at a time. + // StartCallTrace(context.Context, *StartTraceRequest) (*StartTraceResponse, error) + // End the call trace that is currently active on the gateway. + // EndCallTrace(context.Context, *EndTraceRequest) (*EndTraceResponse, error) } @@ -504,3 +622,79 @@ var _CallTraceService_serviceDesc = grpc.ServiceDesc{ Streams: []grpc.StreamDesc{}, Metadata: "orc8r/protos/ctraced.proto", } + +// CallTraceControllerClient is the client API for CallTraceController service. +// +// For semantics around ctx use and closing/ending streaming RPCs, please refer to https://godoc.org/google.golang.org/grpc#ClientConn.NewStream. +type CallTraceControllerClient interface { + // Report that a call trace has ended + // + ReportEndedCallTrace(ctx context.Context, in *ReportEndedTraceRequest, opts ...grpc.CallOption) (*ReportEndedTraceResponse, error) +} + +type callTraceControllerClient struct { + cc grpc.ClientConnInterface +} + +func NewCallTraceControllerClient(cc grpc.ClientConnInterface) CallTraceControllerClient { + return &callTraceControllerClient{cc} +} + +func (c *callTraceControllerClient) ReportEndedCallTrace(ctx context.Context, in *ReportEndedTraceRequest, opts ...grpc.CallOption) (*ReportEndedTraceResponse, error) { + out := new(ReportEndedTraceResponse) + err := c.cc.Invoke(ctx, "/magma.orc8r.CallTraceController/ReportEndedCallTrace", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +// CallTraceControllerServer is the server API for CallTraceController service. +type CallTraceControllerServer interface { + // Report that a call trace has ended + // + ReportEndedCallTrace(context.Context, *ReportEndedTraceRequest) (*ReportEndedTraceResponse, error) +} + +// UnimplementedCallTraceControllerServer can be embedded to have forward compatible implementations. +type UnimplementedCallTraceControllerServer struct { +} + +func (*UnimplementedCallTraceControllerServer) ReportEndedCallTrace(ctx context.Context, req *ReportEndedTraceRequest) (*ReportEndedTraceResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method ReportEndedCallTrace not implemented") +} + +func RegisterCallTraceControllerServer(s *grpc.Server, srv CallTraceControllerServer) { + s.RegisterService(&_CallTraceController_serviceDesc, srv) +} + +func _CallTraceController_ReportEndedCallTrace_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(ReportEndedTraceRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(CallTraceControllerServer).ReportEndedCallTrace(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/magma.orc8r.CallTraceController/ReportEndedCallTrace", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(CallTraceControllerServer).ReportEndedCallTrace(ctx, req.(*ReportEndedTraceRequest)) + } + return interceptor(ctx, in, info, handler) +} + +var _CallTraceController_serviceDesc = grpc.ServiceDesc{ + ServiceName: "magma.orc8r.CallTraceController", + HandlerType: (*CallTraceControllerServer)(nil), + Methods: []grpc.MethodDesc{ + { + MethodName: "ReportEndedCallTrace", + Handler: _CallTraceController_ReportEndedCallTrace_Handler, + }, + }, + Streams: []grpc.StreamDesc{}, + Metadata: "orc8r/protos/ctraced.proto", +} diff --git a/orc8r/protos/ctraced.proto b/orc8r/protos/ctraced.proto index 36915ef5daac..d2f485e1d0e3 100644 --- a/orc8r/protos/ctraced.proto +++ b/orc8r/protos/ctraced.proto @@ -24,13 +24,20 @@ option go_package = "magma/orc8r/lib/go/protos"; // -------------------------------------------------------------------------- service CallTraceService { + // Start a call trace on the gateway with the specified options. + // Only a single call trace can be active on a gateway at a time. + // rpc StartCallTrace (StartTraceRequest) returns (StartTraceResponse) {} + // End the call trace that is currently active on the gateway. + // rpc EndCallTrace (EndTraceRequest) returns (EndTraceResponse) {} } // StartTraceRequest specifies options for a call trace started on a gateway message StartTraceRequest { + string trace_id = 8; + enum TraceType { ALL = 0; SUBSCRIBER = 1; @@ -92,9 +99,30 @@ message StartTraceResponse { bool success = 1; // May fail due to an existing tracing session } -message EndTraceRequest {} +message EndTraceRequest { + string trace_id = 1; +} message EndTraceResponse { bool success = 1; // May fail due to no existing tracing session bytes trace_content = 2; // Max size of 4MB } + +// -------------------------------------------------------------------------- +// CallTraceController definition (runs on cloud) +// -------------------------------------------------------------------------- + +service CallTraceController { + // Report that a call trace has ended + // + rpc ReportEndedCallTrace(ReportEndedTraceRequest) returns (ReportEndedTraceResponse) {} +} + +message ReportEndedTraceRequest { + string trace_id = 1; + bool success = 2; + bytes trace_content = 3; +} + +message ReportEndedTraceResponse { +} From 50fcdafdf415293ec6a49801318c7f53a052046d Mon Sep 17 00:00:00 2001 From: Hunter Gatewood Date: Tue, 6 Apr 2021 16:13:44 -0700 Subject: [PATCH 37/91] [docs] Add contributing guidelines to PR template (#5973) Signed-off-by: Hunter Gatewood --- .github/PULL_REQUEST_TEMPLATE.md | 11 ++ CONTRIBUTING.md | 10 -- README.md | 41 ++++-- docs/docusaurus/i18n/en.json | 5 +- .../static/img/magma-logo-purple.svg | 135 ++++++++++++++++++ .../contributing/contribute_conventions.md | 88 +++++++++--- .../contributing/contribute_onboarding.md | 20 +-- docs/readmes/proposals/README.md | 2 +- 8 files changed, 257 insertions(+), 55 deletions(-) delete mode 100644 CONTRIBUTING.md create mode 100644 docs/docusaurus/static/img/magma-logo-purple.svg diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md index 30541d7cf431..bd14ee367829 100644 --- a/.github/PULL_REQUEST_TEMPLATE.md +++ b/.github/PULL_REQUEST_TEMPLATE.md @@ -23,3 +23,14 @@ All upgrade instructions for backwards-breaking changes will be aggregated in the next release's changelog so this is very important to fill out. --> + + diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md deleted file mode 100644 index a1ba4e3fd4f1..000000000000 --- a/CONTRIBUTING.md +++ /dev/null @@ -1,10 +0,0 @@ -# Contributing to Magma -We want to make contributing to this project as easy and transparent as -possible. - -Please refer to the [Governance](https://github.com/magma/community) -and [Contribution](https://github.com/magma/community/blob/main/CONTRIBUTING.md) -model to get started. - -The [Wiki](https://github.com/magma/magma/wiki) outlines -language specific guidelines. diff --git a/README.md b/README.md index 18f52acf9646..c7cb41ffc090 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,6 @@ -# Magma +[![Magma](https://raw.githubusercontent.com/magma/magma/master/docs/docusaurus/static/img/magma-logo-purple.svg)](https://www.magmacore.org/) + +**Connecting the Next Billion People** [![magma](https://circleci.com/gh/magma/magma.svg?style=shield)](https://circleci.com/gh/magma/magma) @@ -14,28 +16,39 @@ Magma is an open-source software platform that gives network operators an open, The figure below shows the high-level Magma architecture. Magma is 3GPP generation (2G, 3G, 4G or upcoming 5G networks) and access network agnostic (cellular or WiFi). It can flexibly support a radio access network with minimal development and deployment effort. -Magma has three major components: +Magma has three major components -* **Access Gateway:** The Access Gateway (AGW) provides network services and policy enforcement. In an LTE network, the AGW implements an evolved packet core (EPC), and a combination of an AAA and a PGW. It works with existing, unmodified commercial radio hardware. +* **Access Gateway.** The Access Gateway (AGW) provides network services and policy enforcement. In an LTE network, the AGW implements an evolved packet core (EPC), and a combination of an AAA and a PGW. It works with existing, unmodified commercial radio hardware. -* **Orchestrator:** Orchestrator is a cloud service that provides a simple and consistent way to configure and monitor the wireless network securely. The Orchestrator can be hosted on a public/private cloud. The metrics acquired through the platform allows you to see the analytics and traffic flows of the wireless users through the Magma web UI. +* **Orchestrator.** Orchestrator is a cloud service that provides a simple and consistent way to configure and monitor the wireless network securely. The Orchestrator can be hosted on a public/private cloud. The metrics acquired through the platform allows you to see the analytics and traffic flows of the wireless users through the Magma web UI. -* **Federation Gateway:** The Federation Gateway integrates the MNO core network with Magma by using standard 3GPP interfaces to existing MNO components. It acts as a proxy between the Magma AGW and the operator's network and facilitates core functions, such as authentication, data plans, policy enforcement, and charging to stay uniform between an existing MNO network and the expanded network with Magma. +* **Federation Gateway.** The Federation Gateway integrates the MNO core network with Magma by using standard 3GPP interfaces to existing MNO components. It acts as a proxy between the Magma AGW and the operator's network and facilitates core functions, such as authentication, data plans, policy enforcement, and charging to stay uniform between an existing MNO network and the expanded network with Magma. ![Magma architecture diagram](docs/readmes/assets/magma_overview.png?raw=true "Magma Architecture") -## Usage Docs -The documentation for developing and using Magma is available at: [https://docs.magmacore.org/docs/basics/introduction.html](https://docs.magmacore.org) +## Documentation + +Magma's usage docs, and developer docs, are available at [https://docs.magmacore.org/docs/basics/introduction.html](https://docs.magmacore.org). + +## Join the Magma community + +See the [Community](https://www.magmacore.org/community/) page for entry points. + +Start by joining the community on Slack: [magmacore workspace](https://join.slack.com/t/magmacore/shared_invite/zt-g76zkofr-g6~jYiS3KRzC9qhAISUC2A). + +## Contributing + +Start with the project's contributing conventions -## Join the Magma Community +- [Contributing Conventions](https://docs.magmacore.org/docs/next/contributing/contribute_conventions) + for conventions on contributing to the project -- Mailing lists: - - Join [magma-dev](https://groups.google.com/forum/#!forum/magma-dev) for technical discussions - - Join [magma-announce](https://groups.google.com/forum/#!forum/magma-announce) for announcements -- Slack: - - [magma\_dev](https://join.slack.com/t/magmacore/shared_invite/zt-g76zkofr-g6~jYiS3KRzC9qhAISUC2A) channel +If you're new to the project, also consider reading -See the [CONTRIBUTING](CONTRIBUTING.md) file for how to help out. +- [Developer onboarding](https://docs.magmacore.org/docs/next/contributing/contribute_onboarding) + for onboarding to the project +- [Development workflow](https://docs.magmacore.org/docs/next/contributing/contribute_workflow) for how to open a + pull request ## License diff --git a/docs/docusaurus/i18n/en.json b/docs/docusaurus/i18n/en.json index 94bc086e0ed9..af3b3381ecd9 100644 --- a/docs/docusaurus/i18n/en.json +++ b/docs/docusaurus/i18n/en.json @@ -18,7 +18,7 @@ "title": "Codeowners Workflow" }, "contributing/contribute_conventions": { - "title": "Code Conventions" + "title": "Contributing Conventions" }, "contributing/contribute_onboarding": { "title": "Developer Onboarding" @@ -343,6 +343,9 @@ "proposals/p011_victoriametrics": { "title": "VictoriaMetrics as Magma's TSDB" }, + "proposals/p012_resource-tagging": { + "title": "proposals/p012_resource-tagging" + }, "proposals/qos_enforcement": { "title": "proposals/qos_enforcement" }, diff --git a/docs/docusaurus/static/img/magma-logo-purple.svg b/docs/docusaurus/static/img/magma-logo-purple.svg new file mode 100644 index 000000000000..da6209337c40 --- /dev/null +++ b/docs/docusaurus/static/img/magma-logo-purple.svg @@ -0,0 +1,135 @@ + +image/svg+xml + + + + + + + + + + + + + + + + + + + + + + diff --git a/docs/readmes/contributing/contribute_conventions.md b/docs/readmes/contributing/contribute_conventions.md index b7427c7a8ccb..fd65f901b3cf 100644 --- a/docs/readmes/contributing/contribute_conventions.md +++ b/docs/readmes/contributing/contribute_conventions.md @@ -1,42 +1,90 @@ --- id: contribute_conventions -title: Code Conventions +title: Contributing Conventions hide_title: true --- -# Code Conventions +# Contributing Conventions -This document describes code conventions for the Magma project. The goal of this style guide is to +This document describes contributing conventions for the Magma project. The goal of this style guide is to - Steer contributors away from experienced landmines - Align on coding styles in support of a uniform Magma codebase, with the aim to improve developer productivity and codebase approachability ## General -Throughout the Magma codebase, the principal convention is the [boy scout rule](https://www.oreilly.com/library/view/97-things-every/9780596809515/ch08.html): leave it better than you found it. Consider the following conventions when adding a PR - -- Add tests for your changes - - Tests should cover, at minimum, the mainline of the new feature. For reference, that usually ends up around 50-70% coverage. - - Unit tests should default to being placed in the same directory as the files they're testing, except for the following - - Python: directly-adjacent `tests` directory - - C/C++: directly-adjacent `tests` directory - - Integration tests should be placed as close to the code-under-test as possible - - If you're not sure how to test a change, reach out on the community Slack workspace for input -- Cleanup changes should be in separate PRs than functional changes - - Exception: if the area of the codebase you're editing needs a cleanup PR, but you don't have bandwidth to add one, default to mimicking the surrounding code -- Use [Go-style doc comments](https://golang.org/doc/effective_go#commentary), where the doc comment is prefixed by the name of the object being documented +Follow these conventions when making changes to the Magma codebase. + +### Leave it better than you found it + +- The project's principal convention is the [boy scout rule](https://www.oreilly.com/library/view/97-things-every/9780596809515/ch08.html): leave it better than you found it + +### Add tests for your changes + +- Tests should cover, at minimum, the mainline of the new feature. For reference, that usually ends up around 50-70% coverage. +- Unit tests should default to being placed in the same directory as the files they're testing, except for the following + - Python: directly-adjacent `tests` directory + - C/C++: directly-adjacent `tests` directory +- Integration tests should be placed as close to the code-under-test as possible +- If you're not sure how to test a change, reach out on the community Slack workspace for input + +### Separate cleanup PRs from functional changes + +- Keeps PRs small and understandable +- Exception: if the area of the codebase you're editing needs a cleanup PR, but you don't have bandwidth to add one, default to mimicking the surrounding code + +### Scope component responsibilities + +- Functions, and components in general, should be narrowly scoped to a single, specific role +- When writing a function over 100 lines long, consider extracting a helper functions out of the intermediate logical steps + +### Prefer immutability and idempotency + +- Prefer immutable state +- When mutability is necessary, consider the following + - Prefer to set a component's state entirely in its constructor + - Mutate a component's state as close to construction-time as possible + - Perform mutations as high in the call chain as possible +- Prefer side-effect-free functions + - When side-effects are necessary, move them as high in the call chain as possible + +### Prefer composition over inheritance + +- Avoid inheritance as a design pattern, in favor of composition and dependency injection +- If complex logic begins bleeding into test case setup, consider pulling that logic into a dependency interface +- Build a complex component as a composition of multiple simpler components with clear interfaces +- Avoid non-trivial static functions: pull interfaces out of the static functions and inject them into depending components + +### Use simple constructors + +- Split complex logic and side-effect-inducing functionality out of the constructor and into an initialization method +- If desired, can also use a static factory function to construct the component and call its initialization method + +### Comment with why, not what + +- Good code is self-documenting +- Instead of defaulting to inline comments, focus on + - Concise and descriptive identifier names + - Intelligent types and pervasive typing information + - High-quality docstrings on functions, components, and top-level identifiers - Avoid "topic sentence" comments - E.g. "this block of code does X ... this block of code does Y", when there's no value-add other than summarizing the next few lines - - Instead, code paragraphs should be skimmable. Also consider breaking dense code paragraphs out into private functions. + - Instead, code paragraphs should be skimmable + - Consider breaking dense code paragraphs out into private functions. +- Save comments for code blocks that require non-obvious context, e.g. answering why an idiosyncratic or non-obvious decision was made + +### Style conventions + +- Use [Go-style doc comments](https://golang.org/doc/effective_go#commentary), where the doc comment is prefixed by the name of the object being documented +- Use [Americanized spellings](https://en.wikipedia.org/wiki/Wikipedia:List_of_spelling_variants) + - marshaling, marshaled + - canceling, canceled, cancellation - Prefer underscores over hyphens - File, directory names - YAML, JSON - Exception: in certain parts of K8s, underscores are disallowed. In this case, hyphens are preferred, and translation between hyphens and underscores is acceptable. - Omit trailing slash of directory paths, except where semantically meaningful -- Don't terminate service names with `d` -- Use [Americanized spellings](https://en.wikipedia.org/wiki/Wikipedia:List_of_spelling_variants) - - marshaling, marshaled - - canceling, canceled, cancellation +- Don't terminate new service names with `d` ## Documentation diff --git a/docs/readmes/contributing/contribute_onboarding.md b/docs/readmes/contributing/contribute_onboarding.md index 540c590fb39b..c10b2f19c1fb 100644 --- a/docs/readmes/contributing/contribute_onboarding.md +++ b/docs/readmes/contributing/contribute_onboarding.md @@ -6,7 +6,7 @@ hide_title: true # Developer Onboarding -This document walks a new developer through the process of onboarding to Magma, specifically to the Orc8r subproject within the Magma project. +This document walks a new developer through the process of onboarding to the Magma project. ## Project overview @@ -14,7 +14,13 @@ Magma is an open-source software platform that gives network operators an open, In more approachable terms, Magma is a collection of software that makes running things like a cellular network affordable and customizable. With Magma's current rate of growth, increasing stability and reliability of the platform are all huge wins. -There are three main components in the Magma software platform: Access Gateway (AGW), Orc8r (Orc8r), and Federation Gateway (FeG). If you want to read more, see the [Magma intro docs](https://magma.github.io/magma/docs/next/basics/introduction.html). +There are three main components in the Magma software platform: Access Gateway (AGW), Orchestrator (Orc8r), and Federation Gateway (FeG). If you want to read more, see the [Magma intro docs](https://magma.github.io/magma/docs/next/basics/introduction.html). + +### Access Gateway + +The AGW provides network services and policy enforcement. In an LTE network, the AGW implements an evolved packet core (EPC), and a combination of an AAA and a PGW. It works with existing, unmodified commercial radio hardware. + +More generally, the AGW defines datapath rules for connecting subscribers through to the Internet. It pulls configuration from Orc8r, sets datapath rules, manages charging and accounting, reports state and metrics to Orc8r, and more. ### Orchestrator @@ -22,10 +28,6 @@ The Orc8r is a centralized controller for a set of networks. In [SDN](https://en One of the functions of the Orc8r is to expose a management [REST API](https://restfulapi.net/). The REST API is defined as an [OpenAPI](https://swagger.io/solutions/getting-started-with-oas/) [specification](https://swagger.io/specification/) (aka [Swagger](https://swagger.io/blog/api-strategy/difference-between-swagger-and-openapi/) specification), and made available to operators over [mutually-authenticated](https://comodosslstore.com/blog/what-is-ssl-tls-client-authentication-how-does-it-work.html) HTTPS. The Orc8r also exposes a series of [gRPC](https://grpc.io/) services which gateways can call to report state, receive config updates, etc. -### Access Gateway - -The AGW defines datapath rules for connecting subscribers through to the Internet. It pulls configuration from Orc8r, sets datapath rules, manages charging and accounting, reports state and metrics to Orc8r, and more. - ### Federation Gateway The FeG serves as the interface to existing operator cores, affording a modern, flexible interface on top of existing protocols and architectures. @@ -96,8 +98,8 @@ Install Magma locally and get everything running. 1. Follow the [prerequisites guide](https://magma.github.io/magma/docs/next/basics/prerequisites) and install all development tools, up to but not including the "Build/Deploy Tooling" section 2. Run all Orc8r tests - 1. Via Docker build script: `cd ${MAGMA_ROOT}/Orc8r/cloud/docker && ./build.py -t ; noti` - 2. [Via IntelliJ](https://magma.github.io/magma/docs/next/Orc8r/development/testing_tips) + 1. Via Docker build script: `cd ${MAGMA_ROOT}/orc8r/cloud/docker && ./build.py -t ; noti` + 2. [Via IntelliJ](https://magma.github.io/magma/docs/next/orc8r/development/testing_tips) 3. Follow the [quick start guide](https://magma.github.io/magma/docs/next/basics/quick_start_guide) to get an AGW and Orc8r instance running on your dev machine 4. Visit the local [Swagger UI](https://swagger.io/tools/swagger-ui/) view of our REST API (URL is in @hcgatewood's Google Chrome bookmarks) and [list the set of managed networks](https://localhost:9443/apidocs/v1/#/Networks/get_networks) -- there should be one named "test" - You will need to toggle a Google Chrome preference to [allow insecure localhost](https://superuser.com/questions/772762/how-can-i-disable-security-checks-for-localhost) @@ -108,7 +110,7 @@ Note: remember to periodically call `docker system prune` to clear outdated Dock If you haven't already, join our community Slack channel via the link from the [Community](https://www.magmacore.org/community/) page and say hello in `#general`. We can point you toward a good starter task. You can also use the [`good first issue` tag on our GitHub repo to search for good starter tasks](https://github.com/magma/magma/labels/good%20first%20issue). -As you're working on your starter task, refer to [Development Workflow](./contribute_workflow.md) for guidance on how to author a pull request and [Code Conventions](./contribute_conventions.md) for Magma conventions. Once your pull request is approved and merged, you're now an official contributor to the Magma project! +As you're working on your starter task, refer to [Development Workflow](./contribute_workflow.md) for guidance on how to author a pull request and [Contributing Conventions](./contribute_conventions.md) for Magma conventions. Once your pull request is approved and merged, you're now an official contributor to the Magma project! ### Next steps diff --git a/docs/readmes/proposals/README.md b/docs/readmes/proposals/README.md index 2543a57e13f9..5beb8429e823 100644 --- a/docs/readmes/proposals/README.md +++ b/docs/readmes/proposals/README.md @@ -96,7 +96,7 @@ document. the directory and `short-name` is a short name (a few dash-separated words at most). Follow the Magma Github contribution process from the - [Magma Contributing Wiki](https://github.com/magma/magma/wiki/Contributing). + [Magma Contributing Conventions](https://docs.magmacore.org/docs/next/contributing/contribute_conventions). - The design doc should follow [the template](TEMPLATE.md). From a66fe8024bbfb76f468801aba978f90d34d94796 Mon Sep 17 00:00:00 2001 From: Andrei Lee Date: Tue, 6 Apr 2021 17:02:50 -0700 Subject: [PATCH 38/91] Specify orc8r db dialect and port in tf charts (#5975) Signed-off-by: Andrei Lee --- orc8r/cloud/deploy/terraform/orc8r-aws/outputs.tf | 5 +++++ orc8r/cloud/deploy/terraform/orc8r-aws/variables.tf | 6 ++++++ .../terraform/orc8r-helm-aws/examples/basic/main.tf | 10 ++++++---- .../orc8r-helm-aws/examples/online-upgrade/main.tf | 9 +++++---- .../terraform/orc8r-helm-aws/examples/remote/main.tf | 10 ++++++---- orc8r/cloud/deploy/terraform/orc8r-helm-aws/main.tf | 9 +++++---- .../orc8r-helm-aws/templates/orc8r-values.tpl | 3 +++ .../cloud/deploy/terraform/orc8r-helm-aws/variables.tf | 7 ++++++- orc8r/cloud/helm/orc8r/Chart.lock | 6 +++--- orc8r/cloud/helm/orc8r/Chart.yaml | 4 ++-- orc8r/cloud/helm/orc8r/charts/nms/Chart.yaml | 2 +- .../charts/nms/templates/magmalte-deployment.yaml | 2 ++ orc8r/cloud/helm/orc8r/charts/nms/values.yaml | 3 ++- 13 files changed, 52 insertions(+), 24 deletions(-) diff --git a/orc8r/cloud/deploy/terraform/orc8r-aws/outputs.tf b/orc8r/cloud/deploy/terraform/orc8r-aws/outputs.tf index f365b975dc00..9d792fbce38c 100644 --- a/orc8r/cloud/deploy/terraform/orc8r-aws/outputs.tf +++ b/orc8r/cloud/deploy/terraform/orc8r-aws/outputs.tf @@ -67,6 +67,11 @@ output "orc8r_db_port" { value = aws_db_instance.default.port } +output "orc8r_db_dialect" { + description = "Database dialect for Orchestrator RDS instance" + value = var.orc8r_db_dialect +} + output "orc8r_db_user" { description = "Database username for Orchestrator RDS instance" value = aws_db_instance.default.username diff --git a/orc8r/cloud/deploy/terraform/orc8r-aws/variables.tf b/orc8r/cloud/deploy/terraform/orc8r-aws/variables.tf index 63fcbc86a24e..5f901ecc949b 100644 --- a/orc8r/cloud/deploy/terraform/orc8r-aws/variables.tf +++ b/orc8r/cloud/deploy/terraform/orc8r-aws/variables.tf @@ -239,6 +239,12 @@ variable "orc8r_db_engine_version" { default = "9.6.15" } +variable "orc8r_db_dialect" { + description = "Database dialect for Orchestrator DB." + type = string + default = "postgres" +} + ############################################################################## # Secretmanager configuration ############################################################################## diff --git a/orc8r/cloud/deploy/terraform/orc8r-helm-aws/examples/basic/main.tf b/orc8r/cloud/deploy/terraform/orc8r-helm-aws/examples/basic/main.tf index dd106a5c7137..82f56d3931b8 100644 --- a/orc8r/cloud/deploy/terraform/orc8r-helm-aws/examples/basic/main.tf +++ b/orc8r/cloud/deploy/terraform/orc8r-helm-aws/examples/basic/main.tf @@ -49,10 +49,12 @@ module "orc8r-app" { secretsmanager_orc8r_name = module.orc8r.secretsmanager_secret_name seed_certs_dir = "~/secrets/certs" - orc8r_db_host = module.orc8r.orc8r_db_host - orc8r_db_name = module.orc8r.orc8r_db_name - orc8r_db_user = module.orc8r.orc8r_db_user - orc8r_db_pass = module.orc8r.orc8r_db_pass + orc8r_db_host = module.orc8r.orc8r_db_host + orc8r_db_port = module.orc8r.orc8r_db_port + orc8r_db_dialect = module.orc8r.orc8r_db_dialect + orc8r_db_name = module.orc8r.orc8r_db_name + orc8r_db_user = module.orc8r.orc8r_db_user + orc8r_db_pass = module.orc8r.orc8r_db_pass # Note that this can be any container registry provider -- the example below # provides the URL format for Docker Hub, where the user and pass are your diff --git a/orc8r/cloud/deploy/terraform/orc8r-helm-aws/examples/online-upgrade/main.tf b/orc8r/cloud/deploy/terraform/orc8r-helm-aws/examples/online-upgrade/main.tf index a27e1346fc4d..73a0d9731618 100644 --- a/orc8r/cloud/deploy/terraform/orc8r-helm-aws/examples/online-upgrade/main.tf +++ b/orc8r/cloud/deploy/terraform/orc8r-helm-aws/examples/online-upgrade/main.tf @@ -113,10 +113,11 @@ module orc8r-app { existing_tiller_service_account_name = "tiller" helm_deployment_name = var.new_deployment_name - orc8r_db_host = module.orc8r.orc8r_db_host - orc8r_db_name = module.orc8r.orc8r_db_name - orc8r_db_user = module.orc8r.orc8r_db_user - orc8r_db_pass = module.orc8r.orc8r_db_pass + orc8r_db_host = module.orc8r.orc8r_db_host + orc8r_db_dialect = module.orc8r.orc8r_db_dialect + orc8r_db_name = module.orc8r.orc8r_db_name + orc8r_db_user = module.orc8r.orc8r_db_user + orc8r_db_pass = module.orc8r.orc8r_db_pass docker_registry = var.docker_registry docker_user = var.docker_user diff --git a/orc8r/cloud/deploy/terraform/orc8r-helm-aws/examples/remote/main.tf b/orc8r/cloud/deploy/terraform/orc8r-helm-aws/examples/remote/main.tf index f122f303da97..636eb1a1c7cb 100644 --- a/orc8r/cloud/deploy/terraform/orc8r-helm-aws/examples/remote/main.tf +++ b/orc8r/cloud/deploy/terraform/orc8r-helm-aws/examples/remote/main.tf @@ -111,10 +111,12 @@ module "orc8r-app" { secretsmanager_orc8r_name = module.orc8r.secretsmanager_secret_name seed_certs_dir = "~/orc8r.test.secrets/certs" - orc8r_db_host = module.orc8r.orc8r_db_host - orc8r_db_name = module.orc8r.orc8r_db_name - orc8r_db_user = module.orc8r.orc8r_db_user - orc8r_db_pass = module.orc8r.orc8r_db_pass + orc8r_db_host = module.orc8r.orc8r_db_host + orc8r_db_port = module.orc8r.orc8r_db_port + orc8r_db_dialect = module.orc8r.orc8r_db_dialect + orc8r_db_name = module.orc8r.orc8r_db_name + orc8r_db_user = module.orc8r.orc8r_db_user + orc8r_db_pass = module.orc8r.orc8r_db_pass docker_registry = jsondecode(data.aws_secretsmanager_secret_version.root_secrets.secret_string)["docker_registry"] docker_user = jsondecode(data.aws_secretsmanager_secret_version.root_secrets.secret_string)["docker_user"] diff --git a/orc8r/cloud/deploy/terraform/orc8r-helm-aws/main.tf b/orc8r/cloud/deploy/terraform/orc8r-helm-aws/main.tf index 396566ed5af8..8424b7fc14e2 100644 --- a/orc8r/cloud/deploy/terraform/orc8r-helm-aws/main.tf +++ b/orc8r/cloud/deploy/terraform/orc8r-helm-aws/main.tf @@ -173,10 +173,11 @@ data "template_file" "orc8r_values" { api_hostname = format("api.%s", var.orc8r_domain_name) nms_hostname = format("*.nms.%s", var.orc8r_domain_name) - orc8r_db_name = var.orc8r_db_name - orc8r_db_host = var.orc8r_db_host - orc8r_db_port = var.orc8r_db_port - orc8r_db_user = var.orc8r_db_user + orc8r_db_name = var.orc8r_db_name + orc8r_db_host = var.orc8r_db_host + orc8r_db_port = var.orc8r_db_port + orc8r_db_dialect = var.orc8r_db_dialect + orc8r_db_user = var.orc8r_db_user deploy_nms = var.deploy_nms diff --git a/orc8r/cloud/deploy/terraform/orc8r-helm-aws/templates/orc8r-values.tpl b/orc8r/cloud/deploy/terraform/orc8r-helm-aws/templates/orc8r-values.tpl index ab2c0d28363d..b6d9eb054689 100644 --- a/orc8r/cloud/deploy/terraform/orc8r-helm-aws/templates/orc8r-values.tpl +++ b/orc8r/cloud/deploy/terraform/orc8r-helm-aws/templates/orc8r-values.tpl @@ -184,8 +184,11 @@ nms: env: api_host: ${api_hostname} mysql_db: ${orc8r_db_name} + mysql_dialect: ${orc8r_db_dialect} mysql_host: ${orc8r_db_host} + mysql_port: ${orc8r_db_port} mysql_user: ${orc8r_db_user} + mysql_pass: ${orc8r_db_password} grafana_address: ${user_grafana_hostname} nginx: diff --git a/orc8r/cloud/deploy/terraform/orc8r-helm-aws/variables.tf b/orc8r/cloud/deploy/terraform/orc8r-helm-aws/variables.tf index 5ee930d521a3..cb981b734ac8 100644 --- a/orc8r/cloud/deploy/terraform/orc8r-helm-aws/variables.tf +++ b/orc8r/cloud/deploy/terraform/orc8r-helm-aws/variables.tf @@ -96,6 +96,11 @@ variable "orc8r_db_name" { type = string } +variable "orc8r_db_dialect" { + description = "DB dialect for Orchestrator database connection." + type = string +} + variable "orc8r_db_host" { description = "DB hostname for Orchestrator database connection." type = string @@ -156,7 +161,7 @@ variable "orc8r_deployment_type" { variable "orc8r_chart_version" { description = "Version of the core orchestrator Helm chart to install." type = string - default = "1.5.18" + default = "1.5.19" } variable "cwf_orc8r_chart_version" { diff --git a/orc8r/cloud/helm/orc8r/Chart.lock b/orc8r/cloud/helm/orc8r/Chart.lock index 018265b2192d..42c3e04893e3 100644 --- a/orc8r/cloud/helm/orc8r/Chart.lock +++ b/orc8r/cloud/helm/orc8r/Chart.lock @@ -7,12 +7,12 @@ dependencies: version: 1.4.22 - name: nms repository: "" - version: 0.1.10 + version: 0.1.11 - name: logging repository: "" version: 0.1.10 - name: orc8rlib repository: file://../orc8rlib version: 0.1.2 -digest: sha256:7a49d5fba17dce594a8a6c4c89a7204678acca652c4b4d4727fc07f00132117d -generated: "2021-03-05T11:56:20.589138693+05:30" +digest: sha256:73383945a6f49b14bad2d42ffc0223286cd1d3f106319efc3739dc494547f08c +generated: "2021-04-06T16:10:45.754709-07:00" diff --git a/orc8r/cloud/helm/orc8r/Chart.yaml b/orc8r/cloud/helm/orc8r/Chart.yaml index a30ece0e89f8..be1290677d9c 100644 --- a/orc8r/cloud/helm/orc8r/Chart.yaml +++ b/orc8r/cloud/helm/orc8r/Chart.yaml @@ -13,7 +13,7 @@ apiVersion: v2 appVersion: "1.0" description: A Helm chart for magma orchestrator name: orc8r -version: 1.5.18 +version: 1.5.19 engine: gotpl sources: - https://github.com/magma/magma @@ -31,7 +31,7 @@ dependencies: repository: "" condition: metrics.enabled - name: nms - version: 0.1.10 + version: 0.1.11 repository: "" condition: nms.enabled - name: logging diff --git a/orc8r/cloud/helm/orc8r/charts/nms/Chart.yaml b/orc8r/cloud/helm/orc8r/charts/nms/Chart.yaml index f662ad3c29b0..4cfcf3296285 100644 --- a/orc8r/cloud/helm/orc8r/charts/nms/Chart.yaml +++ b/orc8r/cloud/helm/orc8r/charts/nms/Chart.yaml @@ -12,7 +12,7 @@ apiVersion: v1 description: Magma NMS name: nms -version: 0.1.10 +version: 0.1.11 home: https://github.com/magma/magma sources: - https://github.com/magma/magma diff --git a/orc8r/cloud/helm/orc8r/charts/nms/templates/magmalte-deployment.yaml b/orc8r/cloud/helm/orc8r/charts/nms/templates/magmalte-deployment.yaml index 3257b40755c2..47df3e6d6b26 100644 --- a/orc8r/cloud/helm/orc8r/charts/nms/templates/magmalte-deployment.yaml +++ b/orc8r/cloud/helm/orc8r/charts/nms/templates/magmalte-deployment.yaml @@ -80,6 +80,8 @@ spec: value: {{ .Values.magmalte.env.mysql_db | quote }} - name: MYSQL_HOST value: {{ .Values.magmalte.env.mysql_host | quote }} + - name: MYSQL_PORT + value: {{ .Values.magmalte.env.mysql_port | quote }} - name: MYSQL_DIALECT value: {{ .Values.magmalte.env.mysql_dialect | quote }} - name: MYSQL_PASS diff --git a/orc8r/cloud/helm/orc8r/charts/nms/values.yaml b/orc8r/cloud/helm/orc8r/charts/nms/values.yaml index ed1c888cad33..18cbe25308ea 100644 --- a/orc8r/cloud/helm/orc8r/charts/nms/values.yaml +++ b/orc8r/cloud/helm/orc8r/charts/nms/values.yaml @@ -30,10 +30,11 @@ magmalte: port: 8081 mapbox_access_token: "" mysql_host: mariadb.magma.svc.cluster.local + mysql_port: 5432 mysql_db: magma mysql_user: magma mysql_pass: password - mysql_dialect: mysql + mysql_dialect: postgres grafana_address: orc8r-user-grafana:3000 labels: {} From dd2e2a1d93eed9f1782f85f1bccaebccf6013e1d Mon Sep 17 00:00:00 2001 From: Ulas Kozat Date: Tue, 6 Apr 2021 17:27:09 -0700 Subject: [PATCH 39/91] [lte][agw] Error indication handling (#5956) * Add support for s1ap error indication message Signed-off-by: Ulas Kozat --- .../c/oai/tasks/s1ap/s1ap_mme_handlers.c | 159 +++++++++++++++--- .../c/oai/tasks/s1ap/s1ap_mme_handlers.h | 4 + 2 files changed, 143 insertions(+), 20 deletions(-) diff --git a/lte/gateway/c/oai/tasks/s1ap/s1ap_mme_handlers.c b/lte/gateway/c/oai/tasks/s1ap/s1ap_mme_handlers.c index fcf34bb5639b..231c0324a8ba 100644 --- a/lte/gateway/c/oai/tasks/s1ap/s1ap_mme_handlers.c +++ b/lte/gateway/c/oai/tasks/s1ap/s1ap_mme_handlers.c @@ -1036,8 +1036,6 @@ int s1ap_mme_handle_ue_context_release_request( S1ap_UEContextReleaseRequest_t* container; S1ap_UEContextReleaseRequest_IEs_t* ie = NULL; ue_description_t* ue_ref_p = NULL; - enb_description_t* enb_ref_p = NULL; - MessageDef* message_p = NULL; S1ap_Cause_PR cause_type; long cause_value; enum s1cause s1_release_cause = S1AP_RADIO_EUTRAN_GENERATED_REASON; @@ -1180,23 +1178,9 @@ int s1ap_mme_handle_ue_context_release_request( hashtable_uint64_ts_get( imsi_map->mme_ue_id_imsi_htbl, (const hash_key_t) mme_ue_s1ap_id, &imsi64); + rc = s1ap_send_mme_ue_context_release( + state, ue_ref_p, s1_release_cause, ie->value.choice.Cause, imsi64); - message_p = - itti_alloc_new_message(TASK_S1AP, S1AP_UE_CONTEXT_RELEASE_REQ); - AssertFatal(message_p != NULL, "itti_alloc_new_message Failed"); - - enb_ref_p = s1ap_state_get_enb(state, ue_ref_p->sctp_assoc_id); - - S1AP_UE_CONTEXT_RELEASE_REQ(message_p).mme_ue_s1ap_id = - ue_ref_p->mme_ue_s1ap_id; - S1AP_UE_CONTEXT_RELEASE_REQ(message_p).enb_ue_s1ap_id = - ue_ref_p->enb_ue_s1ap_id; - S1AP_UE_CONTEXT_RELEASE_REQ(message_p).enb_id = enb_ref_p->enb_id; - S1AP_UE_CONTEXT_RELEASE_REQ(message_p).relCause = s1_release_cause; - S1AP_UE_CONTEXT_RELEASE_REQ(message_p).cause = ie->value.choice.Cause; - - message_p->ittiMsgHeader.imsi = imsi64; - rc = send_msg_to_task(&s1ap_task_zmq_ctx, TASK_MME_APP, message_p); OAILOG_FUNC_RETURN(LOG_S1AP, rc); } else { // abnormal case. No need to do anything. Ignore the message @@ -2527,9 +2511,117 @@ int s1ap_mme_handle_error_ind_message( s1ap_state_t* state, const sctp_assoc_id_t assoc_id, const sctp_stream_id_t stream, S1ap_S1AP_PDU_t* message) { OAILOG_FUNC_IN(LOG_S1AP); - OAILOG_WARNING( - LOG_S1AP, "ERROR IND RCVD on Stream id %d, ignoring it\n", stream); + OAILOG_WARNING(LOG_S1AP, "ERROR IND RCVD on Stream id %d \n", stream); increment_counter("s1ap_error_ind_rcvd", 1, NO_LABELS); + S1ap_ErrorIndication_t* container = NULL; + S1ap_ErrorIndicationIEs_t* ie = NULL; + ue_description_t* ue_ref_p = NULL; + enb_ue_s1ap_id_t enb_ue_s1ap_id = INVALID_ENB_UE_S1AP_ID; + mme_ue_s1ap_id_t mme_ue_s1ap_id = INVALID_MME_UE_S1AP_ID; + S1ap_Cause_PR cause_type; + long cause_value; + + container = &message->choice.initiatingMessage.value.choice.ErrorIndication; + S1AP_FIND_PROTOCOLIE_BY_ID( + S1ap_ErrorIndicationIEs_t, ie, container, + S1ap_ProtocolIE_ID_id_MME_UE_S1AP_ID, true); + if (ie) { + mme_ue_s1ap_id = ie->value.choice.MME_UE_S1AP_ID; + } else { + OAILOG_FUNC_RETURN(LOG_S1AP, RETURNerror); + } + + S1AP_FIND_PROTOCOLIE_BY_ID( + S1ap_ErrorIndicationIEs_t, ie, container, + S1ap_ProtocolIE_ID_id_eNB_UE_S1AP_ID, true); + if (ie) { + // eNB UE S1AP ID is limited to 24 bits + enb_ue_s1ap_id = (enb_ue_s1ap_id_t)( + ie->value.choice.ENB_UE_S1AP_ID & ENB_UE_S1AP_ID_MASK); + } else { + OAILOG_FUNC_RETURN(LOG_S1AP, RETURNerror); + } + + S1AP_FIND_PROTOCOLIE_BY_ID( + S1ap_ErrorIndicationIEs_t, ie, container, S1ap_ProtocolIE_ID_id_Cause, + true); + if (ie) { + cause_type = ie->value.choice.Cause.present; + } else { + OAILOG_FUNC_RETURN(LOG_S1AP, RETURNerror); + } + + if ((ue_ref_p = s1ap_state_get_ue_mmeid((uint32_t) mme_ue_s1ap_id)) == NULL) { + OAILOG_WARNING( + LOG_S1AP, + "No UE is attached to this mme UE s1ap id: " MME_UE_S1AP_ID_FMT + " and eNB UE s1ap id: \n" ENB_UE_S1AP_ID_FMT, + mme_ue_s1ap_id, enb_ue_s1ap_id); + OAILOG_FUNC_RETURN(LOG_S1AP, RETURNerror); + } + + imsi64_t imsi64 = INVALID_IMSI64; + s1ap_imsi_map_t* imsi_map = get_s1ap_imsi_map(); + hashtable_uint64_ts_get( + imsi_map->mme_ue_id_imsi_htbl, (const hash_key_t) mme_ue_s1ap_id, + &imsi64); + + switch (cause_type) { + case S1ap_Cause_PR_radioNetwork: + cause_value = ie->value.choice.Cause.choice.radioNetwork; + OAILOG_DEBUG_UE( + LOG_S1AP, imsi64, + "Error Indication with Cause_Type = Radio Network " + "and Cause_Value = %ld\n", + cause_value); + s1ap_send_mme_ue_context_release( + state, ue_ref_p, S1AP_RADIO_EUTRAN_GENERATED_REASON, + ie->value.choice.Cause, imsi64); + break; + + case S1ap_Cause_PR_transport: + cause_value = ie->value.choice.Cause.choice.transport; + OAILOG_DEBUG_UE( + LOG_S1AP, imsi64, + "Error Indication with Cause_Type = Transport and " + "Cause_Value = %ld\n", + cause_value); + break; + + case S1ap_Cause_PR_nas: + cause_value = ie->value.choice.Cause.choice.nas; + OAILOG_DEBUG_UE( + LOG_S1AP, imsi64, + "Error Indication with Cause_Type = NAS and " + "Cause_Value = %ld\n", + cause_value); + break; + + case S1ap_Cause_PR_protocol: + cause_value = ie->value.choice.Cause.choice.protocol; + OAILOG_DEBUG_UE( + LOG_S1AP, imsi64, + "Error Indication with Cause_Type = Protocol and " + "Cause_Value = %ld\n", + cause_value); + break; + + case S1ap_Cause_PR_misc: + cause_value = ie->value.choice.Cause.choice.misc; + OAILOG_DEBUG_UE( + LOG_S1AP, imsi64, + "Error Indication with Cause_Type = MISC and " + "Cause_Value = %ld\n", + cause_value); + break; + + default: + OAILOG_ERROR_UE( + LOG_S1AP, imsi64, "Error Indication with Invalid Cause_Type = %d\n", + cause_type); + OAILOG_FUNC_RETURN(LOG_S1AP, RETURNerror); + } + OAILOG_FUNC_RETURN(LOG_S1AP, RETURNok); } @@ -3894,3 +3986,30 @@ int s1ap_mme_remove_stale_ue_context( send_msg_to_task(&s1ap_task_zmq_ctx, TASK_MME_APP, message_p); OAILOG_FUNC_RETURN(LOG_S1AP, RETURNok); } + +int s1ap_send_mme_ue_context_release( + s1ap_state_t* state, ue_description_t* ue_ref_p, + enum s1cause s1_release_cause, S1ap_Cause_t ie_cause, imsi64_t imsi64) { + MessageDef* message_p = NULL; + message_p = itti_alloc_new_message(TASK_S1AP, S1AP_UE_CONTEXT_RELEASE_REQ); + if (!message_p) { + OAILOG_ERROR( + LOG_S1AP, + "Failed to allocate memory for S1AP_REMOVE_STALE_UE_CONTEXT \n"); + OAILOG_FUNC_RETURN(LOG_S1AP, RETURNerror); + } + + enb_description_t* enb_ref_p = + s1ap_state_get_enb(state, ue_ref_p->sctp_assoc_id); + + S1AP_UE_CONTEXT_RELEASE_REQ(message_p).mme_ue_s1ap_id = + ue_ref_p->mme_ue_s1ap_id; + S1AP_UE_CONTEXT_RELEASE_REQ(message_p).enb_ue_s1ap_id = + ue_ref_p->enb_ue_s1ap_id; + S1AP_UE_CONTEXT_RELEASE_REQ(message_p).enb_id = enb_ref_p->enb_id; + S1AP_UE_CONTEXT_RELEASE_REQ(message_p).relCause = s1_release_cause; + S1AP_UE_CONTEXT_RELEASE_REQ(message_p).cause = ie_cause; + + message_p->ittiMsgHeader.imsi = imsi64; + return send_msg_to_task(&s1ap_task_zmq_ctx, TASK_MME_APP, message_p); +} diff --git a/lte/gateway/c/oai/tasks/s1ap/s1ap_mme_handlers.h b/lte/gateway/c/oai/tasks/s1ap/s1ap_mme_handlers.h index b42e2c8e565d..2520f36e8395 100644 --- a/lte/gateway/c/oai/tasks/s1ap/s1ap_mme_handlers.h +++ b/lte/gateway/c/oai/tasks/s1ap/s1ap_mme_handlers.h @@ -173,4 +173,8 @@ int s1ap_mme_generate_ue_context_release_command( int s1ap_mme_remove_stale_ue_context( enb_ue_s1ap_id_t enb_ue_s1ap_id, uint32_t enb_id); + +int s1ap_send_mme_ue_context_release( + s1ap_state_t* state, ue_description_t* ue_ref_p, + enum s1cause s1_release_cause, S1ap_Cause_t ie_cause, imsi64_t imsi64); #endif /* FILE_S1AP_MME_HANDLERS_SEEN */ From d938f420b56b867a7c64101e6fac63f50be58a46 Mon Sep 17 00:00:00 2001 From: Michael Germano Date: Tue, 6 Apr 2021 21:12:21 -0700 Subject: [PATCH 40/91] Add new subscriber proto for paginated based southbound routes (#5903) Signed-off-by: Michael Germano --- lte/cloud/go/protos/subscriberdb.pb.go | 375 +++++++++++++----- .../servicers/subscriberdb_servicer.go | 193 +++++++++ .../servicers/subscriberdb_servicer_test.go | 302 ++++++++++++++ .../subscriberdb/subscriberdb/main.go | 2 + .../test_init/test_service_init.go | 2 + lte/protos/subscriberdb.proto | 24 +- 6 files changed, 800 insertions(+), 98 deletions(-) create mode 100644 lte/cloud/go/services/subscriberdb/servicers/subscriberdb_servicer.go create mode 100644 lte/cloud/go/services/subscriberdb/servicers/subscriberdb_servicer_test.go diff --git a/lte/cloud/go/protos/subscriberdb.pb.go b/lte/cloud/go/protos/subscriberdb.pb.go index 722a4b44ce1d..86b9b93c0d79 100644 --- a/lte/cloud/go/protos/subscriberdb.pb.go +++ b/lte/cloud/go/protos/subscriberdb.pb.go @@ -1049,6 +1049,104 @@ func (m *SubscriberUpdate) GetMask() *field_mask.FieldMask { return nil } +type ListSubscribersRequest struct { + // page_size is the maximum number of entities returned per request. + PageSize uint32 `protobuf:"varint,1,opt,name=page_size,json=pageSize,proto3" json:"page_size,omitempty"` + // page_token is a serialized entity page token for paginated loads. + PageToken string `protobuf:"bytes,2,opt,name=page_token,json=pageToken,proto3" json:"page_token,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` +} + +func (m *ListSubscribersRequest) Reset() { *m = ListSubscribersRequest{} } +func (m *ListSubscribersRequest) String() string { return proto.CompactTextString(m) } +func (*ListSubscribersRequest) ProtoMessage() {} +func (*ListSubscribersRequest) Descriptor() ([]byte, []int) { + return fileDescriptor_d870e4203d378ec0, []int{10} +} + +func (m *ListSubscribersRequest) XXX_Unmarshal(b []byte) error { + return xxx_messageInfo_ListSubscribersRequest.Unmarshal(m, b) +} +func (m *ListSubscribersRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + return xxx_messageInfo_ListSubscribersRequest.Marshal(b, m, deterministic) +} +func (m *ListSubscribersRequest) XXX_Merge(src proto.Message) { + xxx_messageInfo_ListSubscribersRequest.Merge(m, src) +} +func (m *ListSubscribersRequest) XXX_Size() int { + return xxx_messageInfo_ListSubscribersRequest.Size(m) +} +func (m *ListSubscribersRequest) XXX_DiscardUnknown() { + xxx_messageInfo_ListSubscribersRequest.DiscardUnknown(m) +} + +var xxx_messageInfo_ListSubscribersRequest proto.InternalMessageInfo + +func (m *ListSubscribersRequest) GetPageSize() uint32 { + if m != nil { + return m.PageSize + } + return 0 +} + +func (m *ListSubscribersRequest) GetPageToken() string { + if m != nil { + return m.PageToken + } + return "" +} + +type ListSubscribersResponse struct { + Subscribers []*SubscriberData `protobuf:"bytes,1,rep,name=subscribers,proto3" json:"subscribers,omitempty"` + // next_page_token is a serialized entity page token for subsequent paginated + // loads. + NextPageToken string `protobuf:"bytes,2,opt,name=next_page_token,json=nextPageToken,proto3" json:"next_page_token,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` +} + +func (m *ListSubscribersResponse) Reset() { *m = ListSubscribersResponse{} } +func (m *ListSubscribersResponse) String() string { return proto.CompactTextString(m) } +func (*ListSubscribersResponse) ProtoMessage() {} +func (*ListSubscribersResponse) Descriptor() ([]byte, []int) { + return fileDescriptor_d870e4203d378ec0, []int{11} +} + +func (m *ListSubscribersResponse) XXX_Unmarshal(b []byte) error { + return xxx_messageInfo_ListSubscribersResponse.Unmarshal(m, b) +} +func (m *ListSubscribersResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + return xxx_messageInfo_ListSubscribersResponse.Marshal(b, m, deterministic) +} +func (m *ListSubscribersResponse) XXX_Merge(src proto.Message) { + xxx_messageInfo_ListSubscribersResponse.Merge(m, src) +} +func (m *ListSubscribersResponse) XXX_Size() int { + return xxx_messageInfo_ListSubscribersResponse.Size(m) +} +func (m *ListSubscribersResponse) XXX_DiscardUnknown() { + xxx_messageInfo_ListSubscribersResponse.DiscardUnknown(m) +} + +var xxx_messageInfo_ListSubscribersResponse proto.InternalMessageInfo + +func (m *ListSubscribersResponse) GetSubscribers() []*SubscriberData { + if m != nil { + return m.Subscribers + } + return nil +} + +func (m *ListSubscribersResponse) GetNextPageToken() string { + if m != nil { + return m.NextPageToken + } + return "" +} + func init() { proto.RegisterEnum("magma.lte.AccessNetworkIdentifier", AccessNetworkIdentifier_name, AccessNetworkIdentifier_value) proto.RegisterEnum("magma.lte.SubscriberID_IDType", SubscriberID_IDType_name, SubscriberID_IDType_value) @@ -1071,108 +1169,117 @@ func init() { proto.RegisterType((*Non3GPPUserProfile)(nil), "magma.lte.Non3GPPUserProfile") proto.RegisterType((*SubscriberData)(nil), "magma.lte.SubscriberData") proto.RegisterType((*SubscriberUpdate)(nil), "magma.lte.SubscriberUpdate") + proto.RegisterType((*ListSubscribersRequest)(nil), "magma.lte.ListSubscribersRequest") + proto.RegisterType((*ListSubscribersResponse)(nil), "magma.lte.ListSubscribersResponse") } func init() { proto.RegisterFile("lte/protos/subscriberdb.proto", fileDescriptor_d870e4203d378ec0) } var fileDescriptor_d870e4203d378ec0 = []byte{ - // 1528 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x9c, 0x57, 0xdd, 0x52, 0xdb, 0x48, - 0x16, 0xf6, 0x1f, 0x06, 0x1f, 0x83, 0x11, 0xbd, 0x24, 0x18, 0xb3, 0x6c, 0xbc, 0x4a, 0xed, 0x2e, - 0xf9, 0x33, 0x29, 0xb3, 0xc9, 0x66, 0x37, 0x55, 0xbb, 0x2b, 0x63, 0x87, 0xa8, 0xc6, 0x16, 0x9e, - 0x96, 0x21, 0x53, 0xb9, 0x51, 0xb5, 0xa5, 0xc6, 0x51, 0x21, 0x4b, 0x42, 0x2d, 0x13, 0xb8, 0x9c, - 0xfb, 0x79, 0x92, 0x79, 0x81, 0xb9, 0x9b, 0x47, 0x98, 0x17, 0x98, 0xfb, 0x79, 0x8d, 0x99, 0xea, - 0x96, 0x64, 0x84, 0xb1, 0xa9, 0xc9, 0x5c, 0x59, 0x7d, 0xbe, 0xef, 0x9c, 0xee, 0x73, 0xfa, 0xeb, - 0xee, 0x63, 0xd8, 0x75, 0x42, 0xba, 0xef, 0x07, 0x5e, 0xe8, 0xb1, 0x7d, 0x36, 0x19, 0x32, 0x33, - 0xb0, 0x87, 0x34, 0xb0, 0x86, 0x0d, 0x61, 0x43, 0xa5, 0x31, 0x19, 0x8d, 0x49, 0xc3, 0x09, 0x69, - 0x6d, 0xdb, 0x0b, 0xcc, 0x37, 0x41, 0xc2, 0x35, 0xbd, 0xf1, 0xd8, 0x73, 0x23, 0x56, 0xad, 0x3e, - 0xf2, 0xbc, 0x91, 0x13, 0xc7, 0x19, 0x4e, 0xce, 0xf6, 0xcf, 0x6c, 0xea, 0x58, 0xc6, 0x98, 0xb0, - 0xf3, 0x88, 0x21, 0x9f, 0xc1, 0xaa, 0x3e, 0x8d, 0xae, 0xb6, 0x51, 0x05, 0x72, 0xb6, 0x55, 0xcd, - 0xd6, 0xb3, 0x7b, 0x25, 0x9c, 0xb3, 0x2d, 0xd4, 0x84, 0x42, 0x78, 0xed, 0xd3, 0x6a, 0xae, 0x9e, - 0xdd, 0xab, 0x34, 0xff, 0xd2, 0x98, 0x4e, 0xdb, 0x48, 0xbb, 0x35, 0xd4, 0xf6, 0xe0, 0xda, 0xa7, - 0x58, 0x70, 0x65, 0x04, 0xc5, 0x68, 0x8c, 0x56, 0xa0, 0xa0, 0xf6, 0x74, 0x55, 0xca, 0xc8, 0xff, - 0x85, 0xf5, 0xb4, 0x83, 0x4e, 0x43, 0xf4, 0x0c, 0x0a, 0xcc, 0xb6, 0x58, 0x35, 0x5b, 0xcf, 0xef, - 0x95, 0x9b, 0x5b, 0x0b, 0x42, 0x63, 0x41, 0x92, 0x7f, 0xc8, 0xc1, 0xfa, 0x91, 0xde, 0x8b, 0x11, - 0x3f, 0xb4, 0x3d, 0x17, 0x75, 0x60, 0x89, 0x85, 0x24, 0xa4, 0x62, 0xb9, 0x95, 0xe6, 0x7e, 0x2a, - 0xc2, 0x0c, 0x75, 0x76, 0xac, 0x73, 0x37, 0x1c, 0x79, 0xa3, 0x43, 0x28, 0x91, 0x49, 0xf8, 0xc9, - 0x20, 0xce, 0xc8, 0x8b, 0xf3, 0xfc, 0xfb, 0xfd, 0xa1, 0x94, 0x49, 0xf8, 0x49, 0x71, 0x46, 0x1e, - 0x5e, 0x21, 0xf1, 0x17, 0xda, 0x06, 0xf1, 0x6d, 0x9c, 0xd3, 0xeb, 0x6a, 0xbe, 0x9e, 0xdd, 0x5b, - 0xc5, 0xcb, 0x7c, 0xfc, 0x15, 0xbd, 0x46, 0x8f, 0xa0, 0x2c, 0xa0, 0x70, 0xe2, 0x3b, 0x94, 0x55, - 0x0b, 0xf5, 0xfc, 0xde, 0x2a, 0x06, 0x6e, 0x1a, 0x08, 0x8b, 0xfc, 0x12, 0x36, 0xe7, 0xad, 0x0f, - 0xad, 0xc2, 0x8a, 0xaa, 0x29, 0x87, 0x03, 0xf5, 0xb4, 0x23, 0x65, 0x10, 0x40, 0x31, 0xfe, 0xce, - 0xca, 0x4f, 0xa1, 0x9c, 0x5a, 0x06, 0xda, 0x81, 0xad, 0x3e, 0xee, 0x1c, 0x1e, 0xf7, 0xfa, 0x27, - 0x83, 0x4e, 0xdb, 0x50, 0x4e, 0x06, 0xef, 0x8d, 0xc1, 0x49, 0xbf, 0xdb, 0xd1, 0xa5, 0x8c, 0xfc, - 0x6b, 0x0e, 0xd6, 0xbb, 0x83, 0xce, 0xef, 0xad, 0xdc, 0x0c, 0x75, 0x76, 0xfc, 0x25, 0x95, 0x9b, - 0x13, 0xea, 0xcb, 0x2a, 0x97, 0x40, 0x9e, 0x6f, 0x56, 0x0b, 0x37, 0xd0, 0xb1, 0x6f, 0xa2, 0x06, - 0xfc, 0x89, 0x30, 0x66, 0x8f, 0x5c, 0x6a, 0x19, 0x43, 0xc2, 0xa8, 0xe1, 0x92, 0x31, 0x65, 0x55, - 0xa8, 0xe7, 0xf7, 0x4a, 0x78, 0x23, 0x81, 0x5a, 0x84, 0x51, 0x8d, 0x03, 0xe8, 0x19, 0x4c, 0x8d, - 0x86, 0xef, 0x39, 0xb6, 0x69, 0x53, 0x56, 0x2d, 0x0b, 0xb6, 0x94, 0x00, 0xfd, 0xd8, 0xce, 0x37, - 0x64, 0x5e, 0xda, 0xf7, 0x6c, 0xc8, 0x0e, 0x94, 0x53, 0xd9, 0x71, 0x62, 0x4f, 0xed, 0x76, 0x34, - 0xe5, 0xa8, 0x23, 0x65, 0xe4, 0xef, 0xb3, 0x69, 0xf1, 0x47, 0xa1, 0x9e, 0xc0, 0x86, 0x13, 0x52, - 0x43, 0xa4, 0xe7, 0xd2, 0xab, 0xd0, 0x60, 0xf4, 0x42, 0xec, 0x46, 0x01, 0x57, 0x9c, 0x90, 0xf2, - 0x48, 0x1a, 0xbd, 0x0a, 0x75, 0x7a, 0x81, 0xf6, 0x61, 0x33, 0x1c, 0xf9, 0xbe, 0x41, 0x08, 0x31, - 0x18, 0x0d, 0x2e, 0x69, 0x20, 0x92, 0x15, 0x05, 0x2f, 0xe1, 0x0d, 0x8e, 0x29, 0x84, 0xe8, 0x02, - 0xe1, 0xc9, 0xa2, 0xb7, 0x50, 0x9b, 0x75, 0x08, 0xe8, 0xc8, 0x66, 0x21, 0x0d, 0xa8, 0x25, 0x6a, - 0xbc, 0x82, 0xb7, 0x6e, 0xb9, 0xe1, 0x29, 0x2c, 0x7f, 0x57, 0x04, 0x49, 0xe9, 0x6b, 0x87, 0x9e, - 0x7b, 0x66, 0x8f, 0x26, 0x01, 0x11, 0x7a, 0xd9, 0x05, 0x30, 0x3d, 0x37, 0xe4, 0xeb, 0x8c, 0x6f, - 0x87, 0x35, 0x5c, 0x8a, 0x2d, 0xaa, 0xc5, 0x8b, 0xcb, 0xe7, 0xb1, 0x4d, 0x6a, 0x30, 0xea, 0x50, - 0x93, 0xfb, 0xc4, 0xcb, 0x93, 0x62, 0x40, 0x4f, 0xec, 0xe8, 0x08, 0xca, 0x17, 0x1e, 0x33, 0xfc, - 0xc0, 0x3b, 0xb3, 0x1d, 0x2a, 0x96, 0x53, 0xbe, 0x25, 0x9b, 0xd9, 0xd9, 0x1b, 0x5f, 0x7b, 0x7a, - 0x3f, 0x62, 0x63, 0xb8, 0xf0, 0x58, 0xfc, 0x8d, 0xfe, 0x05, 0x05, 0x32, 0x1e, 0x06, 0x42, 0x19, - 0xe5, 0xe6, 0xe3, 0x74, 0x84, 0xd1, 0x28, 0xa0, 0x23, 0x12, 0x52, 0xab, 0x47, 0xae, 0xec, 0xf1, - 0x64, 0xdc, 0xb2, 0xc3, 0x80, 0xeb, 0x56, 0x38, 0xa0, 0x57, 0x90, 0xf7, 0x2d, 0xb7, 0xba, 0x24, - 0x04, 0xfb, 0xf8, 0xbe, 0x99, 0xfb, 0x6d, 0x4d, 0xdc, 0x6b, 0x9c, 0x8f, 0x9e, 0x03, 0x9a, 0x4a, - 0x88, 0xeb, 0xdf, 0x36, 0x0d, 0xdb, 0xaf, 0x16, 0xa3, 0x34, 0x13, 0x44, 0x17, 0x80, 0xea, 0xa3, - 0x43, 0x58, 0x09, 0x28, 0xf3, 0x26, 0x81, 0x49, 0xab, 0xcb, 0x62, 0x85, 0xff, 0xb8, 0x6f, 0x26, - 0xa5, 0xaf, 0xe1, 0x98, 0x8e, 0xa7, 0x8e, 0xb5, 0x1f, 0xb3, 0x00, 0x37, 0xd9, 0xf3, 0xf3, 0x60, - 0x3a, 0x84, 0xb1, 0x64, 0x13, 0x96, 0xf0, 0xb2, 0x18, 0xab, 0x16, 0xfa, 0x1b, 0x54, 0xfc, 0xc0, - 0xf6, 0x02, 0x3b, 0xbc, 0x36, 0x1c, 0x7a, 0x49, 0x1d, 0x51, 0xff, 0x35, 0xbc, 0x96, 0x58, 0xbb, - 0xdc, 0x88, 0x0e, 0xe0, 0x81, 0x1f, 0x50, 0x3a, 0x16, 0xa2, 0x36, 0x4c, 0xe2, 0x93, 0xa1, 0xed, - 0xd8, 0xe1, 0x75, 0xac, 0x8a, 0xcd, 0x1b, 0xf0, 0x70, 0x8a, 0xa1, 0x7f, 0x43, 0x35, 0xe5, 0x74, - 0x39, 0x71, 0x5c, 0x1a, 0x24, 0x7e, 0x85, 0x48, 0x4d, 0x37, 0xf8, 0x69, 0x1a, 0xae, 0x7d, 0x9b, - 0x85, 0x72, 0x2a, 0x35, 0x71, 0xa2, 0x7d, 0x37, 0xd2, 0x6f, 0xf4, 0xc8, 0x2c, 0x13, 0xdf, 0x15, - 0xaa, 0xdd, 0x05, 0xe0, 0x5b, 0xf6, 0x99, 0x5c, 0xf3, 0xb2, 0x46, 0xea, 0x29, 0xc5, 0x16, 0xd5, - 0xe7, 0xb7, 0x68, 0x02, 0x8f, 0x89, 0x29, 0xd6, 0x5b, 0xc2, 0x89, 0x47, 0x8f, 0x98, 0x68, 0x0b, - 0x96, 0x2f, 0x1d, 0xe2, 0xf2, 0xda, 0x14, 0x44, 0xea, 0x45, 0x3e, 0x54, 0x2d, 0xf9, 0x2d, 0x2c, - 0xc7, 0xfb, 0x28, 0xde, 0xa3, 0xfe, 0xe9, 0x3f, 0xa5, 0x4c, 0xfc, 0xf5, 0x5a, 0xca, 0xf2, 0x63, - 0xcc, 0x6d, 0xa7, 0xaf, 0xa5, 0x1c, 0x92, 0x60, 0x95, 0x7f, 0x1b, 0xc7, 0xd8, 0x10, 0x68, 0x5e, - 0x76, 0xa1, 0xba, 0x48, 0x4d, 0x68, 0x0f, 0xa4, 0x31, 0xb9, 0x32, 0x86, 0xc4, 0xb5, 0x3e, 0xdb, - 0x56, 0xf8, 0xc9, 0x98, 0x38, 0xf1, 0xd9, 0xa8, 0x8c, 0xc9, 0x55, 0x2b, 0x31, 0x9f, 0x38, 0x77, - 0x99, 0x56, 0xb2, 0x3f, 0xb7, 0x98, 0x6d, 0x47, 0xfe, 0xa9, 0x00, 0x48, 0xf3, 0xdc, 0x83, 0xa3, - 0x7e, 0xff, 0x84, 0xd1, 0x20, 0xd9, 0xf9, 0x87, 0x50, 0x1c, 0x33, 0x9b, 0x59, 0x6e, 0x5c, 0xb5, - 0x78, 0x84, 0x3e, 0x02, 0x72, 0x3d, 0xd7, 0x38, 0xe0, 0xc7, 0xdd, 0xf6, 0x0d, 0x62, 0x9a, 0x94, - 0xb1, 0xf8, 0x2a, 0x7e, 0x91, 0xd2, 0xdb, 0xdd, 0x90, 0x89, 0x49, 0xed, 0x2b, 0xc2, 0x09, 0xaf, - 0xbb, 0x9e, 0xcb, 0xe3, 0xa8, 0x7e, 0x64, 0x40, 0x16, 0x3c, 0xbc, 0x1b, 0xdb, 0x20, 0xbe, 0x2b, - 0x8a, 0x5f, 0x69, 0xbe, 0xfc, 0xa2, 0xf8, 0x5c, 0x05, 0x68, 0x66, 0x0a, 0xc5, 0x77, 0xff, 0xf8, - 0x29, 0xfe, 0x0f, 0x00, 0x97, 0x92, 0x29, 0x8e, 0x51, 0x75, 0x49, 0x34, 0x11, 0x3b, 0xf7, 0x1c, - 0x31, 0x5c, 0x22, 0xbe, 0x1b, 0x59, 0xd0, 0x3b, 0x58, 0x8b, 0xd3, 0x71, 0xa9, 0xb8, 0xd2, 0x8a, - 0x22, 0x23, 0x39, 0xed, 0x2e, 0x70, 0x8d, 0x86, 0x9f, 0xbd, 0xe0, 0x5c, 0xb5, 0xa8, 0x1b, 0xda, - 0x67, 0x36, 0x0d, 0x70, 0x99, 0x24, 0x80, 0x6a, 0xc9, 0xa7, 0xb0, 0x3e, 0x93, 0x26, 0xfa, 0x2b, - 0xec, 0x6a, 0xc7, 0x9a, 0xc1, 0x6d, 0x86, 0x7e, 0xd2, 0xd2, 0x0f, 0xb1, 0xda, 0x1f, 0xa8, 0xc7, - 0x9a, 0xa1, 0x74, 0xbb, 0xc7, 0x1f, 0x3a, 0x6d, 0x29, 0x83, 0xea, 0xf0, 0xe7, 0xf9, 0x94, 0x96, - 0x82, 0x71, 0xa7, 0x2d, 0x65, 0x65, 0x75, 0x2a, 0x82, 0x54, 0xf9, 0x50, 0x15, 0x36, 0xa7, 0x7e, - 0x4a, 0x5f, 0xd3, 0x8d, 0x8e, 0xa6, 0xb4, 0xba, 0xfc, 0x29, 0xda, 0x86, 0x07, 0xb7, 0x91, 0xb6, - 0xaa, 0x0b, 0x28, 0x2b, 0xff, 0x9c, 0x83, 0xca, 0xcd, 0xe3, 0xd3, 0x26, 0x21, 0x41, 0x4f, 0x20, - 0xcf, 0xe2, 0x1b, 0xe4, 0x9e, 0xbe, 0x8b, 0x73, 0xd0, 0x73, 0xc8, 0x8f, 0xd8, 0x58, 0x08, 0xaa, - 0xdc, 0xac, 0x2d, 0xee, 0x8a, 0x30, 0xa7, 0x71, 0xb6, 0x13, 0x26, 0x57, 0x7a, 0x6d, 0x71, 0x27, - 0x80, 0x39, 0x0d, 0xbd, 0x02, 0x70, 0xa3, 0xf2, 0x26, 0x67, 0xb6, 0xdc, 0x7c, 0x18, 0x3b, 0x89, - 0x96, 0xb6, 0x91, 0x54, 0xbf, 0x8d, 0x4b, 0x6e, 0xb2, 0x11, 0xe8, 0x65, 0xd2, 0xbb, 0x2c, 0xdd, - 0x99, 0x66, 0xe6, 0x91, 0x4d, 0xda, 0x94, 0x47, 0x50, 0x66, 0x93, 0xe1, 0xf4, 0xc5, 0x89, 0x6e, - 0x6c, 0x60, 0x93, 0x61, 0x72, 0xba, 0xde, 0xc0, 0x4a, 0xa2, 0xf4, 0xf8, 0xae, 0xde, 0xbd, 0x57, - 0xdb, 0x78, 0x39, 0x16, 0xb2, 0x7c, 0x01, 0xd2, 0xcd, 0xa4, 0x27, 0xbe, 0xc5, 0xa7, 0x7b, 0x01, - 0x05, 0x8b, 0x84, 0x24, 0xae, 0xef, 0xf6, 0xdc, 0xf5, 0xf1, 0x7d, 0xc0, 0x82, 0x86, 0x1a, 0x50, - 0xe0, 0xfd, 0xf8, 0xb4, 0xc6, 0x51, 0xcb, 0xde, 0x48, 0x5a, 0xf6, 0xc6, 0x3b, 0xde, 0xb2, 0xf7, - 0x08, 0x3b, 0xc7, 0x82, 0xf7, 0xf4, 0x1d, 0x6c, 0x2d, 0xd0, 0x26, 0xbf, 0xd4, 0xde, 0xe3, 0x3e, - 0x97, 0x58, 0x09, 0x96, 0x3e, 0xa8, 0x3d, 0xe5, 0x1b, 0x29, 0xcb, 0x8d, 0x1f, 0xba, 0x8a, 0x26, - 0xe5, 0x78, 0x57, 0xd2, 0x19, 0xbc, 0xef, 0x60, 0xad, 0x33, 0x90, 0xf2, 0xcd, 0x5f, 0x72, 0xe9, - 0xd6, 0xbf, 0xdd, 0x42, 0xff, 0x83, 0x35, 0xc5, 0xb2, 0x6e, 0x4c, 0x68, 0xf1, 0xd2, 0x6b, 0x1b, - 0xb7, 0xf6, 0xe9, 0xd4, 0xb3, 0x2d, 0x39, 0x83, 0xfe, 0x0f, 0x52, 0x9b, 0x3a, 0x34, 0xa4, 0xa9, - 0x18, 0x8b, 0xe4, 0x35, 0x3f, 0x42, 0x1b, 0xa4, 0xa8, 0x88, 0xa9, 0x08, 0x3b, 0x73, 0x23, 0x44, - 0xb4, 0xf9, 0x51, 0x54, 0xd8, 0x38, 0xa2, 0xe1, 0x8c, 0xe8, 0x17, 0x2e, 0x64, 0x71, 0x96, 0x72, - 0x06, 0xb5, 0x60, 0xbd, 0x6b, 0xb3, 0x54, 0x2c, 0x86, 0xee, 0x4e, 0x59, 0xab, 0x2d, 0x88, 0xad, - 0xd3, 0x50, 0xce, 0xb4, 0x76, 0x3e, 0x6e, 0x0b, 0x78, 0x9f, 0xff, 0xa3, 0x33, 0x1d, 0x6f, 0x62, - 0xed, 0x8f, 0xbc, 0xf8, 0xef, 0xda, 0xb0, 0x28, 0x7e, 0x0f, 0x7e, 0x0b, 0x00, 0x00, 0xff, 0xff, - 0xa2, 0x5e, 0x28, 0x70, 0xef, 0x0d, 0x00, 0x00, + // 1634 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x9c, 0x57, 0xdb, 0x72, 0xe3, 0xb8, + 0x11, 0xd5, 0xcd, 0x17, 0xb5, 0x7c, 0xa1, 0x91, 0xd9, 0xb1, 0x2c, 0xc7, 0x59, 0x2f, 0xb7, 0xb2, + 0xf1, 0xde, 0xe4, 0x29, 0x4d, 0x76, 0xb3, 0xc9, 0x56, 0x25, 0xa1, 0x2c, 0x8d, 0x87, 0x15, 0x99, + 0x66, 0x40, 0xd9, 0x93, 0x9a, 0x17, 0x16, 0x44, 0xc2, 0x1a, 0x96, 0x29, 0x92, 0x26, 0x20, 0x8f, + 0x3d, 0x0f, 0xa9, 0xca, 0x7b, 0xbe, 0x24, 0x3f, 0x90, 0xb7, 0x7c, 0x42, 0x7e, 0x20, 0xef, 0xf9, + 0x8d, 0xa4, 0x00, 0x92, 0x12, 0xad, 0x5b, 0x65, 0xf2, 0x24, 0xa2, 0xfb, 0x74, 0x03, 0xdd, 0x38, + 0xdd, 0x68, 0xc1, 0x91, 0xcf, 0xe9, 0x69, 0x14, 0x87, 0x3c, 0x64, 0xa7, 0x6c, 0x3c, 0x60, 0x4e, + 0xec, 0x0d, 0x68, 0xec, 0x0e, 0x9a, 0x52, 0x86, 0xaa, 0x23, 0x32, 0x1c, 0x91, 0xa6, 0xcf, 0x69, + 0xe3, 0x20, 0x8c, 0x9d, 0x1f, 0xe2, 0x0c, 0xeb, 0x84, 0xa3, 0x51, 0x18, 0x24, 0xa8, 0xc6, 0xf1, + 0x30, 0x0c, 0x87, 0x7e, 0xea, 0x67, 0x30, 0xbe, 0x39, 0xbd, 0xf1, 0xa8, 0xef, 0xda, 0x23, 0xc2, + 0x6e, 0x13, 0x84, 0x7a, 0x03, 0x5b, 0xd6, 0xc4, 0xbb, 0xde, 0x41, 0x3b, 0x50, 0xf2, 0xdc, 0x7a, + 0xf1, 0xb8, 0x78, 0x52, 0xc5, 0x25, 0xcf, 0x45, 0x2d, 0xa8, 0xf0, 0xc7, 0x88, 0xd6, 0x4b, 0xc7, + 0xc5, 0x93, 0x9d, 0xd6, 0xcf, 0x9a, 0x93, 0x6d, 0x9b, 0x79, 0xb3, 0xa6, 0xde, 0xe9, 0x3f, 0x46, + 0x14, 0x4b, 0xac, 0x8a, 0x60, 0x3d, 0x59, 0xa3, 0x4d, 0xa8, 0xe8, 0x17, 0x96, 0xae, 0x14, 0xd4, + 0xdf, 0xc2, 0x6e, 0xde, 0xc0, 0xa2, 0x1c, 0x7d, 0x0d, 0x15, 0xe6, 0xb9, 0xac, 0x5e, 0x3c, 0x2e, + 0x9f, 0xd4, 0x5a, 0xfb, 0x4b, 0x5c, 0x63, 0x09, 0x52, 0xff, 0x5e, 0x82, 0xdd, 0x73, 0xeb, 0x22, + 0xd5, 0x44, 0xdc, 0x0b, 0x03, 0xd4, 0x85, 0x35, 0xc6, 0x09, 0xa7, 0xf2, 0xb8, 0x3b, 0xad, 0xd3, + 0x9c, 0x87, 0x19, 0xe8, 0xec, 0xda, 0x12, 0x66, 0x38, 0xb1, 0x46, 0x67, 0x50, 0x25, 0x63, 0xfe, + 0xce, 0x26, 0xfe, 0x30, 0x4c, 0xe3, 0xfc, 0x62, 0xb5, 0x2b, 0x6d, 0xcc, 0xdf, 0x69, 0xfe, 0x30, + 0xc4, 0x9b, 0x24, 0xfd, 0x42, 0x07, 0x20, 0xbf, 0xed, 0x5b, 0xfa, 0x58, 0x2f, 0x1f, 0x17, 0x4f, + 0xb6, 0xf0, 0x86, 0x58, 0xff, 0x81, 0x3e, 0xa2, 0x4f, 0xa1, 0x26, 0x55, 0x7c, 0x1c, 0xf9, 0x94, + 0xd5, 0x2b, 0xc7, 0xe5, 0x93, 0x2d, 0x0c, 0x42, 0xd4, 0x97, 0x12, 0xf5, 0x05, 0x3c, 0x5b, 0x74, + 0x3e, 0xb4, 0x05, 0x9b, 0xba, 0xa1, 0x9d, 0xf5, 0xf5, 0xeb, 0xae, 0x52, 0x40, 0x00, 0xeb, 0xe9, + 0x77, 0x51, 0xfd, 0x0a, 0x6a, 0xb9, 0x63, 0xa0, 0x43, 0xd8, 0x37, 0x71, 0xf7, 0xec, 0xf2, 0xc2, + 0xbc, 0xea, 0x77, 0x3b, 0xb6, 0x76, 0xd5, 0x7f, 0x6d, 0xf7, 0xaf, 0xcc, 0x5e, 0xd7, 0x52, 0x0a, + 0xea, 0x7f, 0x4a, 0xb0, 0xdb, 0xeb, 0x77, 0xff, 0xd7, 0xcc, 0xcd, 0x40, 0x67, 0xd7, 0x1f, 0x93, + 0xb9, 0x05, 0xae, 0x3e, 0x2e, 0x73, 0x99, 0x2a, 0x8c, 0x9c, 0x7a, 0x65, 0xaa, 0xba, 0x8c, 0x1c, + 0xd4, 0x84, 0x9f, 0x10, 0xc6, 0xbc, 0x61, 0x40, 0x5d, 0x7b, 0x40, 0x18, 0xb5, 0x03, 0x32, 0xa2, + 0xac, 0x0e, 0xc7, 0xe5, 0x93, 0x2a, 0xde, 0xcb, 0x54, 0x6d, 0xc2, 0xa8, 0x21, 0x14, 0xe8, 0x6b, + 0x98, 0x08, 0xed, 0x28, 0xf4, 0x3d, 0xc7, 0xa3, 0xac, 0x5e, 0x93, 0x68, 0x25, 0x53, 0x98, 0xa9, + 0x5c, 0x5c, 0xc8, 0xa2, 0xb0, 0x57, 0x5c, 0xc8, 0x21, 0xd4, 0x72, 0xd1, 0x09, 0xe0, 0x85, 0xde, + 0xeb, 0x1a, 0xda, 0x79, 0x57, 0x29, 0xa8, 0x7f, 0x2b, 0xe6, 0xc9, 0x9f, 0xb8, 0xfa, 0x12, 0xf6, + 0x7c, 0x4e, 0x6d, 0x19, 0x5e, 0x40, 0x1f, 0xb8, 0xcd, 0xe8, 0x9d, 0xbc, 0x8d, 0x0a, 0xde, 0xf1, + 0x39, 0x15, 0x9e, 0x0c, 0xfa, 0xc0, 0x2d, 0x7a, 0x87, 0x4e, 0xe1, 0x19, 0x1f, 0x46, 0x91, 0x4d, + 0x08, 0xb1, 0x19, 0x8d, 0xef, 0x69, 0x2c, 0x83, 0x95, 0x09, 0xaf, 0xe2, 0x3d, 0xa1, 0xd3, 0x08, + 0xb1, 0xa4, 0x46, 0x04, 0x8b, 0x7e, 0x84, 0xc6, 0xac, 0x41, 0x4c, 0x87, 0x1e, 0xe3, 0x34, 0xa6, + 0xae, 0xcc, 0xf1, 0x26, 0xde, 0x7f, 0x62, 0x86, 0x27, 0x6a, 0xf5, 0xaf, 0xeb, 0xa0, 0x68, 0xa6, + 0x71, 0x16, 0x06, 0x37, 0xde, 0x70, 0x1c, 0x13, 0xc9, 0x97, 0x23, 0x00, 0x27, 0x0c, 0xb8, 0x38, + 0x67, 0xda, 0x1d, 0xb6, 0x71, 0x35, 0x95, 0xe8, 0xae, 0x48, 0xae, 0xd8, 0xc7, 0x73, 0xa8, 0xcd, + 0xa8, 0x4f, 0x1d, 0x61, 0x93, 0x1e, 0x4f, 0x49, 0x15, 0x56, 0x26, 0x47, 0xe7, 0x50, 0xbb, 0x0b, + 0x99, 0x1d, 0xc5, 0xe1, 0x8d, 0xe7, 0x53, 0x79, 0x9c, 0xda, 0x13, 0xda, 0xcc, 0xee, 0xde, 0xfc, + 0x63, 0x68, 0x99, 0x09, 0x1a, 0xc3, 0x5d, 0xc8, 0xd2, 0x6f, 0xf4, 0x2b, 0xa8, 0x90, 0xd1, 0x20, + 0x96, 0xcc, 0xa8, 0xb5, 0x3e, 0xcf, 0x7b, 0x18, 0x0e, 0x63, 0x3a, 0x24, 0x9c, 0xba, 0x17, 0xe4, + 0xc1, 0x1b, 0x8d, 0x47, 0x6d, 0x8f, 0xc7, 0x82, 0xb7, 0xd2, 0x00, 0x7d, 0x07, 0xe5, 0xc8, 0x0d, + 0xea, 0x6b, 0x92, 0xb0, 0x9f, 0xaf, 0xda, 0xd9, 0xec, 0x18, 0xb2, 0xaf, 0x09, 0x3c, 0xfa, 0x06, + 0xd0, 0x84, 0x42, 0x82, 0xff, 0x9e, 0x63, 0x7b, 0x51, 0x7d, 0x3d, 0x09, 0x33, 0xd3, 0x58, 0x52, + 0xa1, 0x47, 0xe8, 0x0c, 0x36, 0x63, 0xca, 0xc2, 0x71, 0xec, 0xd0, 0xfa, 0x86, 0x3c, 0xe1, 0x2f, + 0x56, 0xed, 0xa4, 0x99, 0x06, 0x4e, 0xe1, 0x78, 0x62, 0xd8, 0xf8, 0x47, 0x11, 0x60, 0x1a, 0xbd, + 0xa8, 0x07, 0xc7, 0x27, 0x8c, 0x65, 0x97, 0xb0, 0x86, 0x37, 0xe4, 0x5a, 0x77, 0xd1, 0xcf, 0x61, + 0x27, 0x8a, 0xbd, 0x30, 0xf6, 0xf8, 0xa3, 0xed, 0xd3, 0x7b, 0xea, 0xcb, 0xfc, 0x6f, 0xe3, 0xed, + 0x4c, 0xda, 0x13, 0x42, 0xf4, 0x12, 0x3e, 0x89, 0x62, 0x4a, 0x47, 0x92, 0xd4, 0xb6, 0x43, 0x22, + 0x32, 0xf0, 0x7c, 0x8f, 0x3f, 0xa6, 0xac, 0x78, 0x36, 0x55, 0x9e, 0x4d, 0x74, 0xe8, 0xd7, 0x50, + 0xcf, 0x19, 0xdd, 0x8f, 0xfd, 0x80, 0xc6, 0x99, 0x5d, 0x25, 0x61, 0xd3, 0x54, 0x7f, 0x9d, 0x57, + 0x37, 0xfe, 0x52, 0x84, 0x5a, 0x2e, 0x34, 0x59, 0xd1, 0x51, 0x90, 0xf0, 0x37, 0x79, 0x64, 0x36, + 0x48, 0x14, 0x48, 0xd6, 0x1e, 0x01, 0x88, 0x2b, 0x7b, 0x4f, 0x1e, 0x45, 0x5a, 0x13, 0xf6, 0x54, + 0x53, 0x89, 0x1e, 0x89, 0x2e, 0x9a, 0xa9, 0x47, 0xc4, 0x91, 0xe7, 0xad, 0xe2, 0xcc, 0xe2, 0x82, + 0x38, 0x68, 0x1f, 0x36, 0xee, 0x7d, 0x12, 0x88, 0xdc, 0x54, 0x64, 0xe8, 0xeb, 0x62, 0xa9, 0xbb, + 0xea, 0x8f, 0xb0, 0x91, 0xde, 0xa3, 0x7c, 0x8f, 0xcc, 0xeb, 0x5f, 0x2a, 0x85, 0xf4, 0xeb, 0x7b, + 0xa5, 0x28, 0xca, 0x58, 0xc8, 0xae, 0xbf, 0x57, 0x4a, 0x48, 0x81, 0x2d, 0xf1, 0x6d, 0x5f, 0x62, + 0x5b, 0x6a, 0xcb, 0x6a, 0x00, 0xf5, 0x65, 0x6c, 0x42, 0x27, 0xa0, 0x8c, 0xc8, 0x83, 0x3d, 0x20, + 0x81, 0xfb, 0xde, 0x73, 0xf9, 0x3b, 0x7b, 0xec, 0xa7, 0xb5, 0xb1, 0x33, 0x22, 0x0f, 0xed, 0x4c, + 0x7c, 0xe5, 0xcf, 0x23, 0xdd, 0xec, 0x7e, 0x9e, 0x20, 0x3b, 0xbe, 0xfa, 0xcf, 0x0a, 0x20, 0x23, + 0x0c, 0x5e, 0x9e, 0x9b, 0xe6, 0x15, 0xa3, 0x71, 0x76, 0xf3, 0xcf, 0x61, 0x7d, 0xc4, 0x3c, 0xe6, + 0x06, 0x69, 0xd6, 0xd2, 0x15, 0x7a, 0x0b, 0x28, 0x08, 0x03, 0xfb, 0xa5, 0x28, 0x77, 0x2f, 0xb2, + 0x89, 0xe3, 0x50, 0xc6, 0xd2, 0x56, 0xfc, 0x6d, 0x8e, 0x6f, 0xf3, 0x2e, 0x33, 0x91, 0x6e, 0x6a, + 0xd2, 0x08, 0xef, 0x06, 0x61, 0x20, 0xfc, 0xe8, 0x51, 0x22, 0x40, 0x2e, 0x3c, 0x9f, 0xf7, 0x6d, + 0x93, 0x28, 0x90, 0xc9, 0xdf, 0x69, 0xbd, 0xf8, 0x28, 0xff, 0x82, 0x05, 0x68, 0x66, 0x0b, 0x2d, + 0x0a, 0xfe, 0xff, 0x2a, 0xfe, 0x0d, 0x80, 0xa0, 0x92, 0x23, 0xcb, 0xa8, 0xbe, 0x26, 0x87, 0x88, + 0xc3, 0x15, 0x25, 0x86, 0xab, 0x24, 0x0a, 0x12, 0x09, 0x7a, 0x05, 0xdb, 0x69, 0x38, 0x01, 0x95, + 0x2d, 0x6d, 0x5d, 0x46, 0xa4, 0xe6, 0xcd, 0xa5, 0xde, 0xa0, 0xfc, 0x7d, 0x18, 0xdf, 0xea, 0x2e, + 0x0d, 0xb8, 0x77, 0xe3, 0xd1, 0x18, 0xd7, 0x48, 0xa6, 0xd0, 0x5d, 0xf5, 0x1a, 0x76, 0x67, 0xc2, + 0x44, 0x9f, 0xc1, 0x91, 0x71, 0x69, 0xd8, 0x42, 0x66, 0x5b, 0x57, 0x6d, 0xeb, 0x0c, 0xeb, 0x66, + 0x5f, 0xbf, 0x34, 0x6c, 0xad, 0xd7, 0xbb, 0x7c, 0xd3, 0xed, 0x28, 0x05, 0x74, 0x0c, 0x3f, 0x5d, + 0x0c, 0x69, 0x6b, 0x18, 0x77, 0x3b, 0x4a, 0x51, 0xd5, 0x27, 0x24, 0xc8, 0xa5, 0x0f, 0xd5, 0xe1, + 0xd9, 0xc4, 0x4e, 0x33, 0x0d, 0xcb, 0xee, 0x1a, 0x5a, 0xbb, 0x27, 0x9e, 0xa2, 0x03, 0xf8, 0xe4, + 0xa9, 0xa6, 0xa3, 0x5b, 0x52, 0x55, 0x54, 0xff, 0x55, 0x82, 0x9d, 0xe9, 0xe3, 0xd3, 0x21, 0x9c, + 0xa0, 0x2f, 0xa1, 0xcc, 0xd2, 0x0e, 0xb2, 0x62, 0xee, 0x12, 0x18, 0xf4, 0x0d, 0x94, 0x87, 0x6c, + 0x24, 0x09, 0x55, 0x6b, 0x35, 0x96, 0x4f, 0x45, 0x58, 0xc0, 0x04, 0xda, 0xe7, 0x59, 0x4b, 0x6f, + 0x2c, 0x9f, 0x04, 0xb0, 0x80, 0xa1, 0xef, 0x00, 0x82, 0x24, 0xbd, 0x59, 0xcd, 0xd6, 0x5a, 0xcf, + 0x53, 0x23, 0x39, 0xd2, 0x36, 0xb3, 0xec, 0x77, 0x70, 0x35, 0xc8, 0x2e, 0x02, 0xbd, 0xc8, 0x66, + 0x97, 0xb5, 0xb9, 0x6d, 0x66, 0x1e, 0xd9, 0x6c, 0x4c, 0xf9, 0x14, 0x6a, 0x6c, 0x3c, 0x98, 0xbc, + 0x38, 0x49, 0xc7, 0x06, 0x36, 0x1e, 0x64, 0xd5, 0xf5, 0x03, 0x6c, 0x66, 0x4c, 0x4f, 0x7b, 0xf5, + 0xd1, 0x4a, 0x6e, 0xe3, 0x8d, 0x94, 0xc8, 0xea, 0x1d, 0x28, 0xd3, 0x4d, 0xaf, 0x22, 0x57, 0x6c, + 0xf7, 0x2d, 0x54, 0x5c, 0xc2, 0x49, 0x9a, 0xdf, 0x83, 0x85, 0xe7, 0x13, 0xf7, 0x80, 0x25, 0x0c, + 0x35, 0xa1, 0x22, 0xe6, 0xf1, 0x49, 0x8e, 0x93, 0x91, 0xbd, 0x99, 0x8d, 0xec, 0xcd, 0x57, 0x62, + 0x64, 0xbf, 0x20, 0xec, 0x16, 0x4b, 0x9c, 0xda, 0x87, 0xe7, 0x3d, 0x8f, 0xf1, 0xa9, 0x2f, 0x86, + 0xe9, 0xdd, 0x98, 0x32, 0x8e, 0x0e, 0xa1, 0x1a, 0x91, 0x21, 0xb5, 0x99, 0xf7, 0x81, 0xa6, 0x8d, + 0x68, 0x53, 0x08, 0x2c, 0xef, 0x83, 0x6c, 0xaf, 0x52, 0xc9, 0xc3, 0x5b, 0x9a, 0x3d, 0xce, 0x12, + 0xde, 0x17, 0x02, 0xf5, 0xcf, 0xb0, 0x3f, 0xe7, 0x95, 0x45, 0x61, 0xc0, 0xc4, 0x38, 0x51, 0x9b, + 0xfe, 0x01, 0xc9, 0xc6, 0xf5, 0x15, 0x61, 0xe5, 0xd1, 0xe8, 0x0b, 0xd8, 0x95, 0xe3, 0xcd, 0xdc, + 0xde, 0xdb, 0x42, 0x6c, 0x66, 0xfb, 0x7f, 0xf5, 0x0a, 0xf6, 0x97, 0x54, 0x9c, 0x68, 0xd5, 0xaf, + 0xb1, 0x29, 0x0a, 0xa7, 0x0a, 0x6b, 0x6f, 0xf4, 0x0b, 0xed, 0x4f, 0x4a, 0x51, 0x08, 0xdf, 0xf4, + 0x34, 0x43, 0x29, 0x89, 0x59, 0xab, 0xdb, 0x7f, 0xdd, 0xc5, 0x46, 0xb7, 0xaf, 0x94, 0x5b, 0xff, + 0x2e, 0xe5, 0xff, 0xd0, 0x74, 0xda, 0xe8, 0x77, 0xb0, 0xad, 0xb9, 0xee, 0x54, 0x84, 0x96, 0x9f, + 0xbc, 0xb1, 0xf7, 0x84, 0x7d, 0xd7, 0xa1, 0xe7, 0xaa, 0x05, 0xf4, 0x7b, 0x50, 0x3a, 0xd4, 0xa7, + 0x9c, 0xe6, 0x7c, 0x2c, 0x2b, 0x9a, 0xc5, 0x1e, 0x3a, 0xa0, 0x24, 0xd4, 0xc8, 0x79, 0x38, 0x5c, + 0xe8, 0x21, 0x81, 0x2d, 0xf6, 0xa2, 0xc3, 0xde, 0x39, 0xe5, 0x33, 0xa5, 0xbc, 0xf4, 0x20, 0xcb, + 0xa3, 0x54, 0x0b, 0xa8, 0x0d, 0xbb, 0x33, 0x97, 0x8d, 0xe6, 0xb7, 0x6c, 0x34, 0x96, 0xf8, 0xb6, + 0x28, 0x57, 0x0b, 0xad, 0x10, 0xf6, 0xf2, 0x79, 0x3e, 0xf3, 0xc3, 0xb1, 0x8b, 0xde, 0xce, 0x3b, + 0xfe, 0x2c, 0xdf, 0x06, 0x16, 0xf2, 0xb6, 0xa1, 0xae, 0x82, 0x24, 0x24, 0x54, 0x0b, 0xed, 0xc3, + 0xb7, 0x07, 0x12, 0x76, 0x2a, 0xfe, 0x18, 0x3b, 0x62, 0xbb, 0xd3, 0x61, 0x98, 0xfe, 0xeb, 0x1d, + 0xac, 0xcb, 0xdf, 0x97, 0xff, 0x0d, 0x00, 0x00, 0xff, 0xff, 0x34, 0xa4, 0xdf, 0xe6, 0x36, 0x0f, + 0x00, 0x00, } // Reference imports to suppress errors if they are not otherwise used. @@ -1426,3 +1533,77 @@ var _SubscriberDB_serviceDesc = grpc.ServiceDesc{ Streams: []grpc.StreamDesc{}, Metadata: "lte/protos/subscriberdb.proto", } + +// SubscriberDBCloudClient is the client API for SubscriberDBCloud service. +// +// For semantics around ctx use and closing/ending streaming RPCs, please refer to https://godoc.org/google.golang.org/grpc#ClientConn.NewStream. +type SubscriberDBCloudClient interface { + // ListSubscribers lists pages of subscribers stored. + ListSubscribers(ctx context.Context, in *ListSubscribersRequest, opts ...grpc.CallOption) (*ListSubscribersResponse, error) +} + +type subscriberDBCloudClient struct { + cc grpc.ClientConnInterface +} + +func NewSubscriberDBCloudClient(cc grpc.ClientConnInterface) SubscriberDBCloudClient { + return &subscriberDBCloudClient{cc} +} + +func (c *subscriberDBCloudClient) ListSubscribers(ctx context.Context, in *ListSubscribersRequest, opts ...grpc.CallOption) (*ListSubscribersResponse, error) { + out := new(ListSubscribersResponse) + err := c.cc.Invoke(ctx, "/magma.lte.SubscriberDBCloud/ListSubscribers", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +// SubscriberDBCloudServer is the server API for SubscriberDBCloud service. +type SubscriberDBCloudServer interface { + // ListSubscribers lists pages of subscribers stored. + ListSubscribers(context.Context, *ListSubscribersRequest) (*ListSubscribersResponse, error) +} + +// UnimplementedSubscriberDBCloudServer can be embedded to have forward compatible implementations. +type UnimplementedSubscriberDBCloudServer struct { +} + +func (*UnimplementedSubscriberDBCloudServer) ListSubscribers(ctx context.Context, req *ListSubscribersRequest) (*ListSubscribersResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method ListSubscribers not implemented") +} + +func RegisterSubscriberDBCloudServer(s *grpc.Server, srv SubscriberDBCloudServer) { + s.RegisterService(&_SubscriberDBCloud_serviceDesc, srv) +} + +func _SubscriberDBCloud_ListSubscribers_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(ListSubscribersRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(SubscriberDBCloudServer).ListSubscribers(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/magma.lte.SubscriberDBCloud/ListSubscribers", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(SubscriberDBCloudServer).ListSubscribers(ctx, req.(*ListSubscribersRequest)) + } + return interceptor(ctx, in, info, handler) +} + +var _SubscriberDBCloud_serviceDesc = grpc.ServiceDesc{ + ServiceName: "magma.lte.SubscriberDBCloud", + HandlerType: (*SubscriberDBCloudServer)(nil), + Methods: []grpc.MethodDesc{ + { + MethodName: "ListSubscribers", + Handler: _SubscriberDBCloud_ListSubscribers_Handler, + }, + }, + Streams: []grpc.StreamDesc{}, + Metadata: "lte/protos/subscriberdb.proto", +} diff --git a/lte/cloud/go/services/subscriberdb/servicers/subscriberdb_servicer.go b/lte/cloud/go/services/subscriberdb/servicers/subscriberdb_servicer.go new file mode 100644 index 000000000000..48390a7a9f3b --- /dev/null +++ b/lte/cloud/go/services/subscriberdb/servicers/subscriberdb_servicer.go @@ -0,0 +1,193 @@ +/* +Copyright 2020 The Magma Authors. + +This source code is licensed under the BSD-style license found in the +LICENSE file in the root directory of this source tree. + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package servicers + +import ( + "context" + "sort" + + "github.com/pkg/errors" + "google.golang.org/grpc/codes" + "google.golang.org/grpc/status" + + "magma/lte/cloud/go/lte" + lte_protos "magma/lte/cloud/go/protos" + "magma/lte/cloud/go/serdes" + lte_models "magma/lte/cloud/go/services/lte/obsidian/models" + "magma/lte/cloud/go/services/subscriberdb/obsidian/models" + "magma/orc8r/cloud/go/services/configurator" + "magma/orc8r/lib/go/protos" +) + +type subscriberdbServicer struct{} + +const defaultSubProfile = "default" + +func NewSubscriberdbServicer() lte_protos.SubscriberDBCloudServer { + return &subscriberdbServicer{} +} + +// ListSubscribers returns a page of subscribers and a token to be used on +// subsequent requests. The page token specified in the request is used to +// determine the first subscriber to include in the page. The page size +// specified in the request determines the maximum number of entities to +// return. If no page size is specified, the maximum size configured in the +// configurator service will be returned. +func (s *subscriberdbServicer) ListSubscribers(ctx context.Context, req *lte_protos.ListSubscribersRequest) (*lte_protos.ListSubscribersResponse, error) { + gateway := protos.GetClientGateway(ctx) + if gateway == nil { + return nil, status.Errorf(codes.PermissionDenied, "missing gateway identity") + } + if !gateway.Registered() { + return nil, status.Errorf(codes.PermissionDenied, "gateway is not registered") + } + networkID := gateway.NetworkId + gatewayID := gateway.LogicalId + lc := configurator.EntityLoadCriteria{ + PageSize: req.PageSize, + PageToken: req.PageToken, + LoadConfig: true, + LoadAssocsToThis: true, + LoadAssocsFromThis: true, + } + subEnts, nextToken, err := configurator.LoadAllEntitiesOfType( + networkID, lte.SubscriberEntityType, lc, serdes.Entity, + ) + if err != nil { + return nil, errors.Wrapf(err, "load subscribers in network of gateway %s", networkID) + } + lteGateway, err := configurator.LoadEntity( + networkID, lte.CellularGatewayEntityType, gatewayID, + configurator.EntityLoadCriteria{LoadAssocsFromThis: true}, + serdes.Entity, + ) + if err != nil { + return nil, errors.Wrapf(err, "load cellular gateway for gateway %s", gatewayID) + } + apnsByName, apnResourcesByAPN, err := loadAPNs(lteGateway) + if err != nil { + return nil, err + } + + subProtos := make([]*lte_protos.SubscriberData, 0, len(subEnts)) + for _, sub := range subEnts { + subProto, err := convertSubEntsToProtos(sub, apnsByName, apnResourcesByAPN) + if err != nil { + return nil, err + } + subProto.NetworkId = &protos.NetworkID{Id: networkID} + subProtos = append(subProtos, subProto) + } + listRes := <e_protos.ListSubscribersResponse{ + Subscribers: subProtos, + NextPageToken: nextToken, + } + return listRes, nil +} + +func loadAPNs(gateway configurator.NetworkEntity) (map[string]*lte_models.ApnConfiguration, lte_models.ApnResources, error) { + apns, _, err := configurator.LoadAllEntitiesOfType( + gateway.NetworkID, lte.APNEntityType, + configurator.EntityLoadCriteria{LoadConfig: true}, + serdes.Entity, + ) + if err != nil { + return nil, nil, err + } + apnsByName := map[string]*lte_models.ApnConfiguration{} + for _, ent := range apns { + apnsByName[ent.Key] = ent.Config.(*lte_models.ApnConfiguration) + } + + apnResources, err := lte_models.LoadAPNResources(gateway.NetworkID, gateway.Associations.Filter(lte.APNResourceEntityType).Keys()) + if err != nil { + return nil, nil, err + } + + return apnsByName, apnResources, nil +} + +func convertSubEntsToProtos(ent configurator.NetworkEntity, apnConfigs map[string]*lte_models.ApnConfiguration, apnResources lte_models.ApnResources) (*lte_protos.SubscriberData, error) { + subData := <e_protos.SubscriberData{} + t, err := lte_protos.SidProto(ent.Key) + if err != nil { + return nil, err + } + + subData.Sid = t + if ent.Config == nil { + return subData, nil + } + + cfg := ent.Config.(*models.SubscriberConfig) + subData.Lte = <e_protos.LTESubscription{ + State: lte_protos.LTESubscription_LTESubscriptionState(lte_protos.LTESubscription_LTESubscriptionState_value[cfg.Lte.State]), + AuthAlgo: lte_protos.LTESubscription_LTEAuthAlgo(lte_protos.LTESubscription_LTEAuthAlgo_value[cfg.Lte.AuthAlgo]), + AuthKey: cfg.Lte.AuthKey, + AuthOpc: cfg.Lte.AuthOpc, + } + + if cfg.Lte.SubProfile != "" { + subData.SubProfile = string(cfg.Lte.SubProfile) + } else { + subData.SubProfile = defaultSubProfile + } + + for _, assoc := range ent.ParentAssociations { + if assoc.Type == lte.BaseNameEntityType { + subData.Lte.AssignedBaseNames = append(subData.Lte.AssignedBaseNames, assoc.Key) + } else if assoc.Type == lte.PolicyRuleEntityType { + subData.Lte.AssignedPolicies = append(subData.Lte.AssignedPolicies, assoc.Key) + } + } + + // Construct the non-3gpp profile + non3gpp := <e_protos.Non3GPPUserProfile{ + ApnConfig: make([]*lte_protos.APNConfiguration, 0, len(ent.Associations)), + } + for _, assoc := range ent.Associations { + apnConfig, apnFound := apnConfigs[assoc.Key] + if !apnFound { + continue + } + var apnResource *lte_protos.APNConfiguration_APNResource + if apnResourceModel, ok := apnResources[assoc.Key]; ok { + apnResource = apnResourceModel.ToProto() + } + apnProto := <e_protos.APNConfiguration{ + ServiceSelection: assoc.Key, + Ambr: <e_protos.AggregatedMaximumBitrate{ + MaxBandwidthUl: *(apnConfig.Ambr.MaxBandwidthUl), + MaxBandwidthDl: *(apnConfig.Ambr.MaxBandwidthDl), + }, + QosProfile: <e_protos.APNConfiguration_QoSProfile{ + ClassId: *(apnConfig.QosProfile.ClassID), + PriorityLevel: *(apnConfig.QosProfile.PriorityLevel), + PreemptionCapability: *(apnConfig.QosProfile.PreemptionCapability), + PreemptionVulnerability: *(apnConfig.QosProfile.PreemptionVulnerability), + }, + Resource: apnResource, + } + if staticIP, found := cfg.StaticIps[assoc.Key]; found { + apnProto.AssignedStaticIp = string(staticIP) + } + non3gpp.ApnConfig = append(non3gpp.ApnConfig, apnProto) + } + sort.Slice(non3gpp.ApnConfig, func(i, j int) bool { + return non3gpp.ApnConfig[i].ServiceSelection < non3gpp.ApnConfig[j].ServiceSelection + }) + subData.Non_3Gpp = non3gpp + + return subData, nil +} diff --git a/lte/cloud/go/services/subscriberdb/servicers/subscriberdb_servicer_test.go b/lte/cloud/go/services/subscriberdb/servicers/subscriberdb_servicer_test.go new file mode 100644 index 000000000000..1cc4e4a8627e --- /dev/null +++ b/lte/cloud/go/services/subscriberdb/servicers/subscriberdb_servicer_test.go @@ -0,0 +1,302 @@ +/* +Copyright 2020 The Magma Authors. + +This source code is licensed under the BSD-style license found in the +LICENSE file in the root directory of this source tree. + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package servicers_test + +import ( + "context" + "encoding/base64" + "testing" + + "magma/lte/cloud/go/lte" + lte_protos "magma/lte/cloud/go/protos" + "magma/lte/cloud/go/serdes" + lte_models "magma/lte/cloud/go/services/lte/obsidian/models" + lte_test_init "magma/lte/cloud/go/services/lte/test_init" + "magma/lte/cloud/go/services/subscriberdb/obsidian/models" + "magma/lte/cloud/go/services/subscriberdb/servicers" + "magma/orc8r/cloud/go/orc8r" + "magma/orc8r/cloud/go/services/configurator" + configurator_storage "magma/orc8r/cloud/go/services/configurator/storage" + configurator_test_init "magma/orc8r/cloud/go/services/configurator/test_init" + "magma/orc8r/cloud/go/storage" + "magma/orc8r/lib/go/protos" + + "github.com/go-openapi/swag" + "github.com/golang/protobuf/proto" + "github.com/stretchr/testify/assert" +) + +func TestSubscriberdbCloudServicer(t *testing.T) { + lte_test_init.StartTestService(t) + configurator_test_init.StartTestService(t) + + servicer := servicers.NewSubscriberdbServicer() + err := configurator.CreateNetwork(configurator.Network{ID: "n1"}, serdes.Network) + assert.NoError(t, err) + _, err = configurator.CreateEntity("n1", configurator.NetworkEntity{Type: orc8r.MagmadGatewayType, Key: "g1", PhysicalID: "hw1"}, serdes.Entity) + assert.NoError(t, err) + gw, err := configurator.CreateEntity("n1", configurator.NetworkEntity{Type: lte.CellularGatewayEntityType, Key: "g1"}, serdes.Entity) + assert.NoError(t, err) + + id := protos.NewGatewayIdentity("hw1", "n1", "g1") + ctx := id.NewContextWithIdentity(context.Background()) + + // 2 subs without a profile on the backend (should fill as "default"), the + // other inactive with a sub profile + // 2 APNs active for the active sub, 1 with an assigned static IP and the + // other without + _, err = configurator.CreateEntities( + "n1", + []configurator.NetworkEntity{ + { + Type: lte.APNEntityType, Key: "apn1", + Config: <e_models.ApnConfiguration{ + Ambr: <e_models.AggregatedMaximumBitrate{ + MaxBandwidthDl: swag.Uint32(42), + MaxBandwidthUl: swag.Uint32(100), + }, + QosProfile: <e_models.QosProfile{ + ClassID: swag.Int32(1), + PreemptionCapability: swag.Bool(true), + PreemptionVulnerability: swag.Bool(true), + PriorityLevel: swag.Uint32(1), + }, + }, + }, + { + Type: lte.APNEntityType, Key: "apn2", + Config: <e_models.ApnConfiguration{ + Ambr: <e_models.AggregatedMaximumBitrate{ + MaxBandwidthDl: swag.Uint32(42), + MaxBandwidthUl: swag.Uint32(100), + }, + QosProfile: <e_models.QosProfile{ + ClassID: swag.Int32(2), + PreemptionCapability: swag.Bool(false), + PreemptionVulnerability: swag.Bool(false), + PriorityLevel: swag.Uint32(2), + }, + }, + }, + { + Type: lte.SubscriberEntityType, Key: "IMSI12345", + Config: &models.SubscriberConfig{ + Lte: &models.LteSubscription{ + State: "ACTIVE", + AuthKey: []byte("\x22\x22\x22\x22\x22\x22\x22\x22\x22\x22\x22\x22\x22\x22\x22\x22"), + AuthOpc: []byte("\x22\x22\x22\x22\x22\x22\x22\x22\x22\x22\x22\x22\x22\x22\x22\x22"), + }, + StaticIps: models.SubscriberStaticIps{"apn1": "192.168.100.1"}, + }, + Associations: []storage.TypeAndKey{{Type: lte.APNEntityType, Key: "apn1"}, {Type: lte.APNEntityType, Key: "apn2"}}, + }, + {Type: lte.SubscriberEntityType, Key: "IMSI67890", Config: &models.SubscriberConfig{Lte: &models.LteSubscription{State: "INACTIVE", SubProfile: "foo"}}}, + {Type: lte.SubscriberEntityType, Key: "IMSI99999", Config: &models.SubscriberConfig{Lte: &models.LteSubscription{State: "INACTIVE", SubProfile: "foo"}}}, + }, + serdes.Entity, + ) + assert.NoError(t, err) + + // Fetch first page of subscribers + expectedProtos := []*lte_protos.SubscriberData{ + { + Sid: <e_protos.SubscriberID{Id: "12345", Type: lte_protos.SubscriberID_IMSI}, + Lte: <e_protos.LTESubscription{ + State: lte_protos.LTESubscription_ACTIVE, + AuthKey: []byte("\x22\x22\x22\x22\x22\x22\x22\x22\x22\x22\x22\x22\x22\x22\x22\x22"), + AuthOpc: []byte("\x22\x22\x22\x22\x22\x22\x22\x22\x22\x22\x22\x22\x22\x22\x22\x22"), + }, + NetworkId: &protos.NetworkID{Id: "n1"}, + SubProfile: "default", + Non_3Gpp: <e_protos.Non3GPPUserProfile{ + ApnConfig: []*lte_protos.APNConfiguration{ + { + ServiceSelection: "apn1", + QosProfile: <e_protos.APNConfiguration_QoSProfile{ + ClassId: 1, + PriorityLevel: 1, + PreemptionCapability: true, + PreemptionVulnerability: true, + }, + Ambr: <e_protos.AggregatedMaximumBitrate{ + MaxBandwidthUl: 100, + MaxBandwidthDl: 42, + }, + AssignedStaticIp: "192.168.100.1", + }, + { + ServiceSelection: "apn2", + QosProfile: <e_protos.APNConfiguration_QoSProfile{ + ClassId: 2, + PriorityLevel: 2, + PreemptionCapability: false, + PreemptionVulnerability: false, + }, + Ambr: <e_protos.AggregatedMaximumBitrate{ + MaxBandwidthUl: 100, + MaxBandwidthDl: 42, + }, + }, + }, + }, + }, + { + Sid: <e_protos.SubscriberID{Id: "67890", Type: lte_protos.SubscriberID_IMSI}, + Lte: <e_protos.LTESubscription{State: lte_protos.LTESubscription_INACTIVE, AuthKey: []byte{}}, + Non_3Gpp: <e_protos.Non3GPPUserProfile{ApnConfig: []*lte_protos.APNConfiguration{}}, + NetworkId: &protos.NetworkID{Id: "n1"}, + SubProfile: "foo", + }, + } + + // Fetch first page of subscribers + req := <e_protos.ListSubscribersRequest{ + PageSize: 2, + PageToken: "", + } + res, err := servicer.ListSubscribers(ctx, req) + token := &configurator_storage.EntityPageToken{ + LastIncludedEntity: "IMSI67890", + } + expectedToken := serializeToken(t, token) + assert.NoError(t, err) + assert.Equal(t, expectedProtos, res.Subscribers) + assert.Equal(t, expectedToken, res.NextPageToken) + + expectedProtos2 := []*lte_protos.SubscriberData{ + { + Sid: <e_protos.SubscriberID{Id: "99999", Type: lte_protos.SubscriberID_IMSI}, + Lte: <e_protos.LTESubscription{State: lte_protos.LTESubscription_INACTIVE, AuthKey: []byte{}}, + Non_3Gpp: <e_protos.Non3GPPUserProfile{ApnConfig: []*lte_protos.APNConfiguration{}}, + NetworkId: &protos.NetworkID{Id: "n1"}, + SubProfile: "foo", + }, + } + + // Fetch final page of subscribers + req = <e_protos.ListSubscribersRequest{ + PageSize: 2, + PageToken: res.NextPageToken, + } + res, err = servicer.ListSubscribers(ctx, req) + assert.NoError(t, err) + assert.Equal(t, expectedProtos2, res.Subscribers) + assert.Equal(t, "", res.NextPageToken) + + // Create policies and base name associated to sub + _, err = configurator.CreateEntities( + "n1", + []configurator.NetworkEntity{ + { + Type: lte.BaseNameEntityType, Key: "bn1", + Associations: []storage.TypeAndKey{{Type: lte.SubscriberEntityType, Key: "IMSI12345"}}, + }, + { + Type: lte.PolicyRuleEntityType, Key: "r1", + Associations: []storage.TypeAndKey{{Type: lte.SubscriberEntityType, Key: "IMSI12345"}}, + }, + { + Type: lte.PolicyRuleEntityType, Key: "r2", + Associations: []storage.TypeAndKey{{Type: lte.SubscriberEntityType, Key: "IMSI12345"}}, + }, + }, + serdes.Entity, + ) + assert.NoError(t, err) + + expectedProtos[0].Lte.AssignedPolicies = []string{"r1", "r2"} + expectedProtos[0].Lte.AssignedBaseNames = []string{"bn1"} + + req = <e_protos.ListSubscribersRequest{ + PageSize: 2, + PageToken: "", + } + res, err = servicer.ListSubscribers(ctx, req) + assert.NoError(t, err) + assert.Equal(t, expectedProtos, res.Subscribers) + assert.Equal(t, expectedToken, res.NextPageToken) + + // Create gateway-specific APN configuration + var writes []configurator.EntityWriteOperation + writes = append(writes, configurator.NetworkEntity{ + NetworkID: "n1", + Type: lte.APNResourceEntityType, + Key: "resource1", + Config: <e_models.ApnResource{ + ApnName: "apn1", + GatewayIP: "172.16.254.1", + GatewayMac: "00:0a:95:9d:68:16", + ID: "resource1", + VlanID: 42, + }, + Associations: storage.TKs{{Type: lte.APNEntityType, Key: "apn1"}}, + }) + writes = append(writes, configurator.EntityUpdateCriteria{ + Type: lte.CellularGatewayEntityType, + Key: gw.Key, + AssociationsToAdd: storage.TKs{{Type: lte.APNResourceEntityType, Key: "resource1"}}, + }) + err = configurator.WriteEntities("n1", writes, serdes.Entity) + assert.NoError(t, err) + + expectedProtos[0].Non_3Gpp.ApnConfig[0].Resource = <e_protos.APNConfiguration_APNResource{ + ApnName: "apn1", + GatewayIp: "172.16.254.1", + GatewayMac: "00:0a:95:9d:68:16", + VlanId: 42, + } + + res, err = servicer.ListSubscribers(ctx, req) + assert.NoError(t, err) + assert.Equal(t, expectedProtos, res.Subscribers) + assert.Equal(t, expectedToken, res.NextPageToken) + + // Create 8 more subscribers to test max page size + _, err = configurator.CreateEntities( + "n1", + []configurator.NetworkEntity{ + {Type: lte.SubscriberEntityType, Key: "IMSI99991", Config: &models.SubscriberConfig{Lte: &models.LteSubscription{State: "INACTIVE", SubProfile: "foo"}}}, + {Type: lte.SubscriberEntityType, Key: "IMSI99992", Config: &models.SubscriberConfig{Lte: &models.LteSubscription{State: "INACTIVE", SubProfile: "foo"}}}, + {Type: lte.SubscriberEntityType, Key: "IMSI99993", Config: &models.SubscriberConfig{Lte: &models.LteSubscription{State: "INACTIVE", SubProfile: "foo"}}}, + {Type: lte.SubscriberEntityType, Key: "IMSI99994", Config: &models.SubscriberConfig{Lte: &models.LteSubscription{State: "INACTIVE", SubProfile: "foo"}}}, + {Type: lte.SubscriberEntityType, Key: "IMSI99995", Config: &models.SubscriberConfig{Lte: &models.LteSubscription{State: "INACTIVE", SubProfile: "foo"}}}, + {Type: lte.SubscriberEntityType, Key: "IMSI99996", Config: &models.SubscriberConfig{Lte: &models.LteSubscription{State: "INACTIVE", SubProfile: "foo"}}}, + {Type: lte.SubscriberEntityType, Key: "IMSI99997", Config: &models.SubscriberConfig{Lte: &models.LteSubscription{State: "INACTIVE", SubProfile: "foo"}}}, + {Type: lte.SubscriberEntityType, Key: "IMSI99998", Config: &models.SubscriberConfig{Lte: &models.LteSubscription{State: "INACTIVE", SubProfile: "foo"}}}, + }, + serdes.Entity, + ) + assert.NoError(t, err) + + // max page size for the configurator test service is 10 entities + // Ensure when page size specified is 0, max size is returned (10/11 subs) + req = <e_protos.ListSubscribersRequest{ + PageSize: 0, + PageToken: "", + } + res, err = servicer.ListSubscribers(ctx, req) + token = &configurator_storage.EntityPageToken{ + LastIncludedEntity: "IMSI99998", + } + expectedToken = serializeToken(t, token) + assert.NoError(t, err) + assert.Equal(t, 10, len(res.Subscribers)) + assert.Equal(t, expectedToken, res.NextPageToken) +} + +func serializeToken(t *testing.T, token *configurator_storage.EntityPageToken) string { + marshalledToken, err := proto.Marshal(token) + assert.NoError(t, err) + return base64.StdEncoding.EncodeToString(marshalledToken) +} diff --git a/lte/cloud/go/services/subscriberdb/subscriberdb/main.go b/lte/cloud/go/services/subscriberdb/subscriberdb/main.go index 0dd730947412..3ffb72f24719 100644 --- a/lte/cloud/go/services/subscriberdb/subscriberdb/main.go +++ b/lte/cloud/go/services/subscriberdb/subscriberdb/main.go @@ -15,6 +15,7 @@ package main import ( "magma/lte/cloud/go/lte" + lte_protos "magma/lte/cloud/go/protos" "magma/lte/cloud/go/services/subscriberdb" "magma/lte/cloud/go/services/subscriberdb/obsidian/handlers" "magma/lte/cloud/go/services/subscriberdb/protos" @@ -57,6 +58,7 @@ func main() { obsidian.AttachHandlers(srv.EchoServer, handlers.GetHandlers()) protos.RegisterSubscriberLookupServer(srv.GrpcServer, servicers.NewLookupServicer(fact, ipStore)) state_protos.RegisterIndexerServer(srv.GrpcServer, servicers.NewIndexerServicer()) + lte_protos.RegisterSubscriberDBCloudServer(srv.GrpcServer, servicers.NewSubscriberdbServicer()) swagger_protos.RegisterSwaggerSpecServer(srv.GrpcServer, swagger.NewSpecServicerFromFile(subscriberdb.ServiceName)) diff --git a/lte/cloud/go/services/subscriberdb/test_init/test_service_init.go b/lte/cloud/go/services/subscriberdb/test_init/test_service_init.go index 1d8559f6300f..b1ef84026670 100644 --- a/lte/cloud/go/services/subscriberdb/test_init/test_service_init.go +++ b/lte/cloud/go/services/subscriberdb/test_init/test_service_init.go @@ -17,6 +17,7 @@ import ( "testing" "magma/lte/cloud/go/lte" + lte_protos "magma/lte/cloud/go/protos" "magma/lte/cloud/go/services/subscriberdb" "magma/lte/cloud/go/services/subscriberdb/protos" "magma/lte/cloud/go/services/subscriberdb/servicers" @@ -52,6 +53,7 @@ func StartTestService(t *testing.T) { // Add servicers protos.RegisterSubscriberLookupServer(srv.GrpcServer, servicers.NewLookupServicer(fact, ipStore)) state_protos.RegisterIndexerServer(srv.GrpcServer, servicers.NewIndexerServicer()) + lte_protos.RegisterSubscriberDBCloudServer(srv.GrpcServer, servicers.NewSubscriberdbServicer()) // Run service go srv.RunTest(lis) diff --git a/lte/protos/subscriberdb.proto b/lte/protos/subscriberdb.proto index da73d23bb428..6069913c4868 100644 --- a/lte/protos/subscriberdb.proto +++ b/lte/protos/subscriberdb.proto @@ -208,7 +208,7 @@ message SubscriberUpdate { } // -------------------------------------------------------------------------- -// SubscriberDB service definition. +// SubscriberDB gateway service definition. // -------------------------------------------------------------------------- service SubscriberDB { @@ -236,3 +236,25 @@ service SubscriberDB { // rpc ListSubscribers (magma.orc8r.Void) returns (SubscriberIDSet) {} } + +// -------------------------------------------------------------------------- +// SubscriberDB cloud service definition. +// -------------------------------------------------------------------------- +service SubscriberDBCloud { + // ListSubscribers lists pages of subscribers stored. + rpc ListSubscribers (ListSubscribersRequest) returns (ListSubscribersResponse) {} +} + +message ListSubscribersRequest { + // page_size is the maximum number of entities returned per request. + uint32 page_size = 1; + // page_token is a serialized entity page token for paginated loads. + string page_token = 2; +} + +message ListSubscribersResponse { + repeated SubscriberData subscribers = 1; + // next_page_token is a serialized entity page token for subsequent paginated + // loads. + string next_page_token = 2; +} From 7e1179b16b9a00efe77e4e148b81f05166810ff5 Mon Sep 17 00:00:00 2001 From: Marie <37634144+themarwhal@users.noreply.github.com> Date: Wed, 7 Apr 2021 08:21:57 -0500 Subject: [PATCH 41/91] [Ubuntu20.04][SentryNative] Build and install sentry native SDK in ubuntu VM (#5922) Signed-off-by: Marie Bremner --- .../deploy/roles/dev_common/defaults/main.yml | 1 + .../deploy/roles/dev_common/tasks/main.yml | 35 +++++++++++++++++++ 2 files changed, 36 insertions(+) diff --git a/lte/gateway/deploy/roles/dev_common/defaults/main.yml b/lte/gateway/deploy/roles/dev_common/defaults/main.yml index 303f97b8bc8e..e7aa3b2f0a21 100644 --- a/lte/gateway/deploy/roles/dev_common/defaults/main.yml +++ b/lte/gateway/deploy/roles/dev_common/defaults/main.yml @@ -1,3 +1,4 @@ +tmp_directory: /var/tmp codegen_root: /var/tmp/codegen mvn_version: 3.5.4 mvn_sha1: 22cac91b3557586bb1eba326f2f7727543ff15e3 diff --git a/lte/gateway/deploy/roles/dev_common/tasks/main.yml b/lte/gateway/deploy/roles/dev_common/tasks/main.yml index d1423f84e8c7..9f695a98eb23 100644 --- a/lte/gateway/deploy/roles/dev_common/tasks/main.yml +++ b/lte/gateway/deploy/roles/dev_common/tasks/main.yml @@ -238,6 +238,7 @@ - libpcap-dev - libtins-dev - libmnl-dev + - libcurl4-openssl-dev retries: 5 when: preburn @@ -265,6 +266,40 @@ - libprotoc17 - libssl-dev +############################################### +# Download and build sentry-native +############################################### + +- name: Install Sentry Native SDK + vars: + sentry_native_version: "0.4.8" + get_url: + url: "https://github.com/getsentry/sentry-native/releases/download/{{ sentry_native_version }}/sentry-native.zip" + dest: "{{ tmp_directory }}/sentry-native.zip" + when: preburn and ansible_distribution == "Ubuntu" + +- name: Create a directory for Sentry Native + file: + path: "{{ tmp_directory }}/sentry-native" + state: directory + when: preburn and ansible_distribution == "Ubuntu" + +- name: Unpack Sentry Native SDK + unarchive: + src: "{{ tmp_directory }}/sentry-native.zip" + dest: "{{ tmp_directory }}/sentry-native" + remote_src: yes + when: preburn and ansible_distribution == "Ubuntu" + +- name: Build and Install Sentry Native SDK + shell: | + cd {{ tmp_directory }}/sentry-native && \ + cmake -B build -DCMAKE_BUILD_TYPE=RelWithDebInfo && \ + cmake --build build --parallel && \ + cmake --install build --prefix install --config RelWithDebInfo && \ + cd build && make install + when: preburn and ansible_distribution == "Ubuntu" + ############################################### # Download and build swagger-codegen ############################################### From fd8740afc3d3827964a0a328291502f252875f2d Mon Sep 17 00:00:00 2001 From: Hunter Gatewood Date: Wed, 7 Apr 2021 10:43:09 -0700 Subject: [PATCH 42/91] [docs] Make base README pretty (#5979) Signed-off-by: Hunter Gatewood --- README.md | 35 +++++++++++++++++++++++------------ 1 file changed, 23 insertions(+), 12 deletions(-) diff --git a/README.md b/README.md index c7cb41ffc090..0d779c60f918 100644 --- a/README.md +++ b/README.md @@ -1,15 +1,26 @@ -[![Magma](https://raw.githubusercontent.com/magma/magma/master/docs/docusaurus/static/img/magma-logo-purple.svg)](https://www.magmacore.org/) - -**Connecting the Next Billion People** - -[![magma](https://circleci.com/gh/magma/magma.svg?style=shield)](https://circleci.com/gh/magma/magma) +

+ Magma +

+ +

Connecting the Next Billion People

+ +

+ License + GitHub Release + PR's Welcome + GitHub contributors + GitHub last commit + GitHub commit activity the past week + CircleCI + CodeCov +

Magma is an open-source software platform that gives network operators an open, flexible and extendable mobile core network solution. Magma enables better connectivity by: -* Allowing operators to offer cellular service without vendor lock-in with a modern, open source core network -* Enabling operators to manage their networks more efficiently with more automation, less downtime, better predictability, and more agility to add new services and applications -* Enabling federation between existing MNOs and new infrastructure providers for expanding rural infrastructure -* Allowing operators who are constrained with licensed spectrum to add capacity and reach by using Wi-Fi and CBRS +- Allowing operators to offer cellular service without vendor lock-in with a modern, open source core network +- Enabling operators to manage their networks more efficiently with more automation, less downtime, better predictability, and more agility to add new services and applications +- Enabling federation between existing MNOs and new infrastructure providers for expanding rural infrastructure +- Allowing operators who are constrained with licensed spectrum to add capacity and reach by using Wi-Fi and CBRS ## Magma Architecture @@ -18,11 +29,11 @@ The figure below shows the high-level Magma architecture. Magma is 3GPP generati Magma has three major components -* **Access Gateway.** The Access Gateway (AGW) provides network services and policy enforcement. In an LTE network, the AGW implements an evolved packet core (EPC), and a combination of an AAA and a PGW. It works with existing, unmodified commercial radio hardware. +- **Access Gateway.** The Access Gateway (AGW) provides network services and policy enforcement. In an LTE network, the AGW implements an evolved packet core (EPC), and a combination of an AAA and a PGW. It works with existing, unmodified commercial radio hardware. -* **Orchestrator.** Orchestrator is a cloud service that provides a simple and consistent way to configure and monitor the wireless network securely. The Orchestrator can be hosted on a public/private cloud. The metrics acquired through the platform allows you to see the analytics and traffic flows of the wireless users through the Magma web UI. +- **Orchestrator.** Orchestrator is a cloud service that provides a simple and consistent way to configure and monitor the wireless network securely. The Orchestrator can be hosted on a public/private cloud. The metrics acquired through the platform allows you to see the analytics and traffic flows of the wireless users through the Magma web UI. -* **Federation Gateway.** The Federation Gateway integrates the MNO core network with Magma by using standard 3GPP interfaces to existing MNO components. It acts as a proxy between the Magma AGW and the operator's network and facilitates core functions, such as authentication, data plans, policy enforcement, and charging to stay uniform between an existing MNO network and the expanded network with Magma. +- **Federation Gateway.** The Federation Gateway integrates the MNO core network with Magma by using standard 3GPP interfaces to existing MNO components. It acts as a proxy between the Magma AGW and the operator's network and facilitates core functions, such as authentication, data plans, policy enforcement, and charging to stay uniform between an existing MNO network and the expanded network with Magma. ![Magma architecture diagram](docs/readmes/assets/magma_overview.png?raw=true "Magma Architecture") From 2faa43c27af53bf480f22ed92a9c9d2321522302 Mon Sep 17 00:00:00 2001 From: Ankit Kumar Aman <50101753+VinashakAnkitAman@users.noreply.github.com> Date: Wed, 7 Apr 2021 23:30:26 +0530 Subject: [PATCH 43/91] [Non_Sanity_Testing] Fixed invalid function call for non-sanity dedicated bearer test cases (#5986) * Fixed invalid function call for non-sanity ipv6 dedicated bearer test cases Signed-off-by: Ankit Kumar Aman --- .../test_ipv4v6_secondary_pdn_with_ded_bearer.py | 8 ++++++-- ...st_ipv4v6_secondary_pdn_with_ded_bearer_multi_ue.py | 10 +++++++--- .../test_ipv6_secondary_pdn_with_ded_bearer.py | 8 ++++++-- .../test_outoforder_erab_setup_rsp_dedicated_bearer.py | 4 ++-- 4 files changed, 21 insertions(+), 9 deletions(-) diff --git a/lte/gateway/python/integ_tests/s1aptests/test_ipv4v6_secondary_pdn_with_ded_bearer.py b/lte/gateway/python/integ_tests/s1aptests/test_ipv4v6_secondary_pdn_with_ded_bearer.py index 3dc0281b0939..d5127d203794 100644 --- a/lte/gateway/python/integ_tests/s1aptests/test_ipv4v6_secondary_pdn_with_ded_bearer.py +++ b/lte/gateway/python/integ_tests/s1aptests/test_ipv4v6_secondary_pdn_with_ded_bearer.py @@ -173,7 +173,11 @@ def test_ipv4v6_secondary_pdn_with_ded_bearer(self): print( "********************** Added default bearer for apn-%s," " bearer id-%d, pdn type-%d" - % (apn, act_def_bearer_req.m.pdnInfo.epsBearerId, pdn_type,) + % ( + apn, + act_def_bearer_req.m.pdnInfo.epsBearerId, + pdn_type, + ) ) # Receive Router Advertisement message @@ -202,7 +206,7 @@ def test_ipv4v6_secondary_pdn_with_ded_bearer(self): "********************** Sending RAR for IMSI", "".join([str(i) for i in req.imsi]), ) - self._sessionManager_util.create_ReAuthRequest( + self._sessionManager_util.send_ReAuthRequest( "IMSI" + "".join([str(i) for i in req.imsi]), policy_id, flow_list, diff --git a/lte/gateway/python/integ_tests/s1aptests/test_ipv4v6_secondary_pdn_with_ded_bearer_multi_ue.py b/lte/gateway/python/integ_tests/s1aptests/test_ipv4v6_secondary_pdn_with_ded_bearer_multi_ue.py index 1458c91a1479..faf798fcacda 100644 --- a/lte/gateway/python/integ_tests/s1aptests/test_ipv4v6_secondary_pdn_with_ded_bearer_multi_ue.py +++ b/lte/gateway/python/integ_tests/s1aptests/test_ipv4v6_secondary_pdn_with_ded_bearer_multi_ue.py @@ -25,7 +25,7 @@ def tearDown(self): self._s1ap_wrapper.cleanup() def test_ipv4v6_secondary_pdn_with_ded_bearer_multi_ue(self): - """ Attach a single UE + add a secondary pdn with + """Attach a single UE + add a secondary pdn with IPv4v6 + add dedicated bearer + detach Repeat for 4 UEs""" num_ues = 4 @@ -179,7 +179,11 @@ def test_ipv4v6_secondary_pdn_with_ded_bearer_multi_ue(self): print( "********************** Added default bearer for apn-%s," " bearer id-%d, pdn type-%d" - % (apn, act_def_bearer_req.m.pdnInfo.epsBearerId, pdn_type,) + % ( + apn, + act_def_bearer_req.m.pdnInfo.epsBearerId, + pdn_type, + ) ) # Receive Router Advertisement message @@ -208,7 +212,7 @@ def test_ipv4v6_secondary_pdn_with_ded_bearer_multi_ue(self): "********************** Sending RAR for IMSI", "".join([str(i) for i in req.imsi]), ) - self._sessionManager_util.create_ReAuthRequest( + self._sessionManager_util.send_ReAuthRequest( "IMSI" + "".join([str(i) for i in req.imsi]), policy_id, flow_list, diff --git a/lte/gateway/python/integ_tests/s1aptests/test_ipv6_secondary_pdn_with_ded_bearer.py b/lte/gateway/python/integ_tests/s1aptests/test_ipv6_secondary_pdn_with_ded_bearer.py index 113a927e7428..60c418549022 100644 --- a/lte/gateway/python/integ_tests/s1aptests/test_ipv6_secondary_pdn_with_ded_bearer.py +++ b/lte/gateway/python/integ_tests/s1aptests/test_ipv6_secondary_pdn_with_ded_bearer.py @@ -149,7 +149,11 @@ def test_ipv6_secondary_pdn_with_ded_bearer(self): print( "********************** Added default bearer for apn-%s," " bearer id-%d, pdn type-%d" - % (apn, act_def_bearer_req.m.pdnInfo.epsBearerId, pdn_type,) + % ( + apn, + act_def_bearer_req.m.pdnInfo.epsBearerId, + pdn_type, + ) ) # Receive Router Advertisement message @@ -178,7 +182,7 @@ def test_ipv6_secondary_pdn_with_ded_bearer(self): "********************** Sending RAR for IMSI", "".join([str(i) for i in req.imsi]), ) - self._sessionManager_util.create_ReAuthRequest( + self._sessionManager_util.send_ReAuthRequest( "IMSI" + "".join([str(i) for i in req.imsi]), policy_id, flow_list, diff --git a/lte/gateway/python/integ_tests/s1aptests/test_outoforder_erab_setup_rsp_dedicated_bearer.py b/lte/gateway/python/integ_tests/s1aptests/test_outoforder_erab_setup_rsp_dedicated_bearer.py index 527962f8578b..963d4c34651a 100755 --- a/lte/gateway/python/integ_tests/s1aptests/test_outoforder_erab_setup_rsp_dedicated_bearer.py +++ b/lte/gateway/python/integ_tests/s1aptests/test_outoforder_erab_setup_rsp_dedicated_bearer.py @@ -30,7 +30,7 @@ def tearDown(self): self._s1ap_wrapper.cleanup() def test_outoforder_erab_setup_rsp_dedicated_bearer(self): - """ Attach a single UE + add dedicated bearer + send erab setup rsp + """Attach a single UE + add dedicated bearer + send erab setup rsp message out of order for the dedicated bearer""" num_ue = 1 @@ -168,7 +168,7 @@ def test_outoforder_erab_setup_rsp_dedicated_bearer(self): "********************** Sending RAR for IMSI", "".join([str(i) for i in req.imsi]), ) - self._sessionManager_util.create_ReAuthRequest( + self._sessionManager_util.send_ReAuthRequest( "IMSI" + "".join([str(i) for i in req.imsi]), policy_id, flow_list, From 2930e592f5b69fb984ba7f25bb2e40bf5a61a0d6 Mon Sep 17 00:00:00 2001 From: Scott Moeller Date: Wed, 7 Apr 2021 14:09:15 -0400 Subject: [PATCH 44/91] [DevExp] Fixup shellcheck configuration (#6003) I have learned three things, this PR fixes them. First, `name: reviewdog` is the canonical name used by these linters which enables them to be grouped by GH in the UI (nice). Second, I used an incorrect `filter_mode: diff_context`, which only captures modified lines not added + modified lines. Third, I updated comments to make clear that `reporter: github-pr-review` will fall back to annotations if permissions fail (as they do today). Signed-off-by: Scott Harrison Moeller --- .github/workflows/shellcheck.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/shellcheck.yml b/.github/workflows/shellcheck.yml index 0e8522c3b713..5a87a73e4de4 100644 --- a/.github/workflows/shellcheck.yml +++ b/.github/workflows/shellcheck.yml @@ -1,4 +1,4 @@ -name: Shellcheck by Reviewdog +name: reviewdog on: [pull_request] jobs: shellcheck: @@ -10,8 +10,8 @@ jobs: uses: reviewdog/action-shellcheck@v1 with: github_token: ${{ secrets.github_token }} - filter_mode: diff_context # Any changed content. - reporter: github-pr-review # Post code review comments. + filter_mode: added # Any added or changed content. + reporter: github-pr-review # Post code review comments. Falls back to Annotations. pattern: "*.sh" # Optional. # Other options omitted here but possible. # - fail_on_error From 234021d815318df66b58105db4e1dae33e9d3010 Mon Sep 17 00:00:00 2001 From: Scott Moeller Date: Wed, 7 Apr 2021 14:10:07 -0400 Subject: [PATCH 45/91] [DevExp] Migrate container to clang-format 7 for backwards compatibility (#5939) Signed-off-by: Scott Harrison Moeller Co-authored-by: Scott Harrison Moeller --- .devcontainer/Dockerfile | 2 ++ lte/gateway/docker/mme/Dockerfile.ubuntu20.04 | 7 +++++-- 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/.devcontainer/Dockerfile b/.devcontainer/Dockerfile index 512f06a48ff5..7e15cba71d45 100644 --- a/.devcontainer/Dockerfile +++ b/.devcontainer/Dockerfile @@ -47,6 +47,7 @@ RUN echo "Install general purpose packages" && \ automake \ build-essential \ ca-certificates \ + clang-format-7 \ clang-format-11 \ clang-tidy-11 \ curl \ @@ -78,6 +79,7 @@ RUN echo "Install general purpose packages" && \ gem install fpm && \ update-alternatives --install /usr/bin/clang clang /usr/lib/llvm-11/bin/clang 10 && \ update-alternatives --install /usr/bin/clang++ clang++ /usr/lib/llvm-11/bin/clang++ 10 && \ + update-alternatives --install /usr/bin/clang-format clang-format /usr/lib/llvm-7/bin/clang-format 10 && \ update-alternatives --install /usr/bin/clang-tidy clang-tidy /usr/lib/llvm-11/bin/clang-tidy 10 && \ update-alternatives --install /usr/bin/clang-apply-replacements clang-apply-replacements /usr/lib/llvm-11/bin/clang-apply-replacements 10 diff --git a/lte/gateway/docker/mme/Dockerfile.ubuntu20.04 b/lte/gateway/docker/mme/Dockerfile.ubuntu20.04 index 585878e773ca..c7319a23446d 100644 --- a/lte/gateway/docker/mme/Dockerfile.ubuntu20.04 +++ b/lte/gateway/docker/mme/Dockerfile.ubuntu20.04 @@ -25,6 +25,7 @@ RUN echo "Install general purpouse packages" && \ build-essential \ ca-certificates \ clang-11 \ + clang-format-7 \ clang-format-11 \ clang-tidy-11 \ curl \ @@ -55,9 +56,11 @@ RUN echo "Install general purpouse packages" && \ vim \ wget && \ gem install fpm && \ - update-alternatives --install /usr/bin/clang-tidy clang-tidy /usr/lib/llvm-11/bin/clang-tidy 10 && \ update-alternatives --install /usr/bin/clang clang /usr/lib/llvm-11/bin/clang 10 && \ - update-alternatives --install /usr/bin/clang++ clang++ /usr/lib/llvm-11/bin/clang++ 10 + update-alternatives --install /usr/bin/clang++ clang++ /usr/lib/llvm-11/bin/clang++ 10 \ + update-alternatives --install /usr/bin/clang-format clang-format /usr/lib/llvm-7/bin/clang-format 10 && \ + update-alternatives --install /usr/bin/clang-tidy clang-tidy /usr/lib/llvm-11/bin/clang-tidy 10 && \ + update-alternatives --install /usr/bin/clang-apply-replacements clang-apply-replacements /usr/lib/llvm-11/bin/clang-apply-replacements 10 RUN echo "Install 3rd party dependencies" && \ apt-get update && \ From 808589f41938738f7370f0e41ca6b11102c242a6 Mon Sep 17 00:00:00 2001 From: Marie <37634144+themarwhal@users.noreply.github.com> Date: Wed, 7 Apr 2021 13:38:50 -0500 Subject: [PATCH 46/91] [AGW][Python] Add reviewdog annotations for Python diffs (#5962) Signed-off-by: Marie Bremner --- .../workflows/wemake-python-styleguide.yml | 23 +++++++++++++++++++ 1 file changed, 23 insertions(+) create mode 100644 .github/workflows/wemake-python-styleguide.yml diff --git a/.github/workflows/wemake-python-styleguide.yml b/.github/workflows/wemake-python-styleguide.yml new file mode 100644 index 000000000000..ce96eec35bbf --- /dev/null +++ b/.github/workflows/wemake-python-styleguide.yml @@ -0,0 +1,23 @@ +name: Python linting by Reviewdog +on: [pull_request] +jobs: + wemake-python-styleguide: + name: runner / wemake-python-styleguide + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v2 + with: + fetch-depth: 0 + - name: Get changed files + id: py-changes + # Set outputs.py to be a list of modified python files + run: | + echo "::set-output name=py::$(git diff --name-only --diff-filter=ACMRT ${{ github.event.pull_request.base.sha }} ${{ github.sha }} | grep .py$ | xargs)" + - if: ${{ steps.py-changes.outputs.py }} + name: wemake-python-styleguide + uses: wemake-services/wemake-python-styleguide@0.15.2 + with: + reporter: 'github-pr-review' + path: ${{ steps.py-changes.outputs.py }} + env: + GITHUB_TOKEN: ${{ secrets.github_token }} From 4a1e36a16ed8035ff535ff71ab92fcc1716c9c56 Mon Sep 17 00:00:00 2001 From: Scott Moeller Date: Wed, 7 Apr 2021 14:51:34 -0400 Subject: [PATCH 47/91] [DevExp] Add reviewdog yamllint linter for PR annotations (#6004) Signed-off-by: Scott Harrison Moeller Co-authored-by: Scott Harrison Moeller --- .github/workflows/yamlint.yml | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) create mode 100644 .github/workflows/yamlint.yml diff --git a/.github/workflows/yamlint.yml b/.github/workflows/yamlint.yml new file mode 100644 index 000000000000..ca9521ed5684 --- /dev/null +++ b/.github/workflows/yamlint.yml @@ -0,0 +1,16 @@ +--- +name: reviewdog +on: [pull_request] # yamllint disable-line rule:truthy +jobs: + yamllint: + name: runner / yamllint + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v1 + - name: yamllint + uses: reviewdog/action-yamllint@v1 + with: + github_token: ${{ secrets.github_token }} + level: warning + filter_mode: added # Any added or changed content. + reporter: github-pr-review # Comments on PR with review comments. From a5dae5f2db53d9b37c43bf95f1edf61ce3233cb9 Mon Sep 17 00:00:00 2001 From: Marie <37634144+themarwhal@users.noreply.github.com> Date: Wed, 7 Apr 2021 13:58:07 -0500 Subject: [PATCH 48/91] [AGW][PyFormat] Apply isort to all smaller lte python services (#5942) Signed-off-by: Marie Bremner --- lte/gateway/python/magma/health/health_service.py | 9 ++++----- lte/gateway/python/magma/health/main.py | 1 - lte/gateway/python/magma/health/state_recovery.py | 4 +--- lte/gateway/python/magma/monitord/cpe_monitoring.py | 2 +- lte/gateway/python/magma/monitord/icmp_monitoring.py | 6 +++--- lte/gateway/python/magma/monitord/main.py | 9 +++++---- .../python/magma/monitord/tests/test_icmp_monitor.py | 2 +- .../python/magma/pkt_tester/tests/test_ovs_gtp.py | 3 +-- .../magma/pkt_tester/tests/test_topology_builder.py | 1 + lte/gateway/python/magma/pkt_tester/topology_builder.py | 3 +-- lte/gateway/python/magma/redirectd/main.py | 4 ++-- lte/gateway/python/magma/redirectd/redirect_server.py | 5 ++--- lte/gateway/python/magma/redirectd/redirect_store.py | 5 ++--- .../python/magma/redirectd/tests/test_redirect.py | 6 ++++-- lte/gateway/python/magma/smsd/main.py | 6 +++++- lte/gateway/python/magma/smsd/relay.py | 7 +++---- 16 files changed, 36 insertions(+), 37 deletions(-) diff --git a/lte/gateway/python/magma/health/health_service.py b/lte/gateway/python/magma/health/health_service.py index 0c49f4af4f94..14565ca47563 100644 --- a/lte/gateway/python/magma/health/health_service.py +++ b/lte/gateway/python/magma/health/health_service.py @@ -14,18 +14,17 @@ """ import glob import ipaddress - import math from os import path from lte.protos.enodebd_pb2_grpc import EnodebdStub -from lte.protos.mobilityd_pb2_grpc import MobilityServiceStub from lte.protos.mobilityd_pb2 import IPAddress -from orc8r.protos.common_pb2 import Void +from lte.protos.mobilityd_pb2_grpc import MobilityServiceStub from magma.common.service_registry import ServiceRegistry from magma.configuration.mconfig_managers import load_service_mconfig_as_json -from magma.health.entities import AGWHealthSummary, RegistrationSuccessRate, \ - CoreDumps +from magma.health.entities import (AGWHealthSummary, CoreDumps, + RegistrationSuccessRate) +from orc8r.protos.common_pb2 import Void class AGWHealth: diff --git a/lte/gateway/python/magma/health/main.py b/lte/gateway/python/magma/health/main.py index 4047538fdb51..12abe582d3dc 100644 --- a/lte/gateway/python/magma/health/main.py +++ b/lte/gateway/python/magma/health/main.py @@ -15,7 +15,6 @@ from magma.common.sentry import sentry_init from magma.common.service import MagmaService from magma.configuration.service_configs import load_service_config - from magma.health.state_recovery import StateRecoveryJob diff --git a/lte/gateway/python/magma/health/state_recovery.py b/lte/gateway/python/magma/health/state_recovery.py index aff3245ec23e..b95e1de80e45 100644 --- a/lte/gateway/python/magma/health/state_recovery.py +++ b/lte/gateway/python/magma/health/state_recovery.py @@ -10,10 +10,9 @@ See the License for the specific language governing permissions and limitations under the License. """ +import logging import os import shutil -import logging - from time import time from typing import Dict, List, NamedTuple, Optional @@ -21,7 +20,6 @@ from magma.common.job import Job from magma.magmad.check import subprocess_workflow from orc8r.protos.service_status_pb2 import ServiceExitStatus - from redis.exceptions import ConnectionError SystemdServiceParams = NamedTuple('SystemdServiceParams', [('service', str)]) diff --git a/lte/gateway/python/magma/monitord/cpe_monitoring.py b/lte/gateway/python/magma/monitord/cpe_monitoring.py index 0c7ce36191a8..5d60eef93307 100644 --- a/lte/gateway/python/magma/monitord/cpe_monitoring.py +++ b/lte/gateway/python/magma/monitord/cpe_monitoring.py @@ -10,11 +10,11 @@ See the License for the specific language governing permissions and limitations under the License. """ +import ipaddress import logging from collections import defaultdict from time import time from typing import Dict, List, NamedTuple, Optional -import ipaddress import grpc from lte.protos.mobilityd_pb2 import IPAddress, SubscriberIPTable diff --git a/lte/gateway/python/magma/monitord/icmp_monitoring.py b/lte/gateway/python/magma/monitord/icmp_monitoring.py index 19e70e21ccd9..db562b3e9f9c 100644 --- a/lte/gateway/python/magma/monitord/icmp_monitoring.py +++ b/lte/gateway/python/magma/monitord/icmp_monitoring.py @@ -15,9 +15,9 @@ from typing import Dict, List, Optional from magma.common.job import Job -from magma.magmad.check.network_check.ping import PingInterfaceCommandParams, \ - ping_interface_async -from magma.magmad.check.network_check.ping import PingCommandResult +from magma.magmad.check.network_check.ping import (PingCommandResult, + PingInterfaceCommandParams, + ping_interface_async) NUM_PACKETS = 4 DEFAULT_POLLING_INTERVAL = 60 diff --git a/lte/gateway/python/magma/monitord/main.py b/lte/gateway/python/magma/monitord/main.py index d276c7255cf5..138d1d9eaed2 100644 --- a/lte/gateway/python/magma/monitord/main.py +++ b/lte/gateway/python/magma/monitord/main.py @@ -13,14 +13,15 @@ import logging -from lte.protos.mconfig import mconfigs_pb2 -from magma.common.service import MagmaService +from lte.protos.mobilityd_pb2 import IPAddress from magma.common.sentry import sentry_init +from magma.common.service import MagmaService from magma.configuration import load_service_config +from magma.monitord.cpe_monitoring import CpeMonitoringModule from magma.monitord.icmp_monitoring import ICMPMonitoring from magma.monitord.icmp_state import serialize_subscriber_states -from magma.monitord.cpe_monitoring import CpeMonitoringModule -from lte.protos.mobilityd_pb2 import IPAddress + +from lte.protos.mconfig import mconfigs_pb2 def _get_serialized_subscriber_states(cpe_monitor: CpeMonitoringModule): diff --git a/lte/gateway/python/magma/monitord/tests/test_icmp_monitor.py b/lte/gateway/python/magma/monitord/tests/test_icmp_monitor.py index 05894f22c47d..0471bac5b947 100644 --- a/lte/gateway/python/magma/monitord/tests/test_icmp_monitor.py +++ b/lte/gateway/python/magma/monitord/tests/test_icmp_monitor.py @@ -16,8 +16,8 @@ import unittest from lte.protos.mobilityd_pb2 import IPAddress, SubscriberIPTable -from magma.monitord.icmp_monitoring import ICMPMonitoring from magma.monitord.cpe_monitoring import CpeMonitoringModule +from magma.monitord.icmp_monitoring import ICMPMonitoring LOCALHOST = '127.0.0.1' diff --git a/lte/gateway/python/magma/pkt_tester/tests/test_ovs_gtp.py b/lte/gateway/python/magma/pkt_tester/tests/test_ovs_gtp.py index 7f90b87765e1..1f2c38a2d050 100644 --- a/lte/gateway/python/magma/pkt_tester/tests/test_ovs_gtp.py +++ b/lte/gateway/python/magma/pkt_tester/tests/test_ovs_gtp.py @@ -13,12 +13,11 @@ import unittest +import test_topology_builder from nose.plugins.skip import SkipTest from scapy.all import IP, UDP, L2Socket # pylint: disable=no-name-in-module from scapy.contrib.gtp import GTPCreatePDPContextRequest, GTPHeader -import test_topology_builder - class TestOvsGtp(unittest.TestCase): """ diff --git a/lte/gateway/python/magma/pkt_tester/tests/test_topology_builder.py b/lte/gateway/python/magma/pkt_tester/tests/test_topology_builder.py index 310740f10799..ef29f6d9003f 100644 --- a/lte/gateway/python/magma/pkt_tester/tests/test_topology_builder.py +++ b/lte/gateway/python/magma/pkt_tester/tests/test_topology_builder.py @@ -13,6 +13,7 @@ import os import unittest + from nose.plugins.skip import SkipTest diff --git a/lte/gateway/python/magma/pkt_tester/topology_builder.py b/lte/gateway/python/magma/pkt_tester/topology_builder.py index 9767f5dcbe17..ed09f8ea34ec 100644 --- a/lte/gateway/python/magma/pkt_tester/topology_builder.py +++ b/lte/gateway/python/magma/pkt_tester/topology_builder.py @@ -11,14 +11,13 @@ limitations under the License. """ -import logging import copy +import logging import re from ovstest import util # pylint: disable=import-error from ovstest import vswitch # pylint: disable=import-error - logger = logging.getLogger(__name__) # pylint: disable=invalid-name diff --git a/lte/gateway/python/magma/redirectd/main.py b/lte/gateway/python/magma/redirectd/main.py index 0ae5bec8b5c0..62ae4dcde3aa 100644 --- a/lte/gateway/python/magma/redirectd/main.py +++ b/lte/gateway/python/magma/redirectd/main.py @@ -14,11 +14,11 @@ import logging import threading -from magma.common.service import MagmaService +from lte.protos.mconfig import mconfigs_pb2 from magma.common.sentry import sentry_init +from magma.common.service import MagmaService from magma.configuration.service_configs import get_service_config_value from magma.redirectd.redirect_server import run_flask -from lte.protos.mconfig import mconfigs_pb2 def main(): diff --git a/lte/gateway/python/magma/redirectd/redirect_server.py b/lte/gateway/python/magma/redirectd/redirect_server.py index 7fafb536f549..a2b369730c4b 100644 --- a/lte/gateway/python/magma/redirectd/redirect_server.py +++ b/lte/gateway/python/magma/redirectd/redirect_server.py @@ -14,10 +14,9 @@ import logging from collections import namedtuple -from magma.redirectd.redirect_store import RedirectDict - import wsgiserver -from flask import Flask, redirect, request, render_template +from flask import Flask, redirect, render_template, request +from magma.redirectd.redirect_store import RedirectDict """ Use 404 when subscriber not found, 302 for 'Found' redirect """ HTTP_NOT_FOUND = 404 diff --git a/lte/gateway/python/magma/redirectd/redirect_store.py b/lte/gateway/python/magma/redirectd/redirect_store.py index bb9f5da37113..14af5c61f7a7 100644 --- a/lte/gateway/python/magma/redirectd/redirect_store.py +++ b/lte/gateway/python/magma/redirectd/redirect_store.py @@ -12,11 +12,10 @@ """ from lte.protos.policydb_pb2 import RedirectInformation - from magma.common.redis.client import get_default_client from magma.common.redis.containers import RedisHashDict -from magma.common.redis.serializers import get_proto_deserializer, \ - get_proto_serializer +from magma.common.redis.serializers import (get_proto_deserializer, + get_proto_serializer) class RedirectDict(RedisHashDict): diff --git a/lte/gateway/python/magma/redirectd/tests/test_redirect.py b/lte/gateway/python/magma/redirectd/tests/test_redirect.py index cab247eb9412..157dc06279a3 100644 --- a/lte/gateway/python/magma/redirectd/tests/test_redirect.py +++ b/lte/gateway/python/magma/redirectd/tests/test_redirect.py @@ -15,8 +15,10 @@ from unittest.mock import MagicMock from lte.protos.policydb_pb2 import RedirectInformation -from magma.redirectd.redirect_server import HTTP_NOT_FOUND, HTTP_REDIRECT, \ - NOT_FOUND_HTML, RedirectInfo, ServerResponse, setup_flask_server +from magma.redirectd.redirect_server import (HTTP_NOT_FOUND, HTTP_REDIRECT, + NOT_FOUND_HTML, RedirectInfo, + ServerResponse, + setup_flask_server) class RedirectdTest(unittest.TestCase): diff --git a/lte/gateway/python/magma/smsd/main.py b/lte/gateway/python/magma/smsd/main.py index 146c0658e00b..a74b65cb2313 100644 --- a/lte/gateway/python/magma/smsd/main.py +++ b/lte/gateway/python/magma/smsd/main.py @@ -11,13 +11,17 @@ limitations under the License. """ +from lte.protos.sms_orc8r_pb2_grpc import (SmsDStub, + SMSOrc8rGatewayServiceStub, + SMSOrc8rServiceStub) from magma.common.sentry import sentry_init from magma.common.service import MagmaService from magma.common.service_registry import ServiceRegistry -from lte.protos.sms_orc8r_pb2_grpc import SMSOrc8rServiceStub, SMSOrc8rGatewayServiceStub, SmsDStub from orc8r.protos.directoryd_pb2_grpc import GatewayDirectoryServiceStub + from .relay import SmsRelay + def main(): """ main() for smsd """ service = MagmaService('smsd', None) diff --git a/lte/gateway/python/magma/smsd/relay.py b/lte/gateway/python/magma/smsd/relay.py index 33857b24ae82..17cc0e796cc2 100644 --- a/lte/gateway/python/magma/smsd/relay.py +++ b/lte/gateway/python/magma/smsd/relay.py @@ -15,13 +15,12 @@ from typing import List import grpc +import lte.protos.sms_orc8r_pb2 as sms_orc8r_pb2 +import lte.protos.sms_orc8r_pb2_grpc as sms_orc8r_pb2_grpc +from lte.protos.mconfig.mconfigs_pb2 import MME from magma.common.job import Job - from magma.common.rpc_utils import grpc_async_wrapper, return_void from magma.configuration.mconfig_managers import load_service_mconfig -from lte.protos.mconfig.mconfigs_pb2 import MME -import lte.protos.sms_orc8r_pb2_grpc as sms_orc8r_pb2_grpc -import lte.protos.sms_orc8r_pb2 as sms_orc8r_pb2 from orc8r.protos.common_pb2 import Void from orc8r.protos.directoryd_pb2_grpc import GatewayDirectoryServiceStub From e3b4197f5007943225320baf4050688ecc0c8c32 Mon Sep 17 00:00:00 2001 From: Scott Moeller Date: Wed, 7 Apr 2021 14:58:40 -0400 Subject: [PATCH 49/91] Revert "[DevExp] Migrate container to clang-format 7 for backwards compatibility (#5939)" (#6007) This reverts commit 234021d815318df66b58105db4e1dae33e9d3010. --- .devcontainer/Dockerfile | 2 -- lte/gateway/docker/mme/Dockerfile.ubuntu20.04 | 7 ++----- 2 files changed, 2 insertions(+), 7 deletions(-) diff --git a/.devcontainer/Dockerfile b/.devcontainer/Dockerfile index 7e15cba71d45..512f06a48ff5 100644 --- a/.devcontainer/Dockerfile +++ b/.devcontainer/Dockerfile @@ -47,7 +47,6 @@ RUN echo "Install general purpose packages" && \ automake \ build-essential \ ca-certificates \ - clang-format-7 \ clang-format-11 \ clang-tidy-11 \ curl \ @@ -79,7 +78,6 @@ RUN echo "Install general purpose packages" && \ gem install fpm && \ update-alternatives --install /usr/bin/clang clang /usr/lib/llvm-11/bin/clang 10 && \ update-alternatives --install /usr/bin/clang++ clang++ /usr/lib/llvm-11/bin/clang++ 10 && \ - update-alternatives --install /usr/bin/clang-format clang-format /usr/lib/llvm-7/bin/clang-format 10 && \ update-alternatives --install /usr/bin/clang-tidy clang-tidy /usr/lib/llvm-11/bin/clang-tidy 10 && \ update-alternatives --install /usr/bin/clang-apply-replacements clang-apply-replacements /usr/lib/llvm-11/bin/clang-apply-replacements 10 diff --git a/lte/gateway/docker/mme/Dockerfile.ubuntu20.04 b/lte/gateway/docker/mme/Dockerfile.ubuntu20.04 index c7319a23446d..585878e773ca 100644 --- a/lte/gateway/docker/mme/Dockerfile.ubuntu20.04 +++ b/lte/gateway/docker/mme/Dockerfile.ubuntu20.04 @@ -25,7 +25,6 @@ RUN echo "Install general purpouse packages" && \ build-essential \ ca-certificates \ clang-11 \ - clang-format-7 \ clang-format-11 \ clang-tidy-11 \ curl \ @@ -56,11 +55,9 @@ RUN echo "Install general purpouse packages" && \ vim \ wget && \ gem install fpm && \ - update-alternatives --install /usr/bin/clang clang /usr/lib/llvm-11/bin/clang 10 && \ - update-alternatives --install /usr/bin/clang++ clang++ /usr/lib/llvm-11/bin/clang++ 10 \ - update-alternatives --install /usr/bin/clang-format clang-format /usr/lib/llvm-7/bin/clang-format 10 && \ update-alternatives --install /usr/bin/clang-tidy clang-tidy /usr/lib/llvm-11/bin/clang-tidy 10 && \ - update-alternatives --install /usr/bin/clang-apply-replacements clang-apply-replacements /usr/lib/llvm-11/bin/clang-apply-replacements 10 + update-alternatives --install /usr/bin/clang clang /usr/lib/llvm-11/bin/clang 10 && \ + update-alternatives --install /usr/bin/clang++ clang++ /usr/lib/llvm-11/bin/clang++ 10 RUN echo "Install 3rd party dependencies" && \ apt-get update && \ From 29bf27bcdb06c6d0714ffc30ebd1766b137ad6cc Mon Sep 17 00:00:00 2001 From: Marie <37634144+themarwhal@users.noreply.github.com> Date: Wed, 7 Apr 2021 14:00:49 -0500 Subject: [PATCH 50/91] [AGW][PyFormat] Apply isort to EnodeBD (#5936) Signed-off-by: Marie Bremner --- .../magma/enodebd/data_models/data_model.py | 3 +- .../data_models/transform_for_magma.py | 4 +- .../device_config/configuration_init.py | 17 ++++---- .../enodeb_config_postprocessor.py | 3 +- .../device_config/enodeb_configuration.py | 7 ++-- .../python/magma/enodebd/devices/baicells.py | 33 ++++++++++------ .../magma/enodebd/devices/baicells_old.py | 33 ++++++++++------ .../magma/enodebd/devices/baicells_qafa.py | 31 +++++++++------ .../magma/enodebd/devices/baicells_qafb.py | 38 +++++++++++------- .../magma/enodebd/devices/baicells_rts.py | 39 ++++++++++++------- .../magma/enodebd/devices/device_map.py | 3 +- .../magma/enodebd/devices/device_utils.py | 3 +- .../enodebd/devices/experimental/cavium.py | 36 +++++++++++------ .../python/magma/enodebd/enodeb_status.py | 12 +++--- .../magma/enodebd/enodebd_iptables_rules.py | 12 +++--- lte/gateway/python/magma/enodebd/logger.py | 1 - lte/gateway/python/magma/enodebd/main.py | 16 ++++---- lte/gateway/python/magma/enodebd/metrics.py | 2 +- .../python/magma/enodebd/rpc_servicer.py | 21 +++++----- .../python/magma/enodebd/s1ap_client.py | 6 +-- .../enodebd/state_machines/acs_state_utils.py | 9 +++-- .../magma/enodebd/state_machines/enb_acs.py | 6 +-- .../enodebd/state_machines/enb_acs_impl.py | 2 +- .../enodebd/state_machines/enb_acs_states.py | 19 +++++---- .../python/magma/enodebd/stats_manager.py | 7 ++-- .../magma/enodebd/tests/baicells_old_tests.py | 6 +-- .../enodebd/tests/baicells_qafb_tests.py | 6 +-- .../magma/enodebd/tests/cavium_tests.py | 8 ++-- .../enodebd/tests/configuration_init_tests.py | 21 ++++++---- .../magma/enodebd/tests/data_model_tests.py | 3 +- .../magma/enodebd/tests/device_utils_tests.py | 6 ++- .../tests/enodeb_configuration_tests.py | 3 +- .../enodebd/tests/enodeb_status_tests.py | 10 +++-- .../enodebd/tests/stats_manager_tests.py | 10 ++--- .../tests/test_utils/enb_acs_builder.py | 3 +- .../tests/test_utils/enodeb_handler.py | 1 + .../tests/test_utils/mock_functions.py | 1 - .../enodebd/tests/test_utils/spyne_builder.py | 1 + .../tests/test_utils/tr069_msg_builder.py | 1 + .../python/magma/enodebd/tests/timer_tests.py | 2 +- .../enodebd/tests/transform_for_enb_tests.py | 1 + .../tests/transform_for_magma_tests.py | 3 +- .../python/magma/enodebd/tr069/models.py | 4 +- .../python/magma/enodebd/tr069/server.py | 12 +++--- .../python/magma/enodebd/tr069/spyne_mods.py | 3 +- .../magma/enodebd/tr069/tests/models_tests.py | 3 +- 46 files changed, 280 insertions(+), 191 deletions(-) diff --git a/lte/gateway/python/magma/enodebd/data_models/data_model.py b/lte/gateway/python/magma/enodebd/data_models/data_model.py index 2e0aaf6aa656..1c0db10d9a99 100644 --- a/lte/gateway/python/magma/enodebd/data_models/data_model.py +++ b/lte/gateway/python/magma/enodebd/data_models/data_model.py @@ -13,7 +13,8 @@ from abc import ABC, abstractmethod from collections import namedtuple -from typing import List, Dict, Any, Callable, Optional +from typing import Any, Callable, Dict, List, Optional + from magma.enodebd.data_models.data_model_parameters import ParameterName TrParam = namedtuple('TrParam', ['path', 'is_invasive', 'type', 'is_optional']) diff --git a/lte/gateway/python/magma/enodebd/data_models/transform_for_magma.py b/lte/gateway/python/magma/enodebd/data_models/transform_for_magma.py index 0ba156536155..7550c2a052db 100644 --- a/lte/gateway/python/magma/enodebd/data_models/transform_for_magma.py +++ b/lte/gateway/python/magma/enodebd/data_models/transform_for_magma.py @@ -10,11 +10,11 @@ See the License for the specific language governing permissions and limitations under the License. """ -from magma.enodebd.logger import EnodebdLogger as logger import textwrap from typing import Optional, Union -from magma.enodebd.exceptions import ConfigurationError +from magma.enodebd.exceptions import ConfigurationError +from magma.enodebd.logger import EnodebdLogger as logger DUPLEX_MAP = { '01': 'TDDMode', diff --git a/lte/gateway/python/magma/enodebd/device_config/configuration_init.py b/lte/gateway/python/magma/enodebd/device_config/configuration_init.py index a847504b82d4..15cc9743ae9a 100644 --- a/lte/gateway/python/magma/enodebd/device_config/configuration_init.py +++ b/lte/gateway/python/magma/enodebd/device_config/configuration_init.py @@ -12,23 +12,24 @@ """ import json -from magma.enodebd.logger import EnodebdLogger as logger from collections import namedtuple -from lte.protos.mconfig import mconfigs_pb2 from typing import Any, Optional, Union + +from lte.protos.mconfig import mconfigs_pb2 from magma.common.misc_utils import get_ip_from_if +from magma.configuration.exceptions import LoadConfigError +from magma.configuration.mconfig_managers import load_service_mconfig_as_json from magma.enodebd.data_models.data_model import DataModel +from magma.enodebd.data_models.data_model_parameters import ParameterName from magma.enodebd.device_config.enodeb_config_postprocessor import \ EnodebConfigurationPostProcessor from magma.enodebd.device_config.enodeb_configuration import \ EnodebConfiguration -from magma.enodebd.data_models.data_model_parameters import ParameterName from magma.enodebd.exceptions import ConfigurationError -from magma.enodebd.lte_utils import DuplexMode, \ - map_earfcndl_to_duplex_mode, map_earfcndl_to_band_earfcnul_mode -from magma.configuration.exceptions import LoadConfigError -from magma.configuration.mconfig_managers import load_service_mconfig_as_json - +from magma.enodebd.logger import EnodebdLogger as logger +from magma.enodebd.lte_utils import (DuplexMode, + map_earfcndl_to_band_earfcnul_mode, + map_earfcndl_to_duplex_mode) # LTE constants DEFAULT_S1_PORT = 36412 diff --git a/lte/gateway/python/magma/enodebd/device_config/enodeb_config_postprocessor.py b/lte/gateway/python/magma/enodebd/device_config/enodeb_config_postprocessor.py index 788553f5fd87..b650d230f842 100644 --- a/lte/gateway/python/magma/enodebd/device_config/enodeb_config_postprocessor.py +++ b/lte/gateway/python/magma/enodebd/device_config/enodeb_config_postprocessor.py @@ -10,7 +10,8 @@ See the License for the specific language governing permissions and limitations under the License. """ -from abc import abstractmethod, ABC +from abc import ABC, abstractmethod + from magma.enodebd.device_config.enodeb_configuration import \ EnodebConfiguration diff --git a/lte/gateway/python/magma/enodebd/device_config/enodeb_configuration.py b/lte/gateway/python/magma/enodebd/device_config/enodeb_configuration.py index 709766950ebe..fe6c8b49cd0d 100644 --- a/lte/gateway/python/magma/enodebd/device_config/enodeb_configuration.py +++ b/lte/gateway/python/magma/enodebd/device_config/enodeb_configuration.py @@ -12,11 +12,12 @@ """ import json -from magma.enodebd.logger import EnodebdLogger as logger -from typing import List, Any +from typing import Any, List + +from magma.enodebd.data_models.data_model import DataModel from magma.enodebd.data_models.data_model_parameters import ParameterName from magma.enodebd.exceptions import ConfigurationError -from magma.enodebd.data_models.data_model import DataModel +from magma.enodebd.logger import EnodebdLogger as logger class EnodebConfiguration(): diff --git a/lte/gateway/python/magma/enodebd/devices/baicells.py b/lte/gateway/python/magma/enodebd/devices/baicells.py index ff6b995e279c..1e83ffde0ce7 100644 --- a/lte/gateway/python/magma/enodebd/devices/baicells.py +++ b/lte/gateway/python/magma/enodebd/devices/baicells.py @@ -16,8 +16,8 @@ from magma.common.service import MagmaService from magma.enodebd.data_models import transform_for_enb, transform_for_magma from magma.enodebd.data_models.data_model import DataModel, TrParam -from magma.enodebd.data_models.data_model_parameters import ParameterName, \ - TrParameterType +from magma.enodebd.data_models.data_model_parameters import (ParameterName, + TrParameterType) from magma.enodebd.device_config.enodeb_config_postprocessor import \ EnodebConfigurationPostProcessor from magma.enodebd.device_config.enodeb_configuration import \ @@ -25,15 +25,26 @@ from magma.enodebd.devices.device_utils import EnodebDeviceName from magma.enodebd.state_machines.enb_acs_impl import \ BasicEnodebAcsStateMachine -from magma.enodebd.state_machines.enb_acs_states import AddObjectsState, \ - BaicellsRemWaitState, BaicellsSendRebootState, CheckOptionalParamsState, \ - DeleteObjectsState, EndSessionState, EnodebAcsState, ErrorState, \ - GetObjectParametersState, GetParametersState, \ - SendGetTransientParametersState, SetParameterValuesState, \ - WaitEmptyMessageState, WaitGetObjectParametersState, \ - WaitGetParametersState, WaitGetTransientParametersState, \ - WaitInformMRebootState, WaitInformState, WaitRebootResponseState, \ - WaitSetParameterValuesState +from magma.enodebd.state_machines.enb_acs_states import (AddObjectsState, + BaicellsRemWaitState, + BaicellsSendRebootState, + CheckOptionalParamsState, + DeleteObjectsState, + EndSessionState, + EnodebAcsState, + ErrorState, + GetObjectParametersState, + GetParametersState, + SendGetTransientParametersState, + SetParameterValuesState, + WaitEmptyMessageState, + WaitGetObjectParametersState, + WaitGetParametersState, + WaitGetTransientParametersState, + WaitInformMRebootState, + WaitInformState, + WaitRebootResponseState, + WaitSetParameterValuesState) class BaicellsHandler(BasicEnodebAcsStateMachine): diff --git a/lte/gateway/python/magma/enodebd/devices/baicells_old.py b/lte/gateway/python/magma/enodebd/devices/baicells_old.py index a0bf7e6a1ca3..60fd85f37c80 100644 --- a/lte/gateway/python/magma/enodebd/devices/baicells_old.py +++ b/lte/gateway/python/magma/enodebd/devices/baicells_old.py @@ -16,8 +16,8 @@ from magma.common.service import MagmaService from magma.enodebd.data_models import transform_for_enb, transform_for_magma from magma.enodebd.data_models.data_model import DataModel, TrParam -from magma.enodebd.data_models.data_model_parameters import ParameterName, \ - TrParameterType +from magma.enodebd.data_models.data_model_parameters import (ParameterName, + TrParameterType) from magma.enodebd.device_config.enodeb_config_postprocessor import \ EnodebConfigurationPostProcessor from magma.enodebd.device_config.enodeb_configuration import \ @@ -25,15 +25,26 @@ from magma.enodebd.devices.device_utils import EnodebDeviceName from magma.enodebd.state_machines.enb_acs_impl import \ BasicEnodebAcsStateMachine -from magma.enodebd.state_machines.enb_acs_states import AddObjectsState, \ - BaicellsRemWaitState, BaicellsSendRebootState, CheckOptionalParamsState, \ - DeleteObjectsState, EndSessionState, EnodebAcsState, ErrorState, \ - GetObjectParametersState, GetParametersState, \ - SendGetTransientParametersState, SetParameterValuesState, \ - WaitEmptyMessageState, WaitGetObjectParametersState, \ - WaitGetParametersState, WaitGetTransientParametersState, \ - WaitInformMRebootState, WaitInformState, WaitRebootResponseState, \ - WaitSetParameterValuesState +from magma.enodebd.state_machines.enb_acs_states import (AddObjectsState, + BaicellsRemWaitState, + BaicellsSendRebootState, + CheckOptionalParamsState, + DeleteObjectsState, + EndSessionState, + EnodebAcsState, + ErrorState, + GetObjectParametersState, + GetParametersState, + SendGetTransientParametersState, + SetParameterValuesState, + WaitEmptyMessageState, + WaitGetObjectParametersState, + WaitGetParametersState, + WaitGetTransientParametersState, + WaitInformMRebootState, + WaitInformState, + WaitRebootResponseState, + WaitSetParameterValuesState) class BaicellsOldHandler(BasicEnodebAcsStateMachine): diff --git a/lte/gateway/python/magma/enodebd/devices/baicells_qafa.py b/lte/gateway/python/magma/enodebd/devices/baicells_qafa.py index 5baacb0a425a..f8ced87b0600 100644 --- a/lte/gateway/python/magma/enodebd/devices/baicells_qafa.py +++ b/lte/gateway/python/magma/enodebd/devices/baicells_qafa.py @@ -16,24 +16,33 @@ from magma.common.service import MagmaService from magma.enodebd.data_models import transform_for_enb, transform_for_magma from magma.enodebd.data_models.data_model import DataModel, TrParam -from magma.enodebd.data_models.data_model_parameters import ParameterName, \ - TrParameterType +from magma.enodebd.data_models.data_model_parameters import (ParameterName, + TrParameterType) from magma.enodebd.device_config.enodeb_config_postprocessor import \ EnodebConfigurationPostProcessor from magma.enodebd.device_config.enodeb_configuration import \ EnodebConfiguration -from magma.enodebd.devices.baicells_qafb import \ - BaicellsQafbGetObjectParametersState, \ - BaicellsQafbWaitGetTransientParametersState +from magma.enodebd.devices.baicells_qafb import (BaicellsQafbGetObjectParametersState, + BaicellsQafbWaitGetTransientParametersState) from magma.enodebd.devices.device_utils import EnodebDeviceName from magma.enodebd.state_machines.enb_acs_impl import \ BasicEnodebAcsStateMachine -from magma.enodebd.state_machines.enb_acs_states import AddObjectsState, \ - BaicellsSendRebootState, DeleteObjectsState, EndSessionState, \ - EnodebAcsState, ErrorState, GetParametersState, GetRPCMethodsState, \ - SendGetTransientParametersState, SetParameterValuesState, \ - WaitEmptyMessageState, WaitGetParametersState, WaitInformMRebootState, \ - WaitInformState, WaitRebootResponseState, WaitSetParameterValuesState +from magma.enodebd.state_machines.enb_acs_states import (AddObjectsState, + BaicellsSendRebootState, + DeleteObjectsState, + EndSessionState, + EnodebAcsState, + ErrorState, + GetParametersState, + GetRPCMethodsState, + SendGetTransientParametersState, + SetParameterValuesState, + WaitEmptyMessageState, + WaitGetParametersState, + WaitInformMRebootState, + WaitInformState, + WaitRebootResponseState, + WaitSetParameterValuesState) class BaicellsQAFAHandler(BasicEnodebAcsStateMachine): diff --git a/lte/gateway/python/magma/enodebd/devices/baicells_qafb.py b/lte/gateway/python/magma/enodebd/devices/baicells_qafb.py index 01e0e3141055..a1c967c9c322 100644 --- a/lte/gateway/python/magma/enodebd/devices/baicells_qafb.py +++ b/lte/gateway/python/magma/enodebd/devices/baicells_qafb.py @@ -16,8 +16,8 @@ from magma.common.service import MagmaService from magma.enodebd.data_models import transform_for_enb, transform_for_magma from magma.enodebd.data_models.data_model import DataModel, TrParam -from magma.enodebd.data_models.data_model_parameters import ParameterName, \ - TrParameterType +from magma.enodebd.data_models.data_model_parameters import (ParameterName, + TrParameterType) from magma.enodebd.device_config.configuration_init import build_desired_config from magma.enodebd.device_config.enodeb_config_postprocessor import \ EnodebConfigurationPostProcessor @@ -25,20 +25,32 @@ EnodebConfiguration from magma.enodebd.devices.device_utils import EnodebDeviceName from magma.enodebd.logger import EnodebdLogger as logger -from magma.enodebd.state_machines.acs_state_utils import \ - get_all_objects_to_add, get_all_objects_to_delete, \ - get_all_param_values_to_set, get_params_to_get, \ - parse_get_parameter_values_response +from magma.enodebd.state_machines.acs_state_utils import (get_all_objects_to_add, + get_all_objects_to_delete, + get_all_param_values_to_set, + get_params_to_get, + parse_get_parameter_values_response) from magma.enodebd.state_machines.enb_acs import EnodebAcsStateMachine from magma.enodebd.state_machines.enb_acs_impl import \ BasicEnodebAcsStateMachine -from magma.enodebd.state_machines.enb_acs_states import AcsMsgAndTransition, \ - AcsReadMsgResult, AddObjectsState, BaicellsSendRebootState, \ - DeleteObjectsState, EndSessionState, EnodebAcsState, ErrorState, \ - GetParametersState, GetRPCMethodsState, SendGetTransientParametersState, \ - SetParameterValuesState, WaitEmptyMessageState, WaitGetParametersState, \ - WaitInformMRebootState, WaitInformState, WaitRebootResponseState, \ - WaitSetParameterValuesState +from magma.enodebd.state_machines.enb_acs_states import (AcsMsgAndTransition, + AcsReadMsgResult, + AddObjectsState, + BaicellsSendRebootState, + DeleteObjectsState, + EndSessionState, + EnodebAcsState, + ErrorState, + GetParametersState, + GetRPCMethodsState, + SendGetTransientParametersState, + SetParameterValuesState, + WaitEmptyMessageState, + WaitGetParametersState, + WaitInformMRebootState, + WaitInformState, + WaitRebootResponseState, + WaitSetParameterValuesState) from magma.enodebd.tr069 import models diff --git a/lte/gateway/python/magma/enodebd/devices/baicells_rts.py b/lte/gateway/python/magma/enodebd/devices/baicells_rts.py index 561a7b3d2c2c..bfa0d83e2b9c 100644 --- a/lte/gateway/python/magma/enodebd/devices/baicells_rts.py +++ b/lte/gateway/python/magma/enodebd/devices/baicells_rts.py @@ -11,12 +11,13 @@ limitations under the License. """ -from typing import Optional, Callable, Any, Dict, List, Type +from typing import Any, Callable, Dict, List, Optional, Type + from magma.common.service import MagmaService -from magma.enodebd.data_models.data_model import TrParam, DataModel -from magma.enodebd.data_models.data_model_parameters import ParameterName, \ - TrParameterType -from magma.enodebd.data_models import transform_for_magma, transform_for_enb +from magma.enodebd.data_models import transform_for_enb, transform_for_magma +from magma.enodebd.data_models.data_model import DataModel, TrParam +from magma.enodebd.data_models.data_model_parameters import (ParameterName, + TrParameterType) from magma.enodebd.device_config.enodeb_config_postprocessor import \ EnodebConfigurationPostProcessor from magma.enodebd.device_config.enodeb_configuration import \ @@ -24,15 +25,25 @@ from magma.enodebd.devices.device_utils import EnodebDeviceName from magma.enodebd.state_machines.enb_acs_impl import \ BasicEnodebAcsStateMachine -from magma.enodebd.state_machines.enb_acs_states import EnodebAcsState, \ - WaitInformState, SendGetTransientParametersState, \ - WaitGetTransientParametersState, GetParametersState, \ - WaitGetParametersState, GetObjectParametersState, \ - WaitGetObjectParametersState, DeleteObjectsState, AddObjectsState, \ - SetParameterValuesState, WaitSetParameterValuesState, \ - BaicellsSendRebootState, WaitRebootResponseState, WaitInformMRebootState, \ - CheckOptionalParamsState, WaitEmptyMessageState, ErrorState, \ - EndSessionState +from magma.enodebd.state_machines.enb_acs_states import (AddObjectsState, + BaicellsSendRebootState, + CheckOptionalParamsState, + DeleteObjectsState, + EndSessionState, + EnodebAcsState, + ErrorState, + GetObjectParametersState, + GetParametersState, + SendGetTransientParametersState, + SetParameterValuesState, + WaitEmptyMessageState, + WaitGetObjectParametersState, + WaitGetParametersState, + WaitGetTransientParametersState, + WaitInformMRebootState, + WaitInformState, + WaitRebootResponseState, + WaitSetParameterValuesState) class BaicellsRTSHandler(BasicEnodebAcsStateMachine): diff --git a/lte/gateway/python/magma/enodebd/devices/device_map.py b/lte/gateway/python/magma/enodebd/devices/device_map.py index 0f8d4071c0d8..2167a74d9099 100644 --- a/lte/gateway/python/magma/enodebd/devices/device_map.py +++ b/lte/gateway/python/magma/enodebd/devices/device_map.py @@ -12,13 +12,14 @@ """ from typing import Type + from magma.enodebd.devices.baicells import BaicellsHandler from magma.enodebd.devices.baicells_old import BaicellsOldHandler from magma.enodebd.devices.baicells_qafa import BaicellsQAFAHandler from magma.enodebd.devices.baicells_qafb import BaicellsQAFBHandler from magma.enodebd.devices.baicells_rts import BaicellsRTSHandler -from magma.enodebd.devices.experimental.cavium import CaviumHandler from magma.enodebd.devices.device_utils import EnodebDeviceName +from magma.enodebd.devices.experimental.cavium import CaviumHandler from magma.enodebd.state_machines.enb_acs import EnodebAcsStateMachine # This exists only to break a circular dependency. Otherwise there's no diff --git a/lte/gateway/python/magma/enodebd/devices/device_utils.py b/lte/gateway/python/magma/enodebd/devices/device_utils.py index cdeef7065d48..8bf50111a713 100644 --- a/lte/gateway/python/magma/enodebd/devices/device_utils.py +++ b/lte/gateway/python/magma/enodebd/devices/device_utils.py @@ -11,9 +11,10 @@ limitations under the License. """ -from magma.enodebd.logger import EnodebdLogger as logger import re + from magma.enodebd.exceptions import UnrecognizedEnodebError +from magma.enodebd.logger import EnodebdLogger as logger class EnodebDeviceName(): diff --git a/lte/gateway/python/magma/enodebd/devices/experimental/cavium.py b/lte/gateway/python/magma/enodebd/devices/experimental/cavium.py index 48b835d731de..b1b05c0f8755 100644 --- a/lte/gateway/python/magma/enodebd/devices/experimental/cavium.py +++ b/lte/gateway/python/magma/enodebd/devices/experimental/cavium.py @@ -16,8 +16,8 @@ from magma.common.service import MagmaService from magma.enodebd.data_models import transform_for_enb, transform_for_magma from magma.enodebd.data_models.data_model import DataModel, TrParam -from magma.enodebd.data_models.data_model_parameters import ParameterName, \ - TrParameterType +from magma.enodebd.data_models.data_model_parameters import (ParameterName, + TrParameterType) from magma.enodebd.device_config.enodeb_config_postprocessor import \ EnodebConfigurationPostProcessor from magma.enodebd.device_config.enodeb_configuration import \ @@ -25,19 +25,31 @@ from magma.enodebd.devices.device_utils import EnodebDeviceName from magma.enodebd.exceptions import Tr069Error from magma.enodebd.logger import EnodebdLogger as logger -from magma.enodebd.state_machines.acs_state_utils import \ - get_all_objects_to_add, get_all_objects_to_delete +from magma.enodebd.state_machines.acs_state_utils import (get_all_objects_to_add, + get_all_objects_to_delete) from magma.enodebd.state_machines.enb_acs import EnodebAcsStateMachine from magma.enodebd.state_machines.enb_acs_impl import \ BasicEnodebAcsStateMachine -from magma.enodebd.state_machines.enb_acs_states import AcsMsgAndTransition, \ - AcsReadMsgResult, AddObjectsState, DeleteObjectsState, EndSessionState, \ - EnodebAcsState, ErrorState, GetParametersState, GetRPCMethodsState, \ - SendGetTransientParametersState, SendRebootState, \ - SetParameterValuesNotAdminState, WaitEmptyMessageState, \ - WaitGetObjectParametersState, WaitGetParametersState, \ - WaitGetTransientParametersState, WaitInformMRebootState, WaitInformState, \ - WaitRebootResponseState, WaitSetParameterValuesState +from magma.enodebd.state_machines.enb_acs_states import (AcsMsgAndTransition, + AcsReadMsgResult, + AddObjectsState, + DeleteObjectsState, + EndSessionState, + EnodebAcsState, + ErrorState, + GetParametersState, + GetRPCMethodsState, + SendGetTransientParametersState, + SendRebootState, + SetParameterValuesNotAdminState, + WaitEmptyMessageState, + WaitGetObjectParametersState, + WaitGetParametersState, + WaitGetTransientParametersState, + WaitInformMRebootState, + WaitInformState, + WaitRebootResponseState, + WaitSetParameterValuesState) from magma.enodebd.tr069 import models diff --git a/lte/gateway/python/magma/enodebd/enodeb_status.py b/lte/gateway/python/magma/enodebd/enodeb_status.py index e17a6f9e22d2..f9757f74b897 100644 --- a/lte/gateway/python/magma/enodebd/enodeb_status.py +++ b/lte/gateway/python/magma/enodebd/enodeb_status.py @@ -12,24 +12,22 @@ """ import json +import os from collections import namedtuple from typing import Any, Dict, List, NamedTuple, Optional, Tuple, Union -import os from lte.protos.enodebd_pb2 import SingleEnodebStatus from lte.protos.mconfig import mconfigs_pb2 from magma.common import serialization_utils from magma.enodebd import metrics from magma.enodebd.data_models.data_model_parameters import ParameterName -from magma.enodebd.device_config.configuration_util import \ - get_enb_rf_tx_desired +from magma.enodebd.device_config.configuration_util import (find_enb_by_cell_id, + get_enb_rf_tx_desired) from magma.enodebd.exceptions import ConfigurationError from magma.enodebd.logger import EnodebdLogger as logger -from magma.enodebd.state_machines.enb_acs import EnodebAcsStateMachine -from magma.enodebd.state_machines.enb_acs_manager import \ - StateMachineManager from magma.enodebd.s1ap_client import get_all_enb_state -from magma.enodebd.device_config.configuration_util import find_enb_by_cell_id +from magma.enodebd.state_machines.enb_acs import EnodebAcsStateMachine +from magma.enodebd.state_machines.enb_acs_manager import StateMachineManager from orc8r.protos.service303_pb2 import State # There are 2 levels of caching for GPS coordinates from the enodeB: module diff --git a/lte/gateway/python/magma/enodebd/enodebd_iptables_rules.py b/lte/gateway/python/magma/enodebd/enodebd_iptables_rules.py index 33c71a843cf9..9edd72833b9f 100644 --- a/lte/gateway/python/magma/enodebd/enodebd_iptables_rules.py +++ b/lte/gateway/python/magma/enodebd/enodebd_iptables_rules.py @@ -15,17 +15,15 @@ import asyncio -from magma.enodebd.logger import EnodebdLogger as logger +import re import shlex import subprocess -import re from typing import List -from magma.common.misc_utils import ( - IpPreference, - get_ip_from_if, - get_if_ip_with_netmask -) + +from magma.common.misc_utils import (IpPreference, get_if_ip_with_netmask, + get_ip_from_if) from magma.configuration.service_configs import load_service_config +from magma.enodebd.logger import EnodebdLogger as logger IPTABLES_RULE_FMT = """sudo iptables -t nat -{add} PREROUTING diff --git a/lte/gateway/python/magma/enodebd/logger.py b/lte/gateway/python/magma/enodebd/logger.py index 04e2512827ce..f45519f6e30b 100644 --- a/lte/gateway/python/magma/enodebd/logger.py +++ b/lte/gateway/python/magma/enodebd/logger.py @@ -14,7 +14,6 @@ import logging from logging.handlers import RotatingFileHandler - LOG_FILE = 'var/log/enodebd.log' MAX_BYTES = 1024 * 1024 * 10 # 10MB BACKUP_COUNT = 5 # 10MB, 5 files, 50MB total diff --git a/lte/gateway/python/magma/enodebd/main.py b/lte/gateway/python/magma/enodebd/main.py index a662d0eb73ff..abe43bbe052d 100644 --- a/lte/gateway/python/magma/enodebd/main.py +++ b/lte/gateway/python/magma/enodebd/main.py @@ -15,19 +15,19 @@ from typing import List from unittest import mock -from magma.enodebd.enodeb_status import get_service_status_old, \ - get_operational_states +from lte.protos.mconfig import mconfigs_pb2 from magma.common.sentry import sentry_init -from magma.enodebd.state_machines.enb_acs_manager import StateMachineManager +from magma.common.service import MagmaService +from magma.enodebd.enodeb_status import (get_operational_states, + get_service_status_old) from magma.enodebd.logger import EnodebdLogger as logger +from magma.enodebd.state_machines.enb_acs_manager import StateMachineManager +from orc8r.protos.service303_pb2 import State + +from .enodebd_iptables_rules import set_enodebd_iptables_rule from .rpc_servicer import EnodebdRpcServicer from .stats_manager import StatsManager from .tr069.server import tr069_server -from .enodebd_iptables_rules import set_enodebd_iptables_rule -from magma.common.service import MagmaService -from orc8r.protos.service303_pb2 import State -from lte.protos.mconfig import mconfigs_pb2 - def get_context(ip: str): diff --git a/lte/gateway/python/magma/enodebd/metrics.py b/lte/gateway/python/magma/enodebd/metrics.py index ba85a568064c..86273ec536dc 100644 --- a/lte/gateway/python/magma/enodebd/metrics.py +++ b/lte/gateway/python/magma/enodebd/metrics.py @@ -11,7 +11,7 @@ limitations under the License. """ -from prometheus_client import Gauge, Counter +from prometheus_client import Counter, Gauge # Gauges for current eNodeB status STAT_ENODEB_CONNECTED = Gauge('enodeb_mgmt_connected', diff --git a/lte/gateway/python/magma/enodebd/rpc_servicer.py b/lte/gateway/python/magma/enodebd/rpc_servicer.py index 0f1d1e5bb5c8..11cd99d273ba 100644 --- a/lte/gateway/python/magma/enodebd/rpc_servicer.py +++ b/lte/gateway/python/magma/enodebd/rpc_servicer.py @@ -11,19 +11,20 @@ limitations under the License. """ -import grpc from typing import Any -from magma.enodebd.enodeb_status import get_service_status, get_single_enb_status -from lte.protos.enodebd_pb2 import GetParameterResponse -from lte.protos.enodebd_pb2_grpc import EnodebdServicer, \ - add_EnodebdServicer_to_server + +import grpc +from lte.protos.enodebd_pb2 import (AllEnodebStatus, EnodebIdentity, + GetParameterRequest, GetParameterResponse, + SetParameterRequest, SingleEnodebStatus) +from lte.protos.enodebd_pb2_grpc import (EnodebdServicer, + add_EnodebdServicer_to_server) +from magma.common.rpc_utils import return_void +from magma.enodebd.enodeb_status import (get_service_status, + get_single_enb_status) from magma.enodebd.state_machines.enb_acs import EnodebAcsStateMachine +from magma.enodebd.state_machines.enb_acs_manager import StateMachineManager from orc8r.protos.service303_pb2 import ServiceStatus -from magma.common.rpc_utils import return_void -from magma.enodebd.state_machines.enb_acs_manager import \ - StateMachineManager -from lte.protos.enodebd_pb2 import GetParameterRequest, SetParameterRequest, \ - EnodebIdentity, AllEnodebStatus, SingleEnodebStatus class EnodebdRpcServicer(EnodebdServicer): diff --git a/lte/gateway/python/magma/enodebd/s1ap_client.py b/lte/gateway/python/magma/enodebd/s1ap_client.py index 3dd375fb746a..80f678f8953b 100644 --- a/lte/gateway/python/magma/enodebd/s1ap_client.py +++ b/lte/gateway/python/magma/enodebd/s1ap_client.py @@ -13,11 +13,10 @@ from typing import Dict, Optional import grpc - -from magma.common.service_registry import ServiceRegistry -from orc8r.protos.common_pb2 import Void from lte.protos.s1ap_service_pb2_grpc import S1apServiceStub +from magma.common.service_registry import ServiceRegistry from magma.enodebd.logger import EnodebdLogger as logger +from orc8r.protos.common_pb2 import Void S1AP_SERVICE_NAME = "s1ap_service" DEFAULT_GRPC_TIMEOUT = 20 @@ -43,4 +42,3 @@ def get_all_enb_state() -> Optional[Dict[int, int]]: err.code(), err.details()) return {} - diff --git a/lte/gateway/python/magma/enodebd/state_machines/acs_state_utils.py b/lte/gateway/python/magma/enodebd/state_machines/acs_state_utils.py index 28a03971ada1..d4f85033f19c 100644 --- a/lte/gateway/python/magma/enodebd/state_machines/acs_state_utils.py +++ b/lte/gateway/python/magma/enodebd/state_machines/acs_state_utils.py @@ -11,15 +11,16 @@ limitations under the License. """ -from magma.enodebd.logger import EnodebdLogger as logger -from typing import Any, Optional, Dict, List +from typing import Any, Dict, List, Optional + from magma.enodebd.data_models.data_model import DataModel from magma.enodebd.data_models.data_model_parameters import ParameterName from magma.enodebd.device_config.enodeb_configuration import \ EnodebConfiguration -from magma.enodebd.devices.device_utils import get_device_name, \ - EnodebDeviceName +from magma.enodebd.devices.device_utils import (EnodebDeviceName, + get_device_name) from magma.enodebd.exceptions import ConfigurationError +from magma.enodebd.logger import EnodebdLogger as logger from magma.enodebd.tr069 import models diff --git a/lte/gateway/python/magma/enodebd/state_machines/enb_acs.py b/lte/gateway/python/magma/enodebd/state_machines/enb_acs.py index 10778fc7bd9d..c790158dfd23 100644 --- a/lte/gateway/python/magma/enodebd/state_machines/enb_acs.py +++ b/lte/gateway/python/magma/enodebd/state_machines/enb_acs.py @@ -10,9 +10,10 @@ See the License for the specific language governing permissions and limitations under the License. """ -from asyncio import BaseEventLoop -from typing import Type, Any from abc import ABC, abstractmethod +from asyncio import BaseEventLoop +from typing import Any, Type + from magma.common.service import MagmaService from magma.enodebd.data_models.data_model import DataModel from magma.enodebd.data_models.data_model_parameters import ParameterName @@ -195,4 +196,3 @@ def is_enodeb_connected(self) -> bool: @abstractmethod def stop_state_machine(self) -> None: pass - diff --git a/lte/gateway/python/magma/enodebd/state_machines/enb_acs_impl.py b/lte/gateway/python/magma/enodebd/state_machines/enb_acs_impl.py index 46b874b8fbeb..122cc302bf9f 100644 --- a/lte/gateway/python/magma/enodebd/state_machines/enb_acs_impl.py +++ b/lte/gateway/python/magma/enodebd/state_machines/enb_acs_impl.py @@ -12,9 +12,9 @@ """ import traceback +from abc import abstractmethod from typing import Any, Dict -from abc import abstractmethod from magma.common.service import MagmaService from magma.enodebd import metrics from magma.enodebd.data_models.data_model_parameters import ParameterName diff --git a/lte/gateway/python/magma/enodebd/state_machines/enb_acs_states.py b/lte/gateway/python/magma/enodebd/state_machines/enb_acs_states.py index 24e311e56d55..09ff6c0d9e24 100644 --- a/lte/gateway/python/magma/enodebd/state_machines/enb_acs_states.py +++ b/lte/gateway/python/magma/enodebd/state_machines/enb_acs_states.py @@ -11,20 +11,25 @@ limitations under the License. """ +from abc import ABC, abstractmethod from collections import namedtuple from typing import Any, Optional -from abc import ABC, abstractmethod from magma.enodebd.data_models.data_model_parameters import ParameterName from magma.enodebd.device_config.configuration_init import build_desired_config from magma.enodebd.exceptions import ConfigurationError, Tr069Error from magma.enodebd.logger import EnodebdLogger as logger -from magma.enodebd.state_machines.acs_state_utils import \ - does_inform_have_event, get_all_objects_to_add, get_all_objects_to_delete, \ - get_all_param_values_to_set, get_obj_param_values_to_set, \ - get_object_params_to_get, get_optional_param_to_check, \ - get_param_values_to_set, get_params_to_get, \ - parse_get_parameter_values_response, process_inform_message +from magma.enodebd.state_machines.acs_state_utils import (does_inform_have_event, + get_all_objects_to_add, + get_all_objects_to_delete, + get_all_param_values_to_set, + get_obj_param_values_to_set, + get_object_params_to_get, + get_optional_param_to_check, + get_param_values_to_set, + get_params_to_get, + parse_get_parameter_values_response, + process_inform_message) from magma.enodebd.state_machines.enb_acs import EnodebAcsStateMachine from magma.enodebd.state_machines.timer import StateMachineTimer from magma.enodebd.tr069 import models diff --git a/lte/gateway/python/magma/enodebd/stats_manager.py b/lte/gateway/python/magma/enodebd/stats_manager.py index 337b17d871dd..c2b0129be416 100644 --- a/lte/gateway/python/magma/enodebd/stats_manager.py +++ b/lte/gateway/python/magma/enodebd/stats_manager.py @@ -12,15 +12,16 @@ """ import asyncio -from magma.enodebd.logger import EnodebdLogger as logger from xml.etree import ElementTree + from aiohttp import web from magma.common.misc_utils import get_ip_from_if from magma.configuration.service_configs import load_service_config -from magma.enodebd.enodeb_status import get_enb_status, \ - update_status_metrics +from magma.enodebd.enodeb_status import get_enb_status, update_status_metrics +from magma.enodebd.logger import EnodebdLogger as logger from magma.enodebd.state_machines.enb_acs import EnodebAcsStateMachine from magma.enodebd.state_machines.enb_acs_manager import StateMachineManager + from . import metrics diff --git a/lte/gateway/python/magma/enodebd/tests/baicells_old_tests.py b/lte/gateway/python/magma/enodebd/tests/baicells_old_tests.py index 82fe1bf235c2..36cbabd5e87d 100644 --- a/lte/gateway/python/magma/enodebd/tests/baicells_old_tests.py +++ b/lte/gateway/python/magma/enodebd/tests/baicells_old_tests.py @@ -13,12 +13,12 @@ # pylint: disable=protected-access from magma.enodebd.devices.device_utils import EnodebDeviceName -from magma.enodebd.tr069 import models -from magma.enodebd.tests.test_utils.tr069_msg_builder import \ - Tr069MessageBuilder from magma.enodebd.tests.test_utils.enb_acs_builder import \ EnodebAcsStateMachineBuilder from magma.enodebd.tests.test_utils.enodeb_handler import EnodebHandlerTestCase +from magma.enodebd.tests.test_utils.tr069_msg_builder import \ + Tr069MessageBuilder +from magma.enodebd.tr069 import models class BaicellsOldHandlerTests(EnodebHandlerTestCase): diff --git a/lte/gateway/python/magma/enodebd/tests/baicells_qafb_tests.py b/lte/gateway/python/magma/enodebd/tests/baicells_qafb_tests.py index 32a5de818e2e..9f23a4488ad5 100644 --- a/lte/gateway/python/magma/enodebd/tests/baicells_qafb_tests.py +++ b/lte/gateway/python/magma/enodebd/tests/baicells_qafb_tests.py @@ -13,12 +13,12 @@ # pylint: disable=protected-access from magma.enodebd.devices.device_utils import EnodebDeviceName -from magma.enodebd.tr069 import models -from magma.enodebd.tests.test_utils.tr069_msg_builder import \ - Tr069MessageBuilder from magma.enodebd.tests.test_utils.enb_acs_builder import \ EnodebAcsStateMachineBuilder from magma.enodebd.tests.test_utils.enodeb_handler import EnodebHandlerTestCase +from magma.enodebd.tests.test_utils.tr069_msg_builder import \ + Tr069MessageBuilder +from magma.enodebd.tr069 import models class BaicellsQAFBHandlerTests(EnodebHandlerTestCase): diff --git a/lte/gateway/python/magma/enodebd/tests/cavium_tests.py b/lte/gateway/python/magma/enodebd/tests/cavium_tests.py index 643dee161337..22d808879675 100644 --- a/lte/gateway/python/magma/enodebd/tests/cavium_tests.py +++ b/lte/gateway/python/magma/enodebd/tests/cavium_tests.py @@ -11,15 +11,15 @@ limitations under the License. """ +from magma.enodebd.data_models.data_model_parameters import ParameterName # pylint: disable=protected-access from magma.enodebd.devices.device_utils import EnodebDeviceName -from magma.enodebd.data_models.data_model_parameters import ParameterName -from magma.enodebd.tr069 import models -from magma.enodebd.tests.test_utils.tr069_msg_builder import \ - Tr069MessageBuilder from magma.enodebd.tests.test_utils.enb_acs_builder import \ EnodebAcsStateMachineBuilder from magma.enodebd.tests.test_utils.enodeb_handler import EnodebHandlerTestCase +from magma.enodebd.tests.test_utils.tr069_msg_builder import \ + Tr069MessageBuilder +from magma.enodebd.tr069 import models class CaviumHandlerTests(EnodebHandlerTestCase): diff --git a/lte/gateway/python/magma/enodebd/tests/configuration_init_tests.py b/lte/gateway/python/magma/enodebd/tests/configuration_init_tests.py index c07a70214e29..52513a6e4aa1 100644 --- a/lte/gateway/python/magma/enodebd/tests/configuration_init_tests.py +++ b/lte/gateway/python/magma/enodebd/tests/configuration_init_tests.py @@ -13,18 +13,23 @@ # pylint: disable=protected-access from unittest import TestCase -from magma.enodebd.devices.baicells import BaicellsTrDataModel + from magma.enodebd.data_models.data_model_parameters import ParameterName -from magma.enodebd.device_config.configuration_init import \ - _set_pci, _set_bandwidth, _set_tdd_subframe_config, \ - _set_management_server, _set_s1_connection, _set_perf_mgmt, \ - _set_misc_static_params, _set_plmnids_tac, \ - _set_earfcn_freq_band_mode, _get_enb_config +from magma.enodebd.device_config.configuration_init import (_get_enb_config, + _set_bandwidth, + _set_earfcn_freq_band_mode, + _set_management_server, + _set_misc_static_params, + _set_pci, + _set_perf_mgmt, + _set_plmnids_tac, + _set_s1_connection, + _set_tdd_subframe_config) from magma.enodebd.device_config.enodeb_configuration import \ EnodebConfiguration +from magma.enodebd.devices.baicells import BaicellsTrDataModel from magma.enodebd.exceptions import ConfigurationError -from magma.enodebd.tests.test_utils.config_builder import \ - EnodebConfigBuilder +from magma.enodebd.tests.test_utils.config_builder import EnodebConfigBuilder class EnodebConfigurationFactoryTest(TestCase): diff --git a/lte/gateway/python/magma/enodebd/tests/data_model_tests.py b/lte/gateway/python/magma/enodebd/tests/data_model_tests.py index 5e2ffb10dd46..1683725b8231 100644 --- a/lte/gateway/python/magma/enodebd/tests/data_model_tests.py +++ b/lte/gateway/python/magma/enodebd/tests/data_model_tests.py @@ -14,8 +14,9 @@ # pylint: disable=protected-access from unittest import TestCase -from magma.enodebd.devices.baicells import BaicellsTrDataModel + from magma.enodebd.data_models.data_model_parameters import ParameterName +from magma.enodebd.devices.baicells import BaicellsTrDataModel class BaicellsTrDataModelTest(TestCase): diff --git a/lte/gateway/python/magma/enodebd/tests/device_utils_tests.py b/lte/gateway/python/magma/enodebd/tests/device_utils_tests.py index d56af9a1c4c8..78ebfb9f5896 100644 --- a/lte/gateway/python/magma/enodebd/tests/device_utils_tests.py +++ b/lte/gateway/python/magma/enodebd/tests/device_utils_tests.py @@ -14,8 +14,10 @@ # pylint: disable=protected-access from unittest import TestCase -from magma.enodebd.devices.device_utils import get_device_name, \ - _parse_sw_version, EnodebDeviceName + +from magma.enodebd.devices.device_utils import (EnodebDeviceName, + _parse_sw_version, + get_device_name) from magma.enodebd.exceptions import UnrecognizedEnodebError diff --git a/lte/gateway/python/magma/enodebd/tests/enodeb_configuration_tests.py b/lte/gateway/python/magma/enodebd/tests/enodeb_configuration_tests.py index 597fb945e342..ea77c17485c3 100644 --- a/lte/gateway/python/magma/enodebd/tests/enodeb_configuration_tests.py +++ b/lte/gateway/python/magma/enodebd/tests/enodeb_configuration_tests.py @@ -14,10 +14,11 @@ # pylint: disable=protected-access from unittest import TestCase -from magma.enodebd.devices.experimental.cavium import CaviumTrDataModel + from magma.enodebd.data_models.data_model_parameters import ParameterName from magma.enodebd.device_config.enodeb_configuration import \ EnodebConfiguration +from magma.enodebd.devices.experimental.cavium import CaviumTrDataModel class EnodebConfigurationTest(TestCase): diff --git a/lte/gateway/python/magma/enodebd/tests/enodeb_status_tests.py b/lte/gateway/python/magma/enodebd/tests/enodeb_status_tests.py index 67949c0bb9f2..f2a91905174a 100644 --- a/lte/gateway/python/magma/enodebd/tests/enodeb_status_tests.py +++ b/lte/gateway/python/magma/enodebd/tests/enodeb_status_tests.py @@ -13,17 +13,19 @@ # pylint: disable=protected-access from unittest import TestCase + from lte.protos.enodebd_pb2 import SingleEnodebStatus from magma.enodebd.devices.device_utils import EnodebDeviceName -from magma.enodebd.enodeb_status import get_service_status_old, \ - get_all_enb_status, get_enb_status, get_single_enb_status +from magma.enodebd.enodeb_status import (get_all_enb_status, get_enb_status, + get_service_status_old, + get_single_enb_status) from magma.enodebd.state_machines.enb_acs_manager import StateMachineManager -from magma.enodebd.tests.test_utils.tr069_msg_builder import \ - Tr069MessageBuilder from magma.enodebd.tests.test_utils.enb_acs_builder import \ EnodebAcsStateMachineBuilder from magma.enodebd.tests.test_utils.spyne_builder import \ get_spyne_context_with_ip +from magma.enodebd.tests.test_utils.tr069_msg_builder import \ + Tr069MessageBuilder class EnodebStatusTests(TestCase): diff --git a/lte/gateway/python/magma/enodebd/tests/stats_manager_tests.py b/lte/gateway/python/magma/enodebd/tests/stats_manager_tests.py index 176f9ad62fc5..a970c89bd20e 100644 --- a/lte/gateway/python/magma/enodebd/tests/stats_manager_tests.py +++ b/lte/gateway/python/magma/enodebd/tests/stats_manager_tests.py @@ -11,19 +11,19 @@ limitations under the License. """ -import pkg_resources from unittest import TestCase, mock from xml.etree import ElementTree + +import pkg_resources + from magma.enodebd import metrics from magma.enodebd.data_models.data_model_parameters import ParameterName from magma.enodebd.devices.device_utils import EnodebDeviceName +from magma.enodebd.state_machines.enb_acs_manager import StateMachineManager from magma.enodebd.stats_manager import StatsManager +from magma.enodebd.tests.test_utils.config_builder import EnodebConfigBuilder from magma.enodebd.tests.test_utils.enb_acs_builder import \ EnodebAcsStateMachineBuilder -from magma.enodebd.state_machines.enb_acs_manager import \ - StateMachineManager -from magma.enodebd.tests.test_utils.config_builder import \ - EnodebConfigBuilder class StatsManagerTest(TestCase): diff --git a/lte/gateway/python/magma/enodebd/tests/test_utils/enb_acs_builder.py b/lte/gateway/python/magma/enodebd/tests/test_utils/enb_acs_builder.py index 6c75bc2510e2..95044e8a84d2 100644 --- a/lte/gateway/python/magma/enodebd/tests/test_utils/enb_acs_builder.py +++ b/lte/gateway/python/magma/enodebd/tests/test_utils/enb_acs_builder.py @@ -13,12 +13,13 @@ import asyncio from unittest import mock + from magma.common.service import MagmaService from magma.enodebd.devices.device_map import get_device_handler_from_name from magma.enodebd.devices.device_utils import EnodebDeviceName from magma.enodebd.state_machines.enb_acs import EnodebAcsStateMachine -from magma.enodebd.tests.test_utils.config_builder import EnodebConfigBuilder from magma.enodebd.state_machines.enb_acs_manager import StateMachineManager +from magma.enodebd.tests.test_utils.config_builder import EnodebConfigBuilder class EnodebAcsStateMachineBuilder: diff --git a/lte/gateway/python/magma/enodebd/tests/test_utils/enodeb_handler.py b/lte/gateway/python/magma/enodebd/tests/test_utils/enodeb_handler.py index 66c1612d1f9c..a48edaf074f0 100644 --- a/lte/gateway/python/magma/enodebd/tests/test_utils/enodeb_handler.py +++ b/lte/gateway/python/magma/enodebd/tests/test_utils/enodeb_handler.py @@ -12,6 +12,7 @@ """ from unittest import TestCase, mock + import magma.enodebd.tests.test_utils.mock_functions as enb_mock diff --git a/lte/gateway/python/magma/enodebd/tests/test_utils/mock_functions.py b/lte/gateway/python/magma/enodebd/tests/test_utils/mock_functions.py index 5125800e4eea..59fb0c73c21b 100644 --- a/lte/gateway/python/magma/enodebd/tests/test_utils/mock_functions.py +++ b/lte/gateway/python/magma/enodebd/tests/test_utils/mock_functions.py @@ -13,7 +13,6 @@ from typing import Any - GET_IP_FROM_IF_PATH = \ 'magma.enodebd.device_config.configuration_init.get_ip_from_if' diff --git a/lte/gateway/python/magma/enodebd/tests/test_utils/spyne_builder.py b/lte/gateway/python/magma/enodebd/tests/test_utils/spyne_builder.py index e7783555fd0d..0717aad3e71c 100644 --- a/lte/gateway/python/magma/enodebd/tests/test_utils/spyne_builder.py +++ b/lte/gateway/python/magma/enodebd/tests/test_utils/spyne_builder.py @@ -12,6 +12,7 @@ """ from unittest import mock + from spyne.server.wsgi import WsgiMethodContext diff --git a/lte/gateway/python/magma/enodebd/tests/test_utils/tr069_msg_builder.py b/lte/gateway/python/magma/enodebd/tests/test_utils/tr069_msg_builder.py index 0bc8ce39f996..90992b6ebcea 100644 --- a/lte/gateway/python/magma/enodebd/tests/test_utils/tr069_msg_builder.py +++ b/lte/gateway/python/magma/enodebd/tests/test_utils/tr069_msg_builder.py @@ -12,6 +12,7 @@ """ from typing import Any, List, Optional + from magma.enodebd.tr069 import models diff --git a/lte/gateway/python/magma/enodebd/tests/timer_tests.py b/lte/gateway/python/magma/enodebd/tests/timer_tests.py index 4efaa485c9fd..6e87a541ea26 100644 --- a/lte/gateway/python/magma/enodebd/tests/timer_tests.py +++ b/lte/gateway/python/magma/enodebd/tests/timer_tests.py @@ -13,6 +13,7 @@ # pylint: disable=protected-access from unittest import TestCase + from magma.enodebd.state_machines.timer import StateMachineTimer @@ -23,4 +24,3 @@ def test_is_done(self): timer_b = StateMachineTimer(600) self.assertFalse(timer_b.is_done(), 'Timer should not be done') - diff --git a/lte/gateway/python/magma/enodebd/tests/transform_for_enb_tests.py b/lte/gateway/python/magma/enodebd/tests/transform_for_enb_tests.py index 7c401e492c57..cd6e3008bc3c 100644 --- a/lte/gateway/python/magma/enodebd/tests/transform_for_enb_tests.py +++ b/lte/gateway/python/magma/enodebd/tests/transform_for_enb_tests.py @@ -13,6 +13,7 @@ # pylint: disable=protected-access from unittest import TestCase + from magma.enodebd.data_models.transform_for_enb import bandwidth diff --git a/lte/gateway/python/magma/enodebd/tests/transform_for_magma_tests.py b/lte/gateway/python/magma/enodebd/tests/transform_for_magma_tests.py index 0297d4e5e2f9..5be0617f5d76 100644 --- a/lte/gateway/python/magma/enodebd/tests/transform_for_magma_tests.py +++ b/lte/gateway/python/magma/enodebd/tests/transform_for_magma_tests.py @@ -13,7 +13,8 @@ # pylint: disable=protected-access from unittest import TestCase -from magma.enodebd.data_models.transform_for_magma import gps_tr181, bandwidth + +from magma.enodebd.data_models.transform_for_magma import bandwidth, gps_tr181 from magma.enodebd.exceptions import ConfigurationError diff --git a/lte/gateway/python/magma/enodebd/tr069/models.py b/lte/gateway/python/magma/enodebd/tr069/models.py index 8dde13d5b18e..37392212e495 100644 --- a/lte/gateway/python/magma/enodebd/tr069/models.py +++ b/lte/gateway/python/magma/enodebd/tr069/models.py @@ -14,8 +14,8 @@ from spyne.model import ComplexModel from spyne.model.complex import XmlAttribute, XmlData -from spyne.model.primitive import Boolean, DateTime, Integer, String, \ - UnsignedInteger +from spyne.model.primitive import (Boolean, DateTime, Integer, String, + UnsignedInteger) from spyne.util.odict import odict # Namespaces diff --git a/lte/gateway/python/magma/enodebd/tr069/server.py b/lte/gateway/python/magma/enodebd/tr069/server.py index 3fa5d9b18a1e..5ee847c87e8a 100644 --- a/lte/gateway/python/magma/enodebd/tr069/server.py +++ b/lte/gateway/python/magma/enodebd/tr069/server.py @@ -11,15 +11,17 @@ limitations under the License. """ -import _thread -from magma.enodebd.logger import EnodebdLogger as logger import socket -from wsgiref.simple_server import ServerHandler, WSGIRequestHandler, \ - WSGIServer, make_server -from spyne.server.wsgi import WsgiApplication +from wsgiref.simple_server import (ServerHandler, WSGIRequestHandler, + WSGIServer, make_server) + +import _thread from magma.common.misc_utils import get_ip_from_if from magma.configuration.service_configs import load_service_config +from magma.enodebd.logger import EnodebdLogger as logger from magma.enodebd.state_machines.enb_acs_manager import StateMachineManager +from spyne.server.wsgi import WsgiApplication + from .models import CWMP_NS from .rpc_methods import AutoConfigServer from .spyne_mods import Tr069Application, Tr069Soap11 diff --git a/lte/gateway/python/magma/enodebd/tr069/spyne_mods.py b/lte/gateway/python/magma/enodebd/tr069/spyne_mods.py index 4870a4c976f8..a50f1069b0c3 100644 --- a/lte/gateway/python/magma/enodebd/tr069/spyne_mods.py +++ b/lte/gateway/python/magma/enodebd/tr069/spyne_mods.py @@ -20,10 +20,9 @@ 3) Minor enhancements for debug-ability """ -from magma.enodebd.logger import EnodebdLogger as logger - from lxml import etree +from magma.enodebd.logger import EnodebdLogger as logger from spyne.application import Application from spyne.interface._base import Interface from spyne.protocol.soap import Soap11 diff --git a/lte/gateway/python/magma/enodebd/tr069/tests/models_tests.py b/lte/gateway/python/magma/enodebd/tr069/tests/models_tests.py index 972cd2c9d607..26d0c70952b4 100644 --- a/lte/gateway/python/magma/enodebd/tr069/tests/models_tests.py +++ b/lte/gateway/python/magma/enodebd/tr069/tests/models_tests.py @@ -12,9 +12,8 @@ """ import unittest -from spyne import ComplexModelBase - from magma.enodebd.tr069.models import DeviceIdStruct +from spyne import ComplexModelBase class DeviceIdStructTests(unittest.TestCase): From 00259b53eda3bd5be8e250b73001f25b996ff466 Mon Sep 17 00:00:00 2001 From: Vitalii Kostenko Date: Wed, 7 Apr 2021 17:49:38 -0400 Subject: [PATCH 51/91] [Terraform] Replace hardcoded unique names with optional configurable variables (#5374) * Replace hardcoded unique names with configurable variables Signed-off-by: Vitalii Kostenko * fix variable name Signed-off-by: Vitalii Kostenko --- .../deploy/terraform/orc8r-helm-aws/efs.tf | 4 +-- .../deploy/terraform/orc8r-helm-aws/k8s.tf | 2 +- .../terraform/orc8r-helm-aws/logging.tf | 4 +-- .../terraform/orc8r-helm-aws/storage.tf | 2 +- .../terraform/orc8r-helm-aws/variables.tf | 30 +++++++++++++++++++ 5 files changed, 36 insertions(+), 6 deletions(-) diff --git a/orc8r/cloud/deploy/terraform/orc8r-helm-aws/efs.tf b/orc8r/cloud/deploy/terraform/orc8r-helm-aws/efs.tf index 3c893fda7cfb..6c1acf2128a4 100644 --- a/orc8r/cloud/deploy/terraform/orc8r-helm-aws/efs.tf +++ b/orc8r/cloud/deploy/terraform/orc8r-helm-aws/efs.tf @@ -13,7 +13,7 @@ # k8s requires provisioner to treat efs as a persistent volume resource "helm_release" "efs_provisioner" { - name = "efs-provisioner" + name = var.efs_provisioner_name repository = local.stable_helm_repo chart = "efs-provisioner" version = "0.11.0" @@ -27,7 +27,7 @@ resource "helm_release" "efs_provisioner" { path: /pv-volume provisionerName: aws-efs storageClass: - name: efs + name: ${var.efs_storage_class_name} podAnnotations: iam-assumable-role: ${var.efs_provisioner_role_arn} VALUES diff --git a/orc8r/cloud/deploy/terraform/orc8r-helm-aws/k8s.tf b/orc8r/cloud/deploy/terraform/orc8r-helm-aws/k8s.tf index 2a4ff76333ac..dfe366e06166 100644 --- a/orc8r/cloud/deploy/terraform/orc8r-helm-aws/k8s.tf +++ b/orc8r/cloud/deploy/terraform/orc8r-helm-aws/k8s.tf @@ -25,7 +25,7 @@ resource "kubernetes_namespace" "monitoring" { # external dns maps route53 to ingress resources resource "helm_release" "external_dns" { - name = "external-dns" + name = var.external_dns_deployment_name repository = local.stable_helm_repo chart = "external-dns" version = "2.19.1" diff --git a/orc8r/cloud/deploy/terraform/orc8r-helm-aws/logging.tf b/orc8r/cloud/deploy/terraform/orc8r-helm-aws/logging.tf index c38b3e4a407d..bbb8f09ca336 100644 --- a/orc8r/cloud/deploy/terraform/orc8r-helm-aws/logging.tf +++ b/orc8r/cloud/deploy/terraform/orc8r-helm-aws/logging.tf @@ -18,7 +18,7 @@ locals { resource "helm_release" "fluentd" { count = var.elasticsearch_endpoint == null ? 0 : 1 - name = "fluentd" + name = var.fluentd_deployment_name namespace = kubernetes_namespace.orc8r.metadata[0].name repository = local.stable_helm_repo chart = "fluentd" @@ -130,7 +130,7 @@ resource "helm_release" "fluentd" { resource "helm_release" "elasticsearch_curator" { count = var.elasticsearch_endpoint == null ? 0 : 1 - name = "elasticsearch-curator" + name = var.elasticsearch_curator_name repository = local.stable_helm_repo chart = "elasticsearch-curator" namespace = kubernetes_namespace.monitoring.metadata[0].name diff --git a/orc8r/cloud/deploy/terraform/orc8r-helm-aws/storage.tf b/orc8r/cloud/deploy/terraform/orc8r-helm-aws/storage.tf index 44be3da08907..c45b33c4b237 100644 --- a/orc8r/cloud/deploy/terraform/orc8r-helm-aws/storage.tf +++ b/orc8r/cloud/deploy/terraform/orc8r-helm-aws/storage.tf @@ -55,7 +55,7 @@ resource "kubernetes_persistent_volume_claim" "storage" { storage = each.value.storage } } - storage_class_name = "efs" + storage_class_name = var.efs_storage_class_name } depends_on = [helm_release.efs_provisioner] diff --git a/orc8r/cloud/deploy/terraform/orc8r-helm-aws/variables.tf b/orc8r/cloud/deploy/terraform/orc8r-helm-aws/variables.tf index cb981b734ac8..8fbd71d73a32 100644 --- a/orc8r/cloud/deploy/terraform/orc8r-helm-aws/variables.tf +++ b/orc8r/cloud/deploy/terraform/orc8r-helm-aws/variables.tf @@ -43,6 +43,12 @@ variable "orc8r_route53_zone_id" { type = string } +variable "external_dns_deployment_name" { + description = "Name of the external dns helm deployment" + type = string + default = "external-dns" +} + variable "external_dns_role_arn" { description = "IAM role ARN for ExternalDNS." type = string @@ -214,6 +220,18 @@ variable "efs_provisioner_role_arn" { type = string } +variable "efs_provisioner_name" { + description = "Name of the efs provisioner helm deployment" + type = string + default = "efs-provisioner" +} + +variable "efs_storage_class_name" { + description = "Name of the Storage class" + type = string + default = "efs" +} + ############################################################################## # Log aggregation configuration ############################################################################## @@ -248,6 +266,18 @@ variable "elasticsearch_curator_log_level" { default = "INFO" } +variable "elasticsearch_curator_name" { + description = "Name of the elasticsearch-curator helm deployment" + type = string + default = "elasticsearch-curator" +} + +variable "fluentd_deployment_name" { + description = "Name of the fluentd helm deployment" + type = string + default = "fluentd" +} + ############################################################################## # Secret configuration and values ############################################################################## From ded3f71f89f2c5a5cf90c5943eafbf918e44094d Mon Sep 17 00:00:00 2001 From: Marie <37634144+themarwhal@users.noreply.github.com> Date: Wed, 7 Apr 2021 19:23:57 -0500 Subject: [PATCH 52/91] =?UTF-8?q?[AGW][PyFormat]=20Apply=20isort=20to=20al?= =?UTF-8?q?l=20python=20scripts=20in=20lte/gateway/pyth=E2=80=A6=20(#5952)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lte/gateway/python/scripts/agw_health_cli.py | 2 +- .../python/scripts/config_stateless_agw.py | 11 ++--- .../python/scripts/cpe_monitoring_cli.py | 7 ++- .../python/scripts/create_oai_certs.py | 3 +- lte/gateway/python/scripts/dp_probe_cli.py | 1 + lte/gateway/python/scripts/enodebd_cli.py | 7 +-- lte/gateway/python/scripts/fake_user.py | 8 ++-- lte/gateway/python/scripts/feg_hello_cli.py | 2 +- .../python/scripts/generate_dnsd_config.py | 2 +- lte/gateway/python/scripts/ha_cli.py | 10 ++--- lte/gateway/python/scripts/hello_cli.py | 2 +- lte/gateway/python/scripts/mobility_cli.py | 7 +-- .../python/scripts/mobility_dhcp_cli.py | 4 +- lte/gateway/python/scripts/ocs_cli.py | 4 +- lte/gateway/python/scripts/packet_ryu_cli.py | 10 +++-- .../python/scripts/packet_tracer_cli.py | 2 +- lte/gateway/python/scripts/pcrf_cli.py | 4 +- lte/gateway/python/scripts/pipelined_cli.py | 45 ++++++++----------- lte/gateway/python/scripts/policydb_cli.py | 7 +-- lte/gateway/python/scripts/s6a_proxy_cli.py | 5 +-- lte/gateway/python/scripts/s6a_service_cli.py | 2 +- .../python/scripts/session_manager_cli.py | 35 +++++---------- lte/gateway/python/scripts/sgs_cli.py | 8 ++-- lte/gateway/python/scripts/sms_cli.py | 5 ++- .../python/scripts/spgw_service_cli.py | 8 ++-- lte/gateway/python/scripts/state_cli.py | 11 +++-- lte/gateway/python/scripts/subscriber_cli.py | 13 ++---- setup.cfg | 5 +++ 28 files changed, 104 insertions(+), 126 deletions(-) create mode 100644 setup.cfg diff --git a/lte/gateway/python/scripts/agw_health_cli.py b/lte/gateway/python/scripts/agw_health_cli.py index 6e8076f59158..22bd65538c58 100644 --- a/lte/gateway/python/scripts/agw_health_cli.py +++ b/lte/gateway/python/scripts/agw_health_cli.py @@ -12,10 +12,10 @@ See the License for the specific language governing permissions and limitations under the License. """ +import math import subprocess import fire -import math from magma.health.health_service import AGWHealth from termcolor import colored diff --git a/lte/gateway/python/scripts/config_stateless_agw.py b/lte/gateway/python/scripts/config_stateless_agw.py index 9f3d9e1ad1da..d8303ea3d9f0 100755 --- a/lte/gateway/python/scripts/config_stateless_agw.py +++ b/lte/gateway/python/scripts/config_stateless_agw.py @@ -17,19 +17,16 @@ import argparse import os +import shlex import subprocess import sys -import shlex import time - from enum import Enum from magma.common.redis.client import get_default_client -from magma.configuration.service_configs import ( - load_override_config, - load_service_config, - save_override_config, -) +from magma.configuration.service_configs import (load_override_config, + load_service_config, + save_override_config) return_codes = Enum( "return_codes", "STATELESS STATEFUL CORRUPT INVALID", start=0 diff --git a/lte/gateway/python/scripts/cpe_monitoring_cli.py b/lte/gateway/python/scripts/cpe_monitoring_cli.py index 45bfe1a718ad..40a45e8c7f80 100755 --- a/lte/gateway/python/scripts/cpe_monitoring_cli.py +++ b/lte/gateway/python/scripts/cpe_monitoring_cli.py @@ -11,6 +11,9 @@ limitations under the License. """ import ipaddress +import re +import subprocess +from datetime import datetime from time import sleep import fire @@ -18,10 +21,6 @@ from magma.common.service_registry import ServiceRegistry from orc8r.protos.common_pb2 import Void -import subprocess -import re -from datetime import datetime - class MonitoringCLI(object): """ diff --git a/lte/gateway/python/scripts/create_oai_certs.py b/lte/gateway/python/scripts/create_oai_certs.py index 2da3f2778cf5..4fc262427082 100755 --- a/lte/gateway/python/scripts/create_oai_certs.py +++ b/lte/gateway/python/scripts/create_oai_certs.py @@ -14,12 +14,13 @@ """ import argparse -import envoy import os import shutil import socket import tempfile +import envoy + OPENSSL_BIN = "/usr/bin/openssl" # Default mme/freediameter (s6a) cert and key file names. diff --git a/lte/gateway/python/scripts/dp_probe_cli.py b/lte/gateway/python/scripts/dp_probe_cli.py index 4359d4161b37..52a451b8097e 100644 --- a/lte/gateway/python/scripts/dp_probe_cli.py +++ b/lte/gateway/python/scripts/dp_probe_cli.py @@ -20,6 +20,7 @@ from lte.protos.mconfig import mconfigs_pb2 from magma.common.service import MagmaService + def create_parser(): """ Creates the argparse parser with all the arguments. diff --git a/lte/gateway/python/scripts/enodebd_cli.py b/lte/gateway/python/scripts/enodebd_cli.py index 8501db901b4e..dcb7992002cf 100755 --- a/lte/gateway/python/scripts/enodebd_cli.py +++ b/lte/gateway/python/scripts/enodebd_cli.py @@ -14,10 +14,11 @@ """ import argparse -from magma.common.rpc_utils import grpc_wrapper -from lte.protos.enodebd_pb2 import GetParameterRequest, SetParameterRequest, \ - EnodebIdentity, SingleEnodebStatus + +from lte.protos.enodebd_pb2 import (EnodebIdentity, GetParameterRequest, + SetParameterRequest, SingleEnodebStatus) from lte.protos.enodebd_pb2_grpc import EnodebdStub +from magma.common.rpc_utils import grpc_wrapper from orc8r.protos.common_pb2 import Void diff --git a/lte/gateway/python/scripts/fake_user.py b/lte/gateway/python/scripts/fake_user.py index cacbe1678a2b..b2d587b9c735 100755 --- a/lte/gateway/python/scripts/fake_user.py +++ b/lte/gateway/python/scripts/fake_user.py @@ -13,16 +13,16 @@ """ import argparse -import netifaces import os import sys +import netifaces +from lte.protos.session_manager_pb2 import LocalCreateSessionRequest +from lte.protos.session_manager_pb2_grpc import LocalSessionManagerStub from magma.common.service_registry import ServiceRegistry -from magma.subscriberdb.sid import SIDUtils from magma.configuration import environment from magma.pipelined.imsi import encode_imsi -from lte.protos.session_manager_pb2 import LocalCreateSessionRequest -from lte.protos.session_manager_pb2_grpc import LocalSessionManagerStub +from magma.subscriberdb.sid import SIDUtils def sample_commands(): diff --git a/lte/gateway/python/scripts/feg_hello_cli.py b/lte/gateway/python/scripts/feg_hello_cli.py index ab8d1d8d9ae0..7447f18bec14 100755 --- a/lte/gateway/python/scripts/feg_hello_cli.py +++ b/lte/gateway/python/scripts/feg_hello_cli.py @@ -15,9 +15,9 @@ import argparse -from magma.common.rpc_utils import cloud_grpc_wrapper from feg.protos.hello_pb2 import HelloRequest from feg.protos.hello_pb2_grpc import HelloStub +from magma.common.rpc_utils import cloud_grpc_wrapper @cloud_grpc_wrapper diff --git a/lte/gateway/python/scripts/generate_dnsd_config.py b/lte/gateway/python/scripts/generate_dnsd_config.py index d8acd57d2dba..f673e9114600 100755 --- a/lte/gateway/python/scripts/generate_dnsd_config.py +++ b/lte/gateway/python/scripts/generate_dnsd_config.py @@ -17,11 +17,11 @@ import logging from generate_service_config import generate_template_config +from lte.protos.mconfig.mconfigs_pb2 import DnsD from magma.common.misc_utils import get_ip_from_if_cidr from magma.configuration.exceptions import LoadConfigError from magma.configuration.mconfig_managers import load_service_mconfig from magma.configuration.service_configs import load_service_config -from lte.protos.mconfig.mconfigs_pb2 import DnsD CONFIG_OVERRIDE_DIR = '/var/opt/magma/tmp' diff --git a/lte/gateway/python/scripts/ha_cli.py b/lte/gateway/python/scripts/ha_cli.py index a6070588f409..55c55656dfed 100644 --- a/lte/gateway/python/scripts/ha_cli.py +++ b/lte/gateway/python/scripts/ha_cli.py @@ -14,14 +14,12 @@ """ import argparse -import grpc -from magma.common.rpc_utils import grpc_wrapper -from lte.protos.ha_service_pb2 import ( - StartAgwOffloadRequest, - StartAgwOffloadResponse -) +import grpc +from lte.protos.ha_service_pb2 import StartAgwOffloadRequest from lte.protos.ha_service_pb2_grpc import HaServiceStub +from magma.common.rpc_utils import grpc_wrapper + @grpc_wrapper def send_offload_trigger(client, args): diff --git a/lte/gateway/python/scripts/hello_cli.py b/lte/gateway/python/scripts/hello_cli.py index d4700a6a8bae..e298c1300bd8 100755 --- a/lte/gateway/python/scripts/hello_cli.py +++ b/lte/gateway/python/scripts/hello_cli.py @@ -13,9 +13,9 @@ limitations under the License. """ -from magma.common.rpc_utils import grpc_wrapper from feg.protos.hello_pb2 import HelloRequest from feg.protos.hello_pb2_grpc import HelloStub +from magma.common.rpc_utils import grpc_wrapper @grpc_wrapper diff --git a/lte/gateway/python/scripts/mobility_cli.py b/lte/gateway/python/scripts/mobility_cli.py index dc48dee2a64c..a519c26dcd2e 100755 --- a/lte/gateway/python/scripts/mobility_cli.py +++ b/lte/gateway/python/scripts/mobility_cli.py @@ -17,12 +17,13 @@ import ipaddress import sys +from lte.protos.mobilityd_pb2 import (AllocateIPRequest, GWInfo, IPAddress, + IPBlock, ReleaseIPRequest, + RemoveIPBlockRequest) +from lte.protos.mobilityd_pb2_grpc import MobilityServiceStub from magma.common.rpc_utils import grpc_wrapper from magma.subscriberdb.sid import SIDUtils from orc8r.protos.common_pb2 import Void -from lte.protos.mobilityd_pb2 import AllocateIPRequest, \ - IPAddress, IPBlock, ReleaseIPRequest, RemoveIPBlockRequest, GWInfo -from lte.protos.mobilityd_pb2_grpc import MobilityServiceStub @grpc_wrapper diff --git a/lte/gateway/python/scripts/mobility_dhcp_cli.py b/lte/gateway/python/scripts/mobility_dhcp_cli.py index 3a7943abb654..fdd952366bf1 100755 --- a/lte/gateway/python/scripts/mobility_dhcp_cli.py +++ b/lte/gateway/python/scripts/mobility_dhcp_cli.py @@ -19,12 +19,12 @@ import argparse import ipaddress -from ipaddress import ip_address, ip_network import random import sys +from ipaddress import ip_address, ip_network from magma.mobilityd import mobility_store as store -from magma.mobilityd.dhcp_desc import DHCPState, DHCPDescriptor +from magma.mobilityd.dhcp_desc import DHCPDescriptor, DHCPState from magma.mobilityd.mac import MacAddress from magma.mobilityd.uplink_gw import UplinkGatewayInfo diff --git a/lte/gateway/python/scripts/ocs_cli.py b/lte/gateway/python/scripts/ocs_cli.py index ad76b9810523..38c5c821fe2a 100755 --- a/lte/gateway/python/scripts/ocs_cli.py +++ b/lte/gateway/python/scripts/ocs_cli.py @@ -14,11 +14,11 @@ """ import argparse -import grpc +import grpc +from feg.protos.mock_core_pb2_grpc import MockCoreConfiguratorStub from magma.common.rpc_utils import cloud_grpc_wrapper from orc8r.protos.common_pb2 import Void -from feg.protos.mock_core_pb2_grpc import MockCoreConfiguratorStub @cloud_grpc_wrapper diff --git a/lte/gateway/python/scripts/packet_ryu_cli.py b/lte/gateway/python/scripts/packet_ryu_cli.py index e9ea02844c17..09196b74583b 100755 --- a/lte/gateway/python/scripts/packet_ryu_cli.py +++ b/lte/gateway/python/scripts/packet_ryu_cli.py @@ -13,13 +13,15 @@ limitations under the License. """ -import logging import argparse +import logging + +from integ_tests.s1aptests.ovs.rest_api import (add_flowentry, + delete_flowentry, get_datapath, + get_flows) +from scapy.all import IP, Ether, sendp -from integ_tests.s1aptests.ovs.rest_api import get_datapath, get_flows,\ - delete_flowentry, add_flowentry logging.getLogger("scapy.runtime").setLevel(logging.ERROR) -from scapy.all import Ether, IP, sendp DEFAULT_PKT_MAC_SRC = "00:00:00:00:00:01" DEFAULT_PKT_MAC_DST = "12:99:cc:97:47:4e" diff --git a/lte/gateway/python/scripts/packet_tracer_cli.py b/lte/gateway/python/scripts/packet_tracer_cli.py index c514d50ad79e..f384ae0d8261 100755 --- a/lte/gateway/python/scripts/packet_tracer_cli.py +++ b/lte/gateway/python/scripts/packet_tracer_cli.py @@ -13,8 +13,8 @@ limitations under the License. """ -import subprocess import argparse +import subprocess from magma.configuration.service_configs import load_service_config diff --git a/lte/gateway/python/scripts/pcrf_cli.py b/lte/gateway/python/scripts/pcrf_cli.py index 0c6b13f72fb3..ebe05ef5501e 100755 --- a/lte/gateway/python/scripts/pcrf_cli.py +++ b/lte/gateway/python/scripts/pcrf_cli.py @@ -14,11 +14,11 @@ """ import argparse -import grpc +import grpc +from feg.protos.mock_core_pb2_grpc import MockCoreConfiguratorStub from magma.common.rpc_utils import cloud_grpc_wrapper from orc8r.protos.common_pb2 import Void -from feg.protos.mock_core_pb2_grpc import MockCoreConfiguratorStub @cloud_grpc_wrapper diff --git a/lte/gateway/python/scripts/pipelined_cli.py b/lte/gateway/python/scripts/pipelined_cli.py index eb671f61c395..baacc94a0d3a 100755 --- a/lte/gateway/python/scripts/pipelined_cli.py +++ b/lte/gateway/python/scripts/pipelined_cli.py @@ -15,44 +15,35 @@ import argparse import errno -import time import random import subprocess -from datetime import datetime +import time from collections import namedtuple +from datetime import datetime from pprint import pprint -from magma.common.rpc_utils import grpc_wrapper, grpc_async_wrapper -from lte.protos.pipelined_pb2 import ( - SubscriberQuotaUpdate, - UpdateSubscriberQuotaStateRequest, -) -from lte.protos.policydb_pb2 import RedirectInformation +from lte.protos.pipelined_pb2 import (ActivateFlowsRequest, + DeactivateFlowsRequest, + DeactivateFlowsResult, RequestOriginType, + RuleModResult, SubscriberQuotaUpdate, + UEMacFlowRequest, + UpdateSubscriberQuotaStateRequest) +from lte.protos.pipelined_pb2_grpc import PipelinedStub +from lte.protos.policydb_pb2 import (FlowDescription, FlowMatch, PolicyRule, + RedirectInformation) +from lte.protos.subscriberdb_pb2 import AggregatedMaximumBitrate +from magma.common.rpc_utils import grpc_wrapper +from magma.configuration.service_configs import load_service_config from magma.pipelined.app.enforcement import EnforcementController from magma.pipelined.app.enforcement_stats import EnforcementStatsController -from magma.pipelined.policy_converters import convert_ipv4_str_to_ip_proto -from magma.subscriberdb.sid import SIDUtils -from magma.configuration.service_configs import load_service_config from magma.pipelined.bridge_util import BridgeTools -from magma.pipelined.service_manager import Tables +from magma.pipelined.policy_converters import convert_ipv4_str_to_ip_proto from magma.pipelined.qos.common import QosManager +from magma.pipelined.service_manager import Tables +from magma.pipelined.tests.app.ng_set_session_msg import CreateSessionUtil +from magma.subscriberdb.sid import SIDUtils from orc8r.protos.common_pb2 import Void -from lte.protos.pipelined_pb2 import ( - ActivateFlowsRequest, - DeactivateFlowsRequest, - RuleModResult, - UEMacFlowRequest, - RequestOriginType, - DeactivateFlowsResult, -) -from lte.protos.subscriberdb_pb2 import ( - AggregatedMaximumBitrate, -) -from magma.pipelined.tests.app.ng_set_session_msg import ( - CreateSessionUtil) -from lte.protos.pipelined_pb2_grpc import PipelinedStub -from lte.protos.policydb_pb2 import FlowMatch, FlowDescription, PolicyRule @grpc_wrapper def set_smf_session(client, args): diff --git a/lte/gateway/python/scripts/policydb_cli.py b/lte/gateway/python/scripts/policydb_cli.py index d53d4faa4855..f63ccc0b80b8 100755 --- a/lte/gateway/python/scripts/policydb_cli.py +++ b/lte/gateway/python/scripts/policydb_cli.py @@ -14,15 +14,16 @@ """ import argparse + import grpc -from lte.protos.policydb_pb2 import FlowMatch, FlowDescription, PolicyRule, \ - EnableStaticRuleRequest, DisableStaticRuleRequest from lte.protos.mobilityd_pb2 import IPAddress +from lte.protos.policydb_pb2 import (DisableStaticRuleRequest, + EnableStaticRuleRequest, FlowDescription, + FlowMatch, PolicyRule) from lte.protos.policydb_pb2_grpc import PolicyDBStub from magma.common.rpc_utils import grpc_wrapper from magma.policydb.rule_store import PolicyRuleDict - DEBUG_MSG = 'You may want to check that a connection can be made to ' \ 'orc8r to update the assignments of rules/basenames to ' \ 'the subscriber.' diff --git a/lte/gateway/python/scripts/s6a_proxy_cli.py b/lte/gateway/python/scripts/s6a_proxy_cli.py index 6868c33c7628..f32f836e7562 100755 --- a/lte/gateway/python/scripts/s6a_proxy_cli.py +++ b/lte/gateway/python/scripts/s6a_proxy_cli.py @@ -16,10 +16,9 @@ import argparse import grpc -from feg.protos.s6a_proxy_pb2 import AuthenticationInformationRequest, \ - UpdateLocationRequest +from feg.protos.s6a_proxy_pb2 import (AuthenticationInformationRequest, + UpdateLocationRequest) from feg.protos.s6a_proxy_pb2_grpc import S6aProxyStub - from magma.common.rpc_utils import cloud_grpc_wrapper RESYNC_INFO_BYTES = 30 diff --git a/lte/gateway/python/scripts/s6a_service_cli.py b/lte/gateway/python/scripts/s6a_service_cli.py index 7dd1fa5b89a5..fbfbeb534916 100755 --- a/lte/gateway/python/scripts/s6a_service_cli.py +++ b/lte/gateway/python/scripts/s6a_service_cli.py @@ -15,9 +15,9 @@ import argparse -from magma.common.rpc_utils import grpc_wrapper from lte.protos.s6a_service_pb2 import DeleteSubscriberRequest from lte.protos.s6a_service_pb2_grpc import S6aServiceStub +from magma.common.rpc_utils import grpc_wrapper @grpc_wrapper diff --git a/lte/gateway/python/scripts/session_manager_cli.py b/lte/gateway/python/scripts/session_manager_cli.py index 45aae150daa3..6cf3f6d69e6c 100755 --- a/lte/gateway/python/scripts/session_manager_cli.py +++ b/lte/gateway/python/scripts/session_manager_cli.py @@ -17,29 +17,16 @@ import grpc from feg.protos.mock_core_pb2_grpc import MockOCSStub, MockPCRFStub -from lte.protos.policydb_pb2 import ( - FlowDescription, - FlowMatch, - FlowQos, - PolicyRule, - QosArp, -) -from lte.protos.session_manager_pb2 import ( - DynamicRuleInstall, - LocalCreateSessionRequest, - PolicyReAuthRequest, - QoSInformation, -) -from lte.protos.session_manager_pb2_grpc import ( - LocalSessionManagerStub, - SessionProxyResponderStub, -) -from lte.protos.abort_session_pb2 import ( - AbortSessionRequest, -) -from lte.protos.abort_session_pb2_grpc import ( - AbortSessionResponderStub, -) +from lte.protos.abort_session_pb2 import AbortSessionRequest +from lte.protos.abort_session_pb2_grpc import AbortSessionResponderStub +from lte.protos.policydb_pb2 import (FlowDescription, FlowMatch, FlowQos, + PolicyRule, QosArp) +from lte.protos.session_manager_pb2 import (DynamicRuleInstall, + LocalCreateSessionRequest, + PolicyReAuthRequest, + QoSInformation) +from lte.protos.session_manager_pb2_grpc import (LocalSessionManagerStub, + SessionProxyResponderStub) from lte.protos.subscriberdb_pb2 import SubscriberID from magma.common.rpc_utils import grpc_wrapper from magma.common.service_registry import ServiceRegistry @@ -303,4 +290,4 @@ def main(): if __name__ == "__main__": - main() \ No newline at end of file + main() diff --git a/lte/gateway/python/scripts/sgs_cli.py b/lte/gateway/python/scripts/sgs_cli.py index 97aa98e22529..a3f8ac542b21 100755 --- a/lte/gateway/python/scripts/sgs_cli.py +++ b/lte/gateway/python/scripts/sgs_cli.py @@ -14,12 +14,12 @@ """ import argparse -import grpc -from magma.common.rpc_utils import cloud_grpc_wrapper -from feg.protos.csfb_pb2 import AlertAck, AlertReject, EPSDetachIndication, \ - IMSIDetachIndication +import grpc +from feg.protos.csfb_pb2 import (AlertAck, AlertReject, EPSDetachIndication, + IMSIDetachIndication) from feg.protos.csfb_pb2_grpc import CSFBFedGWServiceStub +from magma.common.rpc_utils import cloud_grpc_wrapper @cloud_grpc_wrapper diff --git a/lte/gateway/python/scripts/sms_cli.py b/lte/gateway/python/scripts/sms_cli.py index 4477f7b2cf4e..c4584e4a089a 100755 --- a/lte/gateway/python/scripts/sms_cli.py +++ b/lte/gateway/python/scripts/sms_cli.py @@ -14,11 +14,12 @@ """ import argparse -import grpc -from magma.common.rpc_utils import grpc_wrapper +import grpc from lte.protos.sms_orc8r_pb2 import SMODownlinkUnitdata from lte.protos.sms_orc8r_pb2_grpc import SMSOrc8rGatewayServiceStub +from magma.common.rpc_utils import grpc_wrapper + @grpc_wrapper def send_downlink_unitdata(client, args): diff --git a/lte/gateway/python/scripts/spgw_service_cli.py b/lte/gateway/python/scripts/spgw_service_cli.py index 01f27baca446..212341335bc8 100755 --- a/lte/gateway/python/scripts/spgw_service_cli.py +++ b/lte/gateway/python/scripts/spgw_service_cli.py @@ -15,11 +15,11 @@ import argparse -from magma.common.rpc_utils import grpc_wrapper -from lte.protos.spgw_service_pb2 import CreateBearerRequest, \ - DeleteBearerRequest +from lte.protos.policydb_pb2 import FlowQos, PolicyRule, QosArp +from lte.protos.spgw_service_pb2 import (CreateBearerRequest, + DeleteBearerRequest) from lte.protos.spgw_service_pb2_grpc import SpgwServiceStub -from lte.protos.policydb_pb2 import PolicyRule, FlowQos, QosArp +from magma.common.rpc_utils import grpc_wrapper from magma.subscriberdb.sid import SIDUtils diff --git a/lte/gateway/python/scripts/state_cli.py b/lte/gateway/python/scripts/state_cli.py index 1364aa0e356e..41b0515e9026 100644 --- a/lte/gateway/python/scripts/state_cli.py +++ b/lte/gateway/python/scripts/state_cli.py @@ -14,23 +14,22 @@ """ import ast import json +import random from json.decoder import JSONDecodeError from typing import Union import fire import jsonpickle -import random from lte.protos.keyval_pb2 import IPDesc from lte.protos.oai.mme_nas_state_pb2 import MmeNasState, UeContext from lte.protos.oai.s1ap_state_pb2 import S1apState, UeDescription from lte.protos.oai.spgw_state_pb2 import SpgwState, SpgwUeContext from lte.protos.policydb_pb2 import InstalledPolicies, PolicyRule - from magma.common.redis.client import get_default_client -from magma.common.redis.serializers import get_json_deserializer, \ - get_proto_deserializer -from magma.mobilityd.serialize_utils import deserialize_ip_block, \ - deserialize_ip_desc +from magma.common.redis.serializers import (get_json_deserializer, + get_proto_deserializer) +from magma.mobilityd.serialize_utils import (deserialize_ip_block, + deserialize_ip_desc) NO_DESERIAL_MSG = "No deserializer exists for type '{}'" diff --git a/lte/gateway/python/scripts/subscriber_cli.py b/lte/gateway/python/scripts/subscriber_cli.py index 5dd7a5e4e360..e92d1e1b4c42 100755 --- a/lte/gateway/python/scripts/subscriber_cli.py +++ b/lte/gateway/python/scripts/subscriber_cli.py @@ -15,18 +15,13 @@ import argparse -from lte.protos.subscriberdb_pb2 import ( - GSMSubscription, - LTESubscription, - SubscriberData, - SubscriberState, - SubscriberUpdate, -) +from lte.protos.subscriberdb_pb2 import (GSMSubscription, LTESubscription, + SubscriberData, SubscriberState, + SubscriberUpdate) from lte.protos.subscriberdb_pb2_grpc import SubscriberDBStub -from orc8r.protos.common_pb2 import Void - from magma.common.rpc_utils import grpc_wrapper from magma.subscriberdb.sid import SIDUtils +from orc8r.protos.common_pb2 import Void @grpc_wrapper diff --git a/setup.cfg b/setup.cfg new file mode 100644 index 000000000000..411ec724dce0 --- /dev/null +++ b/setup.cfg @@ -0,0 +1,5 @@ +[flake8] +ignore = + WPS115, # Found upper-case constant in a class + WPS318, # Found extra indentation + WPS319 # Found bracket in wrong position From 909b60546928c4f9a54762e346cdf404aad5ae3d Mon Sep 17 00:00:00 2001 From: saurabhm3hra Date: Thu, 8 Apr 2021 12:18:40 +0800 Subject: [PATCH 53/91] Datapath Probe Cli Egress (#5983) Signed-off-by: Saurabh Mehra --- docs/readmes/lte/debug_dp_probe.md | 25 +-- lte/gateway/python/scripts/dp_probe_cli.py | 179 +++++++++++++++------ 2 files changed, 148 insertions(+), 56 deletions(-) diff --git a/docs/readmes/lte/debug_dp_probe.md b/docs/readmes/lte/debug_dp_probe.md index 98eb8540e43c..958e001be101 100644 --- a/docs/readmes/lte/debug_dp_probe.md +++ b/docs/readmes/lte/debug_dp_probe.md @@ -12,11 +12,16 @@ The datapath probe CLI helps an operator to probe the Datapath to the UE. ```sh # On your GW host, run the following command -$ dp_probe_cli.py -i -$ dp_probe_cli.py -i 1010000051011 -IMSI: 1010000051011, IP: 192.168.128.15 -Running: sudo ovs-appctl ofproto/trace gtp_br0 tcp,in_port=local,ip_dst=192.168.128.15,ip_src=8.8.8.8,tcp_src=80,tcp_dst=3372 -Datapath Actions: set(tunnel(tun_id=0x1000308,dst=10.0.2.240,ttl=64,tp_dst=2152,flags(df|key))),pop_eth,2 +$ dp_probe_cli.py -i --direction
+$ dp_probe_cli.py -i 1010000051013 --direction DL +IMSI: 1010000051013, IP: 192.168.128.13 +Running: sudo ovs-appctl ofproto/trace gtp_br0 tcp,in_port=local,ip_dst=192.168.128.13,ip_src=8.8.8.8,tcp_src=80,tcp_dst=3372 +Datapath Actions: set(tunnel(tun_id=0x1000008,dst=10.0.2.241,ttl=64,tp_dst=2152,flags(df|key))),pop_eth,2 + +dp_probe_cli.py -i 1010000051013 --direction UL +IMSI: 1010000051013, IP: 192.168.128.13 +Running: sudo ovs-appctl ofproto/trace gtp_br0 tcp,in_port=4,tun_id=0x2,ip_dst=8.8.8.8,ip_src=192.168.128.13,tcp_src=3372,tcp_dst=80 +Datapath Actions: set(eth(src=02:00:00:00:00:01,dst=92:9d:a2:1f:ea:44)),1 # You can also supply the followin options # -I @@ -24,15 +29,15 @@ Datapath Actions: set(tunnel(tun_id=0x1000308,dst=10.0.2.240,ttl=64,tp_dst=2152, # -UP # -p -$ dp_probe_cli.py -i 1010000051016 -I 4.2.2.2 -p tcp -P 8080 -UP 3172 -IMSI: 1010000051016, IP: 192.168.128.14 -Running: sudo ovs-appctl ofproto/trace gtp_br0 tcp,in_port=local,ip_dst=192.168.128.14,ip_src=4.2.2.2,tcp_src=8080,tcp_dst=3172 -Datapath Actions: set(tunnel(tun_id=0x1000208,dst=10.0.2.240,ttl=64,tp_dst=2152,flags(df|key))),pop_eth,2 +dp_probe_cli.py -i 1010000051013 --direction UL -p tcp -I 4.2.2.2 -P 8080 -UP 3172 +IMSI: 1010000051013, IP: 192.168.128.13 +Running: sudo ovs-appctl ofproto/trace gtp_br0 tcp,in_port=4,tun_id=0x2,ip_dst=4.2.2.2,ip_src=192.168.128.13,tcp_src=3172,tcp_dst=8080 +Datapath Actions: set(eth(src=02:00:00:00:00:01,dst=92:9d:a2:1f:ea:44)),1 ``` ## What is this command doing? -This command is a wrapper around *ovs-appctl ofproto/trace* which simulates the Datapath of the packet. +This command is a wrapper around *ovs-appctl ofproto/trace* which simulates the Datapath of the packet based on the direction specified in the command. 'DL' - Incoming Packet, 'UL' - Outgoing packet. ## How to read the output? diff --git a/lte/gateway/python/scripts/dp_probe_cli.py b/lte/gateway/python/scripts/dp_probe_cli.py index 52a451b8097e..03695751a4ad 100644 --- a/lte/gateway/python/scripts/dp_probe_cli.py +++ b/lte/gateway/python/scripts/dp_probe_cli.py @@ -30,11 +30,32 @@ def create_parser(): "To display the Datapath actions of the supplied IMSI", formatter_class=argparse.ArgumentDefaultsHelpFormatter, ) - parser.add_argument("-i", "--imsi", help="IMSI of the subscriber") - parser.add_argument("-I", "--ip", help="External IP") - parser.add_argument("-P", "--port", help="External Port") - parser.add_argument("-UP", "--ue_port", help="UE Port") - parser.add_argument("-p", "--protocol", help="Portocol (i.e. tcp, udp, icmp)") + parser.add_argument("-i", "--imsi", required=True, help="IMSI of the subscriber") + parser.add_argument( + "-d", + "--direction", + required=True, + choices=["DL", "UL"], + help="Direction - DL/UL", + ) + parser.add_argument( + "-I", "--ip", nargs="?", const="8.8.8.8", default="8.8.8.8", help="External IP" + ) + parser.add_argument( + "-P", "--port", nargs="?", const="80", default="80", help="External Port" + ) + parser.add_argument( + "-UP", "--ue_port", nargs="?", const="3372", default="3372", help="UE Port" + ) + parser.add_argument( + "-p", + "--protocol", + choices=["tcp", "udp", "icmp"], + nargs="?", + const="tcp", + default="tcp", + help="Portocol (i.e. tcp, udp, icmp)", + ) return parser @@ -50,41 +71,92 @@ def find_ue_ip(imsi: str): match = re.search(pattern, output_str) if match: return match.group(1) - else: - return "NA" + return def output_datapath_actions( - ue_ip: str, external_ip: str, external_port: str, ue_port: str, protocol: str + imsi: str, + direction: str, + ue_ip: str, + external_ip: str, + external_port: str, + ue_port: str, + protocol: str, ): """ Returns the Output of Datapath Actions based as per the supplied UE IP """ service = MagmaService("pipelined", mconfigs_pb2.PipelineD()) + cmd = ["sudo", "ovs-appctl", "ofproto/trace", "gtp_br0"] if service.mconfig.nat_enabled: in_port = "local" else: in_port = "patch-up" - cmd = ["sudo", "ovs-appctl", "ofproto/trace", "gtp_br0"] + if direction == "DL": - cmd_append = ( - protocol + ",in_port=" + in_port + ",ip_dst=" + ue_ip + ",ip_src=" + external_ip - ) + cmd_append = ( + protocol + + ",in_port=" + + in_port + + ",ip_dst=" + + ue_ip + + ",ip_src=" + + external_ip + ) - if protocol != "icmp": - cmd_append += ( - "," - + protocol - + "_src=" - + external_port - + "," - + protocol - + "_dst=" - + ue_port + if protocol != "icmp": + cmd_append += ( + "," + + protocol + + "_src=" + + external_port + + "," + + protocol + + "_dst=" + + ue_port + ) + + cmd.append(cmd_append) + + elif direction == "UL": + ingress_tun_id = get_ingress_tunid(ue_ip, in_port) + if not ingress_tun_id: + print("Ingress Tunnel not Found") + exit(1) + + data = get_egress_tunid_and_port(ue_ip, ingress_tun_id) + if not data: + print("Egress Tunnel not Found") + exit(1) + + cmd_append = ( + protocol + + ",in_port=" + + data["in_port"] + + ",tun_id=" + + data["tun_id"] + + ",ip_dst=" + + external_ip + + ",ip_src=" + + ue_ip ) - cmd.append(cmd_append) + if protocol != "icmp": + cmd_append += ( + "," + + protocol + + "_src=" + + ue_port + + "," + + protocol + + "_dst=" + + external_port + ) + + cmd.append(cmd_append) + else: + return print("Running: " + " ".join(cmd)) output = subprocess.check_output(cmd) @@ -94,45 +166,60 @@ def output_datapath_actions( if match: return match.group(1).strip() else: - return "NA" - - -def get_options(args): - external_ip = args.ip if args.ip else "8.8.8.8" - external_port = args.port if args.port else "80" - ue_port = args.ue_port if args.ue_port else "3372" - protocol = args.protocol if args.protocol else "tcp" + return - return { - "external_ip": external_ip, - "external_port": external_port, - "ue_port": ue_port, - "protocol": protocol, - } +def get_ingress_tunid(ue_ip: str, in_port: str): + cmd = ["sudo", "ovs-ofctl", "dump-flows", "gtp_br0", "table=0"] + output = subprocess.check_output(cmd) + output = str(output, "utf-8").strip() + pattern = ( + ".*?in_port=" + + in_port + + ",nw_dst=" + + ue_ip + + ".*?load:(0x(?:[a-z]|[0-9]){1,})->OXM_OF_METADATA.*?" + ) + match = re.findall(pattern, output, re.IGNORECASE) + if match: + return match[0] + return +def get_egress_tunid_and_port(ue_ip: str, ingress_tun: str): + cmd = ["sudo", "ovs-ofctl", "dump-flows", "gtp_br0", "table=0"] + output = subprocess.check_output(cmd) + output = str(output, "utf-8").strip() + pattern = pattern = ( + "tun_id=(0x(?:[a-z]|[0-9]){1,}),in_port=([a-z]|[0-9]).*?load:" + + ingress_tun + + "->OXM_OF_METADATA.*?\n" + ) + match = re.findall(pattern, output) + if match: + return {"tun_id": match[0][0], "in_port": match[0][1]} + return def main(): parser = create_parser() # Parse the args args = parser.parse_args() - if not args.imsi: - parser.print_usage() - exit(1) ue_ip = find_ue_ip(args.imsi) - if ue_ip == "NA": + if not ue_ip: print("UE is not connected") exit(1) print("IMSI: " + args.imsi + ", IP: " + ue_ip) - input_options = get_options(args) dp_actions = output_datapath_actions( + args.imsi, + args.direction, ue_ip, - input_options["external_ip"], - input_options["external_port"], - input_options["ue_port"], - input_options["protocol"], + args.ip, + args.port, + args.ue_port, + args.protocol, ) + if not dp_actions: + print("Cannot find Datapath Actions for the UE") print("Datapath Actions: " + dp_actions) From 5801c2c34abdfb563411726408e75f9e692b379d Mon Sep 17 00:00:00 2001 From: Marie <37634144+themarwhal@users.noreply.github.com> Date: Thu, 8 Apr 2021 06:59:08 -0500 Subject: [PATCH 54/91] [SessionD] Optionally enable sentry reporting for SessionD (#6000) --- lte/gateway/c/session_manager/CMakeLists.txt | 14 ++++++++- .../c/session_manager/sessiond_main.cpp | 31 +++++++++++++++++++ 2 files changed, 44 insertions(+), 1 deletion(-) diff --git a/lte/gateway/c/session_manager/CMakeLists.txt b/lte/gateway/c/session_manager/CMakeLists.txt index 0e79d1681e40..e2cf60714738 100644 --- a/lte/gateway/c/session_manager/CMakeLists.txt +++ b/lte/gateway/c/session_manager/CMakeLists.txt @@ -176,7 +176,19 @@ add_library(SESSION_MANAGER ${PROTO_SRCS} ${PROTO_HDRS}) - +## Find + link sentry if it exists +find_library(SENTRY_LIB NAMES sentry sentry-native) +if (SENTRY_LIB) +# Include dir +message("Building with sentry!") +find_path(SENTRY_INCLUDE_DIR + NAMES + sentry.h +) +target_link_libraries(SESSION_MANAGER ${SENTRY_LIB}) +set (CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -DSENTRY_ENABLED=1") +set (CMAKE_CXX_FLAGS_RELWITHDEBINFO "${CMAKE_CXX_FLAGS_RELWITHDEBINFO} -DSENTRY_ENABLED=1") +endif() target_link_libraries(SESSION_MANAGER SERVICE303_LIB SERVICE_REGISTRY ASYNC_GRPC CONFIG POLICYDB EVENTD diff --git a/lte/gateway/c/session_manager/sessiond_main.cpp b/lte/gateway/c/session_manager/sessiond_main.cpp index fa8281dbda0e..2fa737780f06 100644 --- a/lte/gateway/c/session_manager/sessiond_main.cpp +++ b/lte/gateway/c/session_manager/sessiond_main.cpp @@ -43,6 +43,30 @@ extern "C" void __gcov_flush(void); #endif +// TODO remove this flag once we are on Ubuntu 20.04 by default in 1.6 +#if SENTRY_ENABLED +#include "sentry.h" + +void initialize_sentry() { + auto control_proxy_config = + magma::ServiceConfigLoader{}.load_service_config("control_proxy"); + if (control_proxy_config["sentry_url"].IsDefined()) { + const std::string sentry_dns = + control_proxy_config["sentry_url"].as(); + sentry_options_t* options = sentry_options_new(); + sentry_options_set_dsn(options, sentry_dns.c_str()); + + sentry_init(options); + sentry_capture_event(sentry_value_new_message_event( + SENTRY_LEVEL_INFO, "", "Starting SessionD with Sentry!")); + } +} + +void shutdown_sentry() { + sentry_shutdown(); +} +#endif + static magma::mconfig::SessionD get_default_mconfig() { magma::mconfig::SessionD mconfig; mconfig.set_log_level(magma::orc8r::LogLevel::INFO); @@ -172,6 +196,10 @@ int main(int argc, char* argv[]) { __gcov_flush(); #endif +#ifdef SENTRY_ENABLED + initialize_sentry(); +#endif + magma::init_logging(argv[0]); auto mconfig = load_mconfig(); @@ -423,5 +451,8 @@ int main(int argc, char* argv[]) { } delete session_store; +#ifdef SENTRY_ENABLED + shutdown_sentry(); +#endif return 0; } From b26bff152d13ebc671f4ae6b86cc56d236d49f45 Mon Sep 17 00:00:00 2001 From: rashmi Date: Wed, 7 Apr 2021 22:47:38 +0530 Subject: [PATCH 55/91] Corrected code to send proper IMIESV, ECGI Signed-off-by: rashmi --- lte/gateway/c/oai/lib/pcef/pcef_handlers.cpp | 2 +- lte/gateway/c/oai/lib/pcef/pcef_handlers.h | 2 ++ .../c/oai/lib/s8_proxy/s8_client_api.cpp | 23 ++++++++++++++++--- .../tasks/mme_app/mme_app_itti_messaging.c | 4 ++-- 4 files changed, 25 insertions(+), 6 deletions(-) diff --git a/lte/gateway/c/oai/lib/pcef/pcef_handlers.cpp b/lte/gateway/c/oai/lib/pcef/pcef_handlers.cpp index 3bc9b0a9182f..c9cbcae591e6 100644 --- a/lte/gateway/c/oai/lib/pcef/pcef_handlers.cpp +++ b/lte/gateway/c/oai/lib/pcef/pcef_handlers.cpp @@ -313,7 +313,7 @@ int get_msisdn_from_session_req( return len; } -static int get_imeisv_from_session_req( +int get_imeisv_from_session_req( const itti_s11_create_session_request_t* saved_req, char* imeisv) { if (saved_req->mei.present & MEI_IMEISV) { // IMEISV as defined in 3GPP TS 23.003 MEI_IMEISV diff --git a/lte/gateway/c/oai/lib/pcef/pcef_handlers.h b/lte/gateway/c/oai/lib/pcef/pcef_handlers.h index 48c769601309..af7783ad255a 100644 --- a/lte/gateway/c/oai/lib/pcef/pcef_handlers.h +++ b/lte/gateway/c/oai/lib/pcef/pcef_handlers.h @@ -98,6 +98,8 @@ int get_msisdn_from_session_req( char convert_digit_to_char(char digit); +int get_imeisv_from_session_req( + const itti_s11_create_session_request_t* saved_req, char* imeisv); #ifdef __cplusplus } #endif diff --git a/lte/gateway/c/oai/lib/s8_proxy/s8_client_api.cpp b/lte/gateway/c/oai/lib/s8_proxy/s8_client_api.cpp index d285dc76d46a..5e6579fc056b 100644 --- a/lte/gateway/c/oai/lib/s8_proxy/s8_client_api.cpp +++ b/lte/gateway/c/oai/lib/s8_proxy/s8_client_api.cpp @@ -202,8 +202,8 @@ static void convert_uli_to_proto_msg( uli->set_sac(msg_uli.s.sai.sac); uli->set_rac(msg_uli.s.rai.rac); uli->set_tac(msg_uli.s.tai.tac); - uli->set_eci(msg_uli.s.ecgi.cell_identity.cell_id); - uli->set_menbi(msg_uli.s.ecgi.cell_identity.enb_id); + uli->set_eci(msg_uli.s.ecgi.cell_identity.enb_id); + uli->set_menbi(0); OAILOG_FUNC_OUT(LOG_SGW_S8); } @@ -333,6 +333,17 @@ static void get_msisdn_from_csr_req( OAILOG_FUNC_OUT(LOG_SGW_S8); } +static void convert_imeisv_to_string(char* imeisv) { + OAILOG_FUNC_IN(LOG_SGW_S8); + uint8_t idx = 0; + for (; idx < IMEISV_DIGITS_MAX; idx++) { + imeisv[idx] = convert_digit_to_char(imeisv[idx]); + } + imeisv[idx] = '\0'; + + OAILOG_FUNC_OUT(LOG_SGW_S8); +} + static void fill_s8_create_session_req( const itti_s11_create_session_request_t* msg, magma::feg::CreateSessionRequestPgw* csr, teid_t sgw_s8_teid) { @@ -357,7 +368,10 @@ static void fill_s8_create_session_req( mnc[2] = '\0'; mnc_len = 2; } - + char imeisv[IMEISV_DIGITS_MAX + 1]; + get_imeisv_from_session_req(msg, imeisv); + convert_imeisv_to_string(imeisv); + csr->set_mei(imeisv, IMEISV_DIGITS_MAX); magma::feg::ServingNetwork* serving_network = csr->mutable_serving_network(); serving_network->set_mcc(mcc, 3); serving_network->set_mnc(mnc, mnc_len); @@ -372,6 +386,9 @@ static void fill_s8_create_session_req( magma::feg::BearerContext* bc = csr->mutable_bearer_context(); convert_bearer_context_to_proto( &msg->bearer_contexts_to_be_created.bearer_contexts[0], bc); + // set the mbr within bearer qos same as apn-ambr + bc->mutable_qos()->mutable_mbr()->set_br_ul(msg->ambr.br_ul); + bc->mutable_qos()->mutable_mbr()->set_br_dl(msg->ambr.br_dl); } csr->set_c_agw_teid(sgw_s8_teid); csr->set_charging_characteristics( diff --git a/lte/gateway/c/oai/tasks/mme_app/mme_app_itti_messaging.c b/lte/gateway/c/oai/tasks/mme_app/mme_app_itti_messaging.c index ff37c92449f9..615c2dcb7a66 100644 --- a/lte/gateway/c/oai/tasks/mme_app/mme_app_itti_messaging.c +++ b/lte/gateway/c/oai/tasks/mme_app/mme_app_itti_messaging.c @@ -237,7 +237,8 @@ int mme_app_send_s11_create_session_req( } else { session_request_p->msisdn.length = 0; } - + session_request_p->mei.present = MEI_IMEISV; + session_request_p->mei.choice.imeisv = ue_mm_context->emm_context._imeisv; // Fill User Location Information session_request_p->uli.present = 0; // initialize the presencemask mme_app_get_user_location_information(&session_request_p->uli, ue_mm_context); @@ -646,4 +647,3 @@ void mme_app_itti_sgsap_ue_activity_ind( } OAILOG_FUNC_OUT(LOG_MME_APP); } - From 78028444b8e5cea07995b0684344903faaf31836 Mon Sep 17 00:00:00 2001 From: Scott Moeller Date: Thu, 8 Apr 2021 09:02:21 -0400 Subject: [PATCH 56/91] [CI] Turn up Dockerfile Linting in GH PR (#5628) [Hadolint](https://github.com/hadolint/hadolint) is an industry leading Dockerfile linting tool written in Haskell. This GH action uses [reviewdog](https://github.com/reviewdog/reviewdog) for Github Actions to annotate our Dockerfiles with linting warnings from Hadolint - in Pull Requests containing Dockerfiles. Hadolint also lints shell commands thanks to the fantastic [Shellcheck](https://github.com/koalaman/shellcheck) linting tool for the shell. The sample findings on my existing Dockerfile are very helpful, this seems worth having available to us on PRs. @rdefosse if you find it annoying, or disagree with its advice, we can certainly whitelist some directories so they aren't linted - if you like. Let me know. Presently I have it linting any file that is touched. We can alternatively lint only modified lines (one line change). Up to us. Signed-off-by: Scott Harrison Moeller --- .github/workflows/hadolint.yml | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) create mode 100644 .github/workflows/hadolint.yml diff --git a/.github/workflows/hadolint.yml b/.github/workflows/hadolint.yml new file mode 100644 index 000000000000..75d8f198349a --- /dev/null +++ b/.github/workflows/hadolint.yml @@ -0,0 +1,17 @@ +name: Dockerfile Linting +on: [pull_request] +jobs: + hadolint: + name: Dockerfile Lint + runs-on: ubuntu-latest + steps: + - name: Check out code + uses: actions/checkout@v1 + - name: hadolint + uses: reviewdog/action-hadolint@v1 + with: + github_token: ${{ secrets.github_token }} + reporter: github-pr-review # Default is github-pr-check + # Ignore DL3005-"Do not use apt-get upgrade or dist-upgrade" + hadolint_ignore: DL3005 + filter_mode: file From 95cacc55d97d3e996719f9024b9bb5d7a4737631 Mon Sep 17 00:00:00 2001 From: Karthik Subraveti Date: Thu, 8 Apr 2021 06:29:51 -0700 Subject: [PATCH 57/91] Scott is no longer working on Magma. Removing scott from this list. (#5977) Signed-off-by: Karthik Subraveti --- CODEOWNERS | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CODEOWNERS b/CODEOWNERS index 15c3ba51e1be..b8fceb851111 100644 --- a/CODEOWNERS +++ b/CODEOWNERS @@ -39,6 +39,6 @@ lte/gateway/Vagrantfile @mattymo @119Vik @tmdzk xwf/ @AyliD @aharonnovo @r-i-g feg/radius @AyliD @aharonnovo @r-i-g @themarwhal @mpgermano @uri200 @emakeev -nms/ @karthiksubraveti @andreilee @Scott8440 @HannaFar +nms/ @karthiksubraveti @andreilee @HannaFar CODEOWNERS @amarpad @electronjoe From fe7a998a58242ab9e1454a1580e2e04f12ed52f6 Mon Sep 17 00:00:00 2001 From: Scott Moeller Date: Thu, 8 Apr 2021 11:52:48 -0400 Subject: [PATCH 58/91] [DevExp] Add ReviewDog misspell for PR spellcheck (#5965) ## Summary Some form of spell checking would be quite helpful - but is also fraught. Here we are trying out reviewdog's [action-misspell](https://github.com/reviewdog/action-misspell) implementation of [misspell](https://github.com/client9/misspell). ## Things to Consider We may want to filter by file type, or add a list of acceptable "mis-spellings" e.g. our demon names and 3GPP terms etc. But I am finding that about 50% of the findings in e.g. Attach.c are legitimate mis-spellings in our codebase. So this seems like it might be worth turning on, we will see. Example: ![Screen Shot 2021-04-08 at 9 08 40 AM](https://user-images.githubusercontent.com/3752618/114032329-350d5480-984a-11eb-9127-a68e0ff893b4.png) Signed-off-by: Scott Harrison Moeller --- .github/workflows/misspell.yml | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) create mode 100644 .github/workflows/misspell.yml diff --git a/.github/workflows/misspell.yml b/.github/workflows/misspell.yml new file mode 100644 index 000000000000..b572f2cbef4c --- /dev/null +++ b/.github/workflows/misspell.yml @@ -0,0 +1,17 @@ +--- +name: reviewdog +on: [pull_request] # yamllint disable-line rule:truthy +jobs: + misspell: + name: runner / misspell + runs-on: ubuntu-latest + steps: + - name: Check out code. + uses: actions/checkout@v1 + - name: misspell + uses: reviewdog/action-misspell@v1 + with: + github_token: ${{ secrets.github_token }} + filter_mode: added # Any added or changed content. + reporter: github-pr-review # Post code review comments. + locale: "US" From bac782c02b4e7764e4268da5e4164b6dcab83275 Mon Sep 17 00:00:00 2001 From: Karthik Subraveti Date: Thu, 8 Apr 2021 09:02:40 -0700 Subject: [PATCH 59/91] Add Orcl(orchestrator CLI). (#5548) Signed-off-by: Karthik Subraveti --- .gitignore | 4998 +++++++++++++++++ .../deploy/orc8r_deployer/docker/Dockerfile | 71 + .../deploy/orc8r_deployer/docker/README.md | 243 + .../orc8r_deployer/docker/root/config.yml | 16 + .../orc8r_deployer/docker/root/data/vars.yml | 506 ++ .../docker/root/scripts/cli/__init__.py | 0 .../docker/root/scripts/cli/certs.py | 55 + .../docker/root/scripts/cli/cleanup.py | 96 + .../docker/root/scripts/cli/common.py | 40 + .../docker/root/scripts/cli/configlib.py | 223 + .../docker/root/scripts/cli/configlib_test.py | 242 + .../docker/root/scripts/cli/configure.py | 93 + .../docker/root/scripts/cli/install.py | 91 + .../docker/root/scripts/cli/orcl.py | 62 + .../docker/root/scripts/cli/upgrade.py | 71 + .../docker/root/scripts/cli/verify.py | 65 + .../docker/root/scripts/playbooks/certs.yml | 46 + .../docker/root/scripts/playbooks/cleanup.yml | 10 + .../docker/root/scripts/playbooks/main.yml | 10 + .../playbooks/roles/deploy/tasks/main.yml | 5 + .../playbooks/roles/deploy/tasks/secrets.yml | 20 + .../playbooks/roles/deploy/tasks/tf.yml | 287 + .../playbooks/roles/deploy/vars/all.yml | 29 + .../playbooks/roles/infra/aws/tasks/az.yml | 15 + .../playbooks/roles/infra/aws/tasks/efs.yml | 19 + .../playbooks/roles/infra/aws/tasks/eks.yml | 56 + .../playbooks/roles/infra/aws/tasks/elb.yml | 33 + .../playbooks/roles/infra/aws/tasks/main.yml | 42 + .../roles/infra/aws/tasks/route53.yml | 109 + .../roles/infra/aws/tasks/secrets.yml | 19 + .../playbooks/roles/infra/aws/tasks/vpc.yml | 157 + .../roles/platform/tasks/cloudwatch.yml | 33 + .../roles/platform/tasks/elastic.yml | 30 + .../playbooks/roles/platform/tasks/main.yml | 30 + .../playbooks/roles/platform/tasks/rds.yml | 39 + .../roles/services/orc8r/tasks/main.yml | 82 + .../playbooks/roles/services/tasks/docker.yml | 17 + .../playbooks/roles/services/tasks/helm.yml | 90 + .../playbooks/roles/services/tasks/main.yml | 27 + .../playbooks/roles/services/vars/all.yml | 4 + .../docker/root/scripts/setup.py | 28 + .../orc8r_deployer/docker/root/tf/main.tf.j2 | 54 + .../orc8r_deployer/docker/root/tf/vars.tf.j2 | 4 + .../orc8r_deployer/docker/run_deployer.bash | 75 + 44 files changed, 8242 insertions(+) create mode 100644 orc8r/cloud/deploy/orc8r_deployer/docker/Dockerfile create mode 100644 orc8r/cloud/deploy/orc8r_deployer/docker/README.md create mode 100644 orc8r/cloud/deploy/orc8r_deployer/docker/root/config.yml create mode 100644 orc8r/cloud/deploy/orc8r_deployer/docker/root/data/vars.yml create mode 100644 orc8r/cloud/deploy/orc8r_deployer/docker/root/scripts/cli/__init__.py create mode 100644 orc8r/cloud/deploy/orc8r_deployer/docker/root/scripts/cli/certs.py create mode 100644 orc8r/cloud/deploy/orc8r_deployer/docker/root/scripts/cli/cleanup.py create mode 100644 orc8r/cloud/deploy/orc8r_deployer/docker/root/scripts/cli/common.py create mode 100644 orc8r/cloud/deploy/orc8r_deployer/docker/root/scripts/cli/configlib.py create mode 100644 orc8r/cloud/deploy/orc8r_deployer/docker/root/scripts/cli/configlib_test.py create mode 100644 orc8r/cloud/deploy/orc8r_deployer/docker/root/scripts/cli/configure.py create mode 100644 orc8r/cloud/deploy/orc8r_deployer/docker/root/scripts/cli/install.py create mode 100755 orc8r/cloud/deploy/orc8r_deployer/docker/root/scripts/cli/orcl.py create mode 100644 orc8r/cloud/deploy/orc8r_deployer/docker/root/scripts/cli/upgrade.py create mode 100644 orc8r/cloud/deploy/orc8r_deployer/docker/root/scripts/cli/verify.py create mode 100644 orc8r/cloud/deploy/orc8r_deployer/docker/root/scripts/playbooks/certs.yml create mode 100644 orc8r/cloud/deploy/orc8r_deployer/docker/root/scripts/playbooks/cleanup.yml create mode 100644 orc8r/cloud/deploy/orc8r_deployer/docker/root/scripts/playbooks/main.yml create mode 100644 orc8r/cloud/deploy/orc8r_deployer/docker/root/scripts/playbooks/roles/deploy/tasks/main.yml create mode 100644 orc8r/cloud/deploy/orc8r_deployer/docker/root/scripts/playbooks/roles/deploy/tasks/secrets.yml create mode 100644 orc8r/cloud/deploy/orc8r_deployer/docker/root/scripts/playbooks/roles/deploy/tasks/tf.yml create mode 100644 orc8r/cloud/deploy/orc8r_deployer/docker/root/scripts/playbooks/roles/deploy/vars/all.yml create mode 100644 orc8r/cloud/deploy/orc8r_deployer/docker/root/scripts/playbooks/roles/infra/aws/tasks/az.yml create mode 100644 orc8r/cloud/deploy/orc8r_deployer/docker/root/scripts/playbooks/roles/infra/aws/tasks/efs.yml create mode 100644 orc8r/cloud/deploy/orc8r_deployer/docker/root/scripts/playbooks/roles/infra/aws/tasks/eks.yml create mode 100644 orc8r/cloud/deploy/orc8r_deployer/docker/root/scripts/playbooks/roles/infra/aws/tasks/elb.yml create mode 100644 orc8r/cloud/deploy/orc8r_deployer/docker/root/scripts/playbooks/roles/infra/aws/tasks/main.yml create mode 100644 orc8r/cloud/deploy/orc8r_deployer/docker/root/scripts/playbooks/roles/infra/aws/tasks/route53.yml create mode 100644 orc8r/cloud/deploy/orc8r_deployer/docker/root/scripts/playbooks/roles/infra/aws/tasks/secrets.yml create mode 100644 orc8r/cloud/deploy/orc8r_deployer/docker/root/scripts/playbooks/roles/infra/aws/tasks/vpc.yml create mode 100644 orc8r/cloud/deploy/orc8r_deployer/docker/root/scripts/playbooks/roles/platform/tasks/cloudwatch.yml create mode 100644 orc8r/cloud/deploy/orc8r_deployer/docker/root/scripts/playbooks/roles/platform/tasks/elastic.yml create mode 100644 orc8r/cloud/deploy/orc8r_deployer/docker/root/scripts/playbooks/roles/platform/tasks/main.yml create mode 100644 orc8r/cloud/deploy/orc8r_deployer/docker/root/scripts/playbooks/roles/platform/tasks/rds.yml create mode 100644 orc8r/cloud/deploy/orc8r_deployer/docker/root/scripts/playbooks/roles/services/orc8r/tasks/main.yml create mode 100644 orc8r/cloud/deploy/orc8r_deployer/docker/root/scripts/playbooks/roles/services/tasks/docker.yml create mode 100644 orc8r/cloud/deploy/orc8r_deployer/docker/root/scripts/playbooks/roles/services/tasks/helm.yml create mode 100644 orc8r/cloud/deploy/orc8r_deployer/docker/root/scripts/playbooks/roles/services/tasks/main.yml create mode 100644 orc8r/cloud/deploy/orc8r_deployer/docker/root/scripts/playbooks/roles/services/vars/all.yml create mode 100644 orc8r/cloud/deploy/orc8r_deployer/docker/root/scripts/setup.py create mode 100644 orc8r/cloud/deploy/orc8r_deployer/docker/root/tf/main.tf.j2 create mode 100644 orc8r/cloud/deploy/orc8r_deployer/docker/root/tf/vars.tf.j2 create mode 100755 orc8r/cloud/deploy/orc8r_deployer/docker/run_deployer.bash diff --git a/.gitignore b/.gitignore index 13d072b9e330..5204d9a340a0 100644 --- a/.gitignore +++ b/.gitignore @@ -106,3 +106,5001 @@ fb orc8r/cloud/coverage/ feg/gateway/coverage/ lte/gateway/c/oai/code_coverage/ +pkg/mod/* +pkg/mod/github.com/!masterminds/squirrel@v1.1.1-0.20190513200039-d13326f0be73/.gitignore +pkg/mod/github.com/!masterminds/squirrel@v1.1.1-0.20190513200039-d13326f0be73/.travis.yml +pkg/mod/github.com/!masterminds/squirrel@v1.1.1-0.20190513200039-d13326f0be73/case_test.go +pkg/mod/github.com/!masterminds/squirrel@v1.1.1-0.20190513200039-d13326f0be73/case.go +pkg/mod/github.com/!masterminds/squirrel@v1.1.1-0.20190513200039-d13326f0be73/delete_ctx_test.go +pkg/mod/github.com/!masterminds/squirrel@v1.1.1-0.20190513200039-d13326f0be73/delete_ctx.go +pkg/mod/github.com/!masterminds/squirrel@v1.1.1-0.20190513200039-d13326f0be73/delete_test.go +pkg/mod/github.com/!masterminds/squirrel@v1.1.1-0.20190513200039-d13326f0be73/delete.go +pkg/mod/github.com/!masterminds/squirrel@v1.1.1-0.20190513200039-d13326f0be73/expr_test.go +pkg/mod/github.com/!masterminds/squirrel@v1.1.1-0.20190513200039-d13326f0be73/expr.go +pkg/mod/github.com/!masterminds/squirrel@v1.1.1-0.20190513200039-d13326f0be73/go.mod +pkg/mod/github.com/!masterminds/squirrel@v1.1.1-0.20190513200039-d13326f0be73/go.sum +pkg/mod/github.com/!masterminds/squirrel@v1.1.1-0.20190513200039-d13326f0be73/insert_ctx_test.go +pkg/mod/github.com/!masterminds/squirrel@v1.1.1-0.20190513200039-d13326f0be73/insert_ctx.go +pkg/mod/github.com/!masterminds/squirrel@v1.1.1-0.20190513200039-d13326f0be73/insert_test.go +pkg/mod/github.com/!masterminds/squirrel@v1.1.1-0.20190513200039-d13326f0be73/insert.go +pkg/mod/github.com/!masterminds/squirrel@v1.1.1-0.20190513200039-d13326f0be73/integration_test.go +pkg/mod/github.com/!masterminds/squirrel@v1.1.1-0.20190513200039-d13326f0be73/LICENSE.txt +pkg/mod/github.com/!masterminds/squirrel@v1.1.1-0.20190513200039-d13326f0be73/part.go +pkg/mod/github.com/!masterminds/squirrel@v1.1.1-0.20190513200039-d13326f0be73/placeholder_test.go +pkg/mod/github.com/!masterminds/squirrel@v1.1.1-0.20190513200039-d13326f0be73/placeholder.go +pkg/mod/github.com/!masterminds/squirrel@v1.1.1-0.20190513200039-d13326f0be73/README.md +pkg/mod/github.com/!masterminds/squirrel@v1.1.1-0.20190513200039-d13326f0be73/row_test.go +pkg/mod/github.com/!masterminds/squirrel@v1.1.1-0.20190513200039-d13326f0be73/row.go +pkg/mod/github.com/!masterminds/squirrel@v1.1.1-0.20190513200039-d13326f0be73/select_ctx_test.go +pkg/mod/github.com/!masterminds/squirrel@v1.1.1-0.20190513200039-d13326f0be73/select_ctx.go +pkg/mod/github.com/!masterminds/squirrel@v1.1.1-0.20190513200039-d13326f0be73/select_test.go +pkg/mod/github.com/!masterminds/squirrel@v1.1.1-0.20190513200039-d13326f0be73/select.go +pkg/mod/github.com/!masterminds/squirrel@v1.1.1-0.20190513200039-d13326f0be73/squirrel_ctx_test.go +pkg/mod/github.com/!masterminds/squirrel@v1.1.1-0.20190513200039-d13326f0be73/squirrel_ctx.go +pkg/mod/github.com/!masterminds/squirrel@v1.1.1-0.20190513200039-d13326f0be73/squirrel_test.go +pkg/mod/github.com/!masterminds/squirrel@v1.1.1-0.20190513200039-d13326f0be73/squirrel.go +pkg/mod/github.com/!masterminds/squirrel@v1.1.1-0.20190513200039-d13326f0be73/statement_test.go +pkg/mod/github.com/!masterminds/squirrel@v1.1.1-0.20190513200039-d13326f0be73/statement.go +pkg/mod/github.com/!masterminds/squirrel@v1.1.1-0.20190513200039-d13326f0be73/stmtcacher_ctx_test.go +pkg/mod/github.com/!masterminds/squirrel@v1.1.1-0.20190513200039-d13326f0be73/stmtcacher_ctx.go +pkg/mod/github.com/!masterminds/squirrel@v1.1.1-0.20190513200039-d13326f0be73/stmtcacher_noctx.go +pkg/mod/github.com/!masterminds/squirrel@v1.1.1-0.20190513200039-d13326f0be73/stmtcacher_test.go +pkg/mod/github.com/!masterminds/squirrel@v1.1.1-0.20190513200039-d13326f0be73/stmtcacher.go +pkg/mod/github.com/!masterminds/squirrel@v1.1.1-0.20190513200039-d13326f0be73/update_ctx_test.go +pkg/mod/github.com/!masterminds/squirrel@v1.1.1-0.20190513200039-d13326f0be73/update_ctx.go +pkg/mod/github.com/!masterminds/squirrel@v1.1.1-0.20190513200039-d13326f0be73/update_test.go +pkg/mod/github.com/!masterminds/squirrel@v1.1.1-0.20190513200039-d13326f0be73/update.go +pkg/mod/github.com/!masterminds/squirrel@v1.1.1-0.20190513200039-d13326f0be73/where_test.go +pkg/mod/github.com/!masterminds/squirrel@v1.1.1-0.20190513200039-d13326f0be73/where.go +pkg/mod/github.com/!puerkito!bio/purell@v1.1.1/.gitignore +pkg/mod/github.com/!puerkito!bio/purell@v1.1.1/.travis.yml +pkg/mod/github.com/!puerkito!bio/purell@v1.1.1/bench_test.go +pkg/mod/github.com/!puerkito!bio/purell@v1.1.1/example_test.go +pkg/mod/github.com/!puerkito!bio/purell@v1.1.1/LICENSE +pkg/mod/github.com/!puerkito!bio/purell@v1.1.1/purell_test.go +pkg/mod/github.com/!puerkito!bio/purell@v1.1.1/purell.go +pkg/mod/github.com/!puerkito!bio/purell@v1.1.1/README.md +pkg/mod/github.com/!puerkito!bio/purell@v1.1.1/urlnorm_test.go +pkg/mod/github.com/!puerkito!bio/purell@v1.1.1/benchmarks/v0.1.0 +pkg/mod/github.com/!puerkito!bio/urlesc@v0.0.0-20170810143723-de5bf2ad4578/.travis.yml +pkg/mod/github.com/!puerkito!bio/urlesc@v0.0.0-20170810143723-de5bf2ad4578/LICENSE +pkg/mod/github.com/!puerkito!bio/urlesc@v0.0.0-20170810143723-de5bf2ad4578/README.md +pkg/mod/github.com/!puerkito!bio/urlesc@v0.0.0-20170810143723-de5bf2ad4578/urlesc_test.go +pkg/mod/github.com/!puerkito!bio/urlesc@v0.0.0-20170810143723-de5bf2ad4578/urlesc.go +pkg/mod/github.com/asaskevich/govalidator@v0.0.0-20190424111038-f61b66f89f4a/.travis.yml +pkg/mod/github.com/asaskevich/govalidator@v0.0.0-20190424111038-f61b66f89f4a/arrays_test.go +pkg/mod/github.com/asaskevich/govalidator@v0.0.0-20190424111038-f61b66f89f4a/arrays.go +pkg/mod/github.com/asaskevich/govalidator@v0.0.0-20190424111038-f61b66f89f4a/CONTRIBUTING.md +pkg/mod/github.com/asaskevich/govalidator@v0.0.0-20190424111038-f61b66f89f4a/converter_test.go +pkg/mod/github.com/asaskevich/govalidator@v0.0.0-20190424111038-f61b66f89f4a/converter.go +pkg/mod/github.com/asaskevich/govalidator@v0.0.0-20190424111038-f61b66f89f4a/error_test.go +pkg/mod/github.com/asaskevich/govalidator@v0.0.0-20190424111038-f61b66f89f4a/error.go +pkg/mod/github.com/asaskevich/govalidator@v0.0.0-20190424111038-f61b66f89f4a/LICENSE +pkg/mod/github.com/asaskevich/govalidator@v0.0.0-20190424111038-f61b66f89f4a/numerics_test.go +pkg/mod/github.com/asaskevich/govalidator@v0.0.0-20190424111038-f61b66f89f4a/numerics.go +pkg/mod/github.com/asaskevich/govalidator@v0.0.0-20190424111038-f61b66f89f4a/patterns.go +pkg/mod/github.com/asaskevich/govalidator@v0.0.0-20190424111038-f61b66f89f4a/README.md +pkg/mod/github.com/asaskevich/govalidator@v0.0.0-20190424111038-f61b66f89f4a/types.go +pkg/mod/github.com/asaskevich/govalidator@v0.0.0-20190424111038-f61b66f89f4a/utils_benchmark_test.go +pkg/mod/github.com/asaskevich/govalidator@v0.0.0-20190424111038-f61b66f89f4a/utils_test.go +pkg/mod/github.com/asaskevich/govalidator@v0.0.0-20190424111038-f61b66f89f4a/utils.go +pkg/mod/github.com/asaskevich/govalidator@v0.0.0-20190424111038-f61b66f89f4a/validator_test.go +pkg/mod/github.com/asaskevich/govalidator@v0.0.0-20190424111038-f61b66f89f4a/validator.go +pkg/mod/github.com/asaskevich/govalidator@v0.0.0-20190424111038-f61b66f89f4a/wercker.yml +pkg/mod/github.com/asaskevich/govalidator@v0.0.0-20190424111038-f61b66f89f4a/.circleci/config.yml +pkg/mod/github.com/asaskevich/govalidator@v0.0.0-20190424111038-f61b66f89f4a/.github/ISSUE_TEMPLATE.md +pkg/mod/github.com/beorn7/perks@v1.0.1/.gitignore +pkg/mod/github.com/beorn7/perks@v1.0.1/go.mod +pkg/mod/github.com/beorn7/perks@v1.0.1/LICENSE +pkg/mod/github.com/beorn7/perks@v1.0.1/README.md +pkg/mod/github.com/beorn7/perks@v1.0.1/VERSION +pkg/mod/github.com/beorn7/perks@v1.0.1/histogram/bench_test.go +pkg/mod/github.com/beorn7/perks@v1.0.1/histogram/histogram_test.go +pkg/mod/github.com/beorn7/perks@v1.0.1/histogram/histogram.go +pkg/mod/github.com/beorn7/perks@v1.0.1/quantile/bench_test.go +pkg/mod/github.com/beorn7/perks@v1.0.1/quantile/example_test.go +pkg/mod/github.com/beorn7/perks@v1.0.1/quantile/exampledata.txt +pkg/mod/github.com/beorn7/perks@v1.0.1/quantile/stream_test.go +pkg/mod/github.com/beorn7/perks@v1.0.1/quantile/stream.go +pkg/mod/github.com/beorn7/perks@v1.0.1/topk/topk_test.go +pkg/mod/github.com/beorn7/perks@v1.0.1/topk/topk.go +pkg/mod/github.com/cespare/xxhash/v2@v2.1.1/.travis.yml +pkg/mod/github.com/cespare/xxhash/v2@v2.1.1/go.mod +pkg/mod/github.com/cespare/xxhash/v2@v2.1.1/go.sum +pkg/mod/github.com/cespare/xxhash/v2@v2.1.1/LICENSE.txt +pkg/mod/github.com/cespare/xxhash/v2@v2.1.1/README.md +pkg/mod/github.com/cespare/xxhash/v2@v2.1.1/xxhash_amd64.go +pkg/mod/github.com/cespare/xxhash/v2@v2.1.1/xxhash_amd64.s +pkg/mod/github.com/cespare/xxhash/v2@v2.1.1/xxhash_other.go +pkg/mod/github.com/cespare/xxhash/v2@v2.1.1/xxhash_safe.go +pkg/mod/github.com/cespare/xxhash/v2@v2.1.1/xxhash_test.go +pkg/mod/github.com/cespare/xxhash/v2@v2.1.1/xxhash_unsafe_test.go +pkg/mod/github.com/cespare/xxhash/v2@v2.1.1/xxhash_unsafe.go +pkg/mod/github.com/cespare/xxhash/v2@v2.1.1/xxhash.go +pkg/mod/github.com/cespare/xxhash/v2@v2.1.1/xxhsum/.gitignore +pkg/mod/github.com/cespare/xxhash/v2@v2.1.1/xxhsum/xxhsum.go +pkg/mod/github.com/cespare/xxhash@v1.1.0/go.mod +pkg/mod/github.com/cespare/xxhash@v1.1.0/go.sum +pkg/mod/github.com/cespare/xxhash@v1.1.0/LICENSE.txt +pkg/mod/github.com/cespare/xxhash@v1.1.0/README.md +pkg/mod/github.com/cespare/xxhash@v1.1.0/rotate.go +pkg/mod/github.com/cespare/xxhash@v1.1.0/rotate19.go +pkg/mod/github.com/cespare/xxhash@v1.1.0/xxhash_amd64_test.go +pkg/mod/github.com/cespare/xxhash@v1.1.0/xxhash_amd64.go +pkg/mod/github.com/cespare/xxhash@v1.1.0/xxhash_amd64.s +pkg/mod/github.com/cespare/xxhash@v1.1.0/xxhash_other.go +pkg/mod/github.com/cespare/xxhash@v1.1.0/xxhash_safe.go +pkg/mod/github.com/cespare/xxhash@v1.1.0/xxhash_test.go +pkg/mod/github.com/cespare/xxhash@v1.1.0/xxhash_unsafe.go +pkg/mod/github.com/cespare/xxhash@v1.1.0/xxhash.go +pkg/mod/github.com/cespare/xxhash@v1.1.0/xxhsum/.gitignore +pkg/mod/github.com/cespare/xxhash@v1.1.0/xxhsum/xxhsum.go +pkg/mod/github.com/davecgh/go-spew@v1.1.1/.gitignore +pkg/mod/github.com/davecgh/go-spew@v1.1.1/.travis.yml +pkg/mod/github.com/davecgh/go-spew@v1.1.1/cov_report.sh +pkg/mod/github.com/davecgh/go-spew@v1.1.1/LICENSE +pkg/mod/github.com/davecgh/go-spew@v1.1.1/README.md +pkg/mod/github.com/davecgh/go-spew@v1.1.1/test_coverage.txt +pkg/mod/github.com/davecgh/go-spew@v1.1.1/spew/bypass.go +pkg/mod/github.com/davecgh/go-spew@v1.1.1/spew/bypasssafe.go +pkg/mod/github.com/davecgh/go-spew@v1.1.1/spew/common_test.go +pkg/mod/github.com/davecgh/go-spew@v1.1.1/spew/common.go +pkg/mod/github.com/davecgh/go-spew@v1.1.1/spew/config.go +pkg/mod/github.com/davecgh/go-spew@v1.1.1/spew/doc.go +pkg/mod/github.com/davecgh/go-spew@v1.1.1/spew/dump_test.go +pkg/mod/github.com/davecgh/go-spew@v1.1.1/spew/dump.go +pkg/mod/github.com/davecgh/go-spew@v1.1.1/spew/dumpcgo_test.go +pkg/mod/github.com/davecgh/go-spew@v1.1.1/spew/dumpnocgo_test.go +pkg/mod/github.com/davecgh/go-spew@v1.1.1/spew/example_test.go +pkg/mod/github.com/davecgh/go-spew@v1.1.1/spew/format_test.go +pkg/mod/github.com/davecgh/go-spew@v1.1.1/spew/format.go +pkg/mod/github.com/davecgh/go-spew@v1.1.1/spew/internal_test.go +pkg/mod/github.com/davecgh/go-spew@v1.1.1/spew/internalunsafe_test.go +pkg/mod/github.com/davecgh/go-spew@v1.1.1/spew/spew_test.go +pkg/mod/github.com/davecgh/go-spew@v1.1.1/spew/spew.go +pkg/mod/github.com/davecgh/go-spew@v1.1.1/spew/testdata/dumpcgo.go +pkg/mod/github.com/facebookincubator/ent@v0.0.0-20191128071424-29c7b0a0d805/.golangci.yml +pkg/mod/github.com/facebookincubator/ent@v0.0.0-20191128071424-29c7b0a0d805/CODE_OF_CONDUCT.md +pkg/mod/github.com/facebookincubator/ent@v0.0.0-20191128071424-29c7b0a0d805/CONTRIBUTING.md +pkg/mod/github.com/facebookincubator/ent@v0.0.0-20191128071424-29c7b0a0d805/ent.go +pkg/mod/github.com/facebookincubator/ent@v0.0.0-20191128071424-29c7b0a0d805/go.mod +pkg/mod/github.com/facebookincubator/ent@v0.0.0-20191128071424-29c7b0a0d805/go.sum +pkg/mod/github.com/facebookincubator/ent@v0.0.0-20191128071424-29c7b0a0d805/LICENSE +pkg/mod/github.com/facebookincubator/ent@v0.0.0-20191128071424-29c7b0a0d805/README.md +pkg/mod/github.com/facebookincubator/ent@v0.0.0-20191128071424-29c7b0a0d805/.circleci/config.yml +pkg/mod/github.com/facebookincubator/ent@v0.0.0-20191128071424-29c7b0a0d805/cmd/entc/entc.go +pkg/mod/github.com/facebookincubator/ent@v0.0.0-20191128071424-29c7b0a0d805/cmd/entc/packages_test.go +pkg/mod/github.com/facebookincubator/ent@v0.0.0-20191128071424-29c7b0a0d805/cmd/entc/packages.go +pkg/mod/github.com/facebookincubator/ent@v0.0.0-20191128071424-29c7b0a0d805/cmd/entc/print_test.go +pkg/mod/github.com/facebookincubator/ent@v0.0.0-20191128071424-29c7b0a0d805/cmd/entc/print.go +pkg/mod/github.com/facebookincubator/ent@v0.0.0-20191128071424-29c7b0a0d805/dialect/dialect.go +pkg/mod/github.com/facebookincubator/ent@v0.0.0-20191128071424-29c7b0a0d805/dialect/gremlin/client_test.go +pkg/mod/github.com/facebookincubator/ent@v0.0.0-20191128071424-29c7b0a0d805/dialect/gremlin/client.go +pkg/mod/github.com/facebookincubator/ent@v0.0.0-20191128071424-29c7b0a0d805/dialect/gremlin/config_test.go +pkg/mod/github.com/facebookincubator/ent@v0.0.0-20191128071424-29c7b0a0d805/dialect/gremlin/config.go +pkg/mod/github.com/facebookincubator/ent@v0.0.0-20191128071424-29c7b0a0d805/dialect/gremlin/driver.go +pkg/mod/github.com/facebookincubator/ent@v0.0.0-20191128071424-29c7b0a0d805/dialect/gremlin/example_test.go +pkg/mod/github.com/facebookincubator/ent@v0.0.0-20191128071424-29c7b0a0d805/dialect/gremlin/expand_test.go +pkg/mod/github.com/facebookincubator/ent@v0.0.0-20191128071424-29c7b0a0d805/dialect/gremlin/expand.go +pkg/mod/github.com/facebookincubator/ent@v0.0.0-20191128071424-29c7b0a0d805/dialect/gremlin/http_test.go +pkg/mod/github.com/facebookincubator/ent@v0.0.0-20191128071424-29c7b0a0d805/dialect/gremlin/http.go +pkg/mod/github.com/facebookincubator/ent@v0.0.0-20191128071424-29c7b0a0d805/dialect/gremlin/request_test.go +pkg/mod/github.com/facebookincubator/ent@v0.0.0-20191128071424-29c7b0a0d805/dialect/gremlin/request.go +pkg/mod/github.com/facebookincubator/ent@v0.0.0-20191128071424-29c7b0a0d805/dialect/gremlin/response_test.go +pkg/mod/github.com/facebookincubator/ent@v0.0.0-20191128071424-29c7b0a0d805/dialect/gremlin/response.go +pkg/mod/github.com/facebookincubator/ent@v0.0.0-20191128071424-29c7b0a0d805/dialect/gremlin/status_test.go +pkg/mod/github.com/facebookincubator/ent@v0.0.0-20191128071424-29c7b0a0d805/dialect/gremlin/status.go +pkg/mod/github.com/facebookincubator/ent@v0.0.0-20191128071424-29c7b0a0d805/dialect/gremlin/tokens.go +pkg/mod/github.com/facebookincubator/ent@v0.0.0-20191128071424-29c7b0a0d805/dialect/gremlin/encoding/mime_test.go +pkg/mod/github.com/facebookincubator/ent@v0.0.0-20191128071424-29c7b0a0d805/dialect/gremlin/encoding/mime.go +pkg/mod/github.com/facebookincubator/ent@v0.0.0-20191128071424-29c7b0a0d805/dialect/gremlin/encoding/graphson/bench_test.go +pkg/mod/github.com/facebookincubator/ent@v0.0.0-20191128071424-29c7b0a0d805/dialect/gremlin/encoding/graphson/common_test.go +pkg/mod/github.com/facebookincubator/ent@v0.0.0-20191128071424-29c7b0a0d805/dialect/gremlin/encoding/graphson/decode_test.go +pkg/mod/github.com/facebookincubator/ent@v0.0.0-20191128071424-29c7b0a0d805/dialect/gremlin/encoding/graphson/decode.go +pkg/mod/github.com/facebookincubator/ent@v0.0.0-20191128071424-29c7b0a0d805/dialect/gremlin/encoding/graphson/encode_test.go +pkg/mod/github.com/facebookincubator/ent@v0.0.0-20191128071424-29c7b0a0d805/dialect/gremlin/encoding/graphson/encode.go +pkg/mod/github.com/facebookincubator/ent@v0.0.0-20191128071424-29c7b0a0d805/dialect/gremlin/encoding/graphson/error_test.go +pkg/mod/github.com/facebookincubator/ent@v0.0.0-20191128071424-29c7b0a0d805/dialect/gremlin/encoding/graphson/error.go +pkg/mod/github.com/facebookincubator/ent@v0.0.0-20191128071424-29c7b0a0d805/dialect/gremlin/encoding/graphson/extension.go +pkg/mod/github.com/facebookincubator/ent@v0.0.0-20191128071424-29c7b0a0d805/dialect/gremlin/encoding/graphson/init.go +pkg/mod/github.com/facebookincubator/ent@v0.0.0-20191128071424-29c7b0a0d805/dialect/gremlin/encoding/graphson/interface_test.go +pkg/mod/github.com/facebookincubator/ent@v0.0.0-20191128071424-29c7b0a0d805/dialect/gremlin/encoding/graphson/interface.go +pkg/mod/github.com/facebookincubator/ent@v0.0.0-20191128071424-29c7b0a0d805/dialect/gremlin/encoding/graphson/lazy_test.go +pkg/mod/github.com/facebookincubator/ent@v0.0.0-20191128071424-29c7b0a0d805/dialect/gremlin/encoding/graphson/lazy.go +pkg/mod/github.com/facebookincubator/ent@v0.0.0-20191128071424-29c7b0a0d805/dialect/gremlin/encoding/graphson/map_test.go +pkg/mod/github.com/facebookincubator/ent@v0.0.0-20191128071424-29c7b0a0d805/dialect/gremlin/encoding/graphson/map.go +pkg/mod/github.com/facebookincubator/ent@v0.0.0-20191128071424-29c7b0a0d805/dialect/gremlin/encoding/graphson/marshaler_test.go +pkg/mod/github.com/facebookincubator/ent@v0.0.0-20191128071424-29c7b0a0d805/dialect/gremlin/encoding/graphson/marshaler.go +pkg/mod/github.com/facebookincubator/ent@v0.0.0-20191128071424-29c7b0a0d805/dialect/gremlin/encoding/graphson/native_test.go +pkg/mod/github.com/facebookincubator/ent@v0.0.0-20191128071424-29c7b0a0d805/dialect/gremlin/encoding/graphson/native.go +pkg/mod/github.com/facebookincubator/ent@v0.0.0-20191128071424-29c7b0a0d805/dialect/gremlin/encoding/graphson/raw_test.go +pkg/mod/github.com/facebookincubator/ent@v0.0.0-20191128071424-29c7b0a0d805/dialect/gremlin/encoding/graphson/raw.go +pkg/mod/github.com/facebookincubator/ent@v0.0.0-20191128071424-29c7b0a0d805/dialect/gremlin/encoding/graphson/slice_test.go +pkg/mod/github.com/facebookincubator/ent@v0.0.0-20191128071424-29c7b0a0d805/dialect/gremlin/encoding/graphson/slice.go +pkg/mod/github.com/facebookincubator/ent@v0.0.0-20191128071424-29c7b0a0d805/dialect/gremlin/encoding/graphson/struct_test.go +pkg/mod/github.com/facebookincubator/ent@v0.0.0-20191128071424-29c7b0a0d805/dialect/gremlin/encoding/graphson/struct.go +pkg/mod/github.com/facebookincubator/ent@v0.0.0-20191128071424-29c7b0a0d805/dialect/gremlin/encoding/graphson/tags_test.go +pkg/mod/github.com/facebookincubator/ent@v0.0.0-20191128071424-29c7b0a0d805/dialect/gremlin/encoding/graphson/tags.go +pkg/mod/github.com/facebookincubator/ent@v0.0.0-20191128071424-29c7b0a0d805/dialect/gremlin/encoding/graphson/time_test.go +pkg/mod/github.com/facebookincubator/ent@v0.0.0-20191128071424-29c7b0a0d805/dialect/gremlin/encoding/graphson/time.go +pkg/mod/github.com/facebookincubator/ent@v0.0.0-20191128071424-29c7b0a0d805/dialect/gremlin/encoding/graphson/type_test.go +pkg/mod/github.com/facebookincubator/ent@v0.0.0-20191128071424-29c7b0a0d805/dialect/gremlin/encoding/graphson/type.go +pkg/mod/github.com/facebookincubator/ent@v0.0.0-20191128071424-29c7b0a0d805/dialect/gremlin/encoding/graphson/util_test.go +pkg/mod/github.com/facebookincubator/ent@v0.0.0-20191128071424-29c7b0a0d805/dialect/gremlin/encoding/graphson/util.go +pkg/mod/github.com/facebookincubator/ent@v0.0.0-20191128071424-29c7b0a0d805/dialect/gremlin/graph/edge_test.go +pkg/mod/github.com/facebookincubator/ent@v0.0.0-20191128071424-29c7b0a0d805/dialect/gremlin/graph/edge.go +pkg/mod/github.com/facebookincubator/ent@v0.0.0-20191128071424-29c7b0a0d805/dialect/gremlin/graph/element.go +pkg/mod/github.com/facebookincubator/ent@v0.0.0-20191128071424-29c7b0a0d805/dialect/gremlin/graph/valuemap_test.go +pkg/mod/github.com/facebookincubator/ent@v0.0.0-20191128071424-29c7b0a0d805/dialect/gremlin/graph/valuemap.go +pkg/mod/github.com/facebookincubator/ent@v0.0.0-20191128071424-29c7b0a0d805/dialect/gremlin/graph/vertex_test.go +pkg/mod/github.com/facebookincubator/ent@v0.0.0-20191128071424-29c7b0a0d805/dialect/gremlin/graph/vertex.go +pkg/mod/github.com/facebookincubator/ent@v0.0.0-20191128071424-29c7b0a0d805/dialect/gremlin/graph/dsl/dsl_test.go +pkg/mod/github.com/facebookincubator/ent@v0.0.0-20191128071424-29c7b0a0d805/dialect/gremlin/graph/dsl/dsl.go +pkg/mod/github.com/facebookincubator/ent@v0.0.0-20191128071424-29c7b0a0d805/dialect/gremlin/graph/dsl/traversal.go +pkg/mod/github.com/facebookincubator/ent@v0.0.0-20191128071424-29c7b0a0d805/dialect/gremlin/graph/dsl/__/dsl.go +pkg/mod/github.com/facebookincubator/ent@v0.0.0-20191128071424-29c7b0a0d805/dialect/gremlin/graph/dsl/g/g.go +pkg/mod/github.com/facebookincubator/ent@v0.0.0-20191128071424-29c7b0a0d805/dialect/gremlin/graph/dsl/p/p.go +pkg/mod/github.com/facebookincubator/ent@v0.0.0-20191128071424-29c7b0a0d805/dialect/gremlin/internal/ws/conn_test.go +pkg/mod/github.com/facebookincubator/ent@v0.0.0-20191128071424-29c7b0a0d805/dialect/gremlin/internal/ws/conn.go +pkg/mod/github.com/facebookincubator/ent@v0.0.0-20191128071424-29c7b0a0d805/dialect/gremlin/ocgremlin/client_test.go +pkg/mod/github.com/facebookincubator/ent@v0.0.0-20191128071424-29c7b0a0d805/dialect/gremlin/ocgremlin/client.go +pkg/mod/github.com/facebookincubator/ent@v0.0.0-20191128071424-29c7b0a0d805/dialect/gremlin/ocgremlin/stats_test.go +pkg/mod/github.com/facebookincubator/ent@v0.0.0-20191128071424-29c7b0a0d805/dialect/gremlin/ocgremlin/stats.go +pkg/mod/github.com/facebookincubator/ent@v0.0.0-20191128071424-29c7b0a0d805/dialect/gremlin/ocgremlin/trace_test.go +pkg/mod/github.com/facebookincubator/ent@v0.0.0-20191128071424-29c7b0a0d805/dialect/gremlin/ocgremlin/trace.go +pkg/mod/github.com/facebookincubator/ent@v0.0.0-20191128071424-29c7b0a0d805/dialect/sql/builder_test.go +pkg/mod/github.com/facebookincubator/ent@v0.0.0-20191128071424-29c7b0a0d805/dialect/sql/builder.go +pkg/mod/github.com/facebookincubator/ent@v0.0.0-20191128071424-29c7b0a0d805/dialect/sql/driver.go +pkg/mod/github.com/facebookincubator/ent@v0.0.0-20191128071424-29c7b0a0d805/dialect/sql/graph_test.go +pkg/mod/github.com/facebookincubator/ent@v0.0.0-20191128071424-29c7b0a0d805/dialect/sql/graph.go +pkg/mod/github.com/facebookincubator/ent@v0.0.0-20191128071424-29c7b0a0d805/dialect/sql/scan_test.go +pkg/mod/github.com/facebookincubator/ent@v0.0.0-20191128071424-29c7b0a0d805/dialect/sql/scan.go +pkg/mod/github.com/facebookincubator/ent@v0.0.0-20191128071424-29c7b0a0d805/dialect/sql/sql_112.go +pkg/mod/github.com/facebookincubator/ent@v0.0.0-20191128071424-29c7b0a0d805/dialect/sql/sql_113.go +pkg/mod/github.com/facebookincubator/ent@v0.0.0-20191128071424-29c7b0a0d805/dialect/sql/schema/migrate.go +pkg/mod/github.com/facebookincubator/ent@v0.0.0-20191128071424-29c7b0a0d805/dialect/sql/schema/mysql_test.go +pkg/mod/github.com/facebookincubator/ent@v0.0.0-20191128071424-29c7b0a0d805/dialect/sql/schema/mysql.go +pkg/mod/github.com/facebookincubator/ent@v0.0.0-20191128071424-29c7b0a0d805/dialect/sql/schema/postgres_test.go +pkg/mod/github.com/facebookincubator/ent@v0.0.0-20191128071424-29c7b0a0d805/dialect/sql/schema/postgres.go +pkg/mod/github.com/facebookincubator/ent@v0.0.0-20191128071424-29c7b0a0d805/dialect/sql/schema/schema_test.go +pkg/mod/github.com/facebookincubator/ent@v0.0.0-20191128071424-29c7b0a0d805/dialect/sql/schema/schema.go +pkg/mod/github.com/facebookincubator/ent@v0.0.0-20191128071424-29c7b0a0d805/dialect/sql/schema/sqlite_test.go +pkg/mod/github.com/facebookincubator/ent@v0.0.0-20191128071424-29c7b0a0d805/dialect/sql/schema/sqlite.go +pkg/mod/github.com/facebookincubator/ent@v0.0.0-20191128071424-29c7b0a0d805/dialect/sql/schema/writer_test.go +pkg/mod/github.com/facebookincubator/ent@v0.0.0-20191128071424-29c7b0a0d805/dialect/sql/schema/writer.go +pkg/mod/github.com/facebookincubator/ent@v0.0.0-20191128071424-29c7b0a0d805/doc/.gitignore +pkg/mod/github.com/facebookincubator/ent@v0.0.0-20191128071424-29c7b0a0d805/doc/md/aggregate.md +pkg/mod/github.com/facebookincubator/ent@v0.0.0-20191128071424-29c7b0a0d805/doc/md/code-gen.md +pkg/mod/github.com/facebookincubator/ent@v0.0.0-20191128071424-29c7b0a0d805/doc/md/crud.md +pkg/mod/github.com/facebookincubator/ent@v0.0.0-20191128071424-29c7b0a0d805/doc/md/dialects.md +pkg/mod/github.com/facebookincubator/ent@v0.0.0-20191128071424-29c7b0a0d805/doc/md/getting-started.md +pkg/mod/github.com/facebookincubator/ent@v0.0.0-20191128071424-29c7b0a0d805/doc/md/migrate.md +pkg/mod/github.com/facebookincubator/ent@v0.0.0-20191128071424-29c7b0a0d805/doc/md/paging.md +pkg/mod/github.com/facebookincubator/ent@v0.0.0-20191128071424-29c7b0a0d805/doc/md/predicates.md +pkg/mod/github.com/facebookincubator/ent@v0.0.0-20191128071424-29c7b0a0d805/doc/md/schema-config.md +pkg/mod/github.com/facebookincubator/ent@v0.0.0-20191128071424-29c7b0a0d805/doc/md/schema-def.md +pkg/mod/github.com/facebookincubator/ent@v0.0.0-20191128071424-29c7b0a0d805/doc/md/schema-edges.md +pkg/mod/github.com/facebookincubator/ent@v0.0.0-20191128071424-29c7b0a0d805/doc/md/schema-fields.md +pkg/mod/github.com/facebookincubator/ent@v0.0.0-20191128071424-29c7b0a0d805/doc/md/schema-indexes.md +pkg/mod/github.com/facebookincubator/ent@v0.0.0-20191128071424-29c7b0a0d805/doc/md/schema-mixin.md +pkg/mod/github.com/facebookincubator/ent@v0.0.0-20191128071424-29c7b0a0d805/doc/md/sql-integration.md +pkg/mod/github.com/facebookincubator/ent@v0.0.0-20191128071424-29c7b0a0d805/doc/md/transactions.md +pkg/mod/github.com/facebookincubator/ent@v0.0.0-20191128071424-29c7b0a0d805/doc/md/traversals.md +pkg/mod/github.com/facebookincubator/ent@v0.0.0-20191128071424-29c7b0a0d805/doc/website/package.json +pkg/mod/github.com/facebookincubator/ent@v0.0.0-20191128071424-29c7b0a0d805/doc/website/README.md +pkg/mod/github.com/facebookincubator/ent@v0.0.0-20191128071424-29c7b0a0d805/doc/website/sidebars.json +pkg/mod/github.com/facebookincubator/ent@v0.0.0-20191128071424-29c7b0a0d805/doc/website/siteConfig.js +pkg/mod/github.com/facebookincubator/ent@v0.0.0-20191128071424-29c7b0a0d805/doc/website/blog/2019-10-03-introducing-ent.md +pkg/mod/github.com/facebookincubator/ent@v0.0.0-20191128071424-29c7b0a0d805/doc/website/core/Footer.js +pkg/mod/github.com/facebookincubator/ent@v0.0.0-20191128071424-29c7b0a0d805/doc/website/pages/en/index.js +pkg/mod/github.com/facebookincubator/ent@v0.0.0-20191128071424-29c7b0a0d805/doc/website/static/css/code-block-buttons.css +pkg/mod/github.com/facebookincubator/ent@v0.0.0-20191128071424-29c7b0a0d805/doc/website/static/css/custom.css +pkg/mod/github.com/facebookincubator/ent@v0.0.0-20191128071424-29c7b0a0d805/doc/website/static/font/CalibreBold.woff +pkg/mod/github.com/facebookincubator/ent@v0.0.0-20191128071424-29c7b0a0d805/doc/website/static/font/CalibreLight.woff +pkg/mod/github.com/facebookincubator/ent@v0.0.0-20191128071424-29c7b0a0d805/doc/website/static/font/CalibreLightItalic.woff +pkg/mod/github.com/facebookincubator/ent@v0.0.0-20191128071424-29c7b0a0d805/doc/website/static/font/CalibreMedium.woff +pkg/mod/github.com/facebookincubator/ent@v0.0.0-20191128071424-29c7b0a0d805/doc/website/static/font/CalibreMediumItalic.woff +pkg/mod/github.com/facebookincubator/ent@v0.0.0-20191128071424-29c7b0a0d805/doc/website/static/font/CalibreRegular.woff +pkg/mod/github.com/facebookincubator/ent@v0.0.0-20191128071424-29c7b0a0d805/doc/website/static/font/CalibreRegularItalic.woff +pkg/mod/github.com/facebookincubator/ent@v0.0.0-20191128071424-29c7b0a0d805/doc/website/static/font/CalibreSemibold.woff +pkg/mod/github.com/facebookincubator/ent@v0.0.0-20191128071424-29c7b0a0d805/doc/website/static/font/CalibreThin.woff +pkg/mod/github.com/facebookincubator/ent@v0.0.0-20191128071424-29c7b0a0d805/doc/website/static/font/CalibreThinItalic.woff +pkg/mod/github.com/facebookincubator/ent@v0.0.0-20191128071424-29c7b0a0d805/doc/website/static/img/favicon.ico +pkg/mod/github.com/facebookincubator/ent@v0.0.0-20191128071424-29c7b0a0d805/doc/website/static/img/logo.png +pkg/mod/github.com/facebookincubator/ent@v0.0.0-20191128071424-29c7b0a0d805/doc/website/static/img/oss_logo.png +pkg/mod/github.com/facebookincubator/ent@v0.0.0-20191128071424-29c7b0a0d805/doc/website/static/js/code-block-buttons.js +pkg/mod/github.com/facebookincubator/ent@v0.0.0-20191128071424-29c7b0a0d805/doc/website/static/js/custom.js +pkg/mod/github.com/facebookincubator/ent@v0.0.0-20191128071424-29c7b0a0d805/entc/entc.go +pkg/mod/github.com/facebookincubator/ent@v0.0.0-20191128071424-29c7b0a0d805/entc/tools.go +pkg/mod/github.com/facebookincubator/ent@v0.0.0-20191128071424-29c7b0a0d805/entc/gen/func.go +pkg/mod/github.com/facebookincubator/ent@v0.0.0-20191128071424-29c7b0a0d805/entc/gen/graph_test.go +pkg/mod/github.com/facebookincubator/ent@v0.0.0-20191128071424-29c7b0a0d805/entc/gen/graph.go +pkg/mod/github.com/facebookincubator/ent@v0.0.0-20191128071424-29c7b0a0d805/entc/gen/predicate.go +pkg/mod/github.com/facebookincubator/ent@v0.0.0-20191128071424-29c7b0a0d805/entc/gen/storage.go +pkg/mod/github.com/facebookincubator/ent@v0.0.0-20191128071424-29c7b0a0d805/entc/gen/template.go +pkg/mod/github.com/facebookincubator/ent@v0.0.0-20191128071424-29c7b0a0d805/entc/gen/type_test.go +pkg/mod/github.com/facebookincubator/ent@v0.0.0-20191128071424-29c7b0a0d805/entc/gen/type.go +pkg/mod/github.com/facebookincubator/ent@v0.0.0-20191128071424-29c7b0a0d805/entc/gen/internal/bindata.go +pkg/mod/github.com/facebookincubator/ent@v0.0.0-20191128071424-29c7b0a0d805/entc/gen/template/base.tmpl +pkg/mod/github.com/facebookincubator/ent@v0.0.0-20191128071424-29c7b0a0d805/entc/gen/template/client.tmpl +pkg/mod/github.com/facebookincubator/ent@v0.0.0-20191128071424-29c7b0a0d805/entc/gen/template/config.tmpl +pkg/mod/github.com/facebookincubator/ent@v0.0.0-20191128071424-29c7b0a0d805/entc/gen/template/context.tmpl +pkg/mod/github.com/facebookincubator/ent@v0.0.0-20191128071424-29c7b0a0d805/entc/gen/template/ent.tmpl +pkg/mod/github.com/facebookincubator/ent@v0.0.0-20191128071424-29c7b0a0d805/entc/gen/template/example.tmpl +pkg/mod/github.com/facebookincubator/ent@v0.0.0-20191128071424-29c7b0a0d805/entc/gen/template/header.tmpl +pkg/mod/github.com/facebookincubator/ent@v0.0.0-20191128071424-29c7b0a0d805/entc/gen/template/import.tmpl +pkg/mod/github.com/facebookincubator/ent@v0.0.0-20191128071424-29c7b0a0d805/entc/gen/template/meta.tmpl +pkg/mod/github.com/facebookincubator/ent@v0.0.0-20191128071424-29c7b0a0d805/entc/gen/template/predicate.tmpl +pkg/mod/github.com/facebookincubator/ent@v0.0.0-20191128071424-29c7b0a0d805/entc/gen/template/tx.tmpl +pkg/mod/github.com/facebookincubator/ent@v0.0.0-20191128071424-29c7b0a0d805/entc/gen/template/where.tmpl +pkg/mod/github.com/facebookincubator/ent@v0.0.0-20191128071424-29c7b0a0d805/entc/gen/template/builder/create.tmpl +pkg/mod/github.com/facebookincubator/ent@v0.0.0-20191128071424-29c7b0a0d805/entc/gen/template/builder/delete.tmpl +pkg/mod/github.com/facebookincubator/ent@v0.0.0-20191128071424-29c7b0a0d805/entc/gen/template/builder/query.tmpl +pkg/mod/github.com/facebookincubator/ent@v0.0.0-20191128071424-29c7b0a0d805/entc/gen/template/builder/setter.tmpl +pkg/mod/github.com/facebookincubator/ent@v0.0.0-20191128071424-29c7b0a0d805/entc/gen/template/builder/update.tmpl +pkg/mod/github.com/facebookincubator/ent@v0.0.0-20191128071424-29c7b0a0d805/entc/gen/template/dialect/gremlin/by.tmpl +pkg/mod/github.com/facebookincubator/ent@v0.0.0-20191128071424-29c7b0a0d805/entc/gen/template/dialect/gremlin/create.tmpl +pkg/mod/github.com/facebookincubator/ent@v0.0.0-20191128071424-29c7b0a0d805/entc/gen/template/dialect/gremlin/decode.tmpl +pkg/mod/github.com/facebookincubator/ent@v0.0.0-20191128071424-29c7b0a0d805/entc/gen/template/dialect/gremlin/delete.tmpl +pkg/mod/github.com/facebookincubator/ent@v0.0.0-20191128071424-29c7b0a0d805/entc/gen/template/dialect/gremlin/errors.tmpl +pkg/mod/github.com/facebookincubator/ent@v0.0.0-20191128071424-29c7b0a0d805/entc/gen/template/dialect/gremlin/globals.tmpl +pkg/mod/github.com/facebookincubator/ent@v0.0.0-20191128071424-29c7b0a0d805/entc/gen/template/dialect/gremlin/group.tmpl +pkg/mod/github.com/facebookincubator/ent@v0.0.0-20191128071424-29c7b0a0d805/entc/gen/template/dialect/gremlin/meta.tmpl +pkg/mod/github.com/facebookincubator/ent@v0.0.0-20191128071424-29c7b0a0d805/entc/gen/template/dialect/gremlin/open.tmpl +pkg/mod/github.com/facebookincubator/ent@v0.0.0-20191128071424-29c7b0a0d805/entc/gen/template/dialect/gremlin/predicate.tmpl +pkg/mod/github.com/facebookincubator/ent@v0.0.0-20191128071424-29c7b0a0d805/entc/gen/template/dialect/gremlin/query.tmpl +pkg/mod/github.com/facebookincubator/ent@v0.0.0-20191128071424-29c7b0a0d805/entc/gen/template/dialect/gremlin/select.tmpl +pkg/mod/github.com/facebookincubator/ent@v0.0.0-20191128071424-29c7b0a0d805/entc/gen/template/dialect/gremlin/update.tmpl +pkg/mod/github.com/facebookincubator/ent@v0.0.0-20191128071424-29c7b0a0d805/entc/gen/template/dialect/sql/by.tmpl +pkg/mod/github.com/facebookincubator/ent@v0.0.0-20191128071424-29c7b0a0d805/entc/gen/template/dialect/sql/create.tmpl +pkg/mod/github.com/facebookincubator/ent@v0.0.0-20191128071424-29c7b0a0d805/entc/gen/template/dialect/sql/decode.tmpl +pkg/mod/github.com/facebookincubator/ent@v0.0.0-20191128071424-29c7b0a0d805/entc/gen/template/dialect/sql/delete.tmpl +pkg/mod/github.com/facebookincubator/ent@v0.0.0-20191128071424-29c7b0a0d805/entc/gen/template/dialect/sql/errors.tmpl +pkg/mod/github.com/facebookincubator/ent@v0.0.0-20191128071424-29c7b0a0d805/entc/gen/template/dialect/sql/globals.tmpl +pkg/mod/github.com/facebookincubator/ent@v0.0.0-20191128071424-29c7b0a0d805/entc/gen/template/dialect/sql/group.tmpl +pkg/mod/github.com/facebookincubator/ent@v0.0.0-20191128071424-29c7b0a0d805/entc/gen/template/dialect/sql/meta.tmpl +pkg/mod/github.com/facebookincubator/ent@v0.0.0-20191128071424-29c7b0a0d805/entc/gen/template/dialect/sql/open.tmpl +pkg/mod/github.com/facebookincubator/ent@v0.0.0-20191128071424-29c7b0a0d805/entc/gen/template/dialect/sql/predicate.tmpl +pkg/mod/github.com/facebookincubator/ent@v0.0.0-20191128071424-29c7b0a0d805/entc/gen/template/dialect/sql/query.tmpl +pkg/mod/github.com/facebookincubator/ent@v0.0.0-20191128071424-29c7b0a0d805/entc/gen/template/dialect/sql/select.tmpl +pkg/mod/github.com/facebookincubator/ent@v0.0.0-20191128071424-29c7b0a0d805/entc/gen/template/dialect/sql/update.tmpl +pkg/mod/github.com/facebookincubator/ent@v0.0.0-20191128071424-29c7b0a0d805/entc/gen/template/migrate/migrate.tmpl +pkg/mod/github.com/facebookincubator/ent@v0.0.0-20191128071424-29c7b0a0d805/entc/gen/template/migrate/schema.tmpl +pkg/mod/github.com/facebookincubator/ent@v0.0.0-20191128071424-29c7b0a0d805/entc/integration/generate.go +pkg/mod/github.com/facebookincubator/ent@v0.0.0-20191128071424-29c7b0a0d805/entc/integration/index_test.go +pkg/mod/github.com/facebookincubator/ent@v0.0.0-20191128071424-29c7b0a0d805/entc/integration/integration_test.go +pkg/mod/github.com/facebookincubator/ent@v0.0.0-20191128071424-29c7b0a0d805/entc/integration/README.md +pkg/mod/github.com/facebookincubator/ent@v0.0.0-20191128071424-29c7b0a0d805/entc/integration/relation_test.go +pkg/mod/github.com/facebookincubator/ent@v0.0.0-20191128071424-29c7b0a0d805/entc/integration/type_test.go +pkg/mod/github.com/facebookincubator/ent@v0.0.0-20191128071424-29c7b0a0d805/entc/integration/compose/docker-compose.yaml +pkg/mod/github.com/facebookincubator/ent@v0.0.0-20191128071424-29c7b0a0d805/entc/integration/compose/Dockerfile +pkg/mod/github.com/facebookincubator/ent@v0.0.0-20191128071424-29c7b0a0d805/entc/integration/compose/gremlin-server/Dockerfile +pkg/mod/github.com/facebookincubator/ent@v0.0.0-20191128071424-29c7b0a0d805/entc/integration/compose/gremlin-server/gremlin-server.yaml +pkg/mod/github.com/facebookincubator/ent@v0.0.0-20191128071424-29c7b0a0d805/entc/integration/compose/gremlin-server/tinkergraph-empty.properties +pkg/mod/github.com/facebookincubator/ent@v0.0.0-20191128071424-29c7b0a0d805/entc/integration/config/config_test.go +pkg/mod/github.com/facebookincubator/ent@v0.0.0-20191128071424-29c7b0a0d805/entc/integration/config/ent/client.go +pkg/mod/github.com/facebookincubator/ent@v0.0.0-20191128071424-29c7b0a0d805/entc/integration/config/ent/config.go +pkg/mod/github.com/facebookincubator/ent@v0.0.0-20191128071424-29c7b0a0d805/entc/integration/config/ent/context.go +pkg/mod/github.com/facebookincubator/ent@v0.0.0-20191128071424-29c7b0a0d805/entc/integration/config/ent/ent.go +pkg/mod/github.com/facebookincubator/ent@v0.0.0-20191128071424-29c7b0a0d805/entc/integration/config/ent/example_test.go +pkg/mod/github.com/facebookincubator/ent@v0.0.0-20191128071424-29c7b0a0d805/entc/integration/config/ent/tx.go +pkg/mod/github.com/facebookincubator/ent@v0.0.0-20191128071424-29c7b0a0d805/entc/integration/config/ent/user_create.go +pkg/mod/github.com/facebookincubator/ent@v0.0.0-20191128071424-29c7b0a0d805/entc/integration/config/ent/user_delete.go +pkg/mod/github.com/facebookincubator/ent@v0.0.0-20191128071424-29c7b0a0d805/entc/integration/config/ent/user_query.go +pkg/mod/github.com/facebookincubator/ent@v0.0.0-20191128071424-29c7b0a0d805/entc/integration/config/ent/user_update.go +pkg/mod/github.com/facebookincubator/ent@v0.0.0-20191128071424-29c7b0a0d805/entc/integration/config/ent/user.go +pkg/mod/github.com/facebookincubator/ent@v0.0.0-20191128071424-29c7b0a0d805/entc/integration/config/ent/migrate/migrate.go +pkg/mod/github.com/facebookincubator/ent@v0.0.0-20191128071424-29c7b0a0d805/entc/integration/config/ent/migrate/schema.go +pkg/mod/github.com/facebookincubator/ent@v0.0.0-20191128071424-29c7b0a0d805/entc/integration/config/ent/predicate/predicate.go +pkg/mod/github.com/facebookincubator/ent@v0.0.0-20191128071424-29c7b0a0d805/entc/integration/config/ent/schema/user.go +pkg/mod/github.com/facebookincubator/ent@v0.0.0-20191128071424-29c7b0a0d805/entc/integration/config/ent/user/user.go +pkg/mod/github.com/facebookincubator/ent@v0.0.0-20191128071424-29c7b0a0d805/entc/integration/config/ent/user/where.go +pkg/mod/github.com/facebookincubator/ent@v0.0.0-20191128071424-29c7b0a0d805/entc/integration/customid/customid_test.go +pkg/mod/github.com/facebookincubator/ent@v0.0.0-20191128071424-29c7b0a0d805/entc/integration/customid/ent/blob_create.go +pkg/mod/github.com/facebookincubator/ent@v0.0.0-20191128071424-29c7b0a0d805/entc/integration/customid/ent/blob_delete.go +pkg/mod/github.com/facebookincubator/ent@v0.0.0-20191128071424-29c7b0a0d805/entc/integration/customid/ent/blob_query.go +pkg/mod/github.com/facebookincubator/ent@v0.0.0-20191128071424-29c7b0a0d805/entc/integration/customid/ent/blob_update.go +pkg/mod/github.com/facebookincubator/ent@v0.0.0-20191128071424-29c7b0a0d805/entc/integration/customid/ent/blob.go +pkg/mod/github.com/facebookincubator/ent@v0.0.0-20191128071424-29c7b0a0d805/entc/integration/customid/ent/client.go +pkg/mod/github.com/facebookincubator/ent@v0.0.0-20191128071424-29c7b0a0d805/entc/integration/customid/ent/config.go +pkg/mod/github.com/facebookincubator/ent@v0.0.0-20191128071424-29c7b0a0d805/entc/integration/customid/ent/context.go +pkg/mod/github.com/facebookincubator/ent@v0.0.0-20191128071424-29c7b0a0d805/entc/integration/customid/ent/ent.go +pkg/mod/github.com/facebookincubator/ent@v0.0.0-20191128071424-29c7b0a0d805/entc/integration/customid/ent/example_test.go +pkg/mod/github.com/facebookincubator/ent@v0.0.0-20191128071424-29c7b0a0d805/entc/integration/customid/ent/group_create.go +pkg/mod/github.com/facebookincubator/ent@v0.0.0-20191128071424-29c7b0a0d805/entc/integration/customid/ent/group_delete.go +pkg/mod/github.com/facebookincubator/ent@v0.0.0-20191128071424-29c7b0a0d805/entc/integration/customid/ent/group_query.go +pkg/mod/github.com/facebookincubator/ent@v0.0.0-20191128071424-29c7b0a0d805/entc/integration/customid/ent/group_update.go +pkg/mod/github.com/facebookincubator/ent@v0.0.0-20191128071424-29c7b0a0d805/entc/integration/customid/ent/group.go +pkg/mod/github.com/facebookincubator/ent@v0.0.0-20191128071424-29c7b0a0d805/entc/integration/customid/ent/tx.go +pkg/mod/github.com/facebookincubator/ent@v0.0.0-20191128071424-29c7b0a0d805/entc/integration/customid/ent/user_create.go +pkg/mod/github.com/facebookincubator/ent@v0.0.0-20191128071424-29c7b0a0d805/entc/integration/customid/ent/user_delete.go +pkg/mod/github.com/facebookincubator/ent@v0.0.0-20191128071424-29c7b0a0d805/entc/integration/customid/ent/user_query.go +pkg/mod/github.com/facebookincubator/ent@v0.0.0-20191128071424-29c7b0a0d805/entc/integration/customid/ent/user_update.go +pkg/mod/github.com/facebookincubator/ent@v0.0.0-20191128071424-29c7b0a0d805/entc/integration/customid/ent/user.go +pkg/mod/github.com/facebookincubator/ent@v0.0.0-20191128071424-29c7b0a0d805/entc/integration/customid/ent/blob/blob.go +pkg/mod/github.com/facebookincubator/ent@v0.0.0-20191128071424-29c7b0a0d805/entc/integration/customid/ent/blob/where.go +pkg/mod/github.com/facebookincubator/ent@v0.0.0-20191128071424-29c7b0a0d805/entc/integration/customid/ent/group/group.go +pkg/mod/github.com/facebookincubator/ent@v0.0.0-20191128071424-29c7b0a0d805/entc/integration/customid/ent/group/where.go +pkg/mod/github.com/facebookincubator/ent@v0.0.0-20191128071424-29c7b0a0d805/entc/integration/customid/ent/migrate/migrate.go +pkg/mod/github.com/facebookincubator/ent@v0.0.0-20191128071424-29c7b0a0d805/entc/integration/customid/ent/migrate/schema.go +pkg/mod/github.com/facebookincubator/ent@v0.0.0-20191128071424-29c7b0a0d805/entc/integration/customid/ent/predicate/predicate.go +pkg/mod/github.com/facebookincubator/ent@v0.0.0-20191128071424-29c7b0a0d805/entc/integration/customid/ent/schema/blob.go +pkg/mod/github.com/facebookincubator/ent@v0.0.0-20191128071424-29c7b0a0d805/entc/integration/customid/ent/schema/group.go +pkg/mod/github.com/facebookincubator/ent@v0.0.0-20191128071424-29c7b0a0d805/entc/integration/customid/ent/schema/user.go +pkg/mod/github.com/facebookincubator/ent@v0.0.0-20191128071424-29c7b0a0d805/entc/integration/customid/ent/user/user.go +pkg/mod/github.com/facebookincubator/ent@v0.0.0-20191128071424-29c7b0a0d805/entc/integration/customid/ent/user/where.go +pkg/mod/github.com/facebookincubator/ent@v0.0.0-20191128071424-29c7b0a0d805/entc/integration/ent/card_create.go +pkg/mod/github.com/facebookincubator/ent@v0.0.0-20191128071424-29c7b0a0d805/entc/integration/ent/card_delete.go +pkg/mod/github.com/facebookincubator/ent@v0.0.0-20191128071424-29c7b0a0d805/entc/integration/ent/card_query.go +pkg/mod/github.com/facebookincubator/ent@v0.0.0-20191128071424-29c7b0a0d805/entc/integration/ent/card_update.go +pkg/mod/github.com/facebookincubator/ent@v0.0.0-20191128071424-29c7b0a0d805/entc/integration/ent/card.go +pkg/mod/github.com/facebookincubator/ent@v0.0.0-20191128071424-29c7b0a0d805/entc/integration/ent/client.go +pkg/mod/github.com/facebookincubator/ent@v0.0.0-20191128071424-29c7b0a0d805/entc/integration/ent/comment_create.go +pkg/mod/github.com/facebookincubator/ent@v0.0.0-20191128071424-29c7b0a0d805/entc/integration/ent/comment_delete.go +pkg/mod/github.com/facebookincubator/ent@v0.0.0-20191128071424-29c7b0a0d805/entc/integration/ent/comment_query.go +pkg/mod/github.com/facebookincubator/ent@v0.0.0-20191128071424-29c7b0a0d805/entc/integration/ent/comment_update.go +pkg/mod/github.com/facebookincubator/ent@v0.0.0-20191128071424-29c7b0a0d805/entc/integration/ent/comment.go +pkg/mod/github.com/facebookincubator/ent@v0.0.0-20191128071424-29c7b0a0d805/entc/integration/ent/config.go +pkg/mod/github.com/facebookincubator/ent@v0.0.0-20191128071424-29c7b0a0d805/entc/integration/ent/context.go +pkg/mod/github.com/facebookincubator/ent@v0.0.0-20191128071424-29c7b0a0d805/entc/integration/ent/ent.go +pkg/mod/github.com/facebookincubator/ent@v0.0.0-20191128071424-29c7b0a0d805/entc/integration/ent/example_test.go +pkg/mod/github.com/facebookincubator/ent@v0.0.0-20191128071424-29c7b0a0d805/entc/integration/ent/fieldtype_create.go +pkg/mod/github.com/facebookincubator/ent@v0.0.0-20191128071424-29c7b0a0d805/entc/integration/ent/fieldtype_delete.go +pkg/mod/github.com/facebookincubator/ent@v0.0.0-20191128071424-29c7b0a0d805/entc/integration/ent/fieldtype_query.go +pkg/mod/github.com/facebookincubator/ent@v0.0.0-20191128071424-29c7b0a0d805/entc/integration/ent/fieldtype_update.go +pkg/mod/github.com/facebookincubator/ent@v0.0.0-20191128071424-29c7b0a0d805/entc/integration/ent/fieldtype.go +pkg/mod/github.com/facebookincubator/ent@v0.0.0-20191128071424-29c7b0a0d805/entc/integration/ent/file_create.go +pkg/mod/github.com/facebookincubator/ent@v0.0.0-20191128071424-29c7b0a0d805/entc/integration/ent/file_delete.go +pkg/mod/github.com/facebookincubator/ent@v0.0.0-20191128071424-29c7b0a0d805/entc/integration/ent/file_query.go +pkg/mod/github.com/facebookincubator/ent@v0.0.0-20191128071424-29c7b0a0d805/entc/integration/ent/file_update.go +pkg/mod/github.com/facebookincubator/ent@v0.0.0-20191128071424-29c7b0a0d805/entc/integration/ent/file.go +pkg/mod/github.com/facebookincubator/ent@v0.0.0-20191128071424-29c7b0a0d805/entc/integration/ent/filetype_create.go +pkg/mod/github.com/facebookincubator/ent@v0.0.0-20191128071424-29c7b0a0d805/entc/integration/ent/filetype_delete.go +pkg/mod/github.com/facebookincubator/ent@v0.0.0-20191128071424-29c7b0a0d805/entc/integration/ent/filetype_query.go +pkg/mod/github.com/facebookincubator/ent@v0.0.0-20191128071424-29c7b0a0d805/entc/integration/ent/filetype_update.go +pkg/mod/github.com/facebookincubator/ent@v0.0.0-20191128071424-29c7b0a0d805/entc/integration/ent/filetype.go +pkg/mod/github.com/facebookincubator/ent@v0.0.0-20191128071424-29c7b0a0d805/entc/integration/ent/group_create.go +pkg/mod/github.com/facebookincubator/ent@v0.0.0-20191128071424-29c7b0a0d805/entc/integration/ent/group_delete.go +pkg/mod/github.com/facebookincubator/ent@v0.0.0-20191128071424-29c7b0a0d805/entc/integration/ent/group_query.go +pkg/mod/github.com/facebookincubator/ent@v0.0.0-20191128071424-29c7b0a0d805/entc/integration/ent/group_update.go +pkg/mod/github.com/facebookincubator/ent@v0.0.0-20191128071424-29c7b0a0d805/entc/integration/ent/group.go +pkg/mod/github.com/facebookincubator/ent@v0.0.0-20191128071424-29c7b0a0d805/entc/integration/ent/groupinfo_create.go +pkg/mod/github.com/facebookincubator/ent@v0.0.0-20191128071424-29c7b0a0d805/entc/integration/ent/groupinfo_delete.go +pkg/mod/github.com/facebookincubator/ent@v0.0.0-20191128071424-29c7b0a0d805/entc/integration/ent/groupinfo_query.go +pkg/mod/github.com/facebookincubator/ent@v0.0.0-20191128071424-29c7b0a0d805/entc/integration/ent/groupinfo_update.go +pkg/mod/github.com/facebookincubator/ent@v0.0.0-20191128071424-29c7b0a0d805/entc/integration/ent/groupinfo.go +pkg/mod/github.com/facebookincubator/ent@v0.0.0-20191128071424-29c7b0a0d805/entc/integration/ent/item_create.go +pkg/mod/github.com/facebookincubator/ent@v0.0.0-20191128071424-29c7b0a0d805/entc/integration/ent/item_delete.go +pkg/mod/github.com/facebookincubator/ent@v0.0.0-20191128071424-29c7b0a0d805/entc/integration/ent/item_query.go +pkg/mod/github.com/facebookincubator/ent@v0.0.0-20191128071424-29c7b0a0d805/entc/integration/ent/item_update.go +pkg/mod/github.com/facebookincubator/ent@v0.0.0-20191128071424-29c7b0a0d805/entc/integration/ent/item.go +pkg/mod/github.com/facebookincubator/ent@v0.0.0-20191128071424-29c7b0a0d805/entc/integration/ent/node_create.go +pkg/mod/github.com/facebookincubator/ent@v0.0.0-20191128071424-29c7b0a0d805/entc/integration/ent/node_delete.go +pkg/mod/github.com/facebookincubator/ent@v0.0.0-20191128071424-29c7b0a0d805/entc/integration/ent/node_query.go +pkg/mod/github.com/facebookincubator/ent@v0.0.0-20191128071424-29c7b0a0d805/entc/integration/ent/node_update.go +pkg/mod/github.com/facebookincubator/ent@v0.0.0-20191128071424-29c7b0a0d805/entc/integration/ent/node.go +pkg/mod/github.com/facebookincubator/ent@v0.0.0-20191128071424-29c7b0a0d805/entc/integration/ent/pet_create.go +pkg/mod/github.com/facebookincubator/ent@v0.0.0-20191128071424-29c7b0a0d805/entc/integration/ent/pet_delete.go +pkg/mod/github.com/facebookincubator/ent@v0.0.0-20191128071424-29c7b0a0d805/entc/integration/ent/pet_query.go +pkg/mod/github.com/facebookincubator/ent@v0.0.0-20191128071424-29c7b0a0d805/entc/integration/ent/pet_update.go +pkg/mod/github.com/facebookincubator/ent@v0.0.0-20191128071424-29c7b0a0d805/entc/integration/ent/pet.go +pkg/mod/github.com/facebookincubator/ent@v0.0.0-20191128071424-29c7b0a0d805/entc/integration/ent/tx.go +pkg/mod/github.com/facebookincubator/ent@v0.0.0-20191128071424-29c7b0a0d805/entc/integration/ent/user_create.go +pkg/mod/github.com/facebookincubator/ent@v0.0.0-20191128071424-29c7b0a0d805/entc/integration/ent/user_delete.go +pkg/mod/github.com/facebookincubator/ent@v0.0.0-20191128071424-29c7b0a0d805/entc/integration/ent/user_query.go +pkg/mod/github.com/facebookincubator/ent@v0.0.0-20191128071424-29c7b0a0d805/entc/integration/ent/user_update.go +pkg/mod/github.com/facebookincubator/ent@v0.0.0-20191128071424-29c7b0a0d805/entc/integration/ent/user.go +pkg/mod/github.com/facebookincubator/ent@v0.0.0-20191128071424-29c7b0a0d805/entc/integration/ent/card/card.go +pkg/mod/github.com/facebookincubator/ent@v0.0.0-20191128071424-29c7b0a0d805/entc/integration/ent/card/where.go +pkg/mod/github.com/facebookincubator/ent@v0.0.0-20191128071424-29c7b0a0d805/entc/integration/ent/comment/comment.go +pkg/mod/github.com/facebookincubator/ent@v0.0.0-20191128071424-29c7b0a0d805/entc/integration/ent/comment/where.go +pkg/mod/github.com/facebookincubator/ent@v0.0.0-20191128071424-29c7b0a0d805/entc/integration/ent/fieldtype/fieldtype.go +pkg/mod/github.com/facebookincubator/ent@v0.0.0-20191128071424-29c7b0a0d805/entc/integration/ent/fieldtype/where.go +pkg/mod/github.com/facebookincubator/ent@v0.0.0-20191128071424-29c7b0a0d805/entc/integration/ent/file/file.go +pkg/mod/github.com/facebookincubator/ent@v0.0.0-20191128071424-29c7b0a0d805/entc/integration/ent/file/where.go +pkg/mod/github.com/facebookincubator/ent@v0.0.0-20191128071424-29c7b0a0d805/entc/integration/ent/filetype/filetype.go +pkg/mod/github.com/facebookincubator/ent@v0.0.0-20191128071424-29c7b0a0d805/entc/integration/ent/filetype/where.go +pkg/mod/github.com/facebookincubator/ent@v0.0.0-20191128071424-29c7b0a0d805/entc/integration/ent/group/group.go +pkg/mod/github.com/facebookincubator/ent@v0.0.0-20191128071424-29c7b0a0d805/entc/integration/ent/group/where.go +pkg/mod/github.com/facebookincubator/ent@v0.0.0-20191128071424-29c7b0a0d805/entc/integration/ent/groupinfo/groupinfo.go +pkg/mod/github.com/facebookincubator/ent@v0.0.0-20191128071424-29c7b0a0d805/entc/integration/ent/groupinfo/where.go +pkg/mod/github.com/facebookincubator/ent@v0.0.0-20191128071424-29c7b0a0d805/entc/integration/ent/item/item.go +pkg/mod/github.com/facebookincubator/ent@v0.0.0-20191128071424-29c7b0a0d805/entc/integration/ent/item/where.go +pkg/mod/github.com/facebookincubator/ent@v0.0.0-20191128071424-29c7b0a0d805/entc/integration/ent/migrate/migrate.go +pkg/mod/github.com/facebookincubator/ent@v0.0.0-20191128071424-29c7b0a0d805/entc/integration/ent/migrate/schema.go +pkg/mod/github.com/facebookincubator/ent@v0.0.0-20191128071424-29c7b0a0d805/entc/integration/ent/node/node.go +pkg/mod/github.com/facebookincubator/ent@v0.0.0-20191128071424-29c7b0a0d805/entc/integration/ent/node/where.go +pkg/mod/github.com/facebookincubator/ent@v0.0.0-20191128071424-29c7b0a0d805/entc/integration/ent/pet/pet.go +pkg/mod/github.com/facebookincubator/ent@v0.0.0-20191128071424-29c7b0a0d805/entc/integration/ent/pet/where.go +pkg/mod/github.com/facebookincubator/ent@v0.0.0-20191128071424-29c7b0a0d805/entc/integration/ent/predicate/predicate.go +pkg/mod/github.com/facebookincubator/ent@v0.0.0-20191128071424-29c7b0a0d805/entc/integration/ent/schema/card.go +pkg/mod/github.com/facebookincubator/ent@v0.0.0-20191128071424-29c7b0a0d805/entc/integration/ent/schema/comment.go +pkg/mod/github.com/facebookincubator/ent@v0.0.0-20191128071424-29c7b0a0d805/entc/integration/ent/schema/fieldtype.go +pkg/mod/github.com/facebookincubator/ent@v0.0.0-20191128071424-29c7b0a0d805/entc/integration/ent/schema/file.go +pkg/mod/github.com/facebookincubator/ent@v0.0.0-20191128071424-29c7b0a0d805/entc/integration/ent/schema/filetype.go +pkg/mod/github.com/facebookincubator/ent@v0.0.0-20191128071424-29c7b0a0d805/entc/integration/ent/schema/group.go +pkg/mod/github.com/facebookincubator/ent@v0.0.0-20191128071424-29c7b0a0d805/entc/integration/ent/schema/groupinfo.go +pkg/mod/github.com/facebookincubator/ent@v0.0.0-20191128071424-29c7b0a0d805/entc/integration/ent/schema/item.go +pkg/mod/github.com/facebookincubator/ent@v0.0.0-20191128071424-29c7b0a0d805/entc/integration/ent/schema/node.go +pkg/mod/github.com/facebookincubator/ent@v0.0.0-20191128071424-29c7b0a0d805/entc/integration/ent/schema/pet.go +pkg/mod/github.com/facebookincubator/ent@v0.0.0-20191128071424-29c7b0a0d805/entc/integration/ent/schema/user.go +pkg/mod/github.com/facebookincubator/ent@v0.0.0-20191128071424-29c7b0a0d805/entc/integration/ent/template/fields.tmpl +pkg/mod/github.com/facebookincubator/ent@v0.0.0-20191128071424-29c7b0a0d805/entc/integration/ent/user/user.go +pkg/mod/github.com/facebookincubator/ent@v0.0.0-20191128071424-29c7b0a0d805/entc/integration/ent/user/where.go +pkg/mod/github.com/facebookincubator/ent@v0.0.0-20191128071424-29c7b0a0d805/entc/integration/idtype/idtype_test.go +pkg/mod/github.com/facebookincubator/ent@v0.0.0-20191128071424-29c7b0a0d805/entc/integration/idtype/ent/client.go +pkg/mod/github.com/facebookincubator/ent@v0.0.0-20191128071424-29c7b0a0d805/entc/integration/idtype/ent/config.go +pkg/mod/github.com/facebookincubator/ent@v0.0.0-20191128071424-29c7b0a0d805/entc/integration/idtype/ent/context.go +pkg/mod/github.com/facebookincubator/ent@v0.0.0-20191128071424-29c7b0a0d805/entc/integration/idtype/ent/ent.go +pkg/mod/github.com/facebookincubator/ent@v0.0.0-20191128071424-29c7b0a0d805/entc/integration/idtype/ent/example_test.go +pkg/mod/github.com/facebookincubator/ent@v0.0.0-20191128071424-29c7b0a0d805/entc/integration/idtype/ent/tx.go +pkg/mod/github.com/facebookincubator/ent@v0.0.0-20191128071424-29c7b0a0d805/entc/integration/idtype/ent/user_create.go +pkg/mod/github.com/facebookincubator/ent@v0.0.0-20191128071424-29c7b0a0d805/entc/integration/idtype/ent/user_delete.go +pkg/mod/github.com/facebookincubator/ent@v0.0.0-20191128071424-29c7b0a0d805/entc/integration/idtype/ent/user_query.go +pkg/mod/github.com/facebookincubator/ent@v0.0.0-20191128071424-29c7b0a0d805/entc/integration/idtype/ent/user_update.go +pkg/mod/github.com/facebookincubator/ent@v0.0.0-20191128071424-29c7b0a0d805/entc/integration/idtype/ent/user.go +pkg/mod/github.com/facebookincubator/ent@v0.0.0-20191128071424-29c7b0a0d805/entc/integration/idtype/ent/migrate/migrate.go +pkg/mod/github.com/facebookincubator/ent@v0.0.0-20191128071424-29c7b0a0d805/entc/integration/idtype/ent/migrate/schema.go +pkg/mod/github.com/facebookincubator/ent@v0.0.0-20191128071424-29c7b0a0d805/entc/integration/idtype/ent/predicate/predicate.go +pkg/mod/github.com/facebookincubator/ent@v0.0.0-20191128071424-29c7b0a0d805/entc/integration/idtype/ent/schema/user.go +pkg/mod/github.com/facebookincubator/ent@v0.0.0-20191128071424-29c7b0a0d805/entc/integration/idtype/ent/user/user.go +pkg/mod/github.com/facebookincubator/ent@v0.0.0-20191128071424-29c7b0a0d805/entc/integration/idtype/ent/user/where.go +pkg/mod/github.com/facebookincubator/ent@v0.0.0-20191128071424-29c7b0a0d805/entc/integration/json/json_test.go +pkg/mod/github.com/facebookincubator/ent@v0.0.0-20191128071424-29c7b0a0d805/entc/integration/json/ent/client.go +pkg/mod/github.com/facebookincubator/ent@v0.0.0-20191128071424-29c7b0a0d805/entc/integration/json/ent/config.go +pkg/mod/github.com/facebookincubator/ent@v0.0.0-20191128071424-29c7b0a0d805/entc/integration/json/ent/context.go +pkg/mod/github.com/facebookincubator/ent@v0.0.0-20191128071424-29c7b0a0d805/entc/integration/json/ent/ent.go +pkg/mod/github.com/facebookincubator/ent@v0.0.0-20191128071424-29c7b0a0d805/entc/integration/json/ent/example_test.go +pkg/mod/github.com/facebookincubator/ent@v0.0.0-20191128071424-29c7b0a0d805/entc/integration/json/ent/tx.go +pkg/mod/github.com/facebookincubator/ent@v0.0.0-20191128071424-29c7b0a0d805/entc/integration/json/ent/user_create.go +pkg/mod/github.com/facebookincubator/ent@v0.0.0-20191128071424-29c7b0a0d805/entc/integration/json/ent/user_delete.go +pkg/mod/github.com/facebookincubator/ent@v0.0.0-20191128071424-29c7b0a0d805/entc/integration/json/ent/user_query.go +pkg/mod/github.com/facebookincubator/ent@v0.0.0-20191128071424-29c7b0a0d805/entc/integration/json/ent/user_update.go +pkg/mod/github.com/facebookincubator/ent@v0.0.0-20191128071424-29c7b0a0d805/entc/integration/json/ent/user.go +pkg/mod/github.com/facebookincubator/ent@v0.0.0-20191128071424-29c7b0a0d805/entc/integration/json/ent/migrate/migrate.go +pkg/mod/github.com/facebookincubator/ent@v0.0.0-20191128071424-29c7b0a0d805/entc/integration/json/ent/migrate/schema.go +pkg/mod/github.com/facebookincubator/ent@v0.0.0-20191128071424-29c7b0a0d805/entc/integration/json/ent/predicate/predicate.go +pkg/mod/github.com/facebookincubator/ent@v0.0.0-20191128071424-29c7b0a0d805/entc/integration/json/ent/schema/user.go +pkg/mod/github.com/facebookincubator/ent@v0.0.0-20191128071424-29c7b0a0d805/entc/integration/json/ent/user/user.go +pkg/mod/github.com/facebookincubator/ent@v0.0.0-20191128071424-29c7b0a0d805/entc/integration/json/ent/user/where.go +pkg/mod/github.com/facebookincubator/ent@v0.0.0-20191128071424-29c7b0a0d805/entc/integration/migrate/migrate_test.go +pkg/mod/github.com/facebookincubator/ent@v0.0.0-20191128071424-29c7b0a0d805/entc/integration/migrate/entv1/client.go +pkg/mod/github.com/facebookincubator/ent@v0.0.0-20191128071424-29c7b0a0d805/entc/integration/migrate/entv1/config.go +pkg/mod/github.com/facebookincubator/ent@v0.0.0-20191128071424-29c7b0a0d805/entc/integration/migrate/entv1/context.go +pkg/mod/github.com/facebookincubator/ent@v0.0.0-20191128071424-29c7b0a0d805/entc/integration/migrate/entv1/ent.go +pkg/mod/github.com/facebookincubator/ent@v0.0.0-20191128071424-29c7b0a0d805/entc/integration/migrate/entv1/example_test.go +pkg/mod/github.com/facebookincubator/ent@v0.0.0-20191128071424-29c7b0a0d805/entc/integration/migrate/entv1/tx.go +pkg/mod/github.com/facebookincubator/ent@v0.0.0-20191128071424-29c7b0a0d805/entc/integration/migrate/entv1/user_create.go +pkg/mod/github.com/facebookincubator/ent@v0.0.0-20191128071424-29c7b0a0d805/entc/integration/migrate/entv1/user_delete.go +pkg/mod/github.com/facebookincubator/ent@v0.0.0-20191128071424-29c7b0a0d805/entc/integration/migrate/entv1/user_query.go +pkg/mod/github.com/facebookincubator/ent@v0.0.0-20191128071424-29c7b0a0d805/entc/integration/migrate/entv1/user_update.go +pkg/mod/github.com/facebookincubator/ent@v0.0.0-20191128071424-29c7b0a0d805/entc/integration/migrate/entv1/user.go +pkg/mod/github.com/facebookincubator/ent@v0.0.0-20191128071424-29c7b0a0d805/entc/integration/migrate/entv1/migrate/migrate.go +pkg/mod/github.com/facebookincubator/ent@v0.0.0-20191128071424-29c7b0a0d805/entc/integration/migrate/entv1/migrate/schema.go +pkg/mod/github.com/facebookincubator/ent@v0.0.0-20191128071424-29c7b0a0d805/entc/integration/migrate/entv1/predicate/predicate.go +pkg/mod/github.com/facebookincubator/ent@v0.0.0-20191128071424-29c7b0a0d805/entc/integration/migrate/entv1/schema/user.go +pkg/mod/github.com/facebookincubator/ent@v0.0.0-20191128071424-29c7b0a0d805/entc/integration/migrate/entv1/user/user.go +pkg/mod/github.com/facebookincubator/ent@v0.0.0-20191128071424-29c7b0a0d805/entc/integration/migrate/entv1/user/where.go +pkg/mod/github.com/facebookincubator/ent@v0.0.0-20191128071424-29c7b0a0d805/entc/integration/migrate/entv2/client.go +pkg/mod/github.com/facebookincubator/ent@v0.0.0-20191128071424-29c7b0a0d805/entc/integration/migrate/entv2/config.go +pkg/mod/github.com/facebookincubator/ent@v0.0.0-20191128071424-29c7b0a0d805/entc/integration/migrate/entv2/context.go +pkg/mod/github.com/facebookincubator/ent@v0.0.0-20191128071424-29c7b0a0d805/entc/integration/migrate/entv2/ent.go +pkg/mod/github.com/facebookincubator/ent@v0.0.0-20191128071424-29c7b0a0d805/entc/integration/migrate/entv2/example_test.go +pkg/mod/github.com/facebookincubator/ent@v0.0.0-20191128071424-29c7b0a0d805/entc/integration/migrate/entv2/group_create.go +pkg/mod/github.com/facebookincubator/ent@v0.0.0-20191128071424-29c7b0a0d805/entc/integration/migrate/entv2/group_delete.go +pkg/mod/github.com/facebookincubator/ent@v0.0.0-20191128071424-29c7b0a0d805/entc/integration/migrate/entv2/group_query.go +pkg/mod/github.com/facebookincubator/ent@v0.0.0-20191128071424-29c7b0a0d805/entc/integration/migrate/entv2/group_update.go +pkg/mod/github.com/facebookincubator/ent@v0.0.0-20191128071424-29c7b0a0d805/entc/integration/migrate/entv2/group.go +pkg/mod/github.com/facebookincubator/ent@v0.0.0-20191128071424-29c7b0a0d805/entc/integration/migrate/entv2/pet_create.go +pkg/mod/github.com/facebookincubator/ent@v0.0.0-20191128071424-29c7b0a0d805/entc/integration/migrate/entv2/pet_delete.go +pkg/mod/github.com/facebookincubator/ent@v0.0.0-20191128071424-29c7b0a0d805/entc/integration/migrate/entv2/pet_query.go +pkg/mod/github.com/facebookincubator/ent@v0.0.0-20191128071424-29c7b0a0d805/entc/integration/migrate/entv2/pet_update.go +pkg/mod/github.com/facebookincubator/ent@v0.0.0-20191128071424-29c7b0a0d805/entc/integration/migrate/entv2/pet.go +pkg/mod/github.com/facebookincubator/ent@v0.0.0-20191128071424-29c7b0a0d805/entc/integration/migrate/entv2/tx.go +pkg/mod/github.com/facebookincubator/ent@v0.0.0-20191128071424-29c7b0a0d805/entc/integration/migrate/entv2/user_create.go +pkg/mod/github.com/facebookincubator/ent@v0.0.0-20191128071424-29c7b0a0d805/entc/integration/migrate/entv2/user_delete.go +pkg/mod/github.com/facebookincubator/ent@v0.0.0-20191128071424-29c7b0a0d805/entc/integration/migrate/entv2/user_query.go +pkg/mod/github.com/facebookincubator/ent@v0.0.0-20191128071424-29c7b0a0d805/entc/integration/migrate/entv2/user_update.go +pkg/mod/github.com/facebookincubator/ent@v0.0.0-20191128071424-29c7b0a0d805/entc/integration/migrate/entv2/user.go +pkg/mod/github.com/facebookincubator/ent@v0.0.0-20191128071424-29c7b0a0d805/entc/integration/migrate/entv2/group/group.go +pkg/mod/github.com/facebookincubator/ent@v0.0.0-20191128071424-29c7b0a0d805/entc/integration/migrate/entv2/group/where.go +pkg/mod/github.com/facebookincubator/ent@v0.0.0-20191128071424-29c7b0a0d805/entc/integration/migrate/entv2/migrate/migrate.go +pkg/mod/github.com/facebookincubator/ent@v0.0.0-20191128071424-29c7b0a0d805/entc/integration/migrate/entv2/migrate/schema.go +pkg/mod/github.com/facebookincubator/ent@v0.0.0-20191128071424-29c7b0a0d805/entc/integration/migrate/entv2/pet/pet.go +pkg/mod/github.com/facebookincubator/ent@v0.0.0-20191128071424-29c7b0a0d805/entc/integration/migrate/entv2/pet/where.go +pkg/mod/github.com/facebookincubator/ent@v0.0.0-20191128071424-29c7b0a0d805/entc/integration/migrate/entv2/predicate/predicate.go +pkg/mod/github.com/facebookincubator/ent@v0.0.0-20191128071424-29c7b0a0d805/entc/integration/migrate/entv2/schema/user.go +pkg/mod/github.com/facebookincubator/ent@v0.0.0-20191128071424-29c7b0a0d805/entc/integration/migrate/entv2/user/user.go +pkg/mod/github.com/facebookincubator/ent@v0.0.0-20191128071424-29c7b0a0d805/entc/integration/migrate/entv2/user/where.go +pkg/mod/github.com/facebookincubator/ent@v0.0.0-20191128071424-29c7b0a0d805/entc/integration/template/template_test.go +pkg/mod/github.com/facebookincubator/ent@v0.0.0-20191128071424-29c7b0a0d805/entc/integration/template/ent/client.go +pkg/mod/github.com/facebookincubator/ent@v0.0.0-20191128071424-29c7b0a0d805/entc/integration/template/ent/config.go +pkg/mod/github.com/facebookincubator/ent@v0.0.0-20191128071424-29c7b0a0d805/entc/integration/template/ent/context.go +pkg/mod/github.com/facebookincubator/ent@v0.0.0-20191128071424-29c7b0a0d805/entc/integration/template/ent/ent.go +pkg/mod/github.com/facebookincubator/ent@v0.0.0-20191128071424-29c7b0a0d805/entc/integration/template/ent/example_test.go +pkg/mod/github.com/facebookincubator/ent@v0.0.0-20191128071424-29c7b0a0d805/entc/integration/template/ent/group_create.go +pkg/mod/github.com/facebookincubator/ent@v0.0.0-20191128071424-29c7b0a0d805/entc/integration/template/ent/group_delete.go +pkg/mod/github.com/facebookincubator/ent@v0.0.0-20191128071424-29c7b0a0d805/entc/integration/template/ent/group_query.go +pkg/mod/github.com/facebookincubator/ent@v0.0.0-20191128071424-29c7b0a0d805/entc/integration/template/ent/group_update.go +pkg/mod/github.com/facebookincubator/ent@v0.0.0-20191128071424-29c7b0a0d805/entc/integration/template/ent/group.go +pkg/mod/github.com/facebookincubator/ent@v0.0.0-20191128071424-29c7b0a0d805/entc/integration/template/ent/node.go +pkg/mod/github.com/facebookincubator/ent@v0.0.0-20191128071424-29c7b0a0d805/entc/integration/template/ent/pet_create.go +pkg/mod/github.com/facebookincubator/ent@v0.0.0-20191128071424-29c7b0a0d805/entc/integration/template/ent/pet_delete.go +pkg/mod/github.com/facebookincubator/ent@v0.0.0-20191128071424-29c7b0a0d805/entc/integration/template/ent/pet_query.go +pkg/mod/github.com/facebookincubator/ent@v0.0.0-20191128071424-29c7b0a0d805/entc/integration/template/ent/pet_update.go +pkg/mod/github.com/facebookincubator/ent@v0.0.0-20191128071424-29c7b0a0d805/entc/integration/template/ent/pet.go +pkg/mod/github.com/facebookincubator/ent@v0.0.0-20191128071424-29c7b0a0d805/entc/integration/template/ent/tx.go +pkg/mod/github.com/facebookincubator/ent@v0.0.0-20191128071424-29c7b0a0d805/entc/integration/template/ent/user_create.go +pkg/mod/github.com/facebookincubator/ent@v0.0.0-20191128071424-29c7b0a0d805/entc/integration/template/ent/user_delete.go +pkg/mod/github.com/facebookincubator/ent@v0.0.0-20191128071424-29c7b0a0d805/entc/integration/template/ent/user_query.go +pkg/mod/github.com/facebookincubator/ent@v0.0.0-20191128071424-29c7b0a0d805/entc/integration/template/ent/user_update.go +pkg/mod/github.com/facebookincubator/ent@v0.0.0-20191128071424-29c7b0a0d805/entc/integration/template/ent/user.go +pkg/mod/github.com/facebookincubator/ent@v0.0.0-20191128071424-29c7b0a0d805/entc/integration/template/ent/group/group.go +pkg/mod/github.com/facebookincubator/ent@v0.0.0-20191128071424-29c7b0a0d805/entc/integration/template/ent/group/where.go +pkg/mod/github.com/facebookincubator/ent@v0.0.0-20191128071424-29c7b0a0d805/entc/integration/template/ent/migrate/migrate.go +pkg/mod/github.com/facebookincubator/ent@v0.0.0-20191128071424-29c7b0a0d805/entc/integration/template/ent/migrate/schema.go +pkg/mod/github.com/facebookincubator/ent@v0.0.0-20191128071424-29c7b0a0d805/entc/integration/template/ent/pet/pet.go +pkg/mod/github.com/facebookincubator/ent@v0.0.0-20191128071424-29c7b0a0d805/entc/integration/template/ent/pet/where.go +pkg/mod/github.com/facebookincubator/ent@v0.0.0-20191128071424-29c7b0a0d805/entc/integration/template/ent/predicate/predicate.go +pkg/mod/github.com/facebookincubator/ent@v0.0.0-20191128071424-29c7b0a0d805/entc/integration/template/ent/schema/group.go +pkg/mod/github.com/facebookincubator/ent@v0.0.0-20191128071424-29c7b0a0d805/entc/integration/template/ent/schema/pet.go +pkg/mod/github.com/facebookincubator/ent@v0.0.0-20191128071424-29c7b0a0d805/entc/integration/template/ent/schema/user.go +pkg/mod/github.com/facebookincubator/ent@v0.0.0-20191128071424-29c7b0a0d805/entc/integration/template/ent/template/client.tmpl +pkg/mod/github.com/facebookincubator/ent@v0.0.0-20191128071424-29c7b0a0d805/entc/integration/template/ent/template/node.tmpl +pkg/mod/github.com/facebookincubator/ent@v0.0.0-20191128071424-29c7b0a0d805/entc/integration/template/ent/user/user.go +pkg/mod/github.com/facebookincubator/ent@v0.0.0-20191128071424-29c7b0a0d805/entc/integration/template/ent/user/where.go +pkg/mod/github.com/facebookincubator/ent@v0.0.0-20191128071424-29c7b0a0d805/entc/load/load_test.go +pkg/mod/github.com/facebookincubator/ent@v0.0.0-20191128071424-29c7b0a0d805/entc/load/load.go +pkg/mod/github.com/facebookincubator/ent@v0.0.0-20191128071424-29c7b0a0d805/entc/load/schema_test.go +pkg/mod/github.com/facebookincubator/ent@v0.0.0-20191128071424-29c7b0a0d805/entc/load/schema.go +pkg/mod/github.com/facebookincubator/ent@v0.0.0-20191128071424-29c7b0a0d805/entc/load/internal/bindata.go +pkg/mod/github.com/facebookincubator/ent@v0.0.0-20191128071424-29c7b0a0d805/entc/load/template/main.tmpl +pkg/mod/github.com/facebookincubator/ent@v0.0.0-20191128071424-29c7b0a0d805/entc/load/testdata/base/schema.go +pkg/mod/github.com/facebookincubator/ent@v0.0.0-20191128071424-29c7b0a0d805/entc/load/testdata/failure/user.go +pkg/mod/github.com/facebookincubator/ent@v0.0.0-20191128071424-29c7b0a0d805/entc/load/testdata/invalid/schema.go +pkg/mod/github.com/facebookincubator/ent@v0.0.0-20191128071424-29c7b0a0d805/entc/load/testdata/valid/schema.go +pkg/mod/github.com/facebookincubator/ent@v0.0.0-20191128071424-29c7b0a0d805/examples/README.md +pkg/mod/github.com/facebookincubator/ent@v0.0.0-20191128071424-29c7b0a0d805/examples/edgeindex/edgeindex.go +pkg/mod/github.com/facebookincubator/ent@v0.0.0-20191128071424-29c7b0a0d805/examples/edgeindex/README.md +pkg/mod/github.com/facebookincubator/ent@v0.0.0-20191128071424-29c7b0a0d805/examples/edgeindex/ent/city_create.go +pkg/mod/github.com/facebookincubator/ent@v0.0.0-20191128071424-29c7b0a0d805/examples/edgeindex/ent/city_delete.go +pkg/mod/github.com/facebookincubator/ent@v0.0.0-20191128071424-29c7b0a0d805/examples/edgeindex/ent/city_query.go +pkg/mod/github.com/facebookincubator/ent@v0.0.0-20191128071424-29c7b0a0d805/examples/edgeindex/ent/city_update.go +pkg/mod/github.com/facebookincubator/ent@v0.0.0-20191128071424-29c7b0a0d805/examples/edgeindex/ent/city.go +pkg/mod/github.com/facebookincubator/ent@v0.0.0-20191128071424-29c7b0a0d805/examples/edgeindex/ent/client.go +pkg/mod/github.com/facebookincubator/ent@v0.0.0-20191128071424-29c7b0a0d805/examples/edgeindex/ent/config.go +pkg/mod/github.com/facebookincubator/ent@v0.0.0-20191128071424-29c7b0a0d805/examples/edgeindex/ent/context.go +pkg/mod/github.com/facebookincubator/ent@v0.0.0-20191128071424-29c7b0a0d805/examples/edgeindex/ent/ent.go +pkg/mod/github.com/facebookincubator/ent@v0.0.0-20191128071424-29c7b0a0d805/examples/edgeindex/ent/example_test.go +pkg/mod/github.com/facebookincubator/ent@v0.0.0-20191128071424-29c7b0a0d805/examples/edgeindex/ent/generate.go +pkg/mod/github.com/facebookincubator/ent@v0.0.0-20191128071424-29c7b0a0d805/examples/edgeindex/ent/street_create.go +pkg/mod/github.com/facebookincubator/ent@v0.0.0-20191128071424-29c7b0a0d805/examples/edgeindex/ent/street_delete.go +pkg/mod/github.com/facebookincubator/ent@v0.0.0-20191128071424-29c7b0a0d805/examples/edgeindex/ent/street_query.go +pkg/mod/github.com/facebookincubator/ent@v0.0.0-20191128071424-29c7b0a0d805/examples/edgeindex/ent/street_update.go +pkg/mod/github.com/facebookincubator/ent@v0.0.0-20191128071424-29c7b0a0d805/examples/edgeindex/ent/street.go +pkg/mod/github.com/facebookincubator/ent@v0.0.0-20191128071424-29c7b0a0d805/examples/edgeindex/ent/tx.go +pkg/mod/github.com/facebookincubator/ent@v0.0.0-20191128071424-29c7b0a0d805/examples/edgeindex/ent/city/city.go +pkg/mod/github.com/facebookincubator/ent@v0.0.0-20191128071424-29c7b0a0d805/examples/edgeindex/ent/city/where.go +pkg/mod/github.com/facebookincubator/ent@v0.0.0-20191128071424-29c7b0a0d805/examples/edgeindex/ent/migrate/migrate.go +pkg/mod/github.com/facebookincubator/ent@v0.0.0-20191128071424-29c7b0a0d805/examples/edgeindex/ent/migrate/schema.go +pkg/mod/github.com/facebookincubator/ent@v0.0.0-20191128071424-29c7b0a0d805/examples/edgeindex/ent/predicate/predicate.go +pkg/mod/github.com/facebookincubator/ent@v0.0.0-20191128071424-29c7b0a0d805/examples/edgeindex/ent/schema/city.go +pkg/mod/github.com/facebookincubator/ent@v0.0.0-20191128071424-29c7b0a0d805/examples/edgeindex/ent/schema/street.go +pkg/mod/github.com/facebookincubator/ent@v0.0.0-20191128071424-29c7b0a0d805/examples/edgeindex/ent/street/street.go +pkg/mod/github.com/facebookincubator/ent@v0.0.0-20191128071424-29c7b0a0d805/examples/edgeindex/ent/street/where.go +pkg/mod/github.com/facebookincubator/ent@v0.0.0-20191128071424-29c7b0a0d805/examples/entcpkg/entcpkg.go +pkg/mod/github.com/facebookincubator/ent@v0.0.0-20191128071424-29c7b0a0d805/examples/entcpkg/README.md +pkg/mod/github.com/facebookincubator/ent@v0.0.0-20191128071424-29c7b0a0d805/examples/entcpkg/ent/client.go +pkg/mod/github.com/facebookincubator/ent@v0.0.0-20191128071424-29c7b0a0d805/examples/entcpkg/ent/config.go +pkg/mod/github.com/facebookincubator/ent@v0.0.0-20191128071424-29c7b0a0d805/examples/entcpkg/ent/context.go +pkg/mod/github.com/facebookincubator/ent@v0.0.0-20191128071424-29c7b0a0d805/examples/entcpkg/ent/ent.go +pkg/mod/github.com/facebookincubator/ent@v0.0.0-20191128071424-29c7b0a0d805/examples/entcpkg/ent/entc.go +pkg/mod/github.com/facebookincubator/ent@v0.0.0-20191128071424-29c7b0a0d805/examples/entcpkg/ent/example_test.go +pkg/mod/github.com/facebookincubator/ent@v0.0.0-20191128071424-29c7b0a0d805/examples/entcpkg/ent/generate.go +pkg/mod/github.com/facebookincubator/ent@v0.0.0-20191128071424-29c7b0a0d805/examples/entcpkg/ent/tx.go +pkg/mod/github.com/facebookincubator/ent@v0.0.0-20191128071424-29c7b0a0d805/examples/entcpkg/ent/user_create.go +pkg/mod/github.com/facebookincubator/ent@v0.0.0-20191128071424-29c7b0a0d805/examples/entcpkg/ent/user_delete.go +pkg/mod/github.com/facebookincubator/ent@v0.0.0-20191128071424-29c7b0a0d805/examples/entcpkg/ent/user_query.go +pkg/mod/github.com/facebookincubator/ent@v0.0.0-20191128071424-29c7b0a0d805/examples/entcpkg/ent/user_update.go +pkg/mod/github.com/facebookincubator/ent@v0.0.0-20191128071424-29c7b0a0d805/examples/entcpkg/ent/user.go +pkg/mod/github.com/facebookincubator/ent@v0.0.0-20191128071424-29c7b0a0d805/examples/entcpkg/ent/migrate/migrate.go +pkg/mod/github.com/facebookincubator/ent@v0.0.0-20191128071424-29c7b0a0d805/examples/entcpkg/ent/migrate/schema.go +pkg/mod/github.com/facebookincubator/ent@v0.0.0-20191128071424-29c7b0a0d805/examples/entcpkg/ent/predicate/predicate.go +pkg/mod/github.com/facebookincubator/ent@v0.0.0-20191128071424-29c7b0a0d805/examples/entcpkg/ent/schema/user.go +pkg/mod/github.com/facebookincubator/ent@v0.0.0-20191128071424-29c7b0a0d805/examples/entcpkg/ent/user/user.go +pkg/mod/github.com/facebookincubator/ent@v0.0.0-20191128071424-29c7b0a0d805/examples/entcpkg/ent/user/where.go +pkg/mod/github.com/facebookincubator/ent@v0.0.0-20191128071424-29c7b0a0d805/examples/m2m2types/m2m2types.go +pkg/mod/github.com/facebookincubator/ent@v0.0.0-20191128071424-29c7b0a0d805/examples/m2m2types/README.md +pkg/mod/github.com/facebookincubator/ent@v0.0.0-20191128071424-29c7b0a0d805/examples/m2m2types/ent/client.go +pkg/mod/github.com/facebookincubator/ent@v0.0.0-20191128071424-29c7b0a0d805/examples/m2m2types/ent/config.go +pkg/mod/github.com/facebookincubator/ent@v0.0.0-20191128071424-29c7b0a0d805/examples/m2m2types/ent/context.go +pkg/mod/github.com/facebookincubator/ent@v0.0.0-20191128071424-29c7b0a0d805/examples/m2m2types/ent/ent.go +pkg/mod/github.com/facebookincubator/ent@v0.0.0-20191128071424-29c7b0a0d805/examples/m2m2types/ent/example_test.go +pkg/mod/github.com/facebookincubator/ent@v0.0.0-20191128071424-29c7b0a0d805/examples/m2m2types/ent/generate.go +pkg/mod/github.com/facebookincubator/ent@v0.0.0-20191128071424-29c7b0a0d805/examples/m2m2types/ent/group_create.go +pkg/mod/github.com/facebookincubator/ent@v0.0.0-20191128071424-29c7b0a0d805/examples/m2m2types/ent/group_delete.go +pkg/mod/github.com/facebookincubator/ent@v0.0.0-20191128071424-29c7b0a0d805/examples/m2m2types/ent/group_query.go +pkg/mod/github.com/facebookincubator/ent@v0.0.0-20191128071424-29c7b0a0d805/examples/m2m2types/ent/group_update.go +pkg/mod/github.com/facebookincubator/ent@v0.0.0-20191128071424-29c7b0a0d805/examples/m2m2types/ent/group.go +pkg/mod/github.com/facebookincubator/ent@v0.0.0-20191128071424-29c7b0a0d805/examples/m2m2types/ent/tx.go +pkg/mod/github.com/facebookincubator/ent@v0.0.0-20191128071424-29c7b0a0d805/examples/m2m2types/ent/user_create.go +pkg/mod/github.com/facebookincubator/ent@v0.0.0-20191128071424-29c7b0a0d805/examples/m2m2types/ent/user_delete.go +pkg/mod/github.com/facebookincubator/ent@v0.0.0-20191128071424-29c7b0a0d805/examples/m2m2types/ent/user_query.go +pkg/mod/github.com/facebookincubator/ent@v0.0.0-20191128071424-29c7b0a0d805/examples/m2m2types/ent/user_update.go +pkg/mod/github.com/facebookincubator/ent@v0.0.0-20191128071424-29c7b0a0d805/examples/m2m2types/ent/user.go +pkg/mod/github.com/facebookincubator/ent@v0.0.0-20191128071424-29c7b0a0d805/examples/m2m2types/ent/group/group.go +pkg/mod/github.com/facebookincubator/ent@v0.0.0-20191128071424-29c7b0a0d805/examples/m2m2types/ent/group/where.go +pkg/mod/github.com/facebookincubator/ent@v0.0.0-20191128071424-29c7b0a0d805/examples/m2m2types/ent/migrate/migrate.go +pkg/mod/github.com/facebookincubator/ent@v0.0.0-20191128071424-29c7b0a0d805/examples/m2m2types/ent/migrate/schema.go +pkg/mod/github.com/facebookincubator/ent@v0.0.0-20191128071424-29c7b0a0d805/examples/m2m2types/ent/predicate/predicate.go +pkg/mod/github.com/facebookincubator/ent@v0.0.0-20191128071424-29c7b0a0d805/examples/m2m2types/ent/schema/group.go +pkg/mod/github.com/facebookincubator/ent@v0.0.0-20191128071424-29c7b0a0d805/examples/m2m2types/ent/schema/user.go +pkg/mod/github.com/facebookincubator/ent@v0.0.0-20191128071424-29c7b0a0d805/examples/m2m2types/ent/user/user.go +pkg/mod/github.com/facebookincubator/ent@v0.0.0-20191128071424-29c7b0a0d805/examples/m2m2types/ent/user/where.go +pkg/mod/github.com/facebookincubator/ent@v0.0.0-20191128071424-29c7b0a0d805/examples/m2mbidi/m2mbidi.go +pkg/mod/github.com/facebookincubator/ent@v0.0.0-20191128071424-29c7b0a0d805/examples/m2mbidi/README.md +pkg/mod/github.com/facebookincubator/ent@v0.0.0-20191128071424-29c7b0a0d805/examples/m2mbidi/ent/client.go +pkg/mod/github.com/facebookincubator/ent@v0.0.0-20191128071424-29c7b0a0d805/examples/m2mbidi/ent/config.go +pkg/mod/github.com/facebookincubator/ent@v0.0.0-20191128071424-29c7b0a0d805/examples/m2mbidi/ent/context.go +pkg/mod/github.com/facebookincubator/ent@v0.0.0-20191128071424-29c7b0a0d805/examples/m2mbidi/ent/ent.go +pkg/mod/github.com/facebookincubator/ent@v0.0.0-20191128071424-29c7b0a0d805/examples/m2mbidi/ent/example_test.go +pkg/mod/github.com/facebookincubator/ent@v0.0.0-20191128071424-29c7b0a0d805/examples/m2mbidi/ent/generate.go +pkg/mod/github.com/facebookincubator/ent@v0.0.0-20191128071424-29c7b0a0d805/examples/m2mbidi/ent/tx.go +pkg/mod/github.com/facebookincubator/ent@v0.0.0-20191128071424-29c7b0a0d805/examples/m2mbidi/ent/user_create.go +pkg/mod/github.com/facebookincubator/ent@v0.0.0-20191128071424-29c7b0a0d805/examples/m2mbidi/ent/user_delete.go +pkg/mod/github.com/facebookincubator/ent@v0.0.0-20191128071424-29c7b0a0d805/examples/m2mbidi/ent/user_query.go +pkg/mod/github.com/facebookincubator/ent@v0.0.0-20191128071424-29c7b0a0d805/examples/m2mbidi/ent/user_update.go +pkg/mod/github.com/facebookincubator/ent@v0.0.0-20191128071424-29c7b0a0d805/examples/m2mbidi/ent/user.go +pkg/mod/github.com/facebookincubator/ent@v0.0.0-20191128071424-29c7b0a0d805/examples/m2mbidi/ent/migrate/migrate.go +pkg/mod/github.com/facebookincubator/ent@v0.0.0-20191128071424-29c7b0a0d805/examples/m2mbidi/ent/migrate/schema.go +pkg/mod/github.com/facebookincubator/ent@v0.0.0-20191128071424-29c7b0a0d805/examples/m2mbidi/ent/predicate/predicate.go +pkg/mod/github.com/facebookincubator/ent@v0.0.0-20191128071424-29c7b0a0d805/examples/m2mbidi/ent/schema/user.go +pkg/mod/github.com/facebookincubator/ent@v0.0.0-20191128071424-29c7b0a0d805/examples/m2mbidi/ent/user/user.go +pkg/mod/github.com/facebookincubator/ent@v0.0.0-20191128071424-29c7b0a0d805/examples/m2mbidi/ent/user/where.go +pkg/mod/github.com/facebookincubator/ent@v0.0.0-20191128071424-29c7b0a0d805/examples/m2mrecur/m2mrecur.go +pkg/mod/github.com/facebookincubator/ent@v0.0.0-20191128071424-29c7b0a0d805/examples/m2mrecur/README.md +pkg/mod/github.com/facebookincubator/ent@v0.0.0-20191128071424-29c7b0a0d805/examples/m2mrecur/ent/client.go +pkg/mod/github.com/facebookincubator/ent@v0.0.0-20191128071424-29c7b0a0d805/examples/m2mrecur/ent/config.go +pkg/mod/github.com/facebookincubator/ent@v0.0.0-20191128071424-29c7b0a0d805/examples/m2mrecur/ent/context.go +pkg/mod/github.com/facebookincubator/ent@v0.0.0-20191128071424-29c7b0a0d805/examples/m2mrecur/ent/ent.go +pkg/mod/github.com/facebookincubator/ent@v0.0.0-20191128071424-29c7b0a0d805/examples/m2mrecur/ent/example_test.go +pkg/mod/github.com/facebookincubator/ent@v0.0.0-20191128071424-29c7b0a0d805/examples/m2mrecur/ent/generate.go +pkg/mod/github.com/facebookincubator/ent@v0.0.0-20191128071424-29c7b0a0d805/examples/m2mrecur/ent/tx.go +pkg/mod/github.com/facebookincubator/ent@v0.0.0-20191128071424-29c7b0a0d805/examples/m2mrecur/ent/user_create.go +pkg/mod/github.com/facebookincubator/ent@v0.0.0-20191128071424-29c7b0a0d805/examples/m2mrecur/ent/user_delete.go +pkg/mod/github.com/facebookincubator/ent@v0.0.0-20191128071424-29c7b0a0d805/examples/m2mrecur/ent/user_query.go +pkg/mod/github.com/facebookincubator/ent@v0.0.0-20191128071424-29c7b0a0d805/examples/m2mrecur/ent/user_update.go +pkg/mod/github.com/facebookincubator/ent@v0.0.0-20191128071424-29c7b0a0d805/examples/m2mrecur/ent/user.go +pkg/mod/github.com/facebookincubator/ent@v0.0.0-20191128071424-29c7b0a0d805/examples/m2mrecur/ent/migrate/migrate.go +pkg/mod/github.com/facebookincubator/ent@v0.0.0-20191128071424-29c7b0a0d805/examples/m2mrecur/ent/migrate/schema.go +pkg/mod/github.com/facebookincubator/ent@v0.0.0-20191128071424-29c7b0a0d805/examples/m2mrecur/ent/predicate/predicate.go +pkg/mod/github.com/facebookincubator/ent@v0.0.0-20191128071424-29c7b0a0d805/examples/m2mrecur/ent/schema/user.go +pkg/mod/github.com/facebookincubator/ent@v0.0.0-20191128071424-29c7b0a0d805/examples/m2mrecur/ent/user/user.go +pkg/mod/github.com/facebookincubator/ent@v0.0.0-20191128071424-29c7b0a0d805/examples/m2mrecur/ent/user/where.go +pkg/mod/github.com/facebookincubator/ent@v0.0.0-20191128071424-29c7b0a0d805/examples/o2m2types/o2m2types.go +pkg/mod/github.com/facebookincubator/ent@v0.0.0-20191128071424-29c7b0a0d805/examples/o2m2types/README.md +pkg/mod/github.com/facebookincubator/ent@v0.0.0-20191128071424-29c7b0a0d805/examples/o2m2types/ent/client.go +pkg/mod/github.com/facebookincubator/ent@v0.0.0-20191128071424-29c7b0a0d805/examples/o2m2types/ent/config.go +pkg/mod/github.com/facebookincubator/ent@v0.0.0-20191128071424-29c7b0a0d805/examples/o2m2types/ent/context.go +pkg/mod/github.com/facebookincubator/ent@v0.0.0-20191128071424-29c7b0a0d805/examples/o2m2types/ent/ent.go +pkg/mod/github.com/facebookincubator/ent@v0.0.0-20191128071424-29c7b0a0d805/examples/o2m2types/ent/example_test.go +pkg/mod/github.com/facebookincubator/ent@v0.0.0-20191128071424-29c7b0a0d805/examples/o2m2types/ent/generate.go +pkg/mod/github.com/facebookincubator/ent@v0.0.0-20191128071424-29c7b0a0d805/examples/o2m2types/ent/pet_create.go +pkg/mod/github.com/facebookincubator/ent@v0.0.0-20191128071424-29c7b0a0d805/examples/o2m2types/ent/pet_delete.go +pkg/mod/github.com/facebookincubator/ent@v0.0.0-20191128071424-29c7b0a0d805/examples/o2m2types/ent/pet_query.go +pkg/mod/github.com/facebookincubator/ent@v0.0.0-20191128071424-29c7b0a0d805/examples/o2m2types/ent/pet_update.go +pkg/mod/github.com/facebookincubator/ent@v0.0.0-20191128071424-29c7b0a0d805/examples/o2m2types/ent/pet.go +pkg/mod/github.com/facebookincubator/ent@v0.0.0-20191128071424-29c7b0a0d805/examples/o2m2types/ent/tx.go +pkg/mod/github.com/facebookincubator/ent@v0.0.0-20191128071424-29c7b0a0d805/examples/o2m2types/ent/user_create.go +pkg/mod/github.com/facebookincubator/ent@v0.0.0-20191128071424-29c7b0a0d805/examples/o2m2types/ent/user_delete.go +pkg/mod/github.com/facebookincubator/ent@v0.0.0-20191128071424-29c7b0a0d805/examples/o2m2types/ent/user_query.go +pkg/mod/github.com/facebookincubator/ent@v0.0.0-20191128071424-29c7b0a0d805/examples/o2m2types/ent/user_update.go +pkg/mod/github.com/facebookincubator/ent@v0.0.0-20191128071424-29c7b0a0d805/examples/o2m2types/ent/user.go +pkg/mod/github.com/facebookincubator/ent@v0.0.0-20191128071424-29c7b0a0d805/examples/o2m2types/ent/migrate/migrate.go +pkg/mod/github.com/facebookincubator/ent@v0.0.0-20191128071424-29c7b0a0d805/examples/o2m2types/ent/migrate/schema.go +pkg/mod/github.com/facebookincubator/ent@v0.0.0-20191128071424-29c7b0a0d805/examples/o2m2types/ent/pet/pet.go +pkg/mod/github.com/facebookincubator/ent@v0.0.0-20191128071424-29c7b0a0d805/examples/o2m2types/ent/pet/where.go +pkg/mod/github.com/facebookincubator/ent@v0.0.0-20191128071424-29c7b0a0d805/examples/o2m2types/ent/predicate/predicate.go +pkg/mod/github.com/facebookincubator/ent@v0.0.0-20191128071424-29c7b0a0d805/examples/o2m2types/ent/schema/pet.go +pkg/mod/github.com/facebookincubator/ent@v0.0.0-20191128071424-29c7b0a0d805/examples/o2m2types/ent/schema/user.go +pkg/mod/github.com/facebookincubator/ent@v0.0.0-20191128071424-29c7b0a0d805/examples/o2m2types/ent/user/user.go +pkg/mod/github.com/facebookincubator/ent@v0.0.0-20191128071424-29c7b0a0d805/examples/o2m2types/ent/user/where.go +pkg/mod/github.com/facebookincubator/ent@v0.0.0-20191128071424-29c7b0a0d805/examples/o2mrecur/o2mrecur.go +pkg/mod/github.com/facebookincubator/ent@v0.0.0-20191128071424-29c7b0a0d805/examples/o2mrecur/README.md +pkg/mod/github.com/facebookincubator/ent@v0.0.0-20191128071424-29c7b0a0d805/examples/o2mrecur/ent/client.go +pkg/mod/github.com/facebookincubator/ent@v0.0.0-20191128071424-29c7b0a0d805/examples/o2mrecur/ent/config.go +pkg/mod/github.com/facebookincubator/ent@v0.0.0-20191128071424-29c7b0a0d805/examples/o2mrecur/ent/context.go +pkg/mod/github.com/facebookincubator/ent@v0.0.0-20191128071424-29c7b0a0d805/examples/o2mrecur/ent/ent.go +pkg/mod/github.com/facebookincubator/ent@v0.0.0-20191128071424-29c7b0a0d805/examples/o2mrecur/ent/example_test.go +pkg/mod/github.com/facebookincubator/ent@v0.0.0-20191128071424-29c7b0a0d805/examples/o2mrecur/ent/generate.go +pkg/mod/github.com/facebookincubator/ent@v0.0.0-20191128071424-29c7b0a0d805/examples/o2mrecur/ent/node_create.go +pkg/mod/github.com/facebookincubator/ent@v0.0.0-20191128071424-29c7b0a0d805/examples/o2mrecur/ent/node_delete.go +pkg/mod/github.com/facebookincubator/ent@v0.0.0-20191128071424-29c7b0a0d805/examples/o2mrecur/ent/node_query.go +pkg/mod/github.com/facebookincubator/ent@v0.0.0-20191128071424-29c7b0a0d805/examples/o2mrecur/ent/node_update.go +pkg/mod/github.com/facebookincubator/ent@v0.0.0-20191128071424-29c7b0a0d805/examples/o2mrecur/ent/node.go +pkg/mod/github.com/facebookincubator/ent@v0.0.0-20191128071424-29c7b0a0d805/examples/o2mrecur/ent/tx.go +pkg/mod/github.com/facebookincubator/ent@v0.0.0-20191128071424-29c7b0a0d805/examples/o2mrecur/ent/migrate/migrate.go +pkg/mod/github.com/facebookincubator/ent@v0.0.0-20191128071424-29c7b0a0d805/examples/o2mrecur/ent/migrate/schema.go +pkg/mod/github.com/facebookincubator/ent@v0.0.0-20191128071424-29c7b0a0d805/examples/o2mrecur/ent/node/node.go +pkg/mod/github.com/facebookincubator/ent@v0.0.0-20191128071424-29c7b0a0d805/examples/o2mrecur/ent/node/where.go +pkg/mod/github.com/facebookincubator/ent@v0.0.0-20191128071424-29c7b0a0d805/examples/o2mrecur/ent/predicate/predicate.go +pkg/mod/github.com/facebookincubator/ent@v0.0.0-20191128071424-29c7b0a0d805/examples/o2mrecur/ent/schema/node.go +pkg/mod/github.com/facebookincubator/ent@v0.0.0-20191128071424-29c7b0a0d805/examples/o2o2types/o2o2types.go +pkg/mod/github.com/facebookincubator/ent@v0.0.0-20191128071424-29c7b0a0d805/examples/o2o2types/README.md +pkg/mod/github.com/facebookincubator/ent@v0.0.0-20191128071424-29c7b0a0d805/examples/o2o2types/ent/card_create.go +pkg/mod/github.com/facebookincubator/ent@v0.0.0-20191128071424-29c7b0a0d805/examples/o2o2types/ent/card_delete.go +pkg/mod/github.com/facebookincubator/ent@v0.0.0-20191128071424-29c7b0a0d805/examples/o2o2types/ent/card_query.go +pkg/mod/github.com/facebookincubator/ent@v0.0.0-20191128071424-29c7b0a0d805/examples/o2o2types/ent/card_update.go +pkg/mod/github.com/facebookincubator/ent@v0.0.0-20191128071424-29c7b0a0d805/examples/o2o2types/ent/card.go +pkg/mod/github.com/facebookincubator/ent@v0.0.0-20191128071424-29c7b0a0d805/examples/o2o2types/ent/client.go +pkg/mod/github.com/facebookincubator/ent@v0.0.0-20191128071424-29c7b0a0d805/examples/o2o2types/ent/config.go +pkg/mod/github.com/facebookincubator/ent@v0.0.0-20191128071424-29c7b0a0d805/examples/o2o2types/ent/context.go +pkg/mod/github.com/facebookincubator/ent@v0.0.0-20191128071424-29c7b0a0d805/examples/o2o2types/ent/ent.go +pkg/mod/github.com/facebookincubator/ent@v0.0.0-20191128071424-29c7b0a0d805/examples/o2o2types/ent/example_test.go +pkg/mod/github.com/facebookincubator/ent@v0.0.0-20191128071424-29c7b0a0d805/examples/o2o2types/ent/generate.go +pkg/mod/github.com/facebookincubator/ent@v0.0.0-20191128071424-29c7b0a0d805/examples/o2o2types/ent/tx.go +pkg/mod/github.com/facebookincubator/ent@v0.0.0-20191128071424-29c7b0a0d805/examples/o2o2types/ent/user_create.go +pkg/mod/github.com/facebookincubator/ent@v0.0.0-20191128071424-29c7b0a0d805/examples/o2o2types/ent/user_delete.go +pkg/mod/github.com/facebookincubator/ent@v0.0.0-20191128071424-29c7b0a0d805/examples/o2o2types/ent/user_query.go +pkg/mod/github.com/facebookincubator/ent@v0.0.0-20191128071424-29c7b0a0d805/examples/o2o2types/ent/user_update.go +pkg/mod/github.com/facebookincubator/ent@v0.0.0-20191128071424-29c7b0a0d805/examples/o2o2types/ent/user.go +pkg/mod/github.com/facebookincubator/ent@v0.0.0-20191128071424-29c7b0a0d805/examples/o2o2types/ent/card/card.go +pkg/mod/github.com/facebookincubator/ent@v0.0.0-20191128071424-29c7b0a0d805/examples/o2o2types/ent/card/where.go +pkg/mod/github.com/facebookincubator/ent@v0.0.0-20191128071424-29c7b0a0d805/examples/o2o2types/ent/migrate/migrate.go +pkg/mod/github.com/facebookincubator/ent@v0.0.0-20191128071424-29c7b0a0d805/examples/o2o2types/ent/migrate/schema.go +pkg/mod/github.com/facebookincubator/ent@v0.0.0-20191128071424-29c7b0a0d805/examples/o2o2types/ent/predicate/predicate.go +pkg/mod/github.com/facebookincubator/ent@v0.0.0-20191128071424-29c7b0a0d805/examples/o2o2types/ent/schema/card.go +pkg/mod/github.com/facebookincubator/ent@v0.0.0-20191128071424-29c7b0a0d805/examples/o2o2types/ent/schema/user.go +pkg/mod/github.com/facebookincubator/ent@v0.0.0-20191128071424-29c7b0a0d805/examples/o2o2types/ent/user/user.go +pkg/mod/github.com/facebookincubator/ent@v0.0.0-20191128071424-29c7b0a0d805/examples/o2o2types/ent/user/where.go +pkg/mod/github.com/facebookincubator/ent@v0.0.0-20191128071424-29c7b0a0d805/examples/o2obidi/o2obidi.go +pkg/mod/github.com/facebookincubator/ent@v0.0.0-20191128071424-29c7b0a0d805/examples/o2obidi/README.md +pkg/mod/github.com/facebookincubator/ent@v0.0.0-20191128071424-29c7b0a0d805/examples/o2obidi/ent/client.go +pkg/mod/github.com/facebookincubator/ent@v0.0.0-20191128071424-29c7b0a0d805/examples/o2obidi/ent/config.go +pkg/mod/github.com/facebookincubator/ent@v0.0.0-20191128071424-29c7b0a0d805/examples/o2obidi/ent/context.go +pkg/mod/github.com/facebookincubator/ent@v0.0.0-20191128071424-29c7b0a0d805/examples/o2obidi/ent/ent.go +pkg/mod/github.com/facebookincubator/ent@v0.0.0-20191128071424-29c7b0a0d805/examples/o2obidi/ent/example_test.go +pkg/mod/github.com/facebookincubator/ent@v0.0.0-20191128071424-29c7b0a0d805/examples/o2obidi/ent/generate.go +pkg/mod/github.com/facebookincubator/ent@v0.0.0-20191128071424-29c7b0a0d805/examples/o2obidi/ent/tx.go +pkg/mod/github.com/facebookincubator/ent@v0.0.0-20191128071424-29c7b0a0d805/examples/o2obidi/ent/user_create.go +pkg/mod/github.com/facebookincubator/ent@v0.0.0-20191128071424-29c7b0a0d805/examples/o2obidi/ent/user_delete.go +pkg/mod/github.com/facebookincubator/ent@v0.0.0-20191128071424-29c7b0a0d805/examples/o2obidi/ent/user_query.go +pkg/mod/github.com/facebookincubator/ent@v0.0.0-20191128071424-29c7b0a0d805/examples/o2obidi/ent/user_update.go +pkg/mod/github.com/facebookincubator/ent@v0.0.0-20191128071424-29c7b0a0d805/examples/o2obidi/ent/user.go +pkg/mod/github.com/facebookincubator/ent@v0.0.0-20191128071424-29c7b0a0d805/examples/o2obidi/ent/migrate/migrate.go +pkg/mod/github.com/facebookincubator/ent@v0.0.0-20191128071424-29c7b0a0d805/examples/o2obidi/ent/migrate/schema.go +pkg/mod/github.com/facebookincubator/ent@v0.0.0-20191128071424-29c7b0a0d805/examples/o2obidi/ent/predicate/predicate.go +pkg/mod/github.com/facebookincubator/ent@v0.0.0-20191128071424-29c7b0a0d805/examples/o2obidi/ent/schema/user.go +pkg/mod/github.com/facebookincubator/ent@v0.0.0-20191128071424-29c7b0a0d805/examples/o2obidi/ent/user/user.go +pkg/mod/github.com/facebookincubator/ent@v0.0.0-20191128071424-29c7b0a0d805/examples/o2obidi/ent/user/where.go +pkg/mod/github.com/facebookincubator/ent@v0.0.0-20191128071424-29c7b0a0d805/examples/o2orecur/o2orecur.go +pkg/mod/github.com/facebookincubator/ent@v0.0.0-20191128071424-29c7b0a0d805/examples/o2orecur/README.md +pkg/mod/github.com/facebookincubator/ent@v0.0.0-20191128071424-29c7b0a0d805/examples/o2orecur/ent/client.go +pkg/mod/github.com/facebookincubator/ent@v0.0.0-20191128071424-29c7b0a0d805/examples/o2orecur/ent/config.go +pkg/mod/github.com/facebookincubator/ent@v0.0.0-20191128071424-29c7b0a0d805/examples/o2orecur/ent/context.go +pkg/mod/github.com/facebookincubator/ent@v0.0.0-20191128071424-29c7b0a0d805/examples/o2orecur/ent/ent.go +pkg/mod/github.com/facebookincubator/ent@v0.0.0-20191128071424-29c7b0a0d805/examples/o2orecur/ent/example_test.go +pkg/mod/github.com/facebookincubator/ent@v0.0.0-20191128071424-29c7b0a0d805/examples/o2orecur/ent/generate.go +pkg/mod/github.com/facebookincubator/ent@v0.0.0-20191128071424-29c7b0a0d805/examples/o2orecur/ent/node_create.go +pkg/mod/github.com/facebookincubator/ent@v0.0.0-20191128071424-29c7b0a0d805/examples/o2orecur/ent/node_delete.go +pkg/mod/github.com/facebookincubator/ent@v0.0.0-20191128071424-29c7b0a0d805/examples/o2orecur/ent/node_query.go +pkg/mod/github.com/facebookincubator/ent@v0.0.0-20191128071424-29c7b0a0d805/examples/o2orecur/ent/node_update.go +pkg/mod/github.com/facebookincubator/ent@v0.0.0-20191128071424-29c7b0a0d805/examples/o2orecur/ent/node.go +pkg/mod/github.com/facebookincubator/ent@v0.0.0-20191128071424-29c7b0a0d805/examples/o2orecur/ent/tx.go +pkg/mod/github.com/facebookincubator/ent@v0.0.0-20191128071424-29c7b0a0d805/examples/o2orecur/ent/migrate/migrate.go +pkg/mod/github.com/facebookincubator/ent@v0.0.0-20191128071424-29c7b0a0d805/examples/o2orecur/ent/migrate/schema.go +pkg/mod/github.com/facebookincubator/ent@v0.0.0-20191128071424-29c7b0a0d805/examples/o2orecur/ent/node/node.go +pkg/mod/github.com/facebookincubator/ent@v0.0.0-20191128071424-29c7b0a0d805/examples/o2orecur/ent/node/where.go +pkg/mod/github.com/facebookincubator/ent@v0.0.0-20191128071424-29c7b0a0d805/examples/o2orecur/ent/predicate/predicate.go +pkg/mod/github.com/facebookincubator/ent@v0.0.0-20191128071424-29c7b0a0d805/examples/o2orecur/ent/schema/node.go +pkg/mod/github.com/facebookincubator/ent@v0.0.0-20191128071424-29c7b0a0d805/examples/start/README.md +pkg/mod/github.com/facebookincubator/ent@v0.0.0-20191128071424-29c7b0a0d805/examples/start/start.go +pkg/mod/github.com/facebookincubator/ent@v0.0.0-20191128071424-29c7b0a0d805/examples/start/ent/car_create.go +pkg/mod/github.com/facebookincubator/ent@v0.0.0-20191128071424-29c7b0a0d805/examples/start/ent/car_delete.go +pkg/mod/github.com/facebookincubator/ent@v0.0.0-20191128071424-29c7b0a0d805/examples/start/ent/car_query.go +pkg/mod/github.com/facebookincubator/ent@v0.0.0-20191128071424-29c7b0a0d805/examples/start/ent/car_update.go +pkg/mod/github.com/facebookincubator/ent@v0.0.0-20191128071424-29c7b0a0d805/examples/start/ent/car.go +pkg/mod/github.com/facebookincubator/ent@v0.0.0-20191128071424-29c7b0a0d805/examples/start/ent/client.go +pkg/mod/github.com/facebookincubator/ent@v0.0.0-20191128071424-29c7b0a0d805/examples/start/ent/config.go +pkg/mod/github.com/facebookincubator/ent@v0.0.0-20191128071424-29c7b0a0d805/examples/start/ent/context.go +pkg/mod/github.com/facebookincubator/ent@v0.0.0-20191128071424-29c7b0a0d805/examples/start/ent/ent.go +pkg/mod/github.com/facebookincubator/ent@v0.0.0-20191128071424-29c7b0a0d805/examples/start/ent/example_test.go +pkg/mod/github.com/facebookincubator/ent@v0.0.0-20191128071424-29c7b0a0d805/examples/start/ent/generate.go +pkg/mod/github.com/facebookincubator/ent@v0.0.0-20191128071424-29c7b0a0d805/examples/start/ent/group_create.go +pkg/mod/github.com/facebookincubator/ent@v0.0.0-20191128071424-29c7b0a0d805/examples/start/ent/group_delete.go +pkg/mod/github.com/facebookincubator/ent@v0.0.0-20191128071424-29c7b0a0d805/examples/start/ent/group_query.go +pkg/mod/github.com/facebookincubator/ent@v0.0.0-20191128071424-29c7b0a0d805/examples/start/ent/group_update.go +pkg/mod/github.com/facebookincubator/ent@v0.0.0-20191128071424-29c7b0a0d805/examples/start/ent/group.go +pkg/mod/github.com/facebookincubator/ent@v0.0.0-20191128071424-29c7b0a0d805/examples/start/ent/tx.go +pkg/mod/github.com/facebookincubator/ent@v0.0.0-20191128071424-29c7b0a0d805/examples/start/ent/user_create.go +pkg/mod/github.com/facebookincubator/ent@v0.0.0-20191128071424-29c7b0a0d805/examples/start/ent/user_delete.go +pkg/mod/github.com/facebookincubator/ent@v0.0.0-20191128071424-29c7b0a0d805/examples/start/ent/user_query.go +pkg/mod/github.com/facebookincubator/ent@v0.0.0-20191128071424-29c7b0a0d805/examples/start/ent/user_update.go +pkg/mod/github.com/facebookincubator/ent@v0.0.0-20191128071424-29c7b0a0d805/examples/start/ent/user.go +pkg/mod/github.com/facebookincubator/ent@v0.0.0-20191128071424-29c7b0a0d805/examples/start/ent/car/car.go +pkg/mod/github.com/facebookincubator/ent@v0.0.0-20191128071424-29c7b0a0d805/examples/start/ent/car/where.go +pkg/mod/github.com/facebookincubator/ent@v0.0.0-20191128071424-29c7b0a0d805/examples/start/ent/group/group.go +pkg/mod/github.com/facebookincubator/ent@v0.0.0-20191128071424-29c7b0a0d805/examples/start/ent/group/where.go +pkg/mod/github.com/facebookincubator/ent@v0.0.0-20191128071424-29c7b0a0d805/examples/start/ent/migrate/migrate.go +pkg/mod/github.com/facebookincubator/ent@v0.0.0-20191128071424-29c7b0a0d805/examples/start/ent/migrate/schema.go +pkg/mod/github.com/facebookincubator/ent@v0.0.0-20191128071424-29c7b0a0d805/examples/start/ent/predicate/predicate.go +pkg/mod/github.com/facebookincubator/ent@v0.0.0-20191128071424-29c7b0a0d805/examples/start/ent/schema/car.go +pkg/mod/github.com/facebookincubator/ent@v0.0.0-20191128071424-29c7b0a0d805/examples/start/ent/schema/group.go +pkg/mod/github.com/facebookincubator/ent@v0.0.0-20191128071424-29c7b0a0d805/examples/start/ent/schema/user.go +pkg/mod/github.com/facebookincubator/ent@v0.0.0-20191128071424-29c7b0a0d805/examples/start/ent/user/user.go +pkg/mod/github.com/facebookincubator/ent@v0.0.0-20191128071424-29c7b0a0d805/examples/start/ent/user/where.go +pkg/mod/github.com/facebookincubator/ent@v0.0.0-20191128071424-29c7b0a0d805/examples/traversal/README.md +pkg/mod/github.com/facebookincubator/ent@v0.0.0-20191128071424-29c7b0a0d805/examples/traversal/traversal.go +pkg/mod/github.com/facebookincubator/ent@v0.0.0-20191128071424-29c7b0a0d805/examples/traversal/ent/client.go +pkg/mod/github.com/facebookincubator/ent@v0.0.0-20191128071424-29c7b0a0d805/examples/traversal/ent/config.go +pkg/mod/github.com/facebookincubator/ent@v0.0.0-20191128071424-29c7b0a0d805/examples/traversal/ent/context.go +pkg/mod/github.com/facebookincubator/ent@v0.0.0-20191128071424-29c7b0a0d805/examples/traversal/ent/ent.go +pkg/mod/github.com/facebookincubator/ent@v0.0.0-20191128071424-29c7b0a0d805/examples/traversal/ent/example_test.go +pkg/mod/github.com/facebookincubator/ent@v0.0.0-20191128071424-29c7b0a0d805/examples/traversal/ent/generate.go +pkg/mod/github.com/facebookincubator/ent@v0.0.0-20191128071424-29c7b0a0d805/examples/traversal/ent/group_create.go +pkg/mod/github.com/facebookincubator/ent@v0.0.0-20191128071424-29c7b0a0d805/examples/traversal/ent/group_delete.go +pkg/mod/github.com/facebookincubator/ent@v0.0.0-20191128071424-29c7b0a0d805/examples/traversal/ent/group_query.go +pkg/mod/github.com/facebookincubator/ent@v0.0.0-20191128071424-29c7b0a0d805/examples/traversal/ent/group_update.go +pkg/mod/github.com/facebookincubator/ent@v0.0.0-20191128071424-29c7b0a0d805/examples/traversal/ent/group.go +pkg/mod/github.com/facebookincubator/ent@v0.0.0-20191128071424-29c7b0a0d805/examples/traversal/ent/pet_create.go +pkg/mod/github.com/facebookincubator/ent@v0.0.0-20191128071424-29c7b0a0d805/examples/traversal/ent/pet_delete.go +pkg/mod/github.com/facebookincubator/ent@v0.0.0-20191128071424-29c7b0a0d805/examples/traversal/ent/pet_query.go +pkg/mod/github.com/facebookincubator/ent@v0.0.0-20191128071424-29c7b0a0d805/examples/traversal/ent/pet_update.go +pkg/mod/github.com/facebookincubator/ent@v0.0.0-20191128071424-29c7b0a0d805/examples/traversal/ent/pet.go +pkg/mod/github.com/facebookincubator/ent@v0.0.0-20191128071424-29c7b0a0d805/examples/traversal/ent/tx.go +pkg/mod/github.com/facebookincubator/ent@v0.0.0-20191128071424-29c7b0a0d805/examples/traversal/ent/user_create.go +pkg/mod/github.com/facebookincubator/ent@v0.0.0-20191128071424-29c7b0a0d805/examples/traversal/ent/user_delete.go +pkg/mod/github.com/facebookincubator/ent@v0.0.0-20191128071424-29c7b0a0d805/examples/traversal/ent/user_query.go +pkg/mod/github.com/facebookincubator/ent@v0.0.0-20191128071424-29c7b0a0d805/examples/traversal/ent/user_update.go +pkg/mod/github.com/facebookincubator/ent@v0.0.0-20191128071424-29c7b0a0d805/examples/traversal/ent/user.go +pkg/mod/github.com/facebookincubator/ent@v0.0.0-20191128071424-29c7b0a0d805/examples/traversal/ent/group/group.go +pkg/mod/github.com/facebookincubator/ent@v0.0.0-20191128071424-29c7b0a0d805/examples/traversal/ent/group/where.go +pkg/mod/github.com/facebookincubator/ent@v0.0.0-20191128071424-29c7b0a0d805/examples/traversal/ent/migrate/migrate.go +pkg/mod/github.com/facebookincubator/ent@v0.0.0-20191128071424-29c7b0a0d805/examples/traversal/ent/migrate/schema.go +pkg/mod/github.com/facebookincubator/ent@v0.0.0-20191128071424-29c7b0a0d805/examples/traversal/ent/pet/pet.go +pkg/mod/github.com/facebookincubator/ent@v0.0.0-20191128071424-29c7b0a0d805/examples/traversal/ent/pet/where.go +pkg/mod/github.com/facebookincubator/ent@v0.0.0-20191128071424-29c7b0a0d805/examples/traversal/ent/predicate/predicate.go +pkg/mod/github.com/facebookincubator/ent@v0.0.0-20191128071424-29c7b0a0d805/examples/traversal/ent/schema/group.go +pkg/mod/github.com/facebookincubator/ent@v0.0.0-20191128071424-29c7b0a0d805/examples/traversal/ent/schema/pet.go +pkg/mod/github.com/facebookincubator/ent@v0.0.0-20191128071424-29c7b0a0d805/examples/traversal/ent/schema/user.go +pkg/mod/github.com/facebookincubator/ent@v0.0.0-20191128071424-29c7b0a0d805/examples/traversal/ent/user/user.go +pkg/mod/github.com/facebookincubator/ent@v0.0.0-20191128071424-29c7b0a0d805/examples/traversal/ent/user/where.go +pkg/mod/github.com/facebookincubator/ent@v0.0.0-20191128071424-29c7b0a0d805/schema/edge/edge_test.go +pkg/mod/github.com/facebookincubator/ent@v0.0.0-20191128071424-29c7b0a0d805/schema/edge/edge.go +pkg/mod/github.com/facebookincubator/ent@v0.0.0-20191128071424-29c7b0a0d805/schema/field/field_test.go +pkg/mod/github.com/facebookincubator/ent@v0.0.0-20191128071424-29c7b0a0d805/schema/field/field.go +pkg/mod/github.com/facebookincubator/ent@v0.0.0-20191128071424-29c7b0a0d805/schema/field/numeric.go +pkg/mod/github.com/facebookincubator/ent@v0.0.0-20191128071424-29c7b0a0d805/schema/field/type.go +pkg/mod/github.com/facebookincubator/ent@v0.0.0-20191128071424-29c7b0a0d805/schema/field/gen/gen.go +pkg/mod/github.com/facebookincubator/ent@v0.0.0-20191128071424-29c7b0a0d805/schema/field/gen/numeric.tmpl +pkg/mod/github.com/facebookincubator/ent@v0.0.0-20191128071424-29c7b0a0d805/schema/index/index_test.go +pkg/mod/github.com/facebookincubator/ent@v0.0.0-20191128071424-29c7b0a0d805/schema/index/index.go +pkg/mod/github.com/facebookincubator/ent@v0.0.0-20191128071424-29c7b0a0d805/schema/schemautil/time_test.go +pkg/mod/github.com/facebookincubator/ent@v0.0.0-20191128071424-29c7b0a0d805/schema/schemautil/time.go +pkg/mod/github.com/go-openapi/analysis@v0.19.5/.codecov.yml +pkg/mod/github.com/go-openapi/analysis@v0.19.5/.gitignore +pkg/mod/github.com/go-openapi/analysis@v0.19.5/.golangci.yml +pkg/mod/github.com/go-openapi/analysis@v0.19.5/.travis.yml +pkg/mod/github.com/go-openapi/analysis@v0.19.5/analyzer_test.go +pkg/mod/github.com/go-openapi/analysis@v0.19.5/analyzer.go +pkg/mod/github.com/go-openapi/analysis@v0.19.5/appveyor.yml +pkg/mod/github.com/go-openapi/analysis@v0.19.5/CODE_OF_CONDUCT.md +pkg/mod/github.com/go-openapi/analysis@v0.19.5/debug_test.go +pkg/mod/github.com/go-openapi/analysis@v0.19.5/debug.go +pkg/mod/github.com/go-openapi/analysis@v0.19.5/doc_test.go +pkg/mod/github.com/go-openapi/analysis@v0.19.5/doc.go +pkg/mod/github.com/go-openapi/analysis@v0.19.5/fixer_test.go +pkg/mod/github.com/go-openapi/analysis@v0.19.5/fixer.go +pkg/mod/github.com/go-openapi/analysis@v0.19.5/flatten_test.go +pkg/mod/github.com/go-openapi/analysis@v0.19.5/flatten.go +pkg/mod/github.com/go-openapi/analysis@v0.19.5/go.mod +pkg/mod/github.com/go-openapi/analysis@v0.19.5/go.sum +pkg/mod/github.com/go-openapi/analysis@v0.19.5/LICENSE +pkg/mod/github.com/go-openapi/analysis@v0.19.5/mixin_test.go +pkg/mod/github.com/go-openapi/analysis@v0.19.5/mixin.go +pkg/mod/github.com/go-openapi/analysis@v0.19.5/README.md +pkg/mod/github.com/go-openapi/analysis@v0.19.5/schema_test.go +pkg/mod/github.com/go-openapi/analysis@v0.19.5/schema.go +pkg/mod/github.com/go-openapi/analysis@v0.19.5/.github/CONTRIBUTING.md +pkg/mod/github.com/go-openapi/analysis@v0.19.5/fixtures/allOf.yml +pkg/mod/github.com/go-openapi/analysis@v0.19.5/fixtures/bar-crud.yml +pkg/mod/github.com/go-openapi/analysis@v0.19.5/fixtures/definitions.yml +pkg/mod/github.com/go-openapi/analysis@v0.19.5/fixtures/empty-paths.json +pkg/mod/github.com/go-openapi/analysis@v0.19.5/fixtures/empty-props.yml +pkg/mod/github.com/go-openapi/analysis@v0.19.5/fixtures/enums.yml +pkg/mod/github.com/go-openapi/analysis@v0.19.5/fixtures/errors.yml +pkg/mod/github.com/go-openapi/analysis@v0.19.5/fixtures/external_definitions_valid.yml +pkg/mod/github.com/go-openapi/analysis@v0.19.5/fixtures/external_definitions.yml +pkg/mod/github.com/go-openapi/analysis@v0.19.5/fixtures/fixture-342-2.yaml +pkg/mod/github.com/go-openapi/analysis@v0.19.5/fixtures/fixture-342-3.yaml +pkg/mod/github.com/go-openapi/analysis@v0.19.5/fixtures/fixture-342.yaml +pkg/mod/github.com/go-openapi/analysis@v0.19.5/fixtures/fixture-1289-param.yaml +pkg/mod/github.com/go-openapi/analysis@v0.19.5/fixtures/flatten.yml +pkg/mod/github.com/go-openapi/analysis@v0.19.5/fixtures/foo-crud.yml +pkg/mod/github.com/go-openapi/analysis@v0.19.5/fixtures/inline_schemas.yml +pkg/mod/github.com/go-openapi/analysis@v0.19.5/fixtures/more_nested_inline_schemas.yml +pkg/mod/github.com/go-openapi/analysis@v0.19.5/fixtures/nested_inline_schemas.yml +pkg/mod/github.com/go-openapi/analysis@v0.19.5/fixtures/no-paths.yml +pkg/mod/github.com/go-openapi/analysis@v0.19.5/fixtures/other-mixin.yml +pkg/mod/github.com/go-openapi/analysis@v0.19.5/fixtures/patterns.yml +pkg/mod/github.com/go-openapi/analysis@v0.19.5/fixtures/references.yml +pkg/mod/github.com/go-openapi/analysis@v0.19.5/fixtures/securitydef.yml +pkg/mod/github.com/go-openapi/analysis@v0.19.5/fixtures/swagger-props.yml +pkg/mod/github.com/go-openapi/analysis@v0.19.5/fixtures/widget-crud.yml +pkg/mod/github.com/go-openapi/analysis@v0.19.5/fixtures/bugs/bitbucket.json +pkg/mod/github.com/go-openapi/analysis@v0.19.5/fixtures/bugs/1429/responses.yaml +pkg/mod/github.com/go-openapi/analysis@v0.19.5/fixtures/bugs/1429/swagger.yaml +pkg/mod/github.com/go-openapi/analysis@v0.19.5/fixtures/bugs/1429/remote/remote.yaml +pkg/mod/github.com/go-openapi/analysis@v0.19.5/fixtures/bugs/1429/remote/remote/remote.yaml +pkg/mod/github.com/go-openapi/analysis@v0.19.5/fixtures/bugs/1602/fixture-1602-1.yaml +pkg/mod/github.com/go-openapi/analysis@v0.19.5/fixtures/bugs/1602/fixture-1602-2.yaml +pkg/mod/github.com/go-openapi/analysis@v0.19.5/fixtures/bugs/1602/fixture-1602-3.yaml +pkg/mod/github.com/go-openapi/analysis@v0.19.5/fixtures/bugs/1602/fixture-1602-4.yaml +pkg/mod/github.com/go-openapi/analysis@v0.19.5/fixtures/bugs/1602/fixture-1602-5.yaml +pkg/mod/github.com/go-openapi/analysis@v0.19.5/fixtures/bugs/1602/fixture-1602-6.yaml +pkg/mod/github.com/go-openapi/analysis@v0.19.5/fixtures/bugs/1602/fixture-1602-full.yaml +pkg/mod/github.com/go-openapi/analysis@v0.19.5/fixtures/bugs/1602/other-invalid-pointers.yaml +pkg/mod/github.com/go-openapi/analysis@v0.19.5/fixtures/bugs/1614/gitea.yaml +pkg/mod/github.com/go-openapi/analysis@v0.19.5/fixtures/bugs/1621/definitions.yaml +pkg/mod/github.com/go-openapi/analysis@v0.19.5/fixtures/bugs/1621/fixture-1621.yaml +pkg/mod/github.com/go-openapi/analysis@v0.19.5/fixtures/bugs/1621/parameters.yaml +pkg/mod/github.com/go-openapi/analysis@v0.19.5/fixtures/bugs/1621/responses.yaml +pkg/mod/github.com/go-openapi/analysis@v0.19.5/fixtures/bugs/1767/fixture-1767.yaml +pkg/mod/github.com/go-openapi/analysis@v0.19.5/fixtures/bugs/1767/Identifier.yaml +pkg/mod/github.com/go-openapi/analysis@v0.19.5/fixtures/bugs/1767/Patient.yaml +pkg/mod/github.com/go-openapi/analysis@v0.19.5/fixtures/bugs/1767/Reference.yaml +pkg/mod/github.com/go-openapi/analysis@v0.19.5/fixtures/bugs/1774/AccessToken.yaml +pkg/mod/github.com/go-openapi/analysis@v0.19.5/fixtures/bugs/1774/Credentials.yaml +pkg/mod/github.com/go-openapi/analysis@v0.19.5/fixtures/bugs/1774/Data.yaml +pkg/mod/github.com/go-openapi/analysis@v0.19.5/fixtures/bugs/1774/def_api.yaml +pkg/mod/github.com/go-openapi/analysis@v0.19.5/fixtures/bugs/1774/Error.yaml +pkg/mod/github.com/go-openapi/analysis@v0.19.5/fixtures/bugs/1774/parameters.yaml +pkg/mod/github.com/go-openapi/analysis@v0.19.5/fixtures/bugs/1774/responses.yaml +pkg/mod/github.com/go-openapi/analysis@v0.19.5/fixtures/bugs/1774/Roles.yaml +pkg/mod/github.com/go-openapi/analysis@v0.19.5/fixtures/bugs/1774/User.yaml +pkg/mod/github.com/go-openapi/analysis@v0.19.5/fixtures/bugs/1774/Users.yaml +pkg/mod/github.com/go-openapi/analysis@v0.19.5/fixtures/bugs/1796/queryIssue.json +pkg/mod/github.com/go-openapi/analysis@v0.19.5/fixtures/bugs/1796/models/pair.json +pkg/mod/github.com/go-openapi/analysis@v0.19.5/fixtures/bugs/1796/models/query.json +pkg/mod/github.com/go-openapi/analysis@v0.19.5/fixtures/bugs/1851/definitions-3.yaml +pkg/mod/github.com/go-openapi/analysis@v0.19.5/fixtures/bugs/1851/definitions-31.yaml +pkg/mod/github.com/go-openapi/analysis@v0.19.5/fixtures/bugs/1851/definitions.yaml +pkg/mod/github.com/go-openapi/analysis@v0.19.5/fixtures/bugs/1851/fixture-1851-2.yaml +pkg/mod/github.com/go-openapi/analysis@v0.19.5/fixtures/bugs/1851/fixture-1851-3.yaml +pkg/mod/github.com/go-openapi/analysis@v0.19.5/fixtures/bugs/1851/fixture-1851.yaml +pkg/mod/github.com/go-openapi/analysis@v0.19.5/fixtures/bugs/1851/remote/definitions-32.yaml +pkg/mod/github.com/go-openapi/analysis@v0.19.5/fixtures/bugs/remote-absolute/ce_v0_2_without_data.json +pkg/mod/github.com/go-openapi/analysis@v0.19.5/fixtures/bugs/remote-absolute/remote-spec.json +pkg/mod/github.com/go-openapi/analysis@v0.19.5/fixtures/bugs/remote-absolute/swagger-mini.json +pkg/mod/github.com/go-openapi/analysis@v0.19.5/fixtures/bugs/remote-absolute/swagger-with-local-ref.json +pkg/mod/github.com/go-openapi/analysis@v0.19.5/fixtures/bugs/remote-absolute/swagger-with-ref.json +pkg/mod/github.com/go-openapi/analysis@v0.19.5/fixtures/bugs/remote-absolute/swagger-with-remote-only-ref.json +pkg/mod/github.com/go-openapi/analysis@v0.19.5/fixtures/bugs/remote-absolute/swagger.json +pkg/mod/github.com/go-openapi/analysis@v0.19.5/fixtures/errors/fixture-unexpandable-2.yaml +pkg/mod/github.com/go-openapi/analysis@v0.19.5/fixtures/errors/fixture-unexpandable.yaml +pkg/mod/github.com/go-openapi/analysis@v0.19.5/fixtures/external/definitions.yml +pkg/mod/github.com/go-openapi/analysis@v0.19.5/fixtures/external/definitions2.yml +pkg/mod/github.com/go-openapi/analysis@v0.19.5/fixtures/external/errors.yml +pkg/mod/github.com/go-openapi/analysis@v0.19.5/fixtures/external/nestedParams.yml +pkg/mod/github.com/go-openapi/analysis@v0.19.5/fixtures/external/nestedResponses.yml +pkg/mod/github.com/go-openapi/analysis@v0.19.5/fixtures/external/parameters.yml +pkg/mod/github.com/go-openapi/analysis@v0.19.5/fixtures/external/pathItem.yml +pkg/mod/github.com/go-openapi/analysis@v0.19.5/fixtures/external/responses.yml +pkg/mod/github.com/go-openapi/analysis@v0.19.5/fixtures/fixer/fixer.yaml +pkg/mod/github.com/go-openapi/analysis@v0.19.5/fixtures/oaigen/fixture-oaigen.yaml +pkg/mod/github.com/go-openapi/analysis@v0.19.5/fixtures/oaigen/test3-bis-swagger.yaml +pkg/mod/github.com/go-openapi/analysis@v0.19.5/fixtures/oaigen/test3-model-schema.json +pkg/mod/github.com/go-openapi/analysis@v0.19.5/fixtures/oaigen/test3-swagger.yaml +pkg/mod/github.com/go-openapi/analysis@v0.19.5/fixtures/oaigen/test3-ter-model-schema.json +pkg/mod/github.com/go-openapi/analysis@v0.19.5/fixtures/oaigen/test3-ter-swagger-flat.json +pkg/mod/github.com/go-openapi/analysis@v0.19.5/fixtures/oaigen/test3-ter-swagger.yaml +pkg/mod/github.com/go-openapi/analysis@v0.19.5/fixtures/oaigen/transitive-1.yaml +pkg/mod/github.com/go-openapi/analysis@v0.19.5/fixtures/oaigen/transitive-2.yaml +pkg/mod/github.com/go-openapi/analysis@v0.19.5/fixtures/operations/fixture-operations.yaml +pkg/mod/github.com/go-openapi/analysis@v0.19.5/fixtures/parameters/fixture-parameters.yaml +pkg/mod/github.com/go-openapi/analysis@v0.19.5/fixtures/parameters/other-source.yaml +pkg/mod/github.com/go-openapi/analysis@v0.19.5/fixtures/pointers/fixture-pointers-loop.yaml +pkg/mod/github.com/go-openapi/analysis@v0.19.5/fixtures/pointers/fixture-pointers.yaml +pkg/mod/github.com/go-openapi/analysis@v0.19.5/fixtures/pointers/remote.yaml +pkg/mod/github.com/go-openapi/analysis@v0.19.5/internal/path_unescape_test.go +pkg/mod/github.com/go-openapi/analysis@v0.19.5/internal/post_go18.go +pkg/mod/github.com/go-openapi/analysis@v0.19.5/internal/pre_go18.go +pkg/mod/github.com/go-openapi/errors@v0.19.2/.gitignore +pkg/mod/github.com/go-openapi/errors@v0.19.2/.golangci.yml +pkg/mod/github.com/go-openapi/errors@v0.19.2/.travis.yml +pkg/mod/github.com/go-openapi/errors@v0.19.2/api_test.go +pkg/mod/github.com/go-openapi/errors@v0.19.2/api.go +pkg/mod/github.com/go-openapi/errors@v0.19.2/auth_test.go +pkg/mod/github.com/go-openapi/errors@v0.19.2/auth.go +pkg/mod/github.com/go-openapi/errors@v0.19.2/CODE_OF_CONDUCT.md +pkg/mod/github.com/go-openapi/errors@v0.19.2/doc.go +pkg/mod/github.com/go-openapi/errors@v0.19.2/go.mod +pkg/mod/github.com/go-openapi/errors@v0.19.2/go.sum +pkg/mod/github.com/go-openapi/errors@v0.19.2/headers.go +pkg/mod/github.com/go-openapi/errors@v0.19.2/LICENSE +pkg/mod/github.com/go-openapi/errors@v0.19.2/middleware_test.go +pkg/mod/github.com/go-openapi/errors@v0.19.2/middleware.go +pkg/mod/github.com/go-openapi/errors@v0.19.2/parsing_test.go +pkg/mod/github.com/go-openapi/errors@v0.19.2/parsing.go +pkg/mod/github.com/go-openapi/errors@v0.19.2/README.md +pkg/mod/github.com/go-openapi/errors@v0.19.2/schema_test.go +pkg/mod/github.com/go-openapi/errors@v0.19.2/schema.go +pkg/mod/github.com/go-openapi/errors@v0.19.2/.github/CONTRIBUTING.md +pkg/mod/github.com/go-openapi/jsonpointer@v0.19.3/.editorconfig +pkg/mod/github.com/go-openapi/jsonpointer@v0.19.3/.gitignore +pkg/mod/github.com/go-openapi/jsonpointer@v0.19.3/.travis.yml +pkg/mod/github.com/go-openapi/jsonpointer@v0.19.3/CODE_OF_CONDUCT.md +pkg/mod/github.com/go-openapi/jsonpointer@v0.19.3/go.mod +pkg/mod/github.com/go-openapi/jsonpointer@v0.19.3/go.sum +pkg/mod/github.com/go-openapi/jsonpointer@v0.19.3/LICENSE +pkg/mod/github.com/go-openapi/jsonpointer@v0.19.3/pointer_test.go +pkg/mod/github.com/go-openapi/jsonpointer@v0.19.3/pointer.go +pkg/mod/github.com/go-openapi/jsonpointer@v0.19.3/README.md +pkg/mod/github.com/go-openapi/jsonpointer@v0.19.3/.github/CONTRIBUTING.md +pkg/mod/github.com/go-openapi/jsonreference@v0.19.2/.gitignore +pkg/mod/github.com/go-openapi/jsonreference@v0.19.2/.travis.yml +pkg/mod/github.com/go-openapi/jsonreference@v0.19.2/CODE_OF_CONDUCT.md +pkg/mod/github.com/go-openapi/jsonreference@v0.19.2/go.mod +pkg/mod/github.com/go-openapi/jsonreference@v0.19.2/go.sum +pkg/mod/github.com/go-openapi/jsonreference@v0.19.2/LICENSE +pkg/mod/github.com/go-openapi/jsonreference@v0.19.2/README.md +pkg/mod/github.com/go-openapi/jsonreference@v0.19.2/reference_test.go +pkg/mod/github.com/go-openapi/jsonreference@v0.19.2/reference.go +pkg/mod/github.com/go-openapi/jsonreference@v0.19.2/.github/CONTRIBUTING.md +pkg/mod/github.com/go-openapi/loads@v0.19.3/.editorconfig +pkg/mod/github.com/go-openapi/loads@v0.19.3/.gitignore +pkg/mod/github.com/go-openapi/loads@v0.19.3/.golangci.yml +pkg/mod/github.com/go-openapi/loads@v0.19.3/.travis.yml +pkg/mod/github.com/go-openapi/loads@v0.19.3/CODE_OF_CONDUCT.md +pkg/mod/github.com/go-openapi/loads@v0.19.3/doc.go +pkg/mod/github.com/go-openapi/loads@v0.19.3/go.mod +pkg/mod/github.com/go-openapi/loads@v0.19.3/go.sum +pkg/mod/github.com/go-openapi/loads@v0.19.3/json_test.go +pkg/mod/github.com/go-openapi/loads@v0.19.3/LICENSE +pkg/mod/github.com/go-openapi/loads@v0.19.3/README.md +pkg/mod/github.com/go-openapi/loads@v0.19.3/spec_test.go +pkg/mod/github.com/go-openapi/loads@v0.19.3/spec.go +pkg/mod/github.com/go-openapi/loads@v0.19.3/.github/CONTRIBUTING.md +pkg/mod/github.com/go-openapi/loads@v0.19.3/fixtures/bugs/1816/fixture-1816.yaml +pkg/mod/github.com/go-openapi/loads@v0.19.3/fixtures/json/models/models.json +pkg/mod/github.com/go-openapi/loads@v0.19.3/fixtures/json/models/modelWithArrayRef.json +pkg/mod/github.com/go-openapi/loads@v0.19.3/fixtures/json/models/modelWithComposition.json +pkg/mod/github.com/go-openapi/loads@v0.19.3/fixtures/json/models/modelWithDateTimeMap.json +pkg/mod/github.com/go-openapi/loads@v0.19.3/fixtures/json/models/modelWithExamples.json +pkg/mod/github.com/go-openapi/loads@v0.19.3/fixtures/json/models/modelWithInt32Map.json +pkg/mod/github.com/go-openapi/loads@v0.19.3/fixtures/json/models/modelWithInt64Map.json +pkg/mod/github.com/go-openapi/loads@v0.19.3/fixtures/json/models/modelWithMultipleProperties.json +pkg/mod/github.com/go-openapi/loads@v0.19.3/fixtures/json/models/modelWithObjectMap.json +pkg/mod/github.com/go-openapi/loads@v0.19.3/fixtures/json/models/modelWithPrimitiveArray.json +pkg/mod/github.com/go-openapi/loads@v0.19.3/fixtures/json/models/modelWithStringProperty.json +pkg/mod/github.com/go-openapi/loads@v0.19.3/fixtures/json/models/modelWithXmlAttributes.json +pkg/mod/github.com/go-openapi/loads@v0.19.3/fixtures/json/models/multipleModels.json +pkg/mod/github.com/go-openapi/loads@v0.19.3/fixtures/json/models/properties/propertyWithBooleanArray.json +pkg/mod/github.com/go-openapi/loads@v0.19.3/fixtures/json/models/properties/propertyWithByteArray.json +pkg/mod/github.com/go-openapi/loads@v0.19.3/fixtures/json/models/properties/propertyWithComplexArray.json +pkg/mod/github.com/go-openapi/loads@v0.19.3/fixtures/json/models/properties/propertyWithDateTimeArray.json +pkg/mod/github.com/go-openapi/loads@v0.19.3/fixtures/json/models/properties/propertyWithInt32Array.json +pkg/mod/github.com/go-openapi/loads@v0.19.3/fixtures/json/models/properties/propertyWithInt64Array.json +pkg/mod/github.com/go-openapi/loads@v0.19.3/fixtures/json/models/properties/propertyWithRef.json +pkg/mod/github.com/go-openapi/loads@v0.19.3/fixtures/json/models/properties/propertyWithStringArray.json +pkg/mod/github.com/go-openapi/loads@v0.19.3/fixtures/json/models/properties/simpleBooleanProperty.json +pkg/mod/github.com/go-openapi/loads@v0.19.3/fixtures/json/models/properties/simpleByteProperty.json +pkg/mod/github.com/go-openapi/loads@v0.19.3/fixtures/json/models/properties/simpleDateTimeProperty.json +pkg/mod/github.com/go-openapi/loads@v0.19.3/fixtures/json/models/properties/simpleInt32Property.json +pkg/mod/github.com/go-openapi/loads@v0.19.3/fixtures/json/models/properties/simpleInt64Property.json +pkg/mod/github.com/go-openapi/loads@v0.19.3/fixtures/json/models/properties/simpleStringProperty.json +pkg/mod/github.com/go-openapi/loads@v0.19.3/fixtures/json/resources/cascadingSchemes.json +pkg/mod/github.com/go-openapi/loads@v0.19.3/fixtures/json/resources/commonParameters.json +pkg/mod/github.com/go-openapi/loads@v0.19.3/fixtures/json/resources/multipleMimeTypes.json +pkg/mod/github.com/go-openapi/loads@v0.19.3/fixtures/json/resources/resourceWithExamplePayload.json +pkg/mod/github.com/go-openapi/loads@v0.19.3/fixtures/json/resources/resourceWithLinkedDefinitions_part1.json +pkg/mod/github.com/go-openapi/loads@v0.19.3/fixtures/json/resources/resourceWithLinkedDefinitions.json +pkg/mod/github.com/go-openapi/loads@v0.19.3/fixtures/json/resources/resourceWithRelativeHost.json +pkg/mod/github.com/go-openapi/loads@v0.19.3/fixtures/json/resources/reusableParameters.json +pkg/mod/github.com/go-openapi/loads@v0.19.3/fixtures/json/resources/securityExample.json +pkg/mod/github.com/go-openapi/loads@v0.19.3/fixtures/json/resources/stringPathParamResource.json +pkg/mod/github.com/go-openapi/loads@v0.19.3/fixtures/json/resources/taggedResource.json +pkg/mod/github.com/go-openapi/loads@v0.19.3/fixtures/json/resources/vendorExtensionExamples.json +pkg/mod/github.com/go-openapi/loads@v0.19.3/fixtures/json/resources/operations/operationWithTags.json +pkg/mod/github.com/go-openapi/loads@v0.19.3/fixtures/json/resources/operations/stringPathAndBoolQueryParamResource.json +pkg/mod/github.com/go-openapi/loads@v0.19.3/fixtures/json/resources/operations/stringPathParamResource.json +pkg/mod/github.com/go-openapi/loads@v0.19.3/fixtures/json/resources/parameters/bodyComplexArrayParameter.json +pkg/mod/github.com/go-openapi/loads@v0.19.3/fixtures/json/resources/parameters/bodyComplexParameter.json +pkg/mod/github.com/go-openapi/loads@v0.19.3/fixtures/json/resources/parameters/bodyInt64Parameter.json +pkg/mod/github.com/go-openapi/loads@v0.19.3/fixtures/json/resources/parameters/bodyStringArrayParameter.json +pkg/mod/github.com/go-openapi/loads@v0.19.3/fixtures/json/resources/parameters/bodyStringParameter.json +pkg/mod/github.com/go-openapi/loads@v0.19.3/fixtures/json/resources/parameters/formDataComplexParameter.json +pkg/mod/github.com/go-openapi/loads@v0.19.3/fixtures/json/resources/parameters/formDataInt64Parameter.json +pkg/mod/github.com/go-openapi/loads@v0.19.3/fixtures/json/resources/parameters/formDataStringArrayParameter.json +pkg/mod/github.com/go-openapi/loads@v0.19.3/fixtures/json/resources/parameters/formDataStringParameter.json +pkg/mod/github.com/go-openapi/loads@v0.19.3/fixtures/json/resources/parameters/headerInt64ArrayParameter.json +pkg/mod/github.com/go-openapi/loads@v0.19.3/fixtures/json/resources/parameters/headerStringArrayParameter.json +pkg/mod/github.com/go-openapi/loads@v0.19.3/fixtures/json/resources/parameters/headerStringParameter.json +pkg/mod/github.com/go-openapi/loads@v0.19.3/fixtures/json/resources/parameters/pathInt64Parameter.json +pkg/mod/github.com/go-openapi/loads@v0.19.3/fixtures/json/resources/parameters/pathStringArrayParameter.json +pkg/mod/github.com/go-openapi/loads@v0.19.3/fixtures/json/resources/parameters/pathStringParameter.json +pkg/mod/github.com/go-openapi/loads@v0.19.3/fixtures/json/resources/parameters/queryInt64ArrayParameter.json +pkg/mod/github.com/go-openapi/loads@v0.19.3/fixtures/json/resources/parameters/queryStringParameter.json +pkg/mod/github.com/go-openapi/loads@v0.19.3/fixtures/json/resources/parameters/queryWithComplexParameter.json +pkg/mod/github.com/go-openapi/loads@v0.19.3/fixtures/json/responses/complexArrayResponse.json +pkg/mod/github.com/go-openapi/loads@v0.19.3/fixtures/json/responses/dateTimeResponse.json +pkg/mod/github.com/go-openapi/loads@v0.19.3/fixtures/json/responses/int32Response.json +pkg/mod/github.com/go-openapi/loads@v0.19.3/fixtures/json/responses/int64Response.json +pkg/mod/github.com/go-openapi/loads@v0.19.3/fixtures/json/responses/multipleResponses.json +pkg/mod/github.com/go-openapi/loads@v0.19.3/fixtures/json/responses/stringArrayResponse.json +pkg/mod/github.com/go-openapi/loads@v0.19.3/fixtures/json/responses/stringResponse.json +pkg/mod/github.com/go-openapi/loads@v0.19.3/fixtures/json/responses/stringResponseWithHeader.json +pkg/mod/github.com/go-openapi/loads@v0.19.3/fixtures/json/responses/voidResponse.json +pkg/mod/github.com/go-openapi/loads@v0.19.3/fixtures/yaml/.gitkeep +pkg/mod/github.com/go-openapi/loads@v0.19.3/fixtures/yaml/models/models.yaml +pkg/mod/github.com/go-openapi/loads@v0.19.3/fixtures/yaml/models/modelWithArrayRef.yaml +pkg/mod/github.com/go-openapi/loads@v0.19.3/fixtures/yaml/models/modelWithComposition.yaml +pkg/mod/github.com/go-openapi/loads@v0.19.3/fixtures/yaml/models/modelWithDateTimeMap.yaml +pkg/mod/github.com/go-openapi/loads@v0.19.3/fixtures/yaml/models/modelWithExamples.yaml +pkg/mod/github.com/go-openapi/loads@v0.19.3/fixtures/yaml/models/modelWithInt32Map.yaml +pkg/mod/github.com/go-openapi/loads@v0.19.3/fixtures/yaml/models/modelWithInt64Map.yaml +pkg/mod/github.com/go-openapi/loads@v0.19.3/fixtures/yaml/models/modelWithMultipleProperties.yaml +pkg/mod/github.com/go-openapi/loads@v0.19.3/fixtures/yaml/models/modelWithObjectMap.yaml +pkg/mod/github.com/go-openapi/loads@v0.19.3/fixtures/yaml/models/modelWithPrimitiveArray.yaml +pkg/mod/github.com/go-openapi/loads@v0.19.3/fixtures/yaml/models/modelWithStringProperty.yaml +pkg/mod/github.com/go-openapi/loads@v0.19.3/fixtures/yaml/models/modelWithXmlAttributes.yaml +pkg/mod/github.com/go-openapi/loads@v0.19.3/fixtures/yaml/models/multipleModels.yaml +pkg/mod/github.com/go-openapi/loads@v0.19.3/fixtures/yaml/models/properties/propertyWithBooleanArray.yaml +pkg/mod/github.com/go-openapi/loads@v0.19.3/fixtures/yaml/models/properties/propertyWithByteArray.yaml +pkg/mod/github.com/go-openapi/loads@v0.19.3/fixtures/yaml/models/properties/propertyWithComplexArray.yaml +pkg/mod/github.com/go-openapi/loads@v0.19.3/fixtures/yaml/models/properties/propertyWithDateTimeArray.yaml +pkg/mod/github.com/go-openapi/loads@v0.19.3/fixtures/yaml/models/properties/propertyWithInt32Array.yaml +pkg/mod/github.com/go-openapi/loads@v0.19.3/fixtures/yaml/models/properties/propertyWithInt64Array.yaml +pkg/mod/github.com/go-openapi/loads@v0.19.3/fixtures/yaml/models/properties/propertyWithRef.yaml +pkg/mod/github.com/go-openapi/loads@v0.19.3/fixtures/yaml/models/properties/propertyWithStringArray.yaml +pkg/mod/github.com/go-openapi/loads@v0.19.3/fixtures/yaml/models/properties/simpleBooleanProperty.yaml +pkg/mod/github.com/go-openapi/loads@v0.19.3/fixtures/yaml/models/properties/simpleByteProperty.yaml +pkg/mod/github.com/go-openapi/loads@v0.19.3/fixtures/yaml/models/properties/simpleDateTimeProperty.yaml +pkg/mod/github.com/go-openapi/loads@v0.19.3/fixtures/yaml/models/properties/simpleInt32Property.yaml +pkg/mod/github.com/go-openapi/loads@v0.19.3/fixtures/yaml/models/properties/simpleInt64Property.yaml +pkg/mod/github.com/go-openapi/loads@v0.19.3/fixtures/yaml/models/properties/simpleStringProperty.yaml +pkg/mod/github.com/go-openapi/loads@v0.19.3/fixtures/yaml/resources/cascadingSchemes.yaml +pkg/mod/github.com/go-openapi/loads@v0.19.3/fixtures/yaml/resources/commonParameters.yaml +pkg/mod/github.com/go-openapi/loads@v0.19.3/fixtures/yaml/resources/multipleMimeTypes.yaml +pkg/mod/github.com/go-openapi/loads@v0.19.3/fixtures/yaml/resources/resourceWithExamplePayload.yaml +pkg/mod/github.com/go-openapi/loads@v0.19.3/fixtures/yaml/resources/resourceWithLinkedDefinitions_part1.yaml +pkg/mod/github.com/go-openapi/loads@v0.19.3/fixtures/yaml/resources/resourceWithLinkedDefinitions.yaml +pkg/mod/github.com/go-openapi/loads@v0.19.3/fixtures/yaml/resources/resourceWithRelativeHost.yaml +pkg/mod/github.com/go-openapi/loads@v0.19.3/fixtures/yaml/resources/reusableParameters.yaml +pkg/mod/github.com/go-openapi/loads@v0.19.3/fixtures/yaml/resources/securityExample.yaml +pkg/mod/github.com/go-openapi/loads@v0.19.3/fixtures/yaml/resources/stringPathParamResource.yaml +pkg/mod/github.com/go-openapi/loads@v0.19.3/fixtures/yaml/resources/taggedResource.yaml +pkg/mod/github.com/go-openapi/loads@v0.19.3/fixtures/yaml/resources/vendorExtensionExamples.yaml +pkg/mod/github.com/go-openapi/loads@v0.19.3/fixtures/yaml/resources/operations/operationWithTags.yaml +pkg/mod/github.com/go-openapi/loads@v0.19.3/fixtures/yaml/resources/operations/stringPathAndBoolQueryParamResource.yaml +pkg/mod/github.com/go-openapi/loads@v0.19.3/fixtures/yaml/resources/operations/stringPathParamResource.yaml +pkg/mod/github.com/go-openapi/loads@v0.19.3/fixtures/yaml/resources/parameters/bodyComplexArrayParameter.yaml +pkg/mod/github.com/go-openapi/loads@v0.19.3/fixtures/yaml/resources/parameters/bodyComplexParameter.yaml +pkg/mod/github.com/go-openapi/loads@v0.19.3/fixtures/yaml/resources/parameters/bodyInt64Parameter.yaml +pkg/mod/github.com/go-openapi/loads@v0.19.3/fixtures/yaml/resources/parameters/bodyStringArrayParameter.yaml +pkg/mod/github.com/go-openapi/loads@v0.19.3/fixtures/yaml/resources/parameters/bodyStringParameter.yaml +pkg/mod/github.com/go-openapi/loads@v0.19.3/fixtures/yaml/resources/parameters/formDataComplexParameter.yaml +pkg/mod/github.com/go-openapi/loads@v0.19.3/fixtures/yaml/resources/parameters/formDataInt64Parameter.yaml +pkg/mod/github.com/go-openapi/loads@v0.19.3/fixtures/yaml/resources/parameters/formDataStringArrayParameter.yaml +pkg/mod/github.com/go-openapi/loads@v0.19.3/fixtures/yaml/resources/parameters/formDataStringParameter.yaml +pkg/mod/github.com/go-openapi/loads@v0.19.3/fixtures/yaml/resources/parameters/headerInt64ArrayParameter.yaml +pkg/mod/github.com/go-openapi/loads@v0.19.3/fixtures/yaml/resources/parameters/headerStringArrayParameter.yaml +pkg/mod/github.com/go-openapi/loads@v0.19.3/fixtures/yaml/resources/parameters/headerStringParameter.yaml +pkg/mod/github.com/go-openapi/loads@v0.19.3/fixtures/yaml/resources/parameters/pathInt64Parameter.yaml +pkg/mod/github.com/go-openapi/loads@v0.19.3/fixtures/yaml/resources/parameters/pathStringArrayParameter.yaml +pkg/mod/github.com/go-openapi/loads@v0.19.3/fixtures/yaml/resources/parameters/pathStringParameter.yaml +pkg/mod/github.com/go-openapi/loads@v0.19.3/fixtures/yaml/resources/parameters/queryInt64ArrayParameter.yaml +pkg/mod/github.com/go-openapi/loads@v0.19.3/fixtures/yaml/resources/parameters/queryStringParameter.yaml +pkg/mod/github.com/go-openapi/loads@v0.19.3/fixtures/yaml/resources/parameters/queryWithComplexParameter.yaml +pkg/mod/github.com/go-openapi/loads@v0.19.3/fixtures/yaml/responses/complexArrayResponse.yaml +pkg/mod/github.com/go-openapi/loads@v0.19.3/fixtures/yaml/responses/dateTimeResponse.yaml +pkg/mod/github.com/go-openapi/loads@v0.19.3/fixtures/yaml/responses/int32Response.yaml +pkg/mod/github.com/go-openapi/loads@v0.19.3/fixtures/yaml/responses/int64Response.yaml +pkg/mod/github.com/go-openapi/loads@v0.19.3/fixtures/yaml/responses/multipleResponses.yaml +pkg/mod/github.com/go-openapi/loads@v0.19.3/fixtures/yaml/responses/stringArrayResponse.yaml +pkg/mod/github.com/go-openapi/loads@v0.19.3/fixtures/yaml/responses/stringResponse.yaml +pkg/mod/github.com/go-openapi/loads@v0.19.3/fixtures/yaml/responses/stringResponseWithHeader.yaml +pkg/mod/github.com/go-openapi/loads@v0.19.3/fixtures/yaml/responses/voidResponse.yaml +pkg/mod/github.com/go-openapi/loads@v0.19.3/fixtures/yaml/swagger/spec.yml +pkg/mod/github.com/go-openapi/loads@v0.19.3/fixtures/yaml/swagger/test3-ter-model-schema.json +pkg/mod/github.com/go-openapi/loads@v0.19.3/fixtures/yaml/swagger/1/2/3/4/swagger.yaml +pkg/mod/github.com/go-openapi/loads@v0.19.3/fixtures/yaml/swagger/shared/something.yaml +pkg/mod/github.com/go-openapi/loads@v0.19.3/fixtures/yaml/swagger/shared/definitions/page.yaml +pkg/mod/github.com/go-openapi/loads@v0.19.3/fixtures/yaml/yaml/.gitkeep +pkg/mod/github.com/go-openapi/loads@v0.19.3/fixtures/yaml/yaml/models/models.yaml +pkg/mod/github.com/go-openapi/loads@v0.19.3/fixtures/yaml/yaml/models/modelWithArrayRef.yaml +pkg/mod/github.com/go-openapi/loads@v0.19.3/fixtures/yaml/yaml/models/modelWithComposition.yaml +pkg/mod/github.com/go-openapi/loads@v0.19.3/fixtures/yaml/yaml/models/modelWithDateTimeMap.yaml +pkg/mod/github.com/go-openapi/loads@v0.19.3/fixtures/yaml/yaml/models/modelWithExamples.yaml +pkg/mod/github.com/go-openapi/loads@v0.19.3/fixtures/yaml/yaml/models/modelWithInt32Map.yaml +pkg/mod/github.com/go-openapi/loads@v0.19.3/fixtures/yaml/yaml/models/modelWithInt64Map.yaml +pkg/mod/github.com/go-openapi/loads@v0.19.3/fixtures/yaml/yaml/models/modelWithMultipleProperties.yaml +pkg/mod/github.com/go-openapi/loads@v0.19.3/fixtures/yaml/yaml/models/modelWithObjectMap.yaml +pkg/mod/github.com/go-openapi/loads@v0.19.3/fixtures/yaml/yaml/models/modelWithPrimitiveArray.yaml +pkg/mod/github.com/go-openapi/loads@v0.19.3/fixtures/yaml/yaml/models/modelWithStringProperty.yaml +pkg/mod/github.com/go-openapi/loads@v0.19.3/fixtures/yaml/yaml/models/modelWithXmlAttributes.yaml +pkg/mod/github.com/go-openapi/loads@v0.19.3/fixtures/yaml/yaml/models/multipleModels.yaml +pkg/mod/github.com/go-openapi/loads@v0.19.3/fixtures/yaml/yaml/models/properties/propertyWithBooleanArray.yaml +pkg/mod/github.com/go-openapi/loads@v0.19.3/fixtures/yaml/yaml/models/properties/propertyWithByteArray.yaml +pkg/mod/github.com/go-openapi/loads@v0.19.3/fixtures/yaml/yaml/models/properties/propertyWithComplexArray.yaml +pkg/mod/github.com/go-openapi/loads@v0.19.3/fixtures/yaml/yaml/models/properties/propertyWithDateTimeArray.yaml +pkg/mod/github.com/go-openapi/loads@v0.19.3/fixtures/yaml/yaml/models/properties/propertyWithInt32Array.yaml +pkg/mod/github.com/go-openapi/loads@v0.19.3/fixtures/yaml/yaml/models/properties/propertyWithInt64Array.yaml +pkg/mod/github.com/go-openapi/loads@v0.19.3/fixtures/yaml/yaml/models/properties/propertyWithRef.yaml +pkg/mod/github.com/go-openapi/loads@v0.19.3/fixtures/yaml/yaml/models/properties/propertyWithStringArray.yaml +pkg/mod/github.com/go-openapi/loads@v0.19.3/fixtures/yaml/yaml/models/properties/simpleBooleanProperty.yaml +pkg/mod/github.com/go-openapi/loads@v0.19.3/fixtures/yaml/yaml/models/properties/simpleByteProperty.yaml +pkg/mod/github.com/go-openapi/loads@v0.19.3/fixtures/yaml/yaml/models/properties/simpleDateTimeProperty.yaml +pkg/mod/github.com/go-openapi/loads@v0.19.3/fixtures/yaml/yaml/models/properties/simpleInt32Property.yaml +pkg/mod/github.com/go-openapi/loads@v0.19.3/fixtures/yaml/yaml/models/properties/simpleInt64Property.yaml +pkg/mod/github.com/go-openapi/loads@v0.19.3/fixtures/yaml/yaml/models/properties/simpleStringProperty.yaml +pkg/mod/github.com/go-openapi/loads@v0.19.3/fixtures/yaml/yaml/resources/cascadingSchemes.yaml +pkg/mod/github.com/go-openapi/loads@v0.19.3/fixtures/yaml/yaml/resources/commonParameters.yaml +pkg/mod/github.com/go-openapi/loads@v0.19.3/fixtures/yaml/yaml/resources/multipleMimeTypes.yaml +pkg/mod/github.com/go-openapi/loads@v0.19.3/fixtures/yaml/yaml/resources/resourceWithExamplePayload.yaml +pkg/mod/github.com/go-openapi/loads@v0.19.3/fixtures/yaml/yaml/resources/resourceWithLinkedDefinitions_part1.yaml +pkg/mod/github.com/go-openapi/loads@v0.19.3/fixtures/yaml/yaml/resources/resourceWithLinkedDefinitions.yaml +pkg/mod/github.com/go-openapi/loads@v0.19.3/fixtures/yaml/yaml/resources/resourceWithRelativeHost.yaml +pkg/mod/github.com/go-openapi/loads@v0.19.3/fixtures/yaml/yaml/resources/reusableParameters.yaml +pkg/mod/github.com/go-openapi/loads@v0.19.3/fixtures/yaml/yaml/resources/securityExample.yaml +pkg/mod/github.com/go-openapi/loads@v0.19.3/fixtures/yaml/yaml/resources/stringPathParamResource.yaml +pkg/mod/github.com/go-openapi/loads@v0.19.3/fixtures/yaml/yaml/resources/taggedResource.yaml +pkg/mod/github.com/go-openapi/loads@v0.19.3/fixtures/yaml/yaml/resources/vendorExtensionExamples.yaml +pkg/mod/github.com/go-openapi/loads@v0.19.3/fixtures/yaml/yaml/resources/operations/operationWithTags.yaml +pkg/mod/github.com/go-openapi/loads@v0.19.3/fixtures/yaml/yaml/resources/operations/stringPathAndBoolQueryParamResource.yaml +pkg/mod/github.com/go-openapi/loads@v0.19.3/fixtures/yaml/yaml/resources/operations/stringPathParamResource.yaml +pkg/mod/github.com/go-openapi/loads@v0.19.3/fixtures/yaml/yaml/resources/parameters/bodyComplexArrayParameter.yaml +pkg/mod/github.com/go-openapi/loads@v0.19.3/fixtures/yaml/yaml/resources/parameters/bodyComplexParameter.yaml +pkg/mod/github.com/go-openapi/loads@v0.19.3/fixtures/yaml/yaml/resources/parameters/bodyInt64Parameter.yaml +pkg/mod/github.com/go-openapi/loads@v0.19.3/fixtures/yaml/yaml/resources/parameters/bodyStringArrayParameter.yaml +pkg/mod/github.com/go-openapi/loads@v0.19.3/fixtures/yaml/yaml/resources/parameters/bodyStringParameter.yaml +pkg/mod/github.com/go-openapi/loads@v0.19.3/fixtures/yaml/yaml/resources/parameters/formDataComplexParameter.yaml +pkg/mod/github.com/go-openapi/loads@v0.19.3/fixtures/yaml/yaml/resources/parameters/formDataInt64Parameter.yaml +pkg/mod/github.com/go-openapi/loads@v0.19.3/fixtures/yaml/yaml/resources/parameters/formDataStringArrayParameter.yaml +pkg/mod/github.com/go-openapi/loads@v0.19.3/fixtures/yaml/yaml/resources/parameters/formDataStringParameter.yaml +pkg/mod/github.com/go-openapi/loads@v0.19.3/fixtures/yaml/yaml/resources/parameters/headerInt64ArrayParameter.yaml +pkg/mod/github.com/go-openapi/loads@v0.19.3/fixtures/yaml/yaml/resources/parameters/headerStringArrayParameter.yaml +pkg/mod/github.com/go-openapi/loads@v0.19.3/fixtures/yaml/yaml/resources/parameters/headerStringParameter.yaml +pkg/mod/github.com/go-openapi/loads@v0.19.3/fixtures/yaml/yaml/resources/parameters/pathInt64Parameter.yaml +pkg/mod/github.com/go-openapi/loads@v0.19.3/fixtures/yaml/yaml/resources/parameters/pathStringArrayParameter.yaml +pkg/mod/github.com/go-openapi/loads@v0.19.3/fixtures/yaml/yaml/resources/parameters/pathStringParameter.yaml +pkg/mod/github.com/go-openapi/loads@v0.19.3/fixtures/yaml/yaml/resources/parameters/queryInt64ArrayParameter.yaml +pkg/mod/github.com/go-openapi/loads@v0.19.3/fixtures/yaml/yaml/resources/parameters/queryStringParameter.yaml +pkg/mod/github.com/go-openapi/loads@v0.19.3/fixtures/yaml/yaml/resources/parameters/queryWithComplexParameter.yaml +pkg/mod/github.com/go-openapi/loads@v0.19.3/fixtures/yaml/yaml/responses/complexArrayResponse.yaml +pkg/mod/github.com/go-openapi/loads@v0.19.3/fixtures/yaml/yaml/responses/dateTimeResponse.yaml +pkg/mod/github.com/go-openapi/loads@v0.19.3/fixtures/yaml/yaml/responses/int32Response.yaml +pkg/mod/github.com/go-openapi/loads@v0.19.3/fixtures/yaml/yaml/responses/int64Response.yaml +pkg/mod/github.com/go-openapi/loads@v0.19.3/fixtures/yaml/yaml/responses/multipleResponses.yaml +pkg/mod/github.com/go-openapi/loads@v0.19.3/fixtures/yaml/yaml/responses/stringArrayResponse.yaml +pkg/mod/github.com/go-openapi/loads@v0.19.3/fixtures/yaml/yaml/responses/stringResponse.yaml +pkg/mod/github.com/go-openapi/loads@v0.19.3/fixtures/yaml/yaml/responses/stringResponseWithHeader.yaml +pkg/mod/github.com/go-openapi/loads@v0.19.3/fixtures/yaml/yaml/responses/voidResponse.yaml +pkg/mod/github.com/go-openapi/loads@v0.19.3/fmts/fixture_test.go +pkg/mod/github.com/go-openapi/loads@v0.19.3/fmts/yaml_test.go +pkg/mod/github.com/go-openapi/loads@v0.19.3/fmts/yaml.go +pkg/mod/github.com/go-openapi/runtime@v0.19.5/.editorconfig +pkg/mod/github.com/go-openapi/runtime@v0.19.5/.gitignore +pkg/mod/github.com/go-openapi/runtime@v0.19.5/.travis.yml +pkg/mod/github.com/go-openapi/runtime@v0.19.5/authinfo_test.go +pkg/mod/github.com/go-openapi/runtime@v0.19.5/bytestream_test.go +pkg/mod/github.com/go-openapi/runtime@v0.19.5/bytestream.go +pkg/mod/github.com/go-openapi/runtime@v0.19.5/client_auth_info.go +pkg/mod/github.com/go-openapi/runtime@v0.19.5/client_operation.go +pkg/mod/github.com/go-openapi/runtime@v0.19.5/client_request_test.go +pkg/mod/github.com/go-openapi/runtime@v0.19.5/client_request.go +pkg/mod/github.com/go-openapi/runtime@v0.19.5/client_response_test.go +pkg/mod/github.com/go-openapi/runtime@v0.19.5/client_response.go +pkg/mod/github.com/go-openapi/runtime@v0.19.5/CODE_OF_CONDUCT.md +pkg/mod/github.com/go-openapi/runtime@v0.19.5/constants.go +pkg/mod/github.com/go-openapi/runtime@v0.19.5/csv_test.go +pkg/mod/github.com/go-openapi/runtime@v0.19.5/csv.go +pkg/mod/github.com/go-openapi/runtime@v0.19.5/discard.go +pkg/mod/github.com/go-openapi/runtime@v0.19.5/file_test.go +pkg/mod/github.com/go-openapi/runtime@v0.19.5/file.go +pkg/mod/github.com/go-openapi/runtime@v0.19.5/go.mod +pkg/mod/github.com/go-openapi/runtime@v0.19.5/go.sum +pkg/mod/github.com/go-openapi/runtime@v0.19.5/headers_test.go +pkg/mod/github.com/go-openapi/runtime@v0.19.5/headers.go +pkg/mod/github.com/go-openapi/runtime@v0.19.5/interfaces.go +pkg/mod/github.com/go-openapi/runtime@v0.19.5/json_test.go +pkg/mod/github.com/go-openapi/runtime@v0.19.5/json.go +pkg/mod/github.com/go-openapi/runtime@v0.19.5/LICENSE +pkg/mod/github.com/go-openapi/runtime@v0.19.5/README.md +pkg/mod/github.com/go-openapi/runtime@v0.19.5/request_test.go +pkg/mod/github.com/go-openapi/runtime@v0.19.5/request.go +pkg/mod/github.com/go-openapi/runtime@v0.19.5/statuses.go +pkg/mod/github.com/go-openapi/runtime@v0.19.5/text_test.go +pkg/mod/github.com/go-openapi/runtime@v0.19.5/text.go +pkg/mod/github.com/go-openapi/runtime@v0.19.5/values.go +pkg/mod/github.com/go-openapi/runtime@v0.19.5/xml_test.go +pkg/mod/github.com/go-openapi/runtime@v0.19.5/xml.go +pkg/mod/github.com/go-openapi/runtime@v0.19.5/.github/CONTRIBUTING.md +pkg/mod/github.com/go-openapi/runtime@v0.19.5/client/auth_info_test.go +pkg/mod/github.com/go-openapi/runtime@v0.19.5/client/auth_info.go +pkg/mod/github.com/go-openapi/runtime@v0.19.5/client/keepalive_test.go +pkg/mod/github.com/go-openapi/runtime@v0.19.5/client/keepalive.go +pkg/mod/github.com/go-openapi/runtime@v0.19.5/client/request_test.go +pkg/mod/github.com/go-openapi/runtime@v0.19.5/client/request.go +pkg/mod/github.com/go-openapi/runtime@v0.19.5/client/response_test.go +pkg/mod/github.com/go-openapi/runtime@v0.19.5/client/response.go +pkg/mod/github.com/go-openapi/runtime@v0.19.5/client/runtime_test.go +pkg/mod/github.com/go-openapi/runtime@v0.19.5/client/runtime.go +pkg/mod/github.com/go-openapi/runtime@v0.19.5/fixtures/bugs/264/swagger.yml +pkg/mod/github.com/go-openapi/runtime@v0.19.5/fixtures/certs/myCA.crt +pkg/mod/github.com/go-openapi/runtime@v0.19.5/fixtures/certs/myCA.key +pkg/mod/github.com/go-openapi/runtime@v0.19.5/fixtures/certs/mycert1.crt +pkg/mod/github.com/go-openapi/runtime@v0.19.5/fixtures/certs/mycert1.key +pkg/mod/github.com/go-openapi/runtime@v0.19.5/fixtures/certs/mycert1.req +pkg/mod/github.com/go-openapi/runtime@v0.19.5/fixtures/certs/myclient-ecc.crt +pkg/mod/github.com/go-openapi/runtime@v0.19.5/fixtures/certs/myclient-ecc.csr +pkg/mod/github.com/go-openapi/runtime@v0.19.5/fixtures/certs/myclient-ecc.key +pkg/mod/github.com/go-openapi/runtime@v0.19.5/fixtures/certs/myclient.crt +pkg/mod/github.com/go-openapi/runtime@v0.19.5/fixtures/certs/myclient.csr +pkg/mod/github.com/go-openapi/runtime@v0.19.5/fixtures/certs/myclient.key +pkg/mod/github.com/go-openapi/runtime@v0.19.5/fixtures/certs/myclient.p12 +pkg/mod/github.com/go-openapi/runtime@v0.19.5/fixtures/certs/serial +pkg/mod/github.com/go-openapi/runtime@v0.19.5/flagext/byte_size_test.go +pkg/mod/github.com/go-openapi/runtime@v0.19.5/flagext/byte_size.go +pkg/mod/github.com/go-openapi/runtime@v0.19.5/hack/gen-self-signed-certs.sh +pkg/mod/github.com/go-openapi/runtime@v0.19.5/internal/testing/data.go +pkg/mod/github.com/go-openapi/runtime@v0.19.5/internal/testing/petstore/api.go +pkg/mod/github.com/go-openapi/runtime@v0.19.5/internal/testing/simplepetstore/api_test.go +pkg/mod/github.com/go-openapi/runtime@v0.19.5/internal/testing/simplepetstore/api.go +pkg/mod/github.com/go-openapi/runtime@v0.19.5/logger/logger.go +pkg/mod/github.com/go-openapi/runtime@v0.19.5/logger/standard.go +pkg/mod/github.com/go-openapi/runtime@v0.19.5/middleware/body_test.go +pkg/mod/github.com/go-openapi/runtime@v0.19.5/middleware/context_test.go +pkg/mod/github.com/go-openapi/runtime@v0.19.5/middleware/context.go +pkg/mod/github.com/go-openapi/runtime@v0.19.5/middleware/doc.go +pkg/mod/github.com/go-openapi/runtime@v0.19.5/middleware/go18.go +pkg/mod/github.com/go-openapi/runtime@v0.19.5/middleware/negotiate_test.go +pkg/mod/github.com/go-openapi/runtime@v0.19.5/middleware/negotiate.go +pkg/mod/github.com/go-openapi/runtime@v0.19.5/middleware/not_implemented.go +pkg/mod/github.com/go-openapi/runtime@v0.19.5/middleware/operation_test.go +pkg/mod/github.com/go-openapi/runtime@v0.19.5/middleware/operation.go +pkg/mod/github.com/go-openapi/runtime@v0.19.5/middleware/parameter_test.go +pkg/mod/github.com/go-openapi/runtime@v0.19.5/middleware/parameter.go +pkg/mod/github.com/go-openapi/runtime@v0.19.5/middleware/pre_go18.go +pkg/mod/github.com/go-openapi/runtime@v0.19.5/middleware/redoc_test.go +pkg/mod/github.com/go-openapi/runtime@v0.19.5/middleware/redoc.go +pkg/mod/github.com/go-openapi/runtime@v0.19.5/middleware/request_test.go +pkg/mod/github.com/go-openapi/runtime@v0.19.5/middleware/request.go +pkg/mod/github.com/go-openapi/runtime@v0.19.5/middleware/route_authenticator_test.go +pkg/mod/github.com/go-openapi/runtime@v0.19.5/middleware/route_param_test.go +pkg/mod/github.com/go-openapi/runtime@v0.19.5/middleware/router_test.go +pkg/mod/github.com/go-openapi/runtime@v0.19.5/middleware/router.go +pkg/mod/github.com/go-openapi/runtime@v0.19.5/middleware/security_test.go +pkg/mod/github.com/go-openapi/runtime@v0.19.5/middleware/security.go +pkg/mod/github.com/go-openapi/runtime@v0.19.5/middleware/spec_test.go +pkg/mod/github.com/go-openapi/runtime@v0.19.5/middleware/spec.go +pkg/mod/github.com/go-openapi/runtime@v0.19.5/middleware/string_conversion_test.go +pkg/mod/github.com/go-openapi/runtime@v0.19.5/middleware/untyped_request_test.go +pkg/mod/github.com/go-openapi/runtime@v0.19.5/middleware/validation_test.go +pkg/mod/github.com/go-openapi/runtime@v0.19.5/middleware/validation.go +pkg/mod/github.com/go-openapi/runtime@v0.19.5/middleware/denco/LICENSE +pkg/mod/github.com/go-openapi/runtime@v0.19.5/middleware/denco/README.md +pkg/mod/github.com/go-openapi/runtime@v0.19.5/middleware/denco/router_bench_test.go +pkg/mod/github.com/go-openapi/runtime@v0.19.5/middleware/denco/router_test.go +pkg/mod/github.com/go-openapi/runtime@v0.19.5/middleware/denco/router.go +pkg/mod/github.com/go-openapi/runtime@v0.19.5/middleware/denco/server_test.go +pkg/mod/github.com/go-openapi/runtime@v0.19.5/middleware/denco/server.go +pkg/mod/github.com/go-openapi/runtime@v0.19.5/middleware/denco/util_test.go +pkg/mod/github.com/go-openapi/runtime@v0.19.5/middleware/denco/util.go +pkg/mod/github.com/go-openapi/runtime@v0.19.5/middleware/header/header.go +pkg/mod/github.com/go-openapi/runtime@v0.19.5/middleware/untyped/api_test.go +pkg/mod/github.com/go-openapi/runtime@v0.19.5/middleware/untyped/api.go +pkg/mod/github.com/go-openapi/runtime@v0.19.5/security/apikey_auth_test.go +pkg/mod/github.com/go-openapi/runtime@v0.19.5/security/authenticator.go +pkg/mod/github.com/go-openapi/runtime@v0.19.5/security/authorizer_test.go +pkg/mod/github.com/go-openapi/runtime@v0.19.5/security/authorizer.go +pkg/mod/github.com/go-openapi/runtime@v0.19.5/security/basic_auth_test.go +pkg/mod/github.com/go-openapi/runtime@v0.19.5/security/bearer_auth_test.go +pkg/mod/github.com/go-openapi/runtime@v0.19.5/yamlpc/yaml_test.go +pkg/mod/github.com/go-openapi/runtime@v0.19.5/yamlpc/yaml.go +pkg/mod/github.com/go-openapi/spec@v0.19.3/.editorconfig +pkg/mod/github.com/go-openapi/spec@v0.19.3/.gitignore +pkg/mod/github.com/go-openapi/spec@v0.19.3/.golangci.yml +pkg/mod/github.com/go-openapi/spec@v0.19.3/.travis.yml +pkg/mod/github.com/go-openapi/spec@v0.19.3/auth_test.go +pkg/mod/github.com/go-openapi/spec@v0.19.3/bindata.go +pkg/mod/github.com/go-openapi/spec@v0.19.3/cache.go +pkg/mod/github.com/go-openapi/spec@v0.19.3/CODE_OF_CONDUCT.md +pkg/mod/github.com/go-openapi/spec@v0.19.3/contact_info_test.go +pkg/mod/github.com/go-openapi/spec@v0.19.3/contact_info.go +pkg/mod/github.com/go-openapi/spec@v0.19.3/debug_test.go +pkg/mod/github.com/go-openapi/spec@v0.19.3/debug.go +pkg/mod/github.com/go-openapi/spec@v0.19.3/expander_test.go +pkg/mod/github.com/go-openapi/spec@v0.19.3/expander.go +pkg/mod/github.com/go-openapi/spec@v0.19.3/external_docs_test.go +pkg/mod/github.com/go-openapi/spec@v0.19.3/external_docs.go +pkg/mod/github.com/go-openapi/spec@v0.19.3/go.mod +pkg/mod/github.com/go-openapi/spec@v0.19.3/go.sum +pkg/mod/github.com/go-openapi/spec@v0.19.3/header_test.go +pkg/mod/github.com/go-openapi/spec@v0.19.3/header.go +pkg/mod/github.com/go-openapi/spec@v0.19.3/info_test.go +pkg/mod/github.com/go-openapi/spec@v0.19.3/info.go +pkg/mod/github.com/go-openapi/spec@v0.19.3/items_test.go +pkg/mod/github.com/go-openapi/spec@v0.19.3/items.go +pkg/mod/github.com/go-openapi/spec@v0.19.3/LICENSE +pkg/mod/github.com/go-openapi/spec@v0.19.3/license_test.go +pkg/mod/github.com/go-openapi/spec@v0.19.3/license.go +pkg/mod/github.com/go-openapi/spec@v0.19.3/normalizer.go +pkg/mod/github.com/go-openapi/spec@v0.19.3/operation_test.go +pkg/mod/github.com/go-openapi/spec@v0.19.3/operation.go +pkg/mod/github.com/go-openapi/spec@v0.19.3/parameter.go +pkg/mod/github.com/go-openapi/spec@v0.19.3/parameters_test.go +pkg/mod/github.com/go-openapi/spec@v0.19.3/path_item_test.go +pkg/mod/github.com/go-openapi/spec@v0.19.3/path_item.go +pkg/mod/github.com/go-openapi/spec@v0.19.3/paths_test.go +pkg/mod/github.com/go-openapi/spec@v0.19.3/paths.go +pkg/mod/github.com/go-openapi/spec@v0.19.3/properties_test.go +pkg/mod/github.com/go-openapi/spec@v0.19.3/README.md +pkg/mod/github.com/go-openapi/spec@v0.19.3/ref_test.go +pkg/mod/github.com/go-openapi/spec@v0.19.3/ref.go +pkg/mod/github.com/go-openapi/spec@v0.19.3/response_test.go +pkg/mod/github.com/go-openapi/spec@v0.19.3/response.go +pkg/mod/github.com/go-openapi/spec@v0.19.3/responses.go +pkg/mod/github.com/go-openapi/spec@v0.19.3/schema_loader.go +pkg/mod/github.com/go-openapi/spec@v0.19.3/schema_test.go +pkg/mod/github.com/go-openapi/spec@v0.19.3/schema.go +pkg/mod/github.com/go-openapi/spec@v0.19.3/security_scheme.go +pkg/mod/github.com/go-openapi/spec@v0.19.3/spec_test.go +pkg/mod/github.com/go-openapi/spec@v0.19.3/spec.go +pkg/mod/github.com/go-openapi/spec@v0.19.3/structs_test.go +pkg/mod/github.com/go-openapi/spec@v0.19.3/swagger_test.go +pkg/mod/github.com/go-openapi/spec@v0.19.3/swagger.go +pkg/mod/github.com/go-openapi/spec@v0.19.3/tag.go +pkg/mod/github.com/go-openapi/spec@v0.19.3/unused.go +pkg/mod/github.com/go-openapi/spec@v0.19.3/xml_object_test.go +pkg/mod/github.com/go-openapi/spec@v0.19.3/xml_object.go +pkg/mod/github.com/go-openapi/spec@v0.19.3/.github/CONTRIBUTING.md +pkg/mod/github.com/go-openapi/spec@v0.19.3/fixtures/bugs/1429/responses.yaml +pkg/mod/github.com/go-openapi/spec@v0.19.3/fixtures/bugs/1429/swagger.yaml +pkg/mod/github.com/go-openapi/spec@v0.19.3/fixtures/bugs/1429/remote/remote.yaml +pkg/mod/github.com/go-openapi/spec@v0.19.3/fixtures/bugs/1429/remote/farther/farther.yaml +pkg/mod/github.com/go-openapi/spec@v0.19.3/fixtures/bugs/1429/remote/remote/remote.yaml +pkg/mod/github.com/go-openapi/spec@v0.19.3/fixtures/bugs/1614/gitea.json +pkg/mod/github.com/go-openapi/spec@v0.19.3/fixtures/bugs/1621/definitions.yaml +pkg/mod/github.com/go-openapi/spec@v0.19.3/fixtures/bugs/1621/fixture-1621.yaml +pkg/mod/github.com/go-openapi/spec@v0.19.3/fixtures/bugs/1621/parameters.yaml +pkg/mod/github.com/go-openapi/spec@v0.19.3/fixtures/bugs/1621/responses.yaml +pkg/mod/github.com/go-openapi/spec@v0.19.3/fixtures/bugs/69/dapperbox.json +pkg/mod/github.com/go-openapi/spec@v0.19.3/fixtures/bugs/957/fixture-957.json +pkg/mod/github.com/go-openapi/spec@v0.19.3/fixtures/expansion/all-the-things.json +pkg/mod/github.com/go-openapi/spec@v0.19.3/fixtures/expansion/circular-minimal.json +pkg/mod/github.com/go-openapi/spec@v0.19.3/fixtures/expansion/circularRefs.json +pkg/mod/github.com/go-openapi/spec@v0.19.3/fixtures/expansion/circularSpec.json +pkg/mod/github.com/go-openapi/spec@v0.19.3/fixtures/expansion/circularSpec.yaml +pkg/mod/github.com/go-openapi/spec@v0.19.3/fixtures/expansion/circularSpec2.json +pkg/mod/github.com/go-openapi/spec@v0.19.3/fixtures/expansion/clickmeter.json +pkg/mod/github.com/go-openapi/spec@v0.19.3/fixtures/expansion/clickmeter.yaml +pkg/mod/github.com/go-openapi/spec@v0.19.3/fixtures/expansion/extraRef.json +pkg/mod/github.com/go-openapi/spec@v0.19.3/fixtures/expansion/invalid-refs.json +pkg/mod/github.com/go-openapi/spec@v0.19.3/fixtures/expansion/missingItemRef.json +pkg/mod/github.com/go-openapi/spec@v0.19.3/fixtures/expansion/missingRef.json +pkg/mod/github.com/go-openapi/spec@v0.19.3/fixtures/expansion/overflow.json +pkg/mod/github.com/go-openapi/spec@v0.19.3/fixtures/expansion/params.json +pkg/mod/github.com/go-openapi/spec@v0.19.3/fixtures/expansion/pathItem1.json +pkg/mod/github.com/go-openapi/spec@v0.19.3/fixtures/expansion/pathItems.json +pkg/mod/github.com/go-openapi/spec@v0.19.3/fixtures/expansion/schemas1.json +pkg/mod/github.com/go-openapi/spec@v0.19.3/fixtures/expansion/schemas2.json +pkg/mod/github.com/go-openapi/spec@v0.19.3/fixtures/local_expansion/item.json +pkg/mod/github.com/go-openapi/spec@v0.19.3/fixtures/local_expansion/item2.yaml +pkg/mod/github.com/go-openapi/spec@v0.19.3/fixtures/local_expansion/spec.json +pkg/mod/github.com/go-openapi/spec@v0.19.3/fixtures/local_expansion/spec2.yaml +pkg/mod/github.com/go-openapi/spec@v0.19.3/fixtures/more_circulars/bitbucket.json +pkg/mod/github.com/go-openapi/spec@v0.19.3/fixtures/more_circulars/item.json +pkg/mod/github.com/go-openapi/spec@v0.19.3/fixtures/more_circulars/item2.json +pkg/mod/github.com/go-openapi/spec@v0.19.3/fixtures/more_circulars/item4.json +pkg/mod/github.com/go-openapi/spec@v0.19.3/fixtures/more_circulars/spec.json +pkg/mod/github.com/go-openapi/spec@v0.19.3/fixtures/more_circulars/spec2.json +pkg/mod/github.com/go-openapi/spec@v0.19.3/fixtures/more_circulars/spec3.json +pkg/mod/github.com/go-openapi/spec@v0.19.3/fixtures/more_circulars/spec4.json +pkg/mod/github.com/go-openapi/spec@v0.19.3/fixtures/remote/all-the-things.json +pkg/mod/github.com/go-openapi/spec@v0.19.3/fixtures/remote/pet/pet.json +pkg/mod/github.com/go-openapi/spec@v0.19.3/fixtures/specs/refed.json +pkg/mod/github.com/go-openapi/spec@v0.19.3/fixtures/specs/resolution.json +pkg/mod/github.com/go-openapi/spec@v0.19.3/fixtures/specs/resolution2.json +pkg/mod/github.com/go-openapi/spec@v0.19.3/fixtures/specs/todos.common.json +pkg/mod/github.com/go-openapi/spec@v0.19.3/fixtures/specs/todos.json +pkg/mod/github.com/go-openapi/spec@v0.19.3/fixtures/specs/deeper/arrayProp.json +pkg/mod/github.com/go-openapi/spec@v0.19.3/fixtures/specs/deeper/stringProp.json +pkg/mod/github.com/go-openapi/spec@v0.19.3/schemas/jsonschema-draft-04.json +pkg/mod/github.com/go-openapi/spec@v0.19.3/schemas/v2/README.md +pkg/mod/github.com/go-openapi/spec@v0.19.3/schemas/v2/schema.json +pkg/mod/github.com/go-openapi/strfmt@v0.19.4/.editorconfig +pkg/mod/github.com/go-openapi/strfmt@v0.19.4/.gitignore +pkg/mod/github.com/go-openapi/strfmt@v0.19.4/.golangci.yml +pkg/mod/github.com/go-openapi/strfmt@v0.19.4/.travis.yml +pkg/mod/github.com/go-openapi/strfmt@v0.19.4/bson_test.go +pkg/mod/github.com/go-openapi/strfmt@v0.19.4/bson.go +pkg/mod/github.com/go-openapi/strfmt@v0.19.4/CODE_OF_CONDUCT.md +pkg/mod/github.com/go-openapi/strfmt@v0.19.4/date_test.go +pkg/mod/github.com/go-openapi/strfmt@v0.19.4/date.go +pkg/mod/github.com/go-openapi/strfmt@v0.19.4/default_test.go +pkg/mod/github.com/go-openapi/strfmt@v0.19.4/default.go +pkg/mod/github.com/go-openapi/strfmt@v0.19.4/doc.go +pkg/mod/github.com/go-openapi/strfmt@v0.19.4/duration_test.go +pkg/mod/github.com/go-openapi/strfmt@v0.19.4/duration.go +pkg/mod/github.com/go-openapi/strfmt@v0.19.4/format_test.go +pkg/mod/github.com/go-openapi/strfmt@v0.19.4/format.go +pkg/mod/github.com/go-openapi/strfmt@v0.19.4/go.mod +pkg/mod/github.com/go-openapi/strfmt@v0.19.4/go.sum +pkg/mod/github.com/go-openapi/strfmt@v0.19.4/LICENSE +pkg/mod/github.com/go-openapi/strfmt@v0.19.4/README.md +pkg/mod/github.com/go-openapi/strfmt@v0.19.4/time_test.go +pkg/mod/github.com/go-openapi/strfmt@v0.19.4/time.go +pkg/mod/github.com/go-openapi/strfmt@v0.19.4/.github/CONTRIBUTING.md +pkg/mod/github.com/go-openapi/strfmt@v0.19.4/conv/date_test.go +pkg/mod/github.com/go-openapi/strfmt@v0.19.4/conv/date.go +pkg/mod/github.com/go-openapi/strfmt@v0.19.4/conv/default_test.go +pkg/mod/github.com/go-openapi/strfmt@v0.19.4/conv/default.go +pkg/mod/github.com/go-openapi/strfmt@v0.19.4/conv/duration_test.go +pkg/mod/github.com/go-openapi/strfmt@v0.19.4/conv/duration.go +pkg/mod/github.com/go-openapi/strfmt@v0.19.4/conv/time_test.go +pkg/mod/github.com/go-openapi/strfmt@v0.19.4/conv/time.go +pkg/mod/github.com/go-openapi/strfmt@v0.19.4/hack/coverage +pkg/mod/github.com/go-openapi/swag@v0.19.5/.editorconfig +pkg/mod/github.com/go-openapi/swag@v0.19.5/.gitignore +pkg/mod/github.com/go-openapi/swag@v0.19.5/.golangci.yml +pkg/mod/github.com/go-openapi/swag@v0.19.5/.travis.yml +pkg/mod/github.com/go-openapi/swag@v0.19.5/CODE_OF_CONDUCT.md +pkg/mod/github.com/go-openapi/swag@v0.19.5/convert_test.go +pkg/mod/github.com/go-openapi/swag@v0.19.5/convert_types_test.go +pkg/mod/github.com/go-openapi/swag@v0.19.5/convert_types.go +pkg/mod/github.com/go-openapi/swag@v0.19.5/convert.go +pkg/mod/github.com/go-openapi/swag@v0.19.5/doc.go +pkg/mod/github.com/go-openapi/swag@v0.19.5/go.mod +pkg/mod/github.com/go-openapi/swag@v0.19.5/go.sum +pkg/mod/github.com/go-openapi/swag@v0.19.5/json_test.go +pkg/mod/github.com/go-openapi/swag@v0.19.5/json.go +pkg/mod/github.com/go-openapi/swag@v0.19.5/LICENSE +pkg/mod/github.com/go-openapi/swag@v0.19.5/loading_test.go +pkg/mod/github.com/go-openapi/swag@v0.19.5/loading.go +pkg/mod/github.com/go-openapi/swag@v0.19.5/name_lexem.go +pkg/mod/github.com/go-openapi/swag@v0.19.5/net_test.go +pkg/mod/github.com/go-openapi/swag@v0.19.5/net.go +pkg/mod/github.com/go-openapi/swag@v0.19.5/path_test.go +pkg/mod/github.com/go-openapi/swag@v0.19.5/path.go +pkg/mod/github.com/go-openapi/swag@v0.19.5/post_go18.go +pkg/mod/github.com/go-openapi/swag@v0.19.5/post_go19.go +pkg/mod/github.com/go-openapi/swag@v0.19.5/pre_go18.go +pkg/mod/github.com/go-openapi/swag@v0.19.5/pre_go19.go +pkg/mod/github.com/go-openapi/swag@v0.19.5/README.md +pkg/mod/github.com/go-openapi/swag@v0.19.5/split.go +pkg/mod/github.com/go-openapi/swag@v0.19.5/util_test.go +pkg/mod/github.com/go-openapi/swag@v0.19.5/util.go +pkg/mod/github.com/go-openapi/swag@v0.19.5/yaml_test.go +pkg/mod/github.com/go-openapi/swag@v0.19.5/yaml.go +pkg/mod/github.com/go-openapi/swag@v0.19.5/.github/CONTRIBUTING.md +pkg/mod/github.com/go-openapi/validate@v0.19.3/.editorconfig +pkg/mod/github.com/go-openapi/validate@v0.19.3/.gitignore +pkg/mod/github.com/go-openapi/validate@v0.19.3/.golangci.yml +pkg/mod/github.com/go-openapi/validate@v0.19.3/.travis.yml +pkg/mod/github.com/go-openapi/validate@v0.19.3/CODE_OF_CONDUCT.md +pkg/mod/github.com/go-openapi/validate@v0.19.3/debug_test.go +pkg/mod/github.com/go-openapi/validate@v0.19.3/debug.go +pkg/mod/github.com/go-openapi/validate@v0.19.3/default_validator_test.go +pkg/mod/github.com/go-openapi/validate@v0.19.3/default_validator.go +pkg/mod/github.com/go-openapi/validate@v0.19.3/doc_test.go +pkg/mod/github.com/go-openapi/validate@v0.19.3/doc.go +pkg/mod/github.com/go-openapi/validate@v0.19.3/example_validator_test.go +pkg/mod/github.com/go-openapi/validate@v0.19.3/example_validator.go +pkg/mod/github.com/go-openapi/validate@v0.19.3/formats_test.go +pkg/mod/github.com/go-openapi/validate@v0.19.3/formats.go +pkg/mod/github.com/go-openapi/validate@v0.19.3/go.mod +pkg/mod/github.com/go-openapi/validate@v0.19.3/go.sum +pkg/mod/github.com/go-openapi/validate@v0.19.3/helpers_test.go +pkg/mod/github.com/go-openapi/validate@v0.19.3/helpers.go +pkg/mod/github.com/go-openapi/validate@v0.19.3/items_validator_test.go +pkg/mod/github.com/go-openapi/validate@v0.19.3/jsonschema_test.go +pkg/mod/github.com/go-openapi/validate@v0.19.3/LICENSE +pkg/mod/github.com/go-openapi/validate@v0.19.3/messages_test.go +pkg/mod/github.com/go-openapi/validate@v0.19.3/object_validator_test.go +pkg/mod/github.com/go-openapi/validate@v0.19.3/object_validator.go +pkg/mod/github.com/go-openapi/validate@v0.19.3/options_test.go +pkg/mod/github.com/go-openapi/validate@v0.19.3/options.go +pkg/mod/github.com/go-openapi/validate@v0.19.3/parameter_validator_test.go +pkg/mod/github.com/go-openapi/validate@v0.19.3/README.md +pkg/mod/github.com/go-openapi/validate@v0.19.3/result_test.go +pkg/mod/github.com/go-openapi/validate@v0.19.3/result.go +pkg/mod/github.com/go-openapi/validate@v0.19.3/rexp_test.go +pkg/mod/github.com/go-openapi/validate@v0.19.3/rexp.go +pkg/mod/github.com/go-openapi/validate@v0.19.3/schema_messages.go +pkg/mod/github.com/go-openapi/validate@v0.19.3/schema_option_test.go +pkg/mod/github.com/go-openapi/validate@v0.19.3/schema_option.go +pkg/mod/github.com/go-openapi/validate@v0.19.3/schema_props_test.go +pkg/mod/github.com/go-openapi/validate@v0.19.3/schema_props.go +pkg/mod/github.com/go-openapi/validate@v0.19.3/schema_test.go +pkg/mod/github.com/go-openapi/validate@v0.19.3/schema.go +pkg/mod/github.com/go-openapi/validate@v0.19.3/slice_validator_test.go +pkg/mod/github.com/go-openapi/validate@v0.19.3/slice_validator.go +pkg/mod/github.com/go-openapi/validate@v0.19.3/spec_messages.go +pkg/mod/github.com/go-openapi/validate@v0.19.3/spec_test.go +pkg/mod/github.com/go-openapi/validate@v0.19.3/spec.go +pkg/mod/github.com/go-openapi/validate@v0.19.3/swagger_test.go +pkg/mod/github.com/go-openapi/validate@v0.19.3/type_test.go +pkg/mod/github.com/go-openapi/validate@v0.19.3/type.go +pkg/mod/github.com/go-openapi/validate@v0.19.3/update-fixtures.sh +pkg/mod/github.com/go-openapi/validate@v0.19.3/validator_test.go +pkg/mod/github.com/go-openapi/validate@v0.19.3/validator.go +pkg/mod/github.com/go-openapi/validate@v0.19.3/values_test.go +pkg/mod/github.com/go-openapi/validate@v0.19.3/values.go +pkg/mod/github.com/go-openapi/validate@v0.19.3/.github/CONTRIBUTING.md +pkg/mod/github.com/go-openapi/validate@v0.19.3/fixtures/bugs/123/swagger.yml +pkg/mod/github.com/go-openapi/validate@v0.19.3/fixtures/bugs/1341/fixture-1341-2.yaml +pkg/mod/github.com/go-openapi/validate@v0.19.3/fixtures/bugs/1341/fixture-1341-3.yaml +pkg/mod/github.com/go-openapi/validate@v0.19.3/fixtures/bugs/1341/fixture-1341-4.yaml +pkg/mod/github.com/go-openapi/validate@v0.19.3/fixtures/bugs/1341/fixture-1341-5.yaml +pkg/mod/github.com/go-openapi/validate@v0.19.3/fixtures/bugs/1341/fixture-1341-good.yaml +pkg/mod/github.com/go-openapi/validate@v0.19.3/fixtures/bugs/1341/fixture-1341.yaml +pkg/mod/github.com/go-openapi/validate@v0.19.3/fixtures/bugs/1429/expand-1429.yaml +pkg/mod/github.com/go-openapi/validate@v0.19.3/fixtures/bugs/1429/flatten-1429.yaml +pkg/mod/github.com/go-openapi/validate@v0.19.3/fixtures/bugs/1429/responses.yaml +pkg/mod/github.com/go-openapi/validate@v0.19.3/fixtures/bugs/1429/swagger.yaml +pkg/mod/github.com/go-openapi/validate@v0.19.3/fixtures/bugs/1614/gitea-expanded.json +pkg/mod/github.com/go-openapi/validate@v0.19.3/fixtures/bugs/1614/gitea.json +pkg/mod/github.com/go-openapi/validate@v0.19.3/fixtures/bugs/1621/1621-flat.yaml +pkg/mod/github.com/go-openapi/validate@v0.19.3/fixtures/bugs/1621/definitions.yaml +pkg/mod/github.com/go-openapi/validate@v0.19.3/fixtures/bugs/1621/fixture-1621.yaml +pkg/mod/github.com/go-openapi/validate@v0.19.3/fixtures/bugs/1621/parameters.yaml +pkg/mod/github.com/go-openapi/validate@v0.19.3/fixtures/bugs/1621/responses.yaml +pkg/mod/github.com/go-openapi/validate@v0.19.3/fixtures/bugs/18/headerItems.json +pkg/mod/github.com/go-openapi/validate@v0.19.3/fixtures/bugs/18/headers.json +pkg/mod/github.com/go-openapi/validate@v0.19.3/fixtures/bugs/18/parameters.json +pkg/mod/github.com/go-openapi/validate@v0.19.3/fixtures/bugs/18/paramItems.json +pkg/mod/github.com/go-openapi/validate@v0.19.3/fixtures/bugs/18/schema.json +pkg/mod/github.com/go-openapi/validate@v0.19.3/fixtures/bugs/39/swagger.yml +pkg/mod/github.com/go-openapi/validate@v0.19.3/fixtures/bugs/52/swagger.json +pkg/mod/github.com/go-openapi/validate@v0.19.3/fixtures/bugs/53/noswagger.json +pkg/mod/github.com/go-openapi/validate@v0.19.3/fixtures/bugs/6/empty-responses.json +pkg/mod/github.com/go-openapi/validate@v0.19.3/fixtures/bugs/6/no-responses.json +pkg/mod/github.com/go-openapi/validate@v0.19.3/fixtures/bugs/61/multiple-refs.json +pkg/mod/github.com/go-openapi/validate@v0.19.3/fixtures/bugs/61/unresolved-ref-for-name.json +pkg/mod/github.com/go-openapi/validate@v0.19.3/fixtures/bugs/62/swagger.json +pkg/mod/github.com/go-openapi/validate@v0.19.3/fixtures/bugs/63/swagger.json +pkg/mod/github.com/go-openapi/validate@v0.19.3/fixtures/bugs/73/fixture-responses-2.yaml +pkg/mod/github.com/go-openapi/validate@v0.19.3/fixtures/bugs/73/fixture-responses-3.yaml +pkg/mod/github.com/go-openapi/validate@v0.19.3/fixtures/bugs/73/fixture-responses.yaml +pkg/mod/github.com/go-openapi/validate@v0.19.3/fixtures/bugs/73/fixture-swagger-2.yaml +pkg/mod/github.com/go-openapi/validate@v0.19.3/fixtures/bugs/73/fixture-swagger-3.yaml +pkg/mod/github.com/go-openapi/validate@v0.19.3/fixtures/bugs/73/fixture-swagger-good.yaml +pkg/mod/github.com/go-openapi/validate@v0.19.3/fixtures/bugs/73/fixture-swagger.yaml +pkg/mod/github.com/go-openapi/validate@v0.19.3/fixtures/defaulting/schema.json +pkg/mod/github.com/go-openapi/validate@v0.19.3/fixtures/formats/extended-format.json +pkg/mod/github.com/go-openapi/validate@v0.19.3/fixtures/go-swagger/bugs/1010/swagger.yml +pkg/mod/github.com/go-openapi/validate@v0.19.3/fixtures/go-swagger/bugs/103/swagger.json +pkg/mod/github.com/go-openapi/validate@v0.19.3/fixtures/go-swagger/bugs/106/swagger.json +pkg/mod/github.com/go-openapi/validate@v0.19.3/fixtures/go-swagger/bugs/1111/arrayParam.json +pkg/mod/github.com/go-openapi/validate@v0.19.3/fixtures/go-swagger/bugs/1171/swagger.yaml +pkg/mod/github.com/go-openapi/validate@v0.19.3/fixtures/go-swagger/bugs/1199/nonEmptyBody.json +pkg/mod/github.com/go-openapi/validate@v0.19.3/fixtures/go-swagger/bugs/1216/swagger.yml +pkg/mod/github.com/go-openapi/validate@v0.19.3/fixtures/go-swagger/bugs/1238/swagger.yaml +pkg/mod/github.com/go-openapi/validate@v0.19.3/fixtures/go-swagger/bugs/1289/fixture-1289-2.yaml +pkg/mod/github.com/go-openapi/validate@v0.19.3/fixtures/go-swagger/bugs/1289/fixture-1289-3.yaml +pkg/mod/github.com/go-openapi/validate@v0.19.3/fixtures/go-swagger/bugs/1289/fixture-1289-4.yaml +pkg/mod/github.com/go-openapi/validate@v0.19.3/fixtures/go-swagger/bugs/1289/fixture-1289.yaml +pkg/mod/github.com/go-openapi/validate@v0.19.3/fixtures/go-swagger/bugs/155/swagger.yml +pkg/mod/github.com/go-openapi/validate@v0.19.3/fixtures/go-swagger/bugs/162/swagger.yml +pkg/mod/github.com/go-openapi/validate@v0.19.3/fixtures/go-swagger/bugs/163/swagger.yml +pkg/mod/github.com/go-openapi/validate@v0.19.3/fixtures/go-swagger/bugs/193/spec1.json +pkg/mod/github.com/go-openapi/validate@v0.19.3/fixtures/go-swagger/bugs/193/spec2.json +pkg/mod/github.com/go-openapi/validate@v0.19.3/fixtures/go-swagger/bugs/195/swagger.json +pkg/mod/github.com/go-openapi/validate@v0.19.3/fixtures/go-swagger/bugs/196/swagger.yml +pkg/mod/github.com/go-openapi/validate@v0.19.3/fixtures/go-swagger/bugs/217/array.yml +pkg/mod/github.com/go-openapi/validate@v0.19.3/fixtures/go-swagger/bugs/217/interface.yml +pkg/mod/github.com/go-openapi/validate@v0.19.3/fixtures/go-swagger/bugs/217/map.yml +pkg/mod/github.com/go-openapi/validate@v0.19.3/fixtures/go-swagger/bugs/217/string.yml +pkg/mod/github.com/go-openapi/validate@v0.19.3/fixtures/go-swagger/bugs/248/swagger.json +pkg/mod/github.com/go-openapi/validate@v0.19.3/fixtures/go-swagger/bugs/249/swagger.json +pkg/mod/github.com/go-openapi/validate@v0.19.3/fixtures/go-swagger/bugs/251/swagger.yml +pkg/mod/github.com/go-openapi/validate@v0.19.3/fixtures/go-swagger/bugs/252/swagger.json +pkg/mod/github.com/go-openapi/validate@v0.19.3/fixtures/go-swagger/bugs/287/swagger.yml +pkg/mod/github.com/go-openapi/validate@v0.19.3/fixtures/go-swagger/bugs/319/swagger.yml +pkg/mod/github.com/go-openapi/validate@v0.19.3/fixtures/go-swagger/bugs/342/fixture-342-2.yaml +pkg/mod/github.com/go-openapi/validate@v0.19.3/fixtures/go-swagger/bugs/342/fixture-342-3.yaml +pkg/mod/github.com/go-openapi/validate@v0.19.3/fixtures/go-swagger/bugs/342/fixture-342.yaml +pkg/mod/github.com/go-openapi/validate@v0.19.3/fixtures/go-swagger/bugs/423/swagger.json +pkg/mod/github.com/go-openapi/validate@v0.19.3/fixtures/go-swagger/bugs/436/swagger.yml +pkg/mod/github.com/go-openapi/validate@v0.19.3/fixtures/go-swagger/bugs/453/swagger.yml +pkg/mod/github.com/go-openapi/validate@v0.19.3/fixtures/go-swagger/bugs/454/swagger.yml +pkg/mod/github.com/go-openapi/validate@v0.19.3/fixtures/go-swagger/bugs/455/swagger.yml +pkg/mod/github.com/go-openapi/validate@v0.19.3/fixtures/go-swagger/bugs/465/swagger.yml +pkg/mod/github.com/go-openapi/validate@v0.19.3/fixtures/go-swagger/bugs/500/swagger.yml +pkg/mod/github.com/go-openapi/validate@v0.19.3/fixtures/go-swagger/bugs/511/swagger.yml +pkg/mod/github.com/go-openapi/validate@v0.19.3/fixtures/go-swagger/bugs/524/swagger.yml +pkg/mod/github.com/go-openapi/validate@v0.19.3/fixtures/go-swagger/bugs/540/swagger.yml +pkg/mod/github.com/go-openapi/validate@v0.19.3/fixtures/go-swagger/bugs/541/swagger.json +pkg/mod/github.com/go-openapi/validate@v0.19.3/fixtures/go-swagger/bugs/628/swagger.yml +pkg/mod/github.com/go-openapi/validate@v0.19.3/fixtures/go-swagger/bugs/727/swagger.json +pkg/mod/github.com/go-openapi/validate@v0.19.3/fixtures/go-swagger/bugs/733/swagger.json +pkg/mod/github.com/go-openapi/validate@v0.19.3/fixtures/go-swagger/bugs/740/swagger.yml +pkg/mod/github.com/go-openapi/validate@v0.19.3/fixtures/go-swagger/bugs/743/swagger.yml +pkg/mod/github.com/go-openapi/validate@v0.19.3/fixtures/go-swagger/bugs/763/swagger.yml +pkg/mod/github.com/go-openapi/validate@v0.19.3/fixtures/go-swagger/bugs/774/swagger.yml +pkg/mod/github.com/go-openapi/validate@v0.19.3/fixtures/go-swagger/bugs/776/error.yaml +pkg/mod/github.com/go-openapi/validate@v0.19.3/fixtures/go-swagger/bugs/776/item.yaml +pkg/mod/github.com/go-openapi/validate@v0.19.3/fixtures/go-swagger/bugs/776/param.yaml +pkg/mod/github.com/go-openapi/validate@v0.19.3/fixtures/go-swagger/bugs/776/spec.yaml +pkg/mod/github.com/go-openapi/validate@v0.19.3/fixtures/go-swagger/bugs/776/swagger-template.yml +pkg/mod/github.com/go-openapi/validate@v0.19.3/fixtures/go-swagger/bugs/786/swagger.yml +pkg/mod/github.com/go-openapi/validate@v0.19.3/fixtures/go-swagger/bugs/789/swagger.yml +pkg/mod/github.com/go-openapi/validate@v0.19.3/fixtures/go-swagger/bugs/809/swagger.yml +pkg/mod/github.com/go-openapi/validate@v0.19.3/fixtures/go-swagger/bugs/811/swagger.json +pkg/mod/github.com/go-openapi/validate@v0.19.3/fixtures/go-swagger/bugs/822/swagger.yml +pkg/mod/github.com/go-openapi/validate@v0.19.3/fixtures/go-swagger/bugs/825/swagger.yml +pkg/mod/github.com/go-openapi/validate@v0.19.3/fixtures/go-swagger/bugs/84/swagger.yml +pkg/mod/github.com/go-openapi/validate@v0.19.3/fixtures/go-swagger/bugs/844/swagger.json +pkg/mod/github.com/go-openapi/validate@v0.19.3/fixtures/go-swagger/bugs/846/swagger.yml +pkg/mod/github.com/go-openapi/validate@v0.19.3/fixtures/go-swagger/bugs/881/deep.yml +pkg/mod/github.com/go-openapi/validate@v0.19.3/fixtures/go-swagger/bugs/881/swagger.yml +pkg/mod/github.com/go-openapi/validate@v0.19.3/fixtures/go-swagger/bugs/890/swagger.yaml +pkg/mod/github.com/go-openapi/validate@v0.19.3/fixtures/go-swagger/bugs/890/path/health_check.yaml +pkg/mod/github.com/go-openapi/validate@v0.19.3/fixtures/go-swagger/bugs/899/swagger.yml +pkg/mod/github.com/go-openapi/validate@v0.19.3/fixtures/go-swagger/bugs/981/swagger.json +pkg/mod/github.com/go-openapi/validate@v0.19.3/fixtures/go-swagger/bugs/982/swagger.yaml +pkg/mod/github.com/go-openapi/validate@v0.19.3/fixtures/go-swagger/bugs/987/swagger.yml +pkg/mod/github.com/go-openapi/validate@v0.19.3/fixtures/go-swagger/canary/bitbucket.org/swagger.json +pkg/mod/github.com/go-openapi/validate@v0.19.3/fixtures/go-swagger/canary/docker/swagger.json +pkg/mod/github.com/go-openapi/validate@v0.19.3/fixtures/go-swagger/canary/kubernetes/swagger.json +pkg/mod/github.com/go-openapi/validate@v0.19.3/fixtures/go-swagger/canary/ms-cog-sci/swagger.json +pkg/mod/github.com/go-openapi/validate@v0.19.3/fixtures/go-swagger/canary/petstore/swagger.json +pkg/mod/github.com/go-openapi/validate@v0.19.3/fixtures/go-swagger/canary/quay.io/swagger.json +pkg/mod/github.com/go-openapi/validate@v0.19.3/fixtures/go-swagger/expansion/all-the-things.json +pkg/mod/github.com/go-openapi/validate@v0.19.3/fixtures/go-swagger/expansion/circularRefs.json +pkg/mod/github.com/go-openapi/validate@v0.19.3/fixtures/go-swagger/expansion/circularSpec.json +pkg/mod/github.com/go-openapi/validate@v0.19.3/fixtures/go-swagger/expansion/circularSpec.yaml +pkg/mod/github.com/go-openapi/validate@v0.19.3/fixtures/go-swagger/expansion/clickmeter.json +pkg/mod/github.com/go-openapi/validate@v0.19.3/fixtures/go-swagger/expansion/clickmeter.yaml +pkg/mod/github.com/go-openapi/validate@v0.19.3/fixtures/go-swagger/expansion/invalid-refs.json +pkg/mod/github.com/go-openapi/validate@v0.19.3/fixtures/go-swagger/expansion/params.json +pkg/mod/github.com/go-openapi/validate@v0.19.3/fixtures/go-swagger/expansion/schemas1.json +pkg/mod/github.com/go-openapi/validate@v0.19.3/fixtures/go-swagger/expansion/schemas2.json +pkg/mod/github.com/go-openapi/validate@v0.19.3/fixtures/go-swagger/petstores/petstore-expanded.json +pkg/mod/github.com/go-openapi/validate@v0.19.3/fixtures/go-swagger/petstores/petstore-simple.json +pkg/mod/github.com/go-openapi/validate@v0.19.3/fixtures/go-swagger/petstores/petstore-with-external-docs.json +pkg/mod/github.com/go-openapi/validate@v0.19.3/fixtures/go-swagger/petstores/petstore.json +pkg/mod/github.com/go-openapi/validate@v0.19.3/fixtures/go-swagger/remotes/integer.json +pkg/mod/github.com/go-openapi/validate@v0.19.3/fixtures/go-swagger/remotes/subSchemas.json +pkg/mod/github.com/go-openapi/validate@v0.19.3/fixtures/go-swagger/remotes/folder/folderInteger.json +pkg/mod/github.com/go-openapi/validate@v0.19.3/fixtures/go-swagger/specs/refed.json +pkg/mod/github.com/go-openapi/validate@v0.19.3/fixtures/go-swagger/specs/resolution.json +pkg/mod/github.com/go-openapi/validate@v0.19.3/fixtures/go-swagger/specs/resolution2.json +pkg/mod/github.com/go-openapi/validate@v0.19.3/fixtures/go-swagger/specs/response_name.json +pkg/mod/github.com/go-openapi/validate@v0.19.3/fixtures/go-swagger/specs/deeper/arrayProp.json +pkg/mod/github.com/go-openapi/validate@v0.19.3/fixtures/go-swagger/specs/deeper/stringProp.json +pkg/mod/github.com/go-openapi/validate@v0.19.3/fixtures/go-swagger/templates/swagger_json_embed.gotmpl +pkg/mod/github.com/go-openapi/validate@v0.19.3/fixtures/jsonschema_suite/additionalItems.json +pkg/mod/github.com/go-openapi/validate@v0.19.3/fixtures/jsonschema_suite/additionalProperties.json +pkg/mod/github.com/go-openapi/validate@v0.19.3/fixtures/jsonschema_suite/allOf.json +pkg/mod/github.com/go-openapi/validate@v0.19.3/fixtures/jsonschema_suite/anyOf.json +pkg/mod/github.com/go-openapi/validate@v0.19.3/fixtures/jsonschema_suite/default.json +pkg/mod/github.com/go-openapi/validate@v0.19.3/fixtures/jsonschema_suite/definitions.json +pkg/mod/github.com/go-openapi/validate@v0.19.3/fixtures/jsonschema_suite/dependencies.json +pkg/mod/github.com/go-openapi/validate@v0.19.3/fixtures/jsonschema_suite/enum.json +pkg/mod/github.com/go-openapi/validate@v0.19.3/fixtures/jsonschema_suite/format.json +pkg/mod/github.com/go-openapi/validate@v0.19.3/fixtures/jsonschema_suite/items.json +pkg/mod/github.com/go-openapi/validate@v0.19.3/fixtures/jsonschema_suite/maximum.json +pkg/mod/github.com/go-openapi/validate@v0.19.3/fixtures/jsonschema_suite/maxItems.json +pkg/mod/github.com/go-openapi/validate@v0.19.3/fixtures/jsonschema_suite/maxLength.json +pkg/mod/github.com/go-openapi/validate@v0.19.3/fixtures/jsonschema_suite/maxProperties.json +pkg/mod/github.com/go-openapi/validate@v0.19.3/fixtures/jsonschema_suite/minimum.json +pkg/mod/github.com/go-openapi/validate@v0.19.3/fixtures/jsonschema_suite/minItems.json +pkg/mod/github.com/go-openapi/validate@v0.19.3/fixtures/jsonschema_suite/minLength.json +pkg/mod/github.com/go-openapi/validate@v0.19.3/fixtures/jsonschema_suite/minProperties.json +pkg/mod/github.com/go-openapi/validate@v0.19.3/fixtures/jsonschema_suite/multipleOf.json +pkg/mod/github.com/go-openapi/validate@v0.19.3/fixtures/jsonschema_suite/not.json +pkg/mod/github.com/go-openapi/validate@v0.19.3/fixtures/jsonschema_suite/oneOf.json +pkg/mod/github.com/go-openapi/validate@v0.19.3/fixtures/jsonschema_suite/pattern.json +pkg/mod/github.com/go-openapi/validate@v0.19.3/fixtures/jsonschema_suite/patternProperties.json +pkg/mod/github.com/go-openapi/validate@v0.19.3/fixtures/jsonschema_suite/properties.json +pkg/mod/github.com/go-openapi/validate@v0.19.3/fixtures/jsonschema_suite/ref.json +pkg/mod/github.com/go-openapi/validate@v0.19.3/fixtures/jsonschema_suite/refRemote.json +pkg/mod/github.com/go-openapi/validate@v0.19.3/fixtures/jsonschema_suite/required.json +pkg/mod/github.com/go-openapi/validate@v0.19.3/fixtures/jsonschema_suite/type.json +pkg/mod/github.com/go-openapi/validate@v0.19.3/fixtures/jsonschema_suite/uniqueItems.json +pkg/mod/github.com/go-openapi/validate@v0.19.3/fixtures/jsonschema_suite/optional/bignum.json +pkg/mod/github.com/go-openapi/validate@v0.19.3/fixtures/jsonschema_suite/optional/ecmascript-regex.json +pkg/mod/github.com/go-openapi/validate@v0.19.3/fixtures/jsonschema_suite/optional/format.json +pkg/mod/github.com/go-openapi/validate@v0.19.3/fixtures/jsonschema_suite/optional/zeroTerminatedFloats.json +pkg/mod/github.com/go-openapi/validate@v0.19.3/fixtures/jsonschema_suite/remotes/integer.json +pkg/mod/github.com/go-openapi/validate@v0.19.3/fixtures/jsonschema_suite/remotes/name.json +pkg/mod/github.com/go-openapi/validate@v0.19.3/fixtures/jsonschema_suite/remotes/subSchemas.json +pkg/mod/github.com/go-openapi/validate@v0.19.3/fixtures/jsonschema_suite/remotes/folder/folderInteger.json +pkg/mod/github.com/go-openapi/validate@v0.19.3/fixtures/local_expansion/item.yaml +pkg/mod/github.com/go-openapi/validate@v0.19.3/fixtures/local_expansion/spec.yaml +pkg/mod/github.com/go-openapi/validate@v0.19.3/fixtures/pruning/schema.json +pkg/mod/github.com/go-openapi/validate@v0.19.3/fixtures/recursive_expansion/item.yaml +pkg/mod/github.com/go-openapi/validate@v0.19.3/fixtures/recursive_expansion/spec.yaml +pkg/mod/github.com/go-openapi/validate@v0.19.3/fixtures/schemas/int-enum.json +pkg/mod/github.com/go-openapi/validate@v0.19.3/fixtures/validation/bitbucket.json +pkg/mod/github.com/go-openapi/validate@v0.19.3/fixtures/validation/direct-circular-ancestor.json +pkg/mod/github.com/go-openapi/validate@v0.19.3/fixtures/validation/duplicateprops.json +pkg/mod/github.com/go-openapi/validate@v0.19.3/fixtures/validation/empty-path-param-name.json +pkg/mod/github.com/go-openapi/validate@v0.19.3/fixtures/validation/expected_messages.yaml +pkg/mod/github.com/go-openapi/validate@v0.19.3/fixtures/validation/fixture-43-2.json +pkg/mod/github.com/go-openapi/validate@v0.19.3/fixtures/validation/fixture-43.json +pkg/mod/github.com/go-openapi/validate@v0.19.3/fixtures/validation/fixture-161-2.json +pkg/mod/github.com/go-openapi/validate@v0.19.3/fixtures/validation/fixture-161-good.json +pkg/mod/github.com/go-openapi/validate@v0.19.3/fixtures/validation/fixture-161.json +pkg/mod/github.com/go-openapi/validate@v0.19.3/fixtures/validation/fixture-342-2.yaml +pkg/mod/github.com/go-openapi/validate@v0.19.3/fixtures/validation/fixture-342.yaml +pkg/mod/github.com/go-openapi/validate@v0.19.3/fixtures/validation/fixture-581-good-numbers.yaml +pkg/mod/github.com/go-openapi/validate@v0.19.3/fixtures/validation/fixture-581-good.yaml +pkg/mod/github.com/go-openapi/validate@v0.19.3/fixtures/validation/fixture-581-inline-param-format.yaml +pkg/mod/github.com/go-openapi/validate@v0.19.3/fixtures/validation/fixture-581-inline-param.yaml +pkg/mod/github.com/go-openapi/validate@v0.19.3/fixtures/validation/fixture-581.yaml +pkg/mod/github.com/go-openapi/validate@v0.19.3/fixtures/validation/fixture-859-2.yaml +pkg/mod/github.com/go-openapi/validate@v0.19.3/fixtures/validation/fixture-859-good.yaml +pkg/mod/github.com/go-openapi/validate@v0.19.3/fixtures/validation/fixture-859.yaml +pkg/mod/github.com/go-openapi/validate@v0.19.3/fixtures/validation/fixture-1050.yaml +pkg/mod/github.com/go-openapi/validate@v0.19.3/fixtures/validation/fixture-1171.yaml +pkg/mod/github.com/go-openapi/validate@v0.19.3/fixtures/validation/fixture-1231.yaml +pkg/mod/github.com/go-openapi/validate@v0.19.3/fixtures/validation/fixture-1238.yaml +pkg/mod/github.com/go-openapi/validate@v0.19.3/fixtures/validation/fixture-1243-2.yaml +pkg/mod/github.com/go-openapi/validate@v0.19.3/fixtures/validation/fixture-1243-3.yaml +pkg/mod/github.com/go-openapi/validate@v0.19.3/fixtures/validation/fixture-1243-4.yaml +pkg/mod/github.com/go-openapi/validate@v0.19.3/fixtures/validation/fixture-1243-5.json +pkg/mod/github.com/go-openapi/validate@v0.19.3/fixtures/validation/fixture-1243-5.yaml +pkg/mod/github.com/go-openapi/validate@v0.19.3/fixtures/validation/fixture-1243-good.yaml +pkg/mod/github.com/go-openapi/validate@v0.19.3/fixtures/validation/fixture-1243.yaml +pkg/mod/github.com/go-openapi/validate@v0.19.3/fixtures/validation/fixture-1289-donotload.json +pkg/mod/github.com/go-openapi/validate@v0.19.3/fixtures/validation/fixture-1289-donotload.yaml +pkg/mod/github.com/go-openapi/validate@v0.19.3/fixtures/validation/fixture-1289-good.yaml +pkg/mod/github.com/go-openapi/validate@v0.19.3/fixtures/validation/fixture-1289.yaml +pkg/mod/github.com/go-openapi/validate@v0.19.3/fixtures/validation/fixture-additional-items-2.yaml +pkg/mod/github.com/go-openapi/validate@v0.19.3/fixtures/validation/fixture-additional-items-3.yaml +pkg/mod/github.com/go-openapi/validate@v0.19.3/fixtures/validation/fixture-additional-items-invalid-values.yaml +pkg/mod/github.com/go-openapi/validate@v0.19.3/fixtures/validation/fixture-additional-items.orig +pkg/mod/github.com/go-openapi/validate@v0.19.3/fixtures/validation/fixture-additional-items.yaml +pkg/mod/github.com/go-openapi/validate@v0.19.3/fixtures/validation/fixture-all-formats.yaml +pkg/mod/github.com/go-openapi/validate@v0.19.3/fixtures/validation/fixture-bad-response.yaml +pkg/mod/github.com/go-openapi/validate@v0.19.3/fixtures/validation/fixture-collisions.yaml +pkg/mod/github.com/go-openapi/validate@v0.19.3/fixtures/validation/fixture-constraints-on-numbers.yaml +pkg/mod/github.com/go-openapi/validate@v0.19.3/fixtures/validation/fixture-empty-paths.json +pkg/mod/github.com/go-openapi/validate@v0.19.3/fixtures/validation/fixture-invalid-example-property.yaml +pkg/mod/github.com/go-openapi/validate@v0.19.3/fixtures/validation/fixture-items-items.yaml +pkg/mod/github.com/go-openapi/validate@v0.19.3/fixtures/validation/fixture-no-json-example.yaml +pkg/mod/github.com/go-openapi/validate@v0.19.3/fixtures/validation/fixture-no-response.yaml +pkg/mod/github.com/go-openapi/validate@v0.19.3/fixtures/validation/fixture-patternProperties.json +pkg/mod/github.com/go-openapi/validate@v0.19.3/fixtures/validation/fixture-valid-example-property.yaml +pkg/mod/github.com/go-openapi/validate@v0.19.3/fixtures/validation/gentest.yaml +pkg/mod/github.com/go-openapi/validate@v0.19.3/fixtures/validation/gentest2.yaml +pkg/mod/github.com/go-openapi/validate@v0.19.3/fixtures/validation/gentest3.yaml +pkg/mod/github.com/go-openapi/validate@v0.19.3/fixtures/validation/indirect-circular-ancestor.json +pkg/mod/github.com/go-openapi/validate@v0.19.3/fixtures/validation/invalid-formdata-body-params.json +pkg/mod/github.com/go-openapi/validate@v0.19.3/fixtures/validation/invalid-ref.json +pkg/mod/github.com/go-openapi/validate@v0.19.3/fixtures/validation/invalid-referenced.yml +pkg/mod/github.com/go-openapi/validate@v0.19.3/fixtures/validation/nestedduplicateprops.json +pkg/mod/github.com/go-openapi/validate@v0.19.3/fixtures/validation/petstore-expanded.json +pkg/mod/github.com/go-openapi/validate@v0.19.3/fixtures/validation/recursive-circular-ancestor.json +pkg/mod/github.com/go-openapi/validate@v0.19.3/fixtures/validation/valid-ref.json +pkg/mod/github.com/go-openapi/validate@v0.19.3/fixtures/validation/valid-referenced-variants.yaml +pkg/mod/github.com/go-openapi/validate@v0.19.3/fixtures/validation/valid-referenced.yml +pkg/mod/github.com/go-openapi/validate@v0.19.3/fixtures/validation/default/invalid-default-value-default-response-PatternProperties.json +pkg/mod/github.com/go-openapi/validate@v0.19.3/fixtures/validation/default/invalid-default-value-default-response.json +pkg/mod/github.com/go-openapi/validate@v0.19.3/fixtures/validation/default/invalid-default-value-header-badpattern.json +pkg/mod/github.com/go-openapi/validate@v0.19.3/fixtures/validation/default/invalid-default-value-header-items-default-response.json +pkg/mod/github.com/go-openapi/validate@v0.19.3/fixtures/validation/default/invalid-default-value-header-items-response.json +pkg/mod/github.com/go-openapi/validate@v0.19.3/fixtures/validation/default/invalid-default-value-header-items.json +pkg/mod/github.com/go-openapi/validate@v0.19.3/fixtures/validation/default/invalid-default-value-header-pattern.json +pkg/mod/github.com/go-openapi/validate@v0.19.3/fixtures/validation/default/invalid-default-value-header-response.json +pkg/mod/github.com/go-openapi/validate@v0.19.3/fixtures/validation/default/invalid-default-value-header.json +pkg/mod/github.com/go-openapi/validate@v0.19.3/fixtures/validation/default/invalid-default-value-parameter-items.json +pkg/mod/github.com/go-openapi/validate@v0.19.3/fixtures/validation/default/invalid-default-value-parameter-ref.json +pkg/mod/github.com/go-openapi/validate@v0.19.3/fixtures/validation/default/invalid-default-value-parameter-required.json +pkg/mod/github.com/go-openapi/validate@v0.19.3/fixtures/validation/default/invalid-default-value-parameter-schema.json +pkg/mod/github.com/go-openapi/validate@v0.19.3/fixtures/validation/default/invalid-default-value-parameter.json +pkg/mod/github.com/go-openapi/validate@v0.19.3/fixtures/validation/default/invalid-default-value-response-ref.json +pkg/mod/github.com/go-openapi/validate@v0.19.3/fixtures/validation/default/invalid-default-value-schema-additionalProperties.json +pkg/mod/github.com/go-openapi/validate@v0.19.3/fixtures/validation/default/invalid-default-value-schema-allOf.json +pkg/mod/github.com/go-openapi/validate@v0.19.3/fixtures/validation/default/invalid-default-value-schema-items-allOf.json +pkg/mod/github.com/go-openapi/validate@v0.19.3/fixtures/validation/default/invalid-default-value-schema-items.json +pkg/mod/github.com/go-openapi/validate@v0.19.3/fixtures/validation/default/invalid-default-value-schema-patternProperties.json +pkg/mod/github.com/go-openapi/validate@v0.19.3/fixtures/validation/default/invalid-default-value-schema-ref.json +pkg/mod/github.com/go-openapi/validate@v0.19.3/fixtures/validation/default/invalid-default-value-schema.json +pkg/mod/github.com/go-openapi/validate@v0.19.3/fixtures/validation/default/valid-default-value-default-response-PatternProperties.json +pkg/mod/github.com/go-openapi/validate@v0.19.3/fixtures/validation/default/valid-default-value-default-response.json +pkg/mod/github.com/go-openapi/validate@v0.19.3/fixtures/validation/default/valid-default-value-header-badpattern.json +pkg/mod/github.com/go-openapi/validate@v0.19.3/fixtures/validation/default/valid-default-value-header-items-default-response.json +pkg/mod/github.com/go-openapi/validate@v0.19.3/fixtures/validation/default/valid-default-value-header-items-response.json +pkg/mod/github.com/go-openapi/validate@v0.19.3/fixtures/validation/default/valid-default-value-header-items.json +pkg/mod/github.com/go-openapi/validate@v0.19.3/fixtures/validation/default/valid-default-value-header-pattern.json +pkg/mod/github.com/go-openapi/validate@v0.19.3/fixtures/validation/default/valid-default-value-header-response.json +pkg/mod/github.com/go-openapi/validate@v0.19.3/fixtures/validation/default/valid-default-value-header.json +pkg/mod/github.com/go-openapi/validate@v0.19.3/fixtures/validation/default/valid-default-value-parameter-items.json +pkg/mod/github.com/go-openapi/validate@v0.19.3/fixtures/validation/default/valid-default-value-parameter-ref.json +pkg/mod/github.com/go-openapi/validate@v0.19.3/fixtures/validation/default/valid-default-value-parameter-required.json +pkg/mod/github.com/go-openapi/validate@v0.19.3/fixtures/validation/default/valid-default-value-parameter-schema.json +pkg/mod/github.com/go-openapi/validate@v0.19.3/fixtures/validation/default/valid-default-value-parameter.json +pkg/mod/github.com/go-openapi/validate@v0.19.3/fixtures/validation/default/valid-default-value-response-ref.json +pkg/mod/github.com/go-openapi/validate@v0.19.3/fixtures/validation/default/valid-default-value-schema-additionalProperties.json +pkg/mod/github.com/go-openapi/validate@v0.19.3/fixtures/validation/default/valid-default-value-schema-allOf.json +pkg/mod/github.com/go-openapi/validate@v0.19.3/fixtures/validation/default/valid-default-value-schema-items-allOf.json +pkg/mod/github.com/go-openapi/validate@v0.19.3/fixtures/validation/default/valid-default-value-schema-items.json +pkg/mod/github.com/go-openapi/validate@v0.19.3/fixtures/validation/default/valid-default-value-schema-patternProperties.json +pkg/mod/github.com/go-openapi/validate@v0.19.3/fixtures/validation/default/valid-default-value-schema-ref.json +pkg/mod/github.com/go-openapi/validate@v0.19.3/fixtures/validation/default/valid-default-value-schema.json +pkg/mod/github.com/go-openapi/validate@v0.19.3/fixtures/validation/example/invalid-example-default-response-PatternProperties.json +pkg/mod/github.com/go-openapi/validate@v0.19.3/fixtures/validation/example/invalid-example-default-response.json +pkg/mod/github.com/go-openapi/validate@v0.19.3/fixtures/validation/example/invalid-example-header-badpattern.json +pkg/mod/github.com/go-openapi/validate@v0.19.3/fixtures/validation/example/invalid-example-header-items-default-response.json +pkg/mod/github.com/go-openapi/validate@v0.19.3/fixtures/validation/example/invalid-example-header-items-response.json +pkg/mod/github.com/go-openapi/validate@v0.19.3/fixtures/validation/example/invalid-example-header-items.json +pkg/mod/github.com/go-openapi/validate@v0.19.3/fixtures/validation/example/invalid-example-header-pattern.json +pkg/mod/github.com/go-openapi/validate@v0.19.3/fixtures/validation/example/invalid-example-header-response.json +pkg/mod/github.com/go-openapi/validate@v0.19.3/fixtures/validation/example/invalid-example-header.json +pkg/mod/github.com/go-openapi/validate@v0.19.3/fixtures/validation/example/invalid-example-parameter-items.json +pkg/mod/github.com/go-openapi/validate@v0.19.3/fixtures/validation/example/invalid-example-parameter-ref.json +pkg/mod/github.com/go-openapi/validate@v0.19.3/fixtures/validation/example/invalid-example-parameter-required.json +pkg/mod/github.com/go-openapi/validate@v0.19.3/fixtures/validation/example/invalid-example-parameter-schema.json +pkg/mod/github.com/go-openapi/validate@v0.19.3/fixtures/validation/example/invalid-example-parameter.json +pkg/mod/github.com/go-openapi/validate@v0.19.3/fixtures/validation/example/invalid-example-response-ref.json +pkg/mod/github.com/go-openapi/validate@v0.19.3/fixtures/validation/example/invalid-example-response.json +pkg/mod/github.com/go-openapi/validate@v0.19.3/fixtures/validation/example/invalid-example-schema-additionalProperties.json +pkg/mod/github.com/go-openapi/validate@v0.19.3/fixtures/validation/example/invalid-example-schema-allOf.json +pkg/mod/github.com/go-openapi/validate@v0.19.3/fixtures/validation/example/invalid-example-schema-items-allOf.json +pkg/mod/github.com/go-openapi/validate@v0.19.3/fixtures/validation/example/invalid-example-schema-items.json +pkg/mod/github.com/go-openapi/validate@v0.19.3/fixtures/validation/example/invalid-example-schema-patternProperties.json +pkg/mod/github.com/go-openapi/validate@v0.19.3/fixtures/validation/example/invalid-example-schema-ref.json +pkg/mod/github.com/go-openapi/validate@v0.19.3/fixtures/validation/example/invalid-example-schema.json +pkg/mod/github.com/go-openapi/validate@v0.19.3/fixtures/validation/example/valid-example-default-response-PatternProperties.json +pkg/mod/github.com/go-openapi/validate@v0.19.3/fixtures/validation/example/valid-example-default-response.json +pkg/mod/github.com/go-openapi/validate@v0.19.3/fixtures/validation/example/valid-example-header-badpattern.json +pkg/mod/github.com/go-openapi/validate@v0.19.3/fixtures/validation/example/valid-example-header-items-default-response.json +pkg/mod/github.com/go-openapi/validate@v0.19.3/fixtures/validation/example/valid-example-header-items-response.json +pkg/mod/github.com/go-openapi/validate@v0.19.3/fixtures/validation/example/valid-example-header-items.json +pkg/mod/github.com/go-openapi/validate@v0.19.3/fixtures/validation/example/valid-example-header-pattern.json +pkg/mod/github.com/go-openapi/validate@v0.19.3/fixtures/validation/example/valid-example-header-response.json +pkg/mod/github.com/go-openapi/validate@v0.19.3/fixtures/validation/example/valid-example-header.json +pkg/mod/github.com/go-openapi/validate@v0.19.3/fixtures/validation/example/valid-example-parameter-items.json +pkg/mod/github.com/go-openapi/validate@v0.19.3/fixtures/validation/example/valid-example-parameter-ref.json +pkg/mod/github.com/go-openapi/validate@v0.19.3/fixtures/validation/example/valid-example-parameter-required.json +pkg/mod/github.com/go-openapi/validate@v0.19.3/fixtures/validation/example/valid-example-parameter-schema.json +pkg/mod/github.com/go-openapi/validate@v0.19.3/fixtures/validation/example/valid-example-parameter.json +pkg/mod/github.com/go-openapi/validate@v0.19.3/fixtures/validation/example/valid-example-response-ref.json +pkg/mod/github.com/go-openapi/validate@v0.19.3/fixtures/validation/example/valid-example-response.json +pkg/mod/github.com/go-openapi/validate@v0.19.3/fixtures/validation/example/valid-example-schema-additionalProperties.json +pkg/mod/github.com/go-openapi/validate@v0.19.3/fixtures/validation/example/valid-example-schema-allOf.json +pkg/mod/github.com/go-openapi/validate@v0.19.3/fixtures/validation/example/valid-example-schema-items-allOf.json +pkg/mod/github.com/go-openapi/validate@v0.19.3/fixtures/validation/example/valid-example-schema-items.json +pkg/mod/github.com/go-openapi/validate@v0.19.3/fixtures/validation/example/valid-example-schema-patternProperties.json +pkg/mod/github.com/go-openapi/validate@v0.19.3/fixtures/validation/example/valid-example-schema-ref.json +pkg/mod/github.com/go-openapi/validate@v0.19.3/fixtures/validation/example/valid-example-schema.json +pkg/mod/github.com/go-openapi/validate@v0.19.3/post/defaulter_test.go +pkg/mod/github.com/go-openapi/validate@v0.19.3/post/defaulter.go +pkg/mod/github.com/go-openapi/validate@v0.19.3/post/prune_test.go +pkg/mod/github.com/go-openapi/validate@v0.19.3/post/prune.go +pkg/mod/github.com/go-sql-driver/mysql@v1.4.1/.gitignore +pkg/mod/github.com/go-sql-driver/mysql@v1.4.1/.travis.yml +pkg/mod/github.com/go-sql-driver/mysql@v1.4.1/appengine.go +pkg/mod/github.com/go-sql-driver/mysql@v1.4.1/auth_test.go +pkg/mod/github.com/go-sql-driver/mysql@v1.4.1/auth.go +pkg/mod/github.com/go-sql-driver/mysql@v1.4.1/AUTHORS +pkg/mod/github.com/go-sql-driver/mysql@v1.4.1/benchmark_go18_test.go +pkg/mod/github.com/go-sql-driver/mysql@v1.4.1/benchmark_test.go +pkg/mod/github.com/go-sql-driver/mysql@v1.4.1/buffer.go +pkg/mod/github.com/go-sql-driver/mysql@v1.4.1/CHANGELOG.md +pkg/mod/github.com/go-sql-driver/mysql@v1.4.1/collations.go +pkg/mod/github.com/go-sql-driver/mysql@v1.4.1/connection_go18_test.go +pkg/mod/github.com/go-sql-driver/mysql@v1.4.1/connection_go18.go +pkg/mod/github.com/go-sql-driver/mysql@v1.4.1/connection_test.go +pkg/mod/github.com/go-sql-driver/mysql@v1.4.1/connection.go +pkg/mod/github.com/go-sql-driver/mysql@v1.4.1/const.go +pkg/mod/github.com/go-sql-driver/mysql@v1.4.1/CONTRIBUTING.md +pkg/mod/github.com/go-sql-driver/mysql@v1.4.1/driver_go18_test.go +pkg/mod/github.com/go-sql-driver/mysql@v1.4.1/driver_test.go +pkg/mod/github.com/go-sql-driver/mysql@v1.4.1/driver.go +pkg/mod/github.com/go-sql-driver/mysql@v1.4.1/dsn_test.go +pkg/mod/github.com/go-sql-driver/mysql@v1.4.1/dsn.go +pkg/mod/github.com/go-sql-driver/mysql@v1.4.1/errors_test.go +pkg/mod/github.com/go-sql-driver/mysql@v1.4.1/errors.go +pkg/mod/github.com/go-sql-driver/mysql@v1.4.1/fields.go +pkg/mod/github.com/go-sql-driver/mysql@v1.4.1/infile.go +pkg/mod/github.com/go-sql-driver/mysql@v1.4.1/LICENSE +pkg/mod/github.com/go-sql-driver/mysql@v1.4.1/packets_test.go +pkg/mod/github.com/go-sql-driver/mysql@v1.4.1/packets.go +pkg/mod/github.com/go-sql-driver/mysql@v1.4.1/README.md +pkg/mod/github.com/go-sql-driver/mysql@v1.4.1/result.go +pkg/mod/github.com/go-sql-driver/mysql@v1.4.1/rows.go +pkg/mod/github.com/go-sql-driver/mysql@v1.4.1/statement_test.go +pkg/mod/github.com/go-sql-driver/mysql@v1.4.1/statement.go +pkg/mod/github.com/go-sql-driver/mysql@v1.4.1/transaction.go +pkg/mod/github.com/go-sql-driver/mysql@v1.4.1/utils_go17.go +pkg/mod/github.com/go-sql-driver/mysql@v1.4.1/utils_go18_test.go +pkg/mod/github.com/go-sql-driver/mysql@v1.4.1/utils_go18.go +pkg/mod/github.com/go-sql-driver/mysql@v1.4.1/utils_test.go +pkg/mod/github.com/go-sql-driver/mysql@v1.4.1/utils.go +pkg/mod/github.com/go-sql-driver/mysql@v1.4.1/.github/ISSUE_TEMPLATE.md +pkg/mod/github.com/go-sql-driver/mysql@v1.4.1/.github/PULL_REQUEST_TEMPLATE.md +pkg/mod/github.com/go-sql-driver/mysql@v1.4.1/.travis/docker.cnf +pkg/mod/github.com/go-sql-driver/mysql@v1.4.1/.travis/gofmt.sh +pkg/mod/github.com/go-sql-driver/mysql@v1.4.1/.travis/wait_mysql.sh +pkg/mod/github.com/go-stack/stack@v1.8.0/.travis.yml +pkg/mod/github.com/go-stack/stack@v1.8.0/format_test.go +pkg/mod/github.com/go-stack/stack@v1.8.0/go.mod +pkg/mod/github.com/go-stack/stack@v1.8.0/LICENSE.md +pkg/mod/github.com/go-stack/stack@v1.8.0/README.md +pkg/mod/github.com/go-stack/stack@v1.8.0/stack_test.go +pkg/mod/github.com/go-stack/stack@v1.8.0/stack-go19_test.go +pkg/mod/github.com/go-stack/stack@v1.8.0/stack.go +pkg/mod/github.com/golang/glog@v0.0.0-20160126235308-23def4e6c14b/glog_file.go +pkg/mod/github.com/golang/glog@v0.0.0-20160126235308-23def4e6c14b/glog_test.go +pkg/mod/github.com/golang/glog@v0.0.0-20160126235308-23def4e6c14b/glog.go +pkg/mod/github.com/golang/glog@v0.0.0-20160126235308-23def4e6c14b/LICENSE +pkg/mod/github.com/golang/glog@v0.0.0-20160126235308-23def4e6c14b/README +pkg/mod/github.com/golang/protobuf@v1.3.3/.gitignore +pkg/mod/github.com/golang/protobuf@v1.3.3/.travis.yml +pkg/mod/github.com/golang/protobuf@v1.3.3/AUTHORS +pkg/mod/github.com/golang/protobuf@v1.3.3/CONTRIBUTORS +pkg/mod/github.com/golang/protobuf@v1.3.3/go.mod +pkg/mod/github.com/golang/protobuf@v1.3.3/go.sum +pkg/mod/github.com/golang/protobuf@v1.3.3/LICENSE +pkg/mod/github.com/golang/protobuf@v1.3.3/Makefile +pkg/mod/github.com/golang/protobuf@v1.3.3/README.md +pkg/mod/github.com/golang/protobuf@v1.3.3/regenerate.sh +pkg/mod/github.com/golang/protobuf@v1.3.3/.github/ISSUE_TEMPLATE/bug_report.md +pkg/mod/github.com/golang/protobuf@v1.3.3/.github/ISSUE_TEMPLATE/feature_request.md +pkg/mod/github.com/golang/protobuf@v1.3.3/.github/ISSUE_TEMPLATE/question.md +pkg/mod/github.com/golang/protobuf@v1.3.3/descriptor/descriptor_test.go +pkg/mod/github.com/golang/protobuf@v1.3.3/descriptor/descriptor.go +pkg/mod/github.com/golang/protobuf@v1.3.3/jsonpb/jsonpb_test.go +pkg/mod/github.com/golang/protobuf@v1.3.3/jsonpb/jsonpb.go +pkg/mod/github.com/golang/protobuf@v1.3.3/jsonpb/jsonpb_test_proto/more_test_objects.pb.go +pkg/mod/github.com/golang/protobuf@v1.3.3/jsonpb/jsonpb_test_proto/more_test_objects.proto +pkg/mod/github.com/golang/protobuf@v1.3.3/jsonpb/jsonpb_test_proto/test_objects.pb.go +pkg/mod/github.com/golang/protobuf@v1.3.3/jsonpb/jsonpb_test_proto/test_objects.proto +pkg/mod/github.com/golang/protobuf@v1.3.3/proto/all_test.go +pkg/mod/github.com/golang/protobuf@v1.3.3/proto/any_test.go +pkg/mod/github.com/golang/protobuf@v1.3.3/proto/clone_test.go +pkg/mod/github.com/golang/protobuf@v1.3.3/proto/clone.go +pkg/mod/github.com/golang/protobuf@v1.3.3/proto/decode_test.go +pkg/mod/github.com/golang/protobuf@v1.3.3/proto/decode.go +pkg/mod/github.com/golang/protobuf@v1.3.3/proto/deprecated.go +pkg/mod/github.com/golang/protobuf@v1.3.3/proto/discard_test.go +pkg/mod/github.com/golang/protobuf@v1.3.3/proto/discard.go +pkg/mod/github.com/golang/protobuf@v1.3.3/proto/encode_test.go +pkg/mod/github.com/golang/protobuf@v1.3.3/proto/encode.go +pkg/mod/github.com/golang/protobuf@v1.3.3/proto/equal_test.go +pkg/mod/github.com/golang/protobuf@v1.3.3/proto/equal.go +pkg/mod/github.com/golang/protobuf@v1.3.3/proto/extensions_test.go +pkg/mod/github.com/golang/protobuf@v1.3.3/proto/extensions.go +pkg/mod/github.com/golang/protobuf@v1.3.3/proto/lib.go +pkg/mod/github.com/golang/protobuf@v1.3.3/proto/map_test.go +pkg/mod/github.com/golang/protobuf@v1.3.3/proto/message_set_test.go +pkg/mod/github.com/golang/protobuf@v1.3.3/proto/message_set.go +pkg/mod/github.com/golang/protobuf@v1.3.3/proto/pointer_reflect.go +pkg/mod/github.com/golang/protobuf@v1.3.3/proto/pointer_unsafe.go +pkg/mod/github.com/golang/protobuf@v1.3.3/proto/properties.go +pkg/mod/github.com/golang/protobuf@v1.3.3/proto/proto3_test.go +pkg/mod/github.com/golang/protobuf@v1.3.3/proto/size_test.go +pkg/mod/github.com/golang/protobuf@v1.3.3/proto/size2_test.go +pkg/mod/github.com/golang/protobuf@v1.3.3/proto/table_marshal.go +pkg/mod/github.com/golang/protobuf@v1.3.3/proto/table_merge.go +pkg/mod/github.com/golang/protobuf@v1.3.3/proto/table_unmarshal.go +pkg/mod/github.com/golang/protobuf@v1.3.3/proto/text_parser_test.go +pkg/mod/github.com/golang/protobuf@v1.3.3/proto/text_parser.go +pkg/mod/github.com/golang/protobuf@v1.3.3/proto/text_test.go +pkg/mod/github.com/golang/protobuf@v1.3.3/proto/text.go +pkg/mod/github.com/golang/protobuf@v1.3.3/proto/proto3_proto/proto3.pb.go +pkg/mod/github.com/golang/protobuf@v1.3.3/proto/proto3_proto/proto3.proto +pkg/mod/github.com/golang/protobuf@v1.3.3/proto/test_proto/test.pb.go +pkg/mod/github.com/golang/protobuf@v1.3.3/proto/test_proto/test.proto +pkg/mod/github.com/golang/protobuf@v1.3.3/protoc-gen-go/doc.go +pkg/mod/github.com/golang/protobuf@v1.3.3/protoc-gen-go/golden_test.go +pkg/mod/github.com/golang/protobuf@v1.3.3/protoc-gen-go/link_grpc.go +pkg/mod/github.com/golang/protobuf@v1.3.3/protoc-gen-go/main.go +pkg/mod/github.com/golang/protobuf@v1.3.3/protoc-gen-go/descriptor/descriptor.pb.go +pkg/mod/github.com/golang/protobuf@v1.3.3/protoc-gen-go/descriptor/descriptor.proto +pkg/mod/github.com/golang/protobuf@v1.3.3/protoc-gen-go/generator/generator.go +pkg/mod/github.com/golang/protobuf@v1.3.3/protoc-gen-go/generator/name_test.go +pkg/mod/github.com/golang/protobuf@v1.3.3/protoc-gen-go/generator/internal/remap/remap_test.go +pkg/mod/github.com/golang/protobuf@v1.3.3/protoc-gen-go/generator/internal/remap/remap.go +pkg/mod/github.com/golang/protobuf@v1.3.3/protoc-gen-go/grpc/grpc.go +pkg/mod/github.com/golang/protobuf@v1.3.3/protoc-gen-go/plugin/plugin.pb.go +pkg/mod/github.com/golang/protobuf@v1.3.3/protoc-gen-go/plugin/plugin.pb.golden +pkg/mod/github.com/golang/protobuf@v1.3.3/protoc-gen-go/plugin/plugin.proto +pkg/mod/github.com/golang/protobuf@v1.3.3/protoc-gen-go/testdata/extension_test.go +pkg/mod/github.com/golang/protobuf@v1.3.3/protoc-gen-go/testdata/import_public_test.go +pkg/mod/github.com/golang/protobuf@v1.3.3/protoc-gen-go/testdata/main_test.go +pkg/mod/github.com/golang/protobuf@v1.3.3/protoc-gen-go/testdata/deprecated/deprecated.pb.go +pkg/mod/github.com/golang/protobuf@v1.3.3/protoc-gen-go/testdata/deprecated/deprecated.proto +pkg/mod/github.com/golang/protobuf@v1.3.3/protoc-gen-go/testdata/extension_base/extension_base.pb.go +pkg/mod/github.com/golang/protobuf@v1.3.3/protoc-gen-go/testdata/extension_base/extension_base.proto +pkg/mod/github.com/golang/protobuf@v1.3.3/protoc-gen-go/testdata/extension_extra/extension_extra.pb.go +pkg/mod/github.com/golang/protobuf@v1.3.3/protoc-gen-go/testdata/extension_extra/extension_extra.proto +pkg/mod/github.com/golang/protobuf@v1.3.3/protoc-gen-go/testdata/extension_user/extension_user.pb.go +pkg/mod/github.com/golang/protobuf@v1.3.3/protoc-gen-go/testdata/extension_user/extension_user.proto +pkg/mod/github.com/golang/protobuf@v1.3.3/protoc-gen-go/testdata/import_public/a.pb.go +pkg/mod/github.com/golang/protobuf@v1.3.3/protoc-gen-go/testdata/import_public/a.proto +pkg/mod/github.com/golang/protobuf@v1.3.3/protoc-gen-go/testdata/import_public/b.pb.go +pkg/mod/github.com/golang/protobuf@v1.3.3/protoc-gen-go/testdata/import_public/b.proto +pkg/mod/github.com/golang/protobuf@v1.3.3/protoc-gen-go/testdata/import_public/importing/importing.pb.go +pkg/mod/github.com/golang/protobuf@v1.3.3/protoc-gen-go/testdata/import_public/importing/importing.proto +pkg/mod/github.com/golang/protobuf@v1.3.3/protoc-gen-go/testdata/import_public/sub/a.pb.go +pkg/mod/github.com/golang/protobuf@v1.3.3/protoc-gen-go/testdata/import_public/sub/a.proto +pkg/mod/github.com/golang/protobuf@v1.3.3/protoc-gen-go/testdata/import_public/sub/b.pb.go +pkg/mod/github.com/golang/protobuf@v1.3.3/protoc-gen-go/testdata/import_public/sub/b.proto +pkg/mod/github.com/golang/protobuf@v1.3.3/protoc-gen-go/testdata/imports/test_import_a1m1.pb.go +pkg/mod/github.com/golang/protobuf@v1.3.3/protoc-gen-go/testdata/imports/test_import_a1m1.proto +pkg/mod/github.com/golang/protobuf@v1.3.3/protoc-gen-go/testdata/imports/test_import_a1m2.pb.go +pkg/mod/github.com/golang/protobuf@v1.3.3/protoc-gen-go/testdata/imports/test_import_a1m2.proto +pkg/mod/github.com/golang/protobuf@v1.3.3/protoc-gen-go/testdata/imports/test_import_all.pb.go +pkg/mod/github.com/golang/protobuf@v1.3.3/protoc-gen-go/testdata/imports/test_import_all.proto +pkg/mod/github.com/golang/protobuf@v1.3.3/protoc-gen-go/testdata/imports/fmt/m.pb.go +pkg/mod/github.com/golang/protobuf@v1.3.3/protoc-gen-go/testdata/imports/fmt/m.proto +pkg/mod/github.com/golang/protobuf@v1.3.3/protoc-gen-go/testdata/imports/test_a_1/m1.pb.go +pkg/mod/github.com/golang/protobuf@v1.3.3/protoc-gen-go/testdata/imports/test_a_1/m1.proto +pkg/mod/github.com/golang/protobuf@v1.3.3/protoc-gen-go/testdata/imports/test_a_1/m2.pb.go +pkg/mod/github.com/golang/protobuf@v1.3.3/protoc-gen-go/testdata/imports/test_a_1/m2.proto +pkg/mod/github.com/golang/protobuf@v1.3.3/protoc-gen-go/testdata/imports/test_a_2/m3.pb.go +pkg/mod/github.com/golang/protobuf@v1.3.3/protoc-gen-go/testdata/imports/test_a_2/m3.proto +pkg/mod/github.com/golang/protobuf@v1.3.3/protoc-gen-go/testdata/imports/test_a_2/m4.pb.go +pkg/mod/github.com/golang/protobuf@v1.3.3/protoc-gen-go/testdata/imports/test_a_2/m4.proto +pkg/mod/github.com/golang/protobuf@v1.3.3/protoc-gen-go/testdata/imports/test_b_1/m1.pb.go +pkg/mod/github.com/golang/protobuf@v1.3.3/protoc-gen-go/testdata/imports/test_b_1/m1.proto +pkg/mod/github.com/golang/protobuf@v1.3.3/protoc-gen-go/testdata/imports/test_b_1/m2.pb.go +pkg/mod/github.com/golang/protobuf@v1.3.3/protoc-gen-go/testdata/imports/test_b_1/m2.proto +pkg/mod/github.com/golang/protobuf@v1.3.3/protoc-gen-go/testdata/issue780_oneof_conflict/test.pb.go +pkg/mod/github.com/golang/protobuf@v1.3.3/protoc-gen-go/testdata/issue780_oneof_conflict/test.proto +pkg/mod/github.com/golang/protobuf@v1.3.3/protoc-gen-go/testdata/multi/multi1.pb.go +pkg/mod/github.com/golang/protobuf@v1.3.3/protoc-gen-go/testdata/multi/multi1.proto +pkg/mod/github.com/golang/protobuf@v1.3.3/protoc-gen-go/testdata/multi/multi2.pb.go +pkg/mod/github.com/golang/protobuf@v1.3.3/protoc-gen-go/testdata/multi/multi2.proto +pkg/mod/github.com/golang/protobuf@v1.3.3/protoc-gen-go/testdata/multi/multi3.pb.go +pkg/mod/github.com/golang/protobuf@v1.3.3/protoc-gen-go/testdata/multi/multi3.proto +pkg/mod/github.com/golang/protobuf@v1.3.3/protoc-gen-go/testdata/my_test/test.pb.go +pkg/mod/github.com/golang/protobuf@v1.3.3/protoc-gen-go/testdata/my_test/test.proto +pkg/mod/github.com/golang/protobuf@v1.3.3/protoc-gen-go/testdata/proto3/proto3.pb.go +pkg/mod/github.com/golang/protobuf@v1.3.3/protoc-gen-go/testdata/proto3/proto3.proto +pkg/mod/github.com/golang/protobuf@v1.3.3/ptypes/any_test.go +pkg/mod/github.com/golang/protobuf@v1.3.3/ptypes/any.go +pkg/mod/github.com/golang/protobuf@v1.3.3/ptypes/doc.go +pkg/mod/github.com/golang/protobuf@v1.3.3/ptypes/duration_test.go +pkg/mod/github.com/golang/protobuf@v1.3.3/ptypes/duration.go +pkg/mod/github.com/golang/protobuf@v1.3.3/ptypes/timestamp_test.go +pkg/mod/github.com/golang/protobuf@v1.3.3/ptypes/timestamp.go +pkg/mod/github.com/golang/protobuf@v1.3.3/ptypes/any/any.pb.go +pkg/mod/github.com/golang/protobuf@v1.3.3/ptypes/any/any.proto +pkg/mod/github.com/golang/protobuf@v1.3.3/ptypes/duration/duration.pb.go +pkg/mod/github.com/golang/protobuf@v1.3.3/ptypes/duration/duration.proto +pkg/mod/github.com/golang/protobuf@v1.3.3/ptypes/empty/empty.pb.go +pkg/mod/github.com/golang/protobuf@v1.3.3/ptypes/empty/empty.proto +pkg/mod/github.com/golang/protobuf@v1.3.3/ptypes/struct/struct.pb.go +pkg/mod/github.com/golang/protobuf@v1.3.3/ptypes/struct/struct.proto +pkg/mod/github.com/golang/protobuf@v1.3.3/ptypes/timestamp/timestamp.pb.go +pkg/mod/github.com/golang/protobuf@v1.3.3/ptypes/timestamp/timestamp.proto +pkg/mod/github.com/golang/protobuf@v1.3.3/ptypes/wrappers/wrappers.pb.go +pkg/mod/github.com/golang/protobuf@v1.3.3/ptypes/wrappers/wrappers.proto +pkg/mod/github.com/google/go-cmp@v0.4.0/.travis.yml +pkg/mod/github.com/google/go-cmp@v0.4.0/CONTRIBUTING.md +pkg/mod/github.com/google/go-cmp@v0.4.0/go.mod +pkg/mod/github.com/google/go-cmp@v0.4.0/go.sum +pkg/mod/github.com/google/go-cmp@v0.4.0/LICENSE +pkg/mod/github.com/google/go-cmp@v0.4.0/README.md +pkg/mod/github.com/google/go-cmp@v0.4.0/cmp/compare_test.go +pkg/mod/github.com/google/go-cmp@v0.4.0/cmp/compare.go +pkg/mod/github.com/google/go-cmp@v0.4.0/cmp/example_reporter_test.go +pkg/mod/github.com/google/go-cmp@v0.4.0/cmp/example_test.go +pkg/mod/github.com/google/go-cmp@v0.4.0/cmp/export_panic.go +pkg/mod/github.com/google/go-cmp@v0.4.0/cmp/export_unsafe.go +pkg/mod/github.com/google/go-cmp@v0.4.0/cmp/options_test.go +pkg/mod/github.com/google/go-cmp@v0.4.0/cmp/options.go +pkg/mod/github.com/google/go-cmp@v0.4.0/cmp/path.go +pkg/mod/github.com/google/go-cmp@v0.4.0/cmp/report_compare.go +pkg/mod/github.com/google/go-cmp@v0.4.0/cmp/report_reflect.go +pkg/mod/github.com/google/go-cmp@v0.4.0/cmp/report_slices.go +pkg/mod/github.com/google/go-cmp@v0.4.0/cmp/report_text.go +pkg/mod/github.com/google/go-cmp@v0.4.0/cmp/report_value.go +pkg/mod/github.com/google/go-cmp@v0.4.0/cmp/report.go +pkg/mod/github.com/google/go-cmp@v0.4.0/cmp/cmpopts/equate.go +pkg/mod/github.com/google/go-cmp@v0.4.0/cmp/cmpopts/ignore.go +pkg/mod/github.com/google/go-cmp@v0.4.0/cmp/cmpopts/sort.go +pkg/mod/github.com/google/go-cmp@v0.4.0/cmp/cmpopts/struct_filter.go +pkg/mod/github.com/google/go-cmp@v0.4.0/cmp/cmpopts/util_test.go +pkg/mod/github.com/google/go-cmp@v0.4.0/cmp/cmpopts/xform.go +pkg/mod/github.com/google/go-cmp@v0.4.0/cmp/internal/diff/debug_disable.go +pkg/mod/github.com/google/go-cmp@v0.4.0/cmp/internal/diff/debug_enable.go +pkg/mod/github.com/google/go-cmp@v0.4.0/cmp/internal/diff/diff_test.go +pkg/mod/github.com/google/go-cmp@v0.4.0/cmp/internal/diff/diff.go +pkg/mod/github.com/google/go-cmp@v0.4.0/cmp/internal/flags/flags.go +pkg/mod/github.com/google/go-cmp@v0.4.0/cmp/internal/flags/toolchain_legacy.go +pkg/mod/github.com/google/go-cmp@v0.4.0/cmp/internal/flags/toolchain_recent.go +pkg/mod/github.com/google/go-cmp@v0.4.0/cmp/internal/function/func_test.go +pkg/mod/github.com/google/go-cmp@v0.4.0/cmp/internal/function/func.go +pkg/mod/github.com/google/go-cmp@v0.4.0/cmp/internal/testprotos/protos.go +pkg/mod/github.com/google/go-cmp@v0.4.0/cmp/internal/teststructs/project1.go +pkg/mod/github.com/google/go-cmp@v0.4.0/cmp/internal/teststructs/project2.go +pkg/mod/github.com/google/go-cmp@v0.4.0/cmp/internal/teststructs/project3.go +pkg/mod/github.com/google/go-cmp@v0.4.0/cmp/internal/teststructs/project4.go +pkg/mod/github.com/google/go-cmp@v0.4.0/cmp/internal/teststructs/structs.go +pkg/mod/github.com/google/go-cmp@v0.4.0/cmp/internal/value/pointer_purego.go +pkg/mod/github.com/google/go-cmp@v0.4.0/cmp/internal/value/pointer_unsafe.go +pkg/mod/github.com/google/go-cmp@v0.4.0/cmp/internal/value/sort_test.go +pkg/mod/github.com/google/go-cmp@v0.4.0/cmp/internal/value/sort.go +pkg/mod/github.com/google/go-cmp@v0.4.0/cmp/internal/value/zero_test.go +pkg/mod/github.com/google/go-cmp@v0.4.0/cmp/internal/value/zero.go +pkg/mod/github.com/google/uuid@v1.1.1/.travis.yml +pkg/mod/github.com/google/uuid@v1.1.1/CONTRIBUTING.md +pkg/mod/github.com/google/uuid@v1.1.1/CONTRIBUTORS +pkg/mod/github.com/google/uuid@v1.1.1/dce.go +pkg/mod/github.com/google/uuid@v1.1.1/doc.go +pkg/mod/github.com/google/uuid@v1.1.1/go.mod +pkg/mod/github.com/google/uuid@v1.1.1/hash.go +pkg/mod/github.com/google/uuid@v1.1.1/json_test.go +pkg/mod/github.com/google/uuid@v1.1.1/LICENSE +pkg/mod/github.com/google/uuid@v1.1.1/marshal.go +pkg/mod/github.com/google/uuid@v1.1.1/node_js.go +pkg/mod/github.com/google/uuid@v1.1.1/node_net.go +pkg/mod/github.com/google/uuid@v1.1.1/node.go +pkg/mod/github.com/google/uuid@v1.1.1/README.md +pkg/mod/github.com/google/uuid@v1.1.1/seq_test.go +pkg/mod/github.com/google/uuid@v1.1.1/sql_test.go +pkg/mod/github.com/google/uuid@v1.1.1/sql.go +pkg/mod/github.com/google/uuid@v1.1.1/time.go +pkg/mod/github.com/google/uuid@v1.1.1/util.go +pkg/mod/github.com/google/uuid@v1.1.1/uuid_test.go +pkg/mod/github.com/google/uuid@v1.1.1/uuid.go +pkg/mod/github.com/google/uuid@v1.1.1/version1.go +pkg/mod/github.com/google/uuid@v1.1.1/version4.go +pkg/mod/github.com/json-iterator/go@v1.1.9/.codecov.yml +pkg/mod/github.com/json-iterator/go@v1.1.9/.gitignore +pkg/mod/github.com/json-iterator/go@v1.1.9/.travis.yml +pkg/mod/github.com/json-iterator/go@v1.1.9/adapter.go +pkg/mod/github.com/json-iterator/go@v1.1.9/any_array.go +pkg/mod/github.com/json-iterator/go@v1.1.9/any_bool.go +pkg/mod/github.com/json-iterator/go@v1.1.9/any_float.go +pkg/mod/github.com/json-iterator/go@v1.1.9/any_int32.go +pkg/mod/github.com/json-iterator/go@v1.1.9/any_int64.go +pkg/mod/github.com/json-iterator/go@v1.1.9/any_invalid.go +pkg/mod/github.com/json-iterator/go@v1.1.9/any_nil.go +pkg/mod/github.com/json-iterator/go@v1.1.9/any_number.go +pkg/mod/github.com/json-iterator/go@v1.1.9/any_object.go +pkg/mod/github.com/json-iterator/go@v1.1.9/any_str.go +pkg/mod/github.com/json-iterator/go@v1.1.9/any_uint32.go +pkg/mod/github.com/json-iterator/go@v1.1.9/any_uint64.go +pkg/mod/github.com/json-iterator/go@v1.1.9/any.go +pkg/mod/github.com/json-iterator/go@v1.1.9/build.sh +pkg/mod/github.com/json-iterator/go@v1.1.9/config.go +pkg/mod/github.com/json-iterator/go@v1.1.9/example_test.go +pkg/mod/github.com/json-iterator/go@v1.1.9/fuzzy_mode_convert_table.md +pkg/mod/github.com/json-iterator/go@v1.1.9/go.mod +pkg/mod/github.com/json-iterator/go@v1.1.9/go.sum +pkg/mod/github.com/json-iterator/go@v1.1.9/Gopkg.lock +pkg/mod/github.com/json-iterator/go@v1.1.9/Gopkg.toml +pkg/mod/github.com/json-iterator/go@v1.1.9/iter_array.go +pkg/mod/github.com/json-iterator/go@v1.1.9/iter_float.go +pkg/mod/github.com/json-iterator/go@v1.1.9/iter_int.go +pkg/mod/github.com/json-iterator/go@v1.1.9/iter_object.go +pkg/mod/github.com/json-iterator/go@v1.1.9/iter_skip_sloppy_test.go +pkg/mod/github.com/json-iterator/go@v1.1.9/iter_skip_sloppy.go +pkg/mod/github.com/json-iterator/go@v1.1.9/iter_skip_strict.go +pkg/mod/github.com/json-iterator/go@v1.1.9/iter_skip.go +pkg/mod/github.com/json-iterator/go@v1.1.9/iter_str.go +pkg/mod/github.com/json-iterator/go@v1.1.9/iter.go +pkg/mod/github.com/json-iterator/go@v1.1.9/jsoniter.go +pkg/mod/github.com/json-iterator/go@v1.1.9/LICENSE +pkg/mod/github.com/json-iterator/go@v1.1.9/pool.go +pkg/mod/github.com/json-iterator/go@v1.1.9/README.md +pkg/mod/github.com/json-iterator/go@v1.1.9/reflect_array.go +pkg/mod/github.com/json-iterator/go@v1.1.9/reflect_dynamic.go +pkg/mod/github.com/json-iterator/go@v1.1.9/reflect_extension.go +pkg/mod/github.com/json-iterator/go@v1.1.9/reflect_json_number.go +pkg/mod/github.com/json-iterator/go@v1.1.9/reflect_json_raw_message.go +pkg/mod/github.com/json-iterator/go@v1.1.9/reflect_map.go +pkg/mod/github.com/json-iterator/go@v1.1.9/reflect_marshaler.go +pkg/mod/github.com/json-iterator/go@v1.1.9/reflect_native.go +pkg/mod/github.com/json-iterator/go@v1.1.9/reflect_optional.go +pkg/mod/github.com/json-iterator/go@v1.1.9/reflect_slice.go +pkg/mod/github.com/json-iterator/go@v1.1.9/reflect_struct_decoder.go +pkg/mod/github.com/json-iterator/go@v1.1.9/reflect_struct_encoder.go +pkg/mod/github.com/json-iterator/go@v1.1.9/reflect.go +pkg/mod/github.com/json-iterator/go@v1.1.9/stream_float.go +pkg/mod/github.com/json-iterator/go@v1.1.9/stream_int.go +pkg/mod/github.com/json-iterator/go@v1.1.9/stream_str.go +pkg/mod/github.com/json-iterator/go@v1.1.9/stream_test.go +pkg/mod/github.com/json-iterator/go@v1.1.9/stream.go +pkg/mod/github.com/json-iterator/go@v1.1.9/test.sh +pkg/mod/github.com/json-iterator/go@v1.1.9/any_tests/jsoniter_any_array_test.go +pkg/mod/github.com/json-iterator/go@v1.1.9/any_tests/jsoniter_any_bool_test.go +pkg/mod/github.com/json-iterator/go@v1.1.9/any_tests/jsoniter_any_float_test.go +pkg/mod/github.com/json-iterator/go@v1.1.9/any_tests/jsoniter_any_int_test.go +pkg/mod/github.com/json-iterator/go@v1.1.9/any_tests/jsoniter_any_map_test.go +pkg/mod/github.com/json-iterator/go@v1.1.9/any_tests/jsoniter_any_null_test.go +pkg/mod/github.com/json-iterator/go@v1.1.9/any_tests/jsoniter_any_object_test.go +pkg/mod/github.com/json-iterator/go@v1.1.9/any_tests/jsoniter_any_string_test.go +pkg/mod/github.com/json-iterator/go@v1.1.9/any_tests/jsoniter_must_be_valid_test.go +pkg/mod/github.com/json-iterator/go@v1.1.9/any_tests/jsoniter_wrap_test.go +pkg/mod/github.com/json-iterator/go@v1.1.9/api_tests/config_test.go +pkg/mod/github.com/json-iterator/go@v1.1.9/api_tests/decoder_test.go +pkg/mod/github.com/json-iterator/go@v1.1.9/api_tests/encoder_18_test.go +pkg/mod/github.com/json-iterator/go@v1.1.9/api_tests/encoder_test.go +pkg/mod/github.com/json-iterator/go@v1.1.9/api_tests/marshal_indent_test.go +pkg/mod/github.com/json-iterator/go@v1.1.9/api_tests/marshal_json_escape_test.go +pkg/mod/github.com/json-iterator/go@v1.1.9/api_tests/marshal_json_test.go +pkg/mod/github.com/json-iterator/go@v1.1.9/benchmarks/encode_string_test.go +pkg/mod/github.com/json-iterator/go@v1.1.9/benchmarks/jsoniter_large_file_test.go +pkg/mod/github.com/json-iterator/go@v1.1.9/extension_tests/decoder_test.go +pkg/mod/github.com/json-iterator/go@v1.1.9/extension_tests/extension_test.go +pkg/mod/github.com/json-iterator/go@v1.1.9/extra/binary_as_string_codec_test.go +pkg/mod/github.com/json-iterator/go@v1.1.9/extra/binary_as_string_codec.go +pkg/mod/github.com/json-iterator/go@v1.1.9/extra/fuzzy_decoder_test.go +pkg/mod/github.com/json-iterator/go@v1.1.9/extra/fuzzy_decoder.go +pkg/mod/github.com/json-iterator/go@v1.1.9/extra/naming_strategy_test.go +pkg/mod/github.com/json-iterator/go@v1.1.9/extra/naming_strategy.go +pkg/mod/github.com/json-iterator/go@v1.1.9/extra/privat_fields.go +pkg/mod/github.com/json-iterator/go@v1.1.9/extra/private_fields_test.go +pkg/mod/github.com/json-iterator/go@v1.1.9/extra/time_as_int64_codec_test.go +pkg/mod/github.com/json-iterator/go@v1.1.9/extra/time_as_int64_codec.go +pkg/mod/github.com/json-iterator/go@v1.1.9/misc_tests/jsoniter_array_test.go +pkg/mod/github.com/json-iterator/go@v1.1.9/misc_tests/jsoniter_bool_test.go +pkg/mod/github.com/json-iterator/go@v1.1.9/misc_tests/jsoniter_float_test.go +pkg/mod/github.com/json-iterator/go@v1.1.9/misc_tests/jsoniter_int_test.go +pkg/mod/github.com/json-iterator/go@v1.1.9/misc_tests/jsoniter_interface_test.go +pkg/mod/github.com/json-iterator/go@v1.1.9/misc_tests/jsoniter_iterator_test.go +pkg/mod/github.com/json-iterator/go@v1.1.9/misc_tests/jsoniter_map_test.go +pkg/mod/github.com/json-iterator/go@v1.1.9/misc_tests/jsoniter_nested_test.go +pkg/mod/github.com/json-iterator/go@v1.1.9/misc_tests/jsoniter_null_test.go +pkg/mod/github.com/json-iterator/go@v1.1.9/misc_tests/jsoniter_object_test.go +pkg/mod/github.com/json-iterator/go@v1.1.9/misc_tests/jsoniter_raw_message_test.go +pkg/mod/github.com/json-iterator/go@v1.1.9/skip_tests/array_test.go +pkg/mod/github.com/json-iterator/go@v1.1.9/skip_tests/float64_test.go +pkg/mod/github.com/json-iterator/go@v1.1.9/skip_tests/jsoniter_skip_test.go +pkg/mod/github.com/json-iterator/go@v1.1.9/skip_tests/skip_test.go +pkg/mod/github.com/json-iterator/go@v1.1.9/skip_tests/string_test.go +pkg/mod/github.com/json-iterator/go@v1.1.9/skip_tests/struct_test.go +pkg/mod/github.com/json-iterator/go@v1.1.9/type_tests/array_test.go +pkg/mod/github.com/json-iterator/go@v1.1.9/type_tests/builtin_test.go +pkg/mod/github.com/json-iterator/go@v1.1.9/type_tests/map_key_test.go +pkg/mod/github.com/json-iterator/go@v1.1.9/type_tests/map_test.go +pkg/mod/github.com/json-iterator/go@v1.1.9/type_tests/marshaler_string_test.go +pkg/mod/github.com/json-iterator/go@v1.1.9/type_tests/marshaler_struct_test.go +pkg/mod/github.com/json-iterator/go@v1.1.9/type_tests/slice_test.go +pkg/mod/github.com/json-iterator/go@v1.1.9/type_tests/struct_embedded_test.go +pkg/mod/github.com/json-iterator/go@v1.1.9/type_tests/struct_field_case_test.go +pkg/mod/github.com/json-iterator/go@v1.1.9/type_tests/struct_tags_test.go +pkg/mod/github.com/json-iterator/go@v1.1.9/type_tests/struct_test.go +pkg/mod/github.com/json-iterator/go@v1.1.9/type_tests/text_marshaler_string_test.go +pkg/mod/github.com/json-iterator/go@v1.1.9/type_tests/text_marshaler_struct_test.go +pkg/mod/github.com/json-iterator/go@v1.1.9/type_tests/type_test.go +pkg/mod/github.com/json-iterator/go@v1.1.9/value_tests/array_test.go +pkg/mod/github.com/json-iterator/go@v1.1.9/value_tests/bool_test.go +pkg/mod/github.com/json-iterator/go@v1.1.9/value_tests/eface_test.go +pkg/mod/github.com/json-iterator/go@v1.1.9/value_tests/error_test.go +pkg/mod/github.com/json-iterator/go@v1.1.9/value_tests/float_test.go +pkg/mod/github.com/json-iterator/go@v1.1.9/value_tests/iface_test.go +pkg/mod/github.com/json-iterator/go@v1.1.9/value_tests/int_test.go +pkg/mod/github.com/json-iterator/go@v1.1.9/value_tests/invalid_test.go +pkg/mod/github.com/json-iterator/go@v1.1.9/value_tests/map_test.go +pkg/mod/github.com/json-iterator/go@v1.1.9/value_tests/marshaler_test.go +pkg/mod/github.com/json-iterator/go@v1.1.9/value_tests/number_test.go +pkg/mod/github.com/json-iterator/go@v1.1.9/value_tests/ptr_test.go +pkg/mod/github.com/json-iterator/go@v1.1.9/value_tests/raw_message_test.go +pkg/mod/github.com/json-iterator/go@v1.1.9/value_tests/slice_test.go +pkg/mod/github.com/json-iterator/go@v1.1.9/value_tests/string_test.go +pkg/mod/github.com/json-iterator/go@v1.1.9/value_tests/struct_test.go +pkg/mod/github.com/json-iterator/go@v1.1.9/value_tests/value_test.go +pkg/mod/github.com/labstack/echo@v3.3.10+incompatible/.editorconfig +pkg/mod/github.com/labstack/echo@v3.3.10+incompatible/.gitattributes +pkg/mod/github.com/labstack/echo@v3.3.10+incompatible/.gitignore +pkg/mod/github.com/labstack/echo@v3.3.10+incompatible/.travis.yml +pkg/mod/github.com/labstack/echo@v3.3.10+incompatible/bind_test.go +pkg/mod/github.com/labstack/echo@v3.3.10+incompatible/bind.go +pkg/mod/github.com/labstack/echo@v3.3.10+incompatible/context_test.go +pkg/mod/github.com/labstack/echo@v3.3.10+incompatible/context.go +pkg/mod/github.com/labstack/echo@v3.3.10+incompatible/echo_test.go +pkg/mod/github.com/labstack/echo@v3.3.10+incompatible/echo.go +pkg/mod/github.com/labstack/echo@v3.3.10+incompatible/group_test.go +pkg/mod/github.com/labstack/echo@v3.3.10+incompatible/group.go +pkg/mod/github.com/labstack/echo@v3.3.10+incompatible/LICENSE +pkg/mod/github.com/labstack/echo@v3.3.10+incompatible/log.go +pkg/mod/github.com/labstack/echo@v3.3.10+incompatible/Makefile +pkg/mod/github.com/labstack/echo@v3.3.10+incompatible/README.md +pkg/mod/github.com/labstack/echo@v3.3.10+incompatible/response_test.go +pkg/mod/github.com/labstack/echo@v3.3.10+incompatible/response.go +pkg/mod/github.com/labstack/echo@v3.3.10+incompatible/router_test.go +pkg/mod/github.com/labstack/echo@v3.3.10+incompatible/router.go +pkg/mod/github.com/labstack/echo@v3.3.10+incompatible/.github/ISSUE_TEMPLATE.md +pkg/mod/github.com/labstack/echo@v3.3.10+incompatible/.github/stale.yml +pkg/mod/github.com/labstack/echo@v3.3.10+incompatible/_fixture/favicon.ico +pkg/mod/github.com/labstack/echo@v3.3.10+incompatible/_fixture/index.html +pkg/mod/github.com/labstack/echo@v3.3.10+incompatible/_fixture/certs/cert.pem +pkg/mod/github.com/labstack/echo@v3.3.10+incompatible/_fixture/certs/key.pem +pkg/mod/github.com/labstack/echo@v3.3.10+incompatible/_fixture/folder/index.html +pkg/mod/github.com/labstack/echo@v3.3.10+incompatible/_fixture/images/walle.png +pkg/mod/github.com/labstack/echo@v3.3.10+incompatible/middleware/basic_auth_test.go +pkg/mod/github.com/labstack/echo@v3.3.10+incompatible/middleware/basic_auth.go +pkg/mod/github.com/labstack/echo@v3.3.10+incompatible/middleware/body_dump_test.go +pkg/mod/github.com/labstack/echo@v3.3.10+incompatible/middleware/body_dump.go +pkg/mod/github.com/labstack/echo@v3.3.10+incompatible/middleware/body_limit_test.go +pkg/mod/github.com/labstack/echo@v3.3.10+incompatible/middleware/body_limit.go +pkg/mod/github.com/labstack/echo@v3.3.10+incompatible/middleware/compress_test.go +pkg/mod/github.com/labstack/echo@v3.3.10+incompatible/middleware/compress.go +pkg/mod/github.com/labstack/echo@v3.3.10+incompatible/middleware/cors_test.go +pkg/mod/github.com/labstack/echo@v3.3.10+incompatible/middleware/cors.go +pkg/mod/github.com/labstack/echo@v3.3.10+incompatible/middleware/csrf_test.go +pkg/mod/github.com/labstack/echo@v3.3.10+incompatible/middleware/csrf.go +pkg/mod/github.com/labstack/echo@v3.3.10+incompatible/middleware/jwt_test.go +pkg/mod/github.com/labstack/echo@v3.3.10+incompatible/middleware/jwt.go +pkg/mod/github.com/labstack/echo@v3.3.10+incompatible/middleware/key_auth_test.go +pkg/mod/github.com/labstack/echo@v3.3.10+incompatible/middleware/key_auth.go +pkg/mod/github.com/labstack/echo@v3.3.10+incompatible/middleware/logger_test.go +pkg/mod/github.com/labstack/echo@v3.3.10+incompatible/middleware/logger.go +pkg/mod/github.com/labstack/echo@v3.3.10+incompatible/middleware/method_override_test.go +pkg/mod/github.com/labstack/echo@v3.3.10+incompatible/middleware/method_override.go +pkg/mod/github.com/labstack/echo@v3.3.10+incompatible/middleware/middleware.go +pkg/mod/github.com/labstack/echo@v3.3.10+incompatible/middleware/proxy_1_11_n.go +pkg/mod/github.com/labstack/echo@v3.3.10+incompatible/middleware/proxy_1_11_test.go +pkg/mod/github.com/labstack/echo@v3.3.10+incompatible/middleware/proxy_1_11.go +pkg/mod/github.com/labstack/echo@v3.3.10+incompatible/middleware/proxy_test.go +pkg/mod/github.com/labstack/echo@v3.3.10+incompatible/middleware/proxy.go +pkg/mod/github.com/labstack/echo@v3.3.10+incompatible/middleware/recover_test.go +pkg/mod/github.com/labstack/echo@v3.3.10+incompatible/middleware/recover.go +pkg/mod/github.com/labstack/echo@v3.3.10+incompatible/middleware/redirect_test.go +pkg/mod/github.com/labstack/echo@v3.3.10+incompatible/middleware/redirect.go +pkg/mod/github.com/labstack/echo@v3.3.10+incompatible/middleware/request_id_test.go +pkg/mod/github.com/labstack/echo@v3.3.10+incompatible/middleware/request_id.go +pkg/mod/github.com/labstack/echo@v3.3.10+incompatible/middleware/rewrite_test.go +pkg/mod/github.com/labstack/echo@v3.3.10+incompatible/middleware/rewrite.go +pkg/mod/github.com/labstack/echo@v3.3.10+incompatible/middleware/secure_test.go +pkg/mod/github.com/labstack/echo@v3.3.10+incompatible/middleware/secure.go +pkg/mod/github.com/labstack/echo@v3.3.10+incompatible/middleware/slash_test.go +pkg/mod/github.com/labstack/echo@v3.3.10+incompatible/middleware/slash.go +pkg/mod/github.com/labstack/echo@v3.3.10+incompatible/middleware/static_test.go +pkg/mod/github.com/labstack/echo@v3.3.10+incompatible/middleware/static.go +pkg/mod/github.com/labstack/gommon@v0.3.0/.gitignore +pkg/mod/github.com/labstack/gommon@v0.3.0/.travis.yml +pkg/mod/github.com/labstack/gommon@v0.3.0/go.mod +pkg/mod/github.com/labstack/gommon@v0.3.0/go.sum +pkg/mod/github.com/labstack/gommon@v0.3.0/gommon.go +pkg/mod/github.com/labstack/gommon@v0.3.0/LICENSE +pkg/mod/github.com/labstack/gommon@v0.3.0/README.md +pkg/mod/github.com/labstack/gommon@v0.3.0/bytes/bytes_test.go +pkg/mod/github.com/labstack/gommon@v0.3.0/bytes/bytes.go +pkg/mod/github.com/labstack/gommon@v0.3.0/bytes/README.md +pkg/mod/github.com/labstack/gommon@v0.3.0/color/color_test.go +pkg/mod/github.com/labstack/gommon@v0.3.0/color/color.go +pkg/mod/github.com/labstack/gommon@v0.3.0/color/README.md +pkg/mod/github.com/labstack/gommon@v0.3.0/email/email_test.go +pkg/mod/github.com/labstack/gommon@v0.3.0/email/email.go +pkg/mod/github.com/labstack/gommon@v0.3.0/log/color.go +pkg/mod/github.com/labstack/gommon@v0.3.0/log/log_test.go +pkg/mod/github.com/labstack/gommon@v0.3.0/log/log.go +pkg/mod/github.com/labstack/gommon@v0.3.0/log/README.md +pkg/mod/github.com/labstack/gommon@v0.3.0/log/white.go +pkg/mod/github.com/labstack/gommon@v0.3.0/random/random_test.go +pkg/mod/github.com/labstack/gommon@v0.3.0/random/random.go +pkg/mod/github.com/lann/builder@v0.0.0-20180802200727-47ae307949d0/.gitignore +pkg/mod/github.com/lann/builder@v0.0.0-20180802200727-47ae307949d0/.travis.yml +pkg/mod/github.com/lann/builder@v0.0.0-20180802200727-47ae307949d0/builder_test.go +pkg/mod/github.com/lann/builder@v0.0.0-20180802200727-47ae307949d0/builder.go +pkg/mod/github.com/lann/builder@v0.0.0-20180802200727-47ae307949d0/example_test.go +pkg/mod/github.com/lann/builder@v0.0.0-20180802200727-47ae307949d0/LICENSE +pkg/mod/github.com/lann/builder@v0.0.0-20180802200727-47ae307949d0/README.md +pkg/mod/github.com/lann/builder@v0.0.0-20180802200727-47ae307949d0/reflect.go +pkg/mod/github.com/lann/builder@v0.0.0-20180802200727-47ae307949d0/registry.go +pkg/mod/github.com/lann/ps@v0.0.0-20150810152359-62de8c46ede0/LICENSE +pkg/mod/github.com/lann/ps@v0.0.0-20150810152359-62de8c46ede0/list_test.go +pkg/mod/github.com/lann/ps@v0.0.0-20150810152359-62de8c46ede0/list.go +pkg/mod/github.com/lann/ps@v0.0.0-20150810152359-62de8c46ede0/map_test.go +pkg/mod/github.com/lann/ps@v0.0.0-20150810152359-62de8c46ede0/map.go +pkg/mod/github.com/lann/ps@v0.0.0-20150810152359-62de8c46ede0/profile.sh +pkg/mod/github.com/lann/ps@v0.0.0-20150810152359-62de8c46ede0/README.md +pkg/mod/github.com/mailru/easyjson@v0.0.0-20190626092158-b2ccc519800e/.gitignore +pkg/mod/github.com/mailru/easyjson@v0.0.0-20190626092158-b2ccc519800e/.travis.yml +pkg/mod/github.com/mailru/easyjson@v0.0.0-20190626092158-b2ccc519800e/helpers.go +pkg/mod/github.com/mailru/easyjson@v0.0.0-20190626092158-b2ccc519800e/LICENSE +pkg/mod/github.com/mailru/easyjson@v0.0.0-20190626092158-b2ccc519800e/Makefile +pkg/mod/github.com/mailru/easyjson@v0.0.0-20190626092158-b2ccc519800e/raw.go +pkg/mod/github.com/mailru/easyjson@v0.0.0-20190626092158-b2ccc519800e/README.md +pkg/mod/github.com/mailru/easyjson@v0.0.0-20190626092158-b2ccc519800e/benchmark/codec_test.go +pkg/mod/github.com/mailru/easyjson@v0.0.0-20190626092158-b2ccc519800e/benchmark/data_codec.go +pkg/mod/github.com/mailru/easyjson@v0.0.0-20190626092158-b2ccc519800e/benchmark/data_ffjson.go +pkg/mod/github.com/mailru/easyjson@v0.0.0-20190626092158-b2ccc519800e/benchmark/data_var.go +pkg/mod/github.com/mailru/easyjson@v0.0.0-20190626092158-b2ccc519800e/benchmark/data.go +pkg/mod/github.com/mailru/easyjson@v0.0.0-20190626092158-b2ccc519800e/benchmark/default_test.go +pkg/mod/github.com/mailru/easyjson@v0.0.0-20190626092158-b2ccc519800e/benchmark/dummy_test.go +pkg/mod/github.com/mailru/easyjson@v0.0.0-20190626092158-b2ccc519800e/benchmark/easyjson_test.go +pkg/mod/github.com/mailru/easyjson@v0.0.0-20190626092158-b2ccc519800e/benchmark/example.json +pkg/mod/github.com/mailru/easyjson@v0.0.0-20190626092158-b2ccc519800e/benchmark/ffjson_test.go +pkg/mod/github.com/mailru/easyjson@v0.0.0-20190626092158-b2ccc519800e/benchmark/jsoniter_test.go +pkg/mod/github.com/mailru/easyjson@v0.0.0-20190626092158-b2ccc519800e/benchmark/ujson.sh +pkg/mod/github.com/mailru/easyjson@v0.0.0-20190626092158-b2ccc519800e/bootstrap/bootstrap.go +pkg/mod/github.com/mailru/easyjson@v0.0.0-20190626092158-b2ccc519800e/buffer/pool_test.go +pkg/mod/github.com/mailru/easyjson@v0.0.0-20190626092158-b2ccc519800e/buffer/pool.go +pkg/mod/github.com/mailru/easyjson@v0.0.0-20190626092158-b2ccc519800e/easyjson/main.go +pkg/mod/github.com/mailru/easyjson@v0.0.0-20190626092158-b2ccc519800e/gen/decoder.go +pkg/mod/github.com/mailru/easyjson@v0.0.0-20190626092158-b2ccc519800e/gen/encoder.go +pkg/mod/github.com/mailru/easyjson@v0.0.0-20190626092158-b2ccc519800e/gen/generator_test.go +pkg/mod/github.com/mailru/easyjson@v0.0.0-20190626092158-b2ccc519800e/gen/generator.go +pkg/mod/github.com/mailru/easyjson@v0.0.0-20190626092158-b2ccc519800e/jlexer/bytestostr_nounsafe.go +pkg/mod/github.com/mailru/easyjson@v0.0.0-20190626092158-b2ccc519800e/jlexer/bytestostr.go +pkg/mod/github.com/mailru/easyjson@v0.0.0-20190626092158-b2ccc519800e/jlexer/error.go +pkg/mod/github.com/mailru/easyjson@v0.0.0-20190626092158-b2ccc519800e/jlexer/lexer_test.go +pkg/mod/github.com/mailru/easyjson@v0.0.0-20190626092158-b2ccc519800e/jlexer/lexer.go +pkg/mod/github.com/mailru/easyjson@v0.0.0-20190626092158-b2ccc519800e/jwriter/writer.go +pkg/mod/github.com/mailru/easyjson@v0.0.0-20190626092158-b2ccc519800e/opt/gotemplate_Bool.go +pkg/mod/github.com/mailru/easyjson@v0.0.0-20190626092158-b2ccc519800e/opt/gotemplate_Float32.go +pkg/mod/github.com/mailru/easyjson@v0.0.0-20190626092158-b2ccc519800e/opt/gotemplate_Float64.go +pkg/mod/github.com/mailru/easyjson@v0.0.0-20190626092158-b2ccc519800e/opt/gotemplate_Int.go +pkg/mod/github.com/mailru/easyjson@v0.0.0-20190626092158-b2ccc519800e/opt/gotemplate_Int8.go +pkg/mod/github.com/mailru/easyjson@v0.0.0-20190626092158-b2ccc519800e/opt/gotemplate_Int16.go +pkg/mod/github.com/mailru/easyjson@v0.0.0-20190626092158-b2ccc519800e/opt/gotemplate_Int32.go +pkg/mod/github.com/mailru/easyjson@v0.0.0-20190626092158-b2ccc519800e/opt/gotemplate_Int64.go +pkg/mod/github.com/mailru/easyjson@v0.0.0-20190626092158-b2ccc519800e/opt/gotemplate_String.go +pkg/mod/github.com/mailru/easyjson@v0.0.0-20190626092158-b2ccc519800e/opt/gotemplate_Uint.go +pkg/mod/github.com/mailru/easyjson@v0.0.0-20190626092158-b2ccc519800e/opt/gotemplate_Uint8.go +pkg/mod/github.com/mailru/easyjson@v0.0.0-20190626092158-b2ccc519800e/opt/gotemplate_Uint16.go +pkg/mod/github.com/mailru/easyjson@v0.0.0-20190626092158-b2ccc519800e/opt/gotemplate_Uint32.go +pkg/mod/github.com/mailru/easyjson@v0.0.0-20190626092158-b2ccc519800e/opt/gotemplate_Uint64.go +pkg/mod/github.com/mailru/easyjson@v0.0.0-20190626092158-b2ccc519800e/opt/opts.go +pkg/mod/github.com/mailru/easyjson@v0.0.0-20190626092158-b2ccc519800e/opt/optional/opt.go +pkg/mod/github.com/mailru/easyjson@v0.0.0-20190626092158-b2ccc519800e/parser/parser.go +pkg/mod/github.com/mailru/easyjson@v0.0.0-20190626092158-b2ccc519800e/parser/pkgpath.go +pkg/mod/github.com/mailru/easyjson@v0.0.0-20190626092158-b2ccc519800e/tests/basic_test.go +pkg/mod/github.com/mailru/easyjson@v0.0.0-20190626092158-b2ccc519800e/tests/custom_map_key_type.go +pkg/mod/github.com/mailru/easyjson@v0.0.0-20190626092158-b2ccc519800e/tests/data.go +pkg/mod/github.com/mailru/easyjson@v0.0.0-20190626092158-b2ccc519800e/tests/disallow_unknown.go +pkg/mod/github.com/mailru/easyjson@v0.0.0-20190626092158-b2ccc519800e/tests/embedded_type.go +pkg/mod/github.com/mailru/easyjson@v0.0.0-20190626092158-b2ccc519800e/tests/errors_test.go +pkg/mod/github.com/mailru/easyjson@v0.0.0-20190626092158-b2ccc519800e/tests/errors.go +pkg/mod/github.com/mailru/easyjson@v0.0.0-20190626092158-b2ccc519800e/tests/key_marshaler_map.go +pkg/mod/github.com/mailru/easyjson@v0.0.0-20190626092158-b2ccc519800e/tests/named_type.go +pkg/mod/github.com/mailru/easyjson@v0.0.0-20190626092158-b2ccc519800e/tests/nested_easy.go +pkg/mod/github.com/mailru/easyjson@v0.0.0-20190626092158-b2ccc519800e/tests/nothing.go +pkg/mod/github.com/mailru/easyjson@v0.0.0-20190626092158-b2ccc519800e/tests/omitempty.go +pkg/mod/github.com/mailru/easyjson@v0.0.0-20190626092158-b2ccc519800e/tests/opt_test.go +pkg/mod/github.com/mailru/easyjson@v0.0.0-20190626092158-b2ccc519800e/tests/reference_to_pointer.go +pkg/mod/github.com/mailru/easyjson@v0.0.0-20190626092158-b2ccc519800e/tests/required_test.go +pkg/mod/github.com/mailru/easyjson@v0.0.0-20190626092158-b2ccc519800e/tests/snake.go +pkg/mod/github.com/mattn/go-colorable@v0.1.2/.travis.yml +pkg/mod/github.com/mattn/go-colorable@v0.1.2/colorable_appengine.go +pkg/mod/github.com/mattn/go-colorable@v0.1.2/colorable_others.go +pkg/mod/github.com/mattn/go-colorable@v0.1.2/colorable_test.go +pkg/mod/github.com/mattn/go-colorable@v0.1.2/colorable_windows.go +pkg/mod/github.com/mattn/go-colorable@v0.1.2/go.mod +pkg/mod/github.com/mattn/go-colorable@v0.1.2/go.sum +pkg/mod/github.com/mattn/go-colorable@v0.1.2/LICENSE +pkg/mod/github.com/mattn/go-colorable@v0.1.2/noncolorable.go +pkg/mod/github.com/mattn/go-colorable@v0.1.2/README.md +pkg/mod/github.com/mattn/go-colorable@v0.1.2/_example/escape-seq/main.go +pkg/mod/github.com/mattn/go-colorable@v0.1.2/_example/logrus/main.go +pkg/mod/github.com/mattn/go-colorable@v0.1.2/_example/title/main.go +pkg/mod/github.com/mattn/go-colorable@v0.1.2/cmd/colorable/colorable.go +pkg/mod/github.com/mattn/go-isatty@v0.0.9/.travis.yml +pkg/mod/github.com/mattn/go-isatty@v0.0.9/doc.go +pkg/mod/github.com/mattn/go-isatty@v0.0.9/example_test.go +pkg/mod/github.com/mattn/go-isatty@v0.0.9/go.mod +pkg/mod/github.com/mattn/go-isatty@v0.0.9/go.sum +pkg/mod/github.com/mattn/go-isatty@v0.0.9/isatty_android.go +pkg/mod/github.com/mattn/go-isatty@v0.0.9/isatty_bsd.go +pkg/mod/github.com/mattn/go-isatty@v0.0.9/isatty_others_test.go +pkg/mod/github.com/mattn/go-isatty@v0.0.9/isatty_others.go +pkg/mod/github.com/mattn/go-isatty@v0.0.9/isatty_solaris.go +pkg/mod/github.com/mattn/go-isatty@v0.0.9/isatty_tcgets.go +pkg/mod/github.com/mattn/go-isatty@v0.0.9/isatty_windows_test.go +pkg/mod/github.com/mattn/go-isatty@v0.0.9/isatty_windows.go +pkg/mod/github.com/mattn/go-isatty@v0.0.9/LICENSE +pkg/mod/github.com/mattn/go-isatty@v0.0.9/README.md +pkg/mod/github.com/mattn/go-isatty@v0.0.9/.github/FUNDING.yml +pkg/mod/github.com/mattn/go-sqlite3@v1.11.0/.gitignore +pkg/mod/github.com/mattn/go-sqlite3@v1.11.0/.travis.yml +pkg/mod/github.com/mattn/go-sqlite3@v1.11.0/backup_test.go +pkg/mod/github.com/mattn/go-sqlite3@v1.11.0/backup.go +pkg/mod/github.com/mattn/go-sqlite3@v1.11.0/callback_test.go +pkg/mod/github.com/mattn/go-sqlite3@v1.11.0/callback.go +pkg/mod/github.com/mattn/go-sqlite3@v1.11.0/doc.go +pkg/mod/github.com/mattn/go-sqlite3@v1.11.0/error_test.go +pkg/mod/github.com/mattn/go-sqlite3@v1.11.0/error.go +pkg/mod/github.com/mattn/go-sqlite3@v1.11.0/LICENSE +pkg/mod/github.com/mattn/go-sqlite3@v1.11.0/README.md +pkg/mod/github.com/mattn/go-sqlite3@v1.11.0/sqlite3_context.go +pkg/mod/github.com/mattn/go-sqlite3@v1.11.0/sqlite3_func_crypt_test.go +pkg/mod/github.com/mattn/go-sqlite3@v1.11.0/sqlite3_func_crypt.go +pkg/mod/github.com/mattn/go-sqlite3@v1.11.0/sqlite3_go18_test.go +pkg/mod/github.com/mattn/go-sqlite3@v1.11.0/sqlite3_go18.go +pkg/mod/github.com/mattn/go-sqlite3@v1.11.0/sqlite3_libsqlite3.go +pkg/mod/github.com/mattn/go-sqlite3@v1.11.0/sqlite3_load_extension_omit.go +pkg/mod/github.com/mattn/go-sqlite3@v1.11.0/sqlite3_load_extension.go +pkg/mod/github.com/mattn/go-sqlite3@v1.11.0/sqlite3_opt_allow_uri_authority.go +pkg/mod/github.com/mattn/go-sqlite3@v1.11.0/sqlite3_opt_app_armor.go +pkg/mod/github.com/mattn/go-sqlite3@v1.11.0/sqlite3_opt_foreign_keys.go +pkg/mod/github.com/mattn/go-sqlite3@v1.11.0/sqlite3_opt_fts3_test.go +pkg/mod/github.com/mattn/go-sqlite3@v1.11.0/sqlite3_opt_fts5.go +pkg/mod/github.com/mattn/go-sqlite3@v1.11.0/sqlite3_opt_icu.go +pkg/mod/github.com/mattn/go-sqlite3@v1.11.0/sqlite3_opt_introspect.go +pkg/mod/github.com/mattn/go-sqlite3@v1.11.0/sqlite3_opt_json1.go +pkg/mod/github.com/mattn/go-sqlite3@v1.11.0/sqlite3_opt_secure_delete_fast.go +pkg/mod/github.com/mattn/go-sqlite3@v1.11.0/sqlite3_opt_secure_delete.go +pkg/mod/github.com/mattn/go-sqlite3@v1.11.0/sqlite3_opt_stat4.go +pkg/mod/github.com/mattn/go-sqlite3@v1.11.0/sqlite3_opt_unlock_notify_test.go +pkg/mod/github.com/mattn/go-sqlite3@v1.11.0/sqlite3_opt_unlock_notify.c +pkg/mod/github.com/mattn/go-sqlite3@v1.11.0/sqlite3_opt_unlock_notify.go +pkg/mod/github.com/mattn/go-sqlite3@v1.11.0/sqlite3_opt_userauth_omit.go +pkg/mod/github.com/mattn/go-sqlite3@v1.11.0/sqlite3_opt_userauth_test.go +pkg/mod/github.com/mattn/go-sqlite3@v1.11.0/sqlite3_opt_userauth.go +pkg/mod/github.com/mattn/go-sqlite3@v1.11.0/sqlite3_opt_vacuum_full.go +pkg/mod/github.com/mattn/go-sqlite3@v1.11.0/sqlite3_opt_vacuum_incr.go +pkg/mod/github.com/mattn/go-sqlite3@v1.11.0/sqlite3_opt_vtable_test.go +pkg/mod/github.com/mattn/go-sqlite3@v1.11.0/sqlite3_opt_vtable.go +pkg/mod/github.com/mattn/go-sqlite3@v1.11.0/sqlite3_other.go +pkg/mod/github.com/mattn/go-sqlite3@v1.11.0/sqlite3_solaris.go +pkg/mod/github.com/mattn/go-sqlite3@v1.11.0/sqlite3_test.go +pkg/mod/github.com/mattn/go-sqlite3@v1.11.0/sqlite3_trace.go +pkg/mod/github.com/mattn/go-sqlite3@v1.11.0/sqlite3_type.go +pkg/mod/github.com/mattn/go-sqlite3@v1.11.0/sqlite3_usleep_windows.go +pkg/mod/github.com/mattn/go-sqlite3@v1.11.0/sqlite3_windows.go +pkg/mod/github.com/mattn/go-sqlite3@v1.11.0/sqlite3-binding.c +pkg/mod/github.com/mattn/go-sqlite3@v1.11.0/sqlite3-binding.h +pkg/mod/github.com/mattn/go-sqlite3@v1.11.0/sqlite3.go +pkg/mod/github.com/mattn/go-sqlite3@v1.11.0/sqlite3ext.h +pkg/mod/github.com/mattn/go-sqlite3@v1.11.0/static_mock.go +pkg/mod/github.com/mattn/go-sqlite3@v1.11.0/.github/FUNDING.yml +pkg/mod/github.com/mattn/go-sqlite3@v1.11.0/_example/custom_func/main.go +pkg/mod/github.com/mattn/go-sqlite3@v1.11.0/_example/hook/hook.go +pkg/mod/github.com/mattn/go-sqlite3@v1.11.0/_example/limit/limit.go +pkg/mod/github.com/mattn/go-sqlite3@v1.11.0/_example/mod_regexp/extension.go +pkg/mod/github.com/mattn/go-sqlite3@v1.11.0/_example/mod_regexp/Makefile +pkg/mod/github.com/mattn/go-sqlite3@v1.11.0/_example/mod_regexp/sqlite3_mod_regexp.c +pkg/mod/github.com/mattn/go-sqlite3@v1.11.0/_example/mod_vtable/extension.go +pkg/mod/github.com/mattn/go-sqlite3@v1.11.0/_example/mod_vtable/Makefile +pkg/mod/github.com/mattn/go-sqlite3@v1.11.0/_example/mod_vtable/picojson.h +pkg/mod/github.com/mattn/go-sqlite3@v1.11.0/_example/mod_vtable/sqlite3_mod_vtable.cc +pkg/mod/github.com/mattn/go-sqlite3@v1.11.0/_example/simple/simple.go +pkg/mod/github.com/mattn/go-sqlite3@v1.11.0/_example/trace/main.go +pkg/mod/github.com/mattn/go-sqlite3@v1.11.0/_example/vtable/main.go +pkg/mod/github.com/mattn/go-sqlite3@v1.11.0/_example/vtable/vtable.go +pkg/mod/github.com/mattn/go-sqlite3@v1.11.0/upgrade/package.go +pkg/mod/github.com/mattn/go-sqlite3@v1.11.0/upgrade/upgrade.go +pkg/mod/github.com/matttproud/golang_protobuf_extensions@v1.0.1/.travis.yml +pkg/mod/github.com/matttproud/golang_protobuf_extensions@v1.0.1/LICENSE +pkg/mod/github.com/matttproud/golang_protobuf_extensions@v1.0.1/Makefile.TRAVIS +pkg/mod/github.com/matttproud/golang_protobuf_extensions@v1.0.1/NOTICE +pkg/mod/github.com/matttproud/golang_protobuf_extensions@v1.0.1/README.md +pkg/mod/github.com/matttproud/golang_protobuf_extensions@v1.0.1/ext/moved.go +pkg/mod/github.com/matttproud/golang_protobuf_extensions@v1.0.1/pbtest/deleted.go +pkg/mod/github.com/matttproud/golang_protobuf_extensions@v1.0.1/pbutil/.gitignore +pkg/mod/github.com/matttproud/golang_protobuf_extensions@v1.0.1/pbutil/all_test.go +pkg/mod/github.com/matttproud/golang_protobuf_extensions@v1.0.1/pbutil/decode_test.go +pkg/mod/github.com/matttproud/golang_protobuf_extensions@v1.0.1/pbutil/decode.go +pkg/mod/github.com/matttproud/golang_protobuf_extensions@v1.0.1/pbutil/doc.go +pkg/mod/github.com/matttproud/golang_protobuf_extensions@v1.0.1/pbutil/encode_test.go +pkg/mod/github.com/matttproud/golang_protobuf_extensions@v1.0.1/pbutil/encode.go +pkg/mod/github.com/matttproud/golang_protobuf_extensions@v1.0.1/pbutil/Makefile +pkg/mod/github.com/matttproud/golang_protobuf_extensions@v1.0.1/testdata/README.THIRD_PARTY +pkg/mod/github.com/matttproud/golang_protobuf_extensions@v1.0.1/testdata/test.pb.go +pkg/mod/github.com/matttproud/golang_protobuf_extensions@v1.0.1/testdata/test.proto +pkg/mod/github.com/mitchellh/mapstructure@v1.3.2/.travis.yml +pkg/mod/github.com/mitchellh/mapstructure@v1.3.2/CHANGELOG.md +pkg/mod/github.com/mitchellh/mapstructure@v1.3.2/decode_hooks_test.go +pkg/mod/github.com/mitchellh/mapstructure@v1.3.2/decode_hooks.go +pkg/mod/github.com/mitchellh/mapstructure@v1.3.2/error.go +pkg/mod/github.com/mitchellh/mapstructure@v1.3.2/go.mod +pkg/mod/github.com/mitchellh/mapstructure@v1.3.2/LICENSE +pkg/mod/github.com/mitchellh/mapstructure@v1.3.2/mapstructure_benchmark_test.go +pkg/mod/github.com/mitchellh/mapstructure@v1.3.2/mapstructure_bugs_test.go +pkg/mod/github.com/mitchellh/mapstructure@v1.3.2/mapstructure_examples_test.go +pkg/mod/github.com/mitchellh/mapstructure@v1.3.2/mapstructure_test.go +pkg/mod/github.com/mitchellh/mapstructure@v1.3.2/mapstructure.go +pkg/mod/github.com/mitchellh/mapstructure@v1.3.2/README.md +pkg/mod/github.com/modern-go/concurrent@v0.0.0-20180306012644-bacd9c7ef1dd/.gitignore +pkg/mod/github.com/modern-go/concurrent@v0.0.0-20180306012644-bacd9c7ef1dd/.travis.yml +pkg/mod/github.com/modern-go/concurrent@v0.0.0-20180306012644-bacd9c7ef1dd/executor.go +pkg/mod/github.com/modern-go/concurrent@v0.0.0-20180306012644-bacd9c7ef1dd/go_above_19.go +pkg/mod/github.com/modern-go/concurrent@v0.0.0-20180306012644-bacd9c7ef1dd/go_below_19.go +pkg/mod/github.com/modern-go/concurrent@v0.0.0-20180306012644-bacd9c7ef1dd/LICENSE +pkg/mod/github.com/modern-go/concurrent@v0.0.0-20180306012644-bacd9c7ef1dd/log.go +pkg/mod/github.com/modern-go/concurrent@v0.0.0-20180306012644-bacd9c7ef1dd/map_test.go +pkg/mod/github.com/modern-go/concurrent@v0.0.0-20180306012644-bacd9c7ef1dd/README.md +pkg/mod/github.com/modern-go/concurrent@v0.0.0-20180306012644-bacd9c7ef1dd/test.sh +pkg/mod/github.com/modern-go/concurrent@v0.0.0-20180306012644-bacd9c7ef1dd/unbounded_executor_test.go +pkg/mod/github.com/modern-go/concurrent@v0.0.0-20180306012644-bacd9c7ef1dd/unbounded_executor.go +pkg/mod/github.com/modern-go/reflect2@v1.0.1/.gitignore +pkg/mod/github.com/modern-go/reflect2@v1.0.1/.travis.yml +pkg/mod/github.com/modern-go/reflect2@v1.0.1/go_above_17.go +pkg/mod/github.com/modern-go/reflect2@v1.0.1/go_above_19.go +pkg/mod/github.com/modern-go/reflect2@v1.0.1/go_below_17.go +pkg/mod/github.com/modern-go/reflect2@v1.0.1/go_below_19.go +pkg/mod/github.com/modern-go/reflect2@v1.0.1/Gopkg.lock +pkg/mod/github.com/modern-go/reflect2@v1.0.1/Gopkg.toml +pkg/mod/github.com/modern-go/reflect2@v1.0.1/LICENSE +pkg/mod/github.com/modern-go/reflect2@v1.0.1/README.md +pkg/mod/github.com/modern-go/reflect2@v1.0.1/reflect2_amd64.s +pkg/mod/github.com/modern-go/reflect2@v1.0.1/reflect2_kind.go +pkg/mod/github.com/modern-go/reflect2@v1.0.1/reflect2.go +pkg/mod/github.com/modern-go/reflect2@v1.0.1/relfect2_386.s +pkg/mod/github.com/modern-go/reflect2@v1.0.1/relfect2_amd64p32.s +pkg/mod/github.com/modern-go/reflect2@v1.0.1/relfect2_arm.s +pkg/mod/github.com/modern-go/reflect2@v1.0.1/relfect2_arm64.s +pkg/mod/github.com/modern-go/reflect2@v1.0.1/relfect2_mips64x.s +pkg/mod/github.com/modern-go/reflect2@v1.0.1/relfect2_mipsx.s +pkg/mod/github.com/modern-go/reflect2@v1.0.1/relfect2_ppc64x.s +pkg/mod/github.com/modern-go/reflect2@v1.0.1/relfect2_s390x.s +pkg/mod/github.com/modern-go/reflect2@v1.0.1/safe_field.go +pkg/mod/github.com/modern-go/reflect2@v1.0.1/safe_map.go +pkg/mod/github.com/modern-go/reflect2@v1.0.1/safe_slice.go +pkg/mod/github.com/modern-go/reflect2@v1.0.1/safe_struct.go +pkg/mod/github.com/modern-go/reflect2@v1.0.1/safe_type.go +pkg/mod/github.com/modern-go/reflect2@v1.0.1/test.sh +pkg/mod/github.com/modern-go/reflect2@v1.0.1/type_map.go +pkg/mod/github.com/modern-go/reflect2@v1.0.1/unsafe_array.go +pkg/mod/github.com/modern-go/reflect2@v1.0.1/unsafe_eface.go +pkg/mod/github.com/modern-go/reflect2@v1.0.1/unsafe_field.go +pkg/mod/github.com/modern-go/reflect2@v1.0.1/unsafe_iface.go +pkg/mod/github.com/modern-go/reflect2@v1.0.1/unsafe_link.go +pkg/mod/github.com/modern-go/reflect2@v1.0.1/unsafe_map.go +pkg/mod/github.com/modern-go/reflect2@v1.0.1/unsafe_ptr.go +pkg/mod/github.com/modern-go/reflect2@v1.0.1/unsafe_slice.go +pkg/mod/github.com/modern-go/reflect2@v1.0.1/unsafe_struct.go +pkg/mod/github.com/modern-go/reflect2@v1.0.1/unsafe_type.go +pkg/mod/github.com/olivere/elastic/v7@v7.0.6/.fossa.yml +pkg/mod/github.com/olivere/elastic/v7@v7.0.6/.gitignore +pkg/mod/github.com/olivere/elastic/v7@v7.0.6/.golangci.yml +pkg/mod/github.com/olivere/elastic/v7@v7.0.6/.travis.yml +pkg/mod/github.com/olivere/elastic/v7@v7.0.6/acknowledged_response.go +pkg/mod/github.com/olivere/elastic/v7@v7.0.6/backoff_test.go +pkg/mod/github.com/olivere/elastic/v7@v7.0.6/backoff.go +pkg/mod/github.com/olivere/elastic/v7@v7.0.6/bulk_delete_request_easyjson.go +pkg/mod/github.com/olivere/elastic/v7@v7.0.6/bulk_delete_request_test.go +pkg/mod/github.com/olivere/elastic/v7@v7.0.6/bulk_delete_request.go +pkg/mod/github.com/olivere/elastic/v7@v7.0.6/bulk_index_request_easyjson.go +pkg/mod/github.com/olivere/elastic/v7@v7.0.6/bulk_index_request_test.go +pkg/mod/github.com/olivere/elastic/v7@v7.0.6/bulk_index_request.go +pkg/mod/github.com/olivere/elastic/v7@v7.0.6/bulk_processor_test.go +pkg/mod/github.com/olivere/elastic/v7@v7.0.6/bulk_processor.go +pkg/mod/github.com/olivere/elastic/v7@v7.0.6/bulk_request.go +pkg/mod/github.com/olivere/elastic/v7@v7.0.6/bulk_test.go +pkg/mod/github.com/olivere/elastic/v7@v7.0.6/bulk_update_request_easyjson.go +pkg/mod/github.com/olivere/elastic/v7@v7.0.6/bulk_update_request_test.go +pkg/mod/github.com/olivere/elastic/v7@v7.0.6/bulk_update_request.go +pkg/mod/github.com/olivere/elastic/v7@v7.0.6/bulk.go +pkg/mod/github.com/olivere/elastic/v7@v7.0.6/canonicalize_test.go +pkg/mod/github.com/olivere/elastic/v7@v7.0.6/canonicalize.go +pkg/mod/github.com/olivere/elastic/v7@v7.0.6/cat_aliases_test.go +pkg/mod/github.com/olivere/elastic/v7@v7.0.6/cat_aliases.go +pkg/mod/github.com/olivere/elastic/v7@v7.0.6/cat_allocation_test.go +pkg/mod/github.com/olivere/elastic/v7@v7.0.6/cat_allocation.go +pkg/mod/github.com/olivere/elastic/v7@v7.0.6/cat_count_test.go +pkg/mod/github.com/olivere/elastic/v7@v7.0.6/cat_count.go +pkg/mod/github.com/olivere/elastic/v7@v7.0.6/cat_health_test.go +pkg/mod/github.com/olivere/elastic/v7@v7.0.6/cat_health.go +pkg/mod/github.com/olivere/elastic/v7@v7.0.6/cat_indices_test.go +pkg/mod/github.com/olivere/elastic/v7@v7.0.6/cat_indices.go +pkg/mod/github.com/olivere/elastic/v7@v7.0.6/CHANGELOG-3.0.md +pkg/mod/github.com/olivere/elastic/v7@v7.0.6/CHANGELOG-5.0.md +pkg/mod/github.com/olivere/elastic/v7@v7.0.6/CHANGELOG-6.0.md +pkg/mod/github.com/olivere/elastic/v7@v7.0.6/CHANGELOG-7.0.md +pkg/mod/github.com/olivere/elastic/v7@v7.0.6/clear_scroll_test.go +pkg/mod/github.com/olivere/elastic/v7@v7.0.6/clear_scroll.go +pkg/mod/github.com/olivere/elastic/v7@v7.0.6/client_test.go +pkg/mod/github.com/olivere/elastic/v7@v7.0.6/client.go +pkg/mod/github.com/olivere/elastic/v7@v7.0.6/cluster_health_test.go +pkg/mod/github.com/olivere/elastic/v7@v7.0.6/cluster_health.go +pkg/mod/github.com/olivere/elastic/v7@v7.0.6/cluster_reroute_test.go +pkg/mod/github.com/olivere/elastic/v7@v7.0.6/cluster_reroute.go +pkg/mod/github.com/olivere/elastic/v7@v7.0.6/cluster_state_test.go +pkg/mod/github.com/olivere/elastic/v7@v7.0.6/cluster_state.go +pkg/mod/github.com/olivere/elastic/v7@v7.0.6/cluster_stats_integration_test.go +pkg/mod/github.com/olivere/elastic/v7@v7.0.6/cluster_stats_test.go +pkg/mod/github.com/olivere/elastic/v7@v7.0.6/cluster_stats.go +pkg/mod/github.com/olivere/elastic/v7@v7.0.6/CODE_OF_CONDUCT.md +pkg/mod/github.com/olivere/elastic/v7@v7.0.6/connection.go +pkg/mod/github.com/olivere/elastic/v7@v7.0.6/CONTRIBUTING.md +pkg/mod/github.com/olivere/elastic/v7@v7.0.6/CONTRIBUTORS +pkg/mod/github.com/olivere/elastic/v7@v7.0.6/count_test.go +pkg/mod/github.com/olivere/elastic/v7@v7.0.6/count.go +pkg/mod/github.com/olivere/elastic/v7@v7.0.6/decoder_test.go +pkg/mod/github.com/olivere/elastic/v7@v7.0.6/decoder.go +pkg/mod/github.com/olivere/elastic/v7@v7.0.6/delete_by_query_test.go +pkg/mod/github.com/olivere/elastic/v7@v7.0.6/delete_by_query.go +pkg/mod/github.com/olivere/elastic/v7@v7.0.6/delete_test.go +pkg/mod/github.com/olivere/elastic/v7@v7.0.6/delete.go +pkg/mod/github.com/olivere/elastic/v7@v7.0.6/doc.go +pkg/mod/github.com/olivere/elastic/v7@v7.0.6/docker-compose.yml +pkg/mod/github.com/olivere/elastic/v7@v7.0.6/docvalue_field_test.go +pkg/mod/github.com/olivere/elastic/v7@v7.0.6/docvalue_field.go +pkg/mod/github.com/olivere/elastic/v7@v7.0.6/errors_test.go +pkg/mod/github.com/olivere/elastic/v7@v7.0.6/errors.go +pkg/mod/github.com/olivere/elastic/v7@v7.0.6/example_test.go +pkg/mod/github.com/olivere/elastic/v7@v7.0.6/exists_test.go +pkg/mod/github.com/olivere/elastic/v7@v7.0.6/exists.go +pkg/mod/github.com/olivere/elastic/v7@v7.0.6/explain_test.go +pkg/mod/github.com/olivere/elastic/v7@v7.0.6/explain.go +pkg/mod/github.com/olivere/elastic/v7@v7.0.6/fetch_source_context_test.go +pkg/mod/github.com/olivere/elastic/v7@v7.0.6/fetch_source_context.go +pkg/mod/github.com/olivere/elastic/v7@v7.0.6/field_caps_test.go +pkg/mod/github.com/olivere/elastic/v7@v7.0.6/field_caps.go +pkg/mod/github.com/olivere/elastic/v7@v7.0.6/geo_point_test.go +pkg/mod/github.com/olivere/elastic/v7@v7.0.6/geo_point.go +pkg/mod/github.com/olivere/elastic/v7@v7.0.6/get_test.go +pkg/mod/github.com/olivere/elastic/v7@v7.0.6/get.go +pkg/mod/github.com/olivere/elastic/v7@v7.0.6/go.mod +pkg/mod/github.com/olivere/elastic/v7@v7.0.6/highlight_test.go +pkg/mod/github.com/olivere/elastic/v7@v7.0.6/highlight.go +pkg/mod/github.com/olivere/elastic/v7@v7.0.6/index_test.go +pkg/mod/github.com/olivere/elastic/v7@v7.0.6/index.go +pkg/mod/github.com/olivere/elastic/v7@v7.0.6/indices_analyze_test.go +pkg/mod/github.com/olivere/elastic/v7@v7.0.6/indices_analyze.go +pkg/mod/github.com/olivere/elastic/v7@v7.0.6/indices_close_test.go +pkg/mod/github.com/olivere/elastic/v7@v7.0.6/indices_close.go +pkg/mod/github.com/olivere/elastic/v7@v7.0.6/indices_create_test.go +pkg/mod/github.com/olivere/elastic/v7@v7.0.6/indices_create.go +pkg/mod/github.com/olivere/elastic/v7@v7.0.6/indices_delete_template.go +pkg/mod/github.com/olivere/elastic/v7@v7.0.6/indices_delete_test.go +pkg/mod/github.com/olivere/elastic/v7@v7.0.6/indices_delete.go +pkg/mod/github.com/olivere/elastic/v7@v7.0.6/indices_exists_template_test.go +pkg/mod/github.com/olivere/elastic/v7@v7.0.6/indices_exists_template.go +pkg/mod/github.com/olivere/elastic/v7@v7.0.6/indices_exists_test.go +pkg/mod/github.com/olivere/elastic/v7@v7.0.6/indices_exists.go +pkg/mod/github.com/olivere/elastic/v7@v7.0.6/indices_flush_synced_test.go +pkg/mod/github.com/olivere/elastic/v7@v7.0.6/indices_flush_synced.go +pkg/mod/github.com/olivere/elastic/v7@v7.0.6/indices_flush_test.go +pkg/mod/github.com/olivere/elastic/v7@v7.0.6/indices_flush.go +pkg/mod/github.com/olivere/elastic/v7@v7.0.6/indices_forcemerge_test.go +pkg/mod/github.com/olivere/elastic/v7@v7.0.6/indices_forcemerge.go +pkg/mod/github.com/olivere/elastic/v7@v7.0.6/indices_freeze_test.go +pkg/mod/github.com/olivere/elastic/v7@v7.0.6/indices_freeze.go +pkg/mod/github.com/olivere/elastic/v7@v7.0.6/indices_get_aliases_test.go +pkg/mod/github.com/olivere/elastic/v7@v7.0.6/indices_get_aliases.go +pkg/mod/github.com/olivere/elastic/v7@v7.0.6/indices_get_field_mapping_test.go +pkg/mod/github.com/olivere/elastic/v7@v7.0.6/indices_get_field_mapping.go +pkg/mod/github.com/olivere/elastic/v7@v7.0.6/indices_get_mapping_test.go +pkg/mod/github.com/olivere/elastic/v7@v7.0.6/indices_get_mapping.go +pkg/mod/github.com/olivere/elastic/v7@v7.0.6/indices_get_settings_test.go +pkg/mod/github.com/olivere/elastic/v7@v7.0.6/indices_get_settings.go +pkg/mod/github.com/olivere/elastic/v7@v7.0.6/indices_get_template_test.go +pkg/mod/github.com/olivere/elastic/v7@v7.0.6/indices_get_template.go +pkg/mod/github.com/olivere/elastic/v7@v7.0.6/indices_get_test.go +pkg/mod/github.com/olivere/elastic/v7@v7.0.6/indices_get.go +pkg/mod/github.com/olivere/elastic/v7@v7.0.6/indices_open_test.go +pkg/mod/github.com/olivere/elastic/v7@v7.0.6/indices_open.go +pkg/mod/github.com/olivere/elastic/v7@v7.0.6/indices_put_alias_test.go +pkg/mod/github.com/olivere/elastic/v7@v7.0.6/indices_put_alias.go +pkg/mod/github.com/olivere/elastic/v7@v7.0.6/indices_put_mapping_test.go +pkg/mod/github.com/olivere/elastic/v7@v7.0.6/indices_put_mapping.go +pkg/mod/github.com/olivere/elastic/v7@v7.0.6/indices_put_settings_test.go +pkg/mod/github.com/olivere/elastic/v7@v7.0.6/indices_put_settings.go +pkg/mod/github.com/olivere/elastic/v7@v7.0.6/indices_put_template.go +pkg/mod/github.com/olivere/elastic/v7@v7.0.6/indices_refresh_test.go +pkg/mod/github.com/olivere/elastic/v7@v7.0.6/indices_refresh.go +pkg/mod/github.com/olivere/elastic/v7@v7.0.6/indices_rollover_test.go +pkg/mod/github.com/olivere/elastic/v7@v7.0.6/indices_rollover.go +pkg/mod/github.com/olivere/elastic/v7@v7.0.6/indices_segments_test.go +pkg/mod/github.com/olivere/elastic/v7@v7.0.6/indices_segments.go +pkg/mod/github.com/olivere/elastic/v7@v7.0.6/indices_shrink_test.go +pkg/mod/github.com/olivere/elastic/v7@v7.0.6/indices_shrink.go +pkg/mod/github.com/olivere/elastic/v7@v7.0.6/indices_stats_test.go +pkg/mod/github.com/olivere/elastic/v7@v7.0.6/indices_stats.go +pkg/mod/github.com/olivere/elastic/v7@v7.0.6/indices_unfreeze_test.go +pkg/mod/github.com/olivere/elastic/v7@v7.0.6/indices_unfreeze.go +pkg/mod/github.com/olivere/elastic/v7@v7.0.6/ingest_delete_pipeline_test.go +pkg/mod/github.com/olivere/elastic/v7@v7.0.6/ingest_delete_pipeline.go +pkg/mod/github.com/olivere/elastic/v7@v7.0.6/ingest_get_pipeline_test.go +pkg/mod/github.com/olivere/elastic/v7@v7.0.6/ingest_get_pipeline.go +pkg/mod/github.com/olivere/elastic/v7@v7.0.6/ingest_put_pipeline_test.go +pkg/mod/github.com/olivere/elastic/v7@v7.0.6/ingest_put_pipeline.go +pkg/mod/github.com/olivere/elastic/v7@v7.0.6/ingest_simulate_pipeline_test.go +pkg/mod/github.com/olivere/elastic/v7@v7.0.6/ingest_simulate_pipeline.go +pkg/mod/github.com/olivere/elastic/v7@v7.0.6/inner_hit_test.go +pkg/mod/github.com/olivere/elastic/v7@v7.0.6/inner_hit.go +pkg/mod/github.com/olivere/elastic/v7@v7.0.6/ISSUE_TEMPLATE.md +pkg/mod/github.com/olivere/elastic/v7@v7.0.6/LICENSE +pkg/mod/github.com/olivere/elastic/v7@v7.0.6/logger.go +pkg/mod/github.com/olivere/elastic/v7@v7.0.6/mget_test.go +pkg/mod/github.com/olivere/elastic/v7@v7.0.6/mget.go +pkg/mod/github.com/olivere/elastic/v7@v7.0.6/msearch_test.go +pkg/mod/github.com/olivere/elastic/v7@v7.0.6/msearch.go +pkg/mod/github.com/olivere/elastic/v7@v7.0.6/mtermvectors_test.go +pkg/mod/github.com/olivere/elastic/v7@v7.0.6/mtermvectors.go +pkg/mod/github.com/olivere/elastic/v7@v7.0.6/nodes_info_test.go +pkg/mod/github.com/olivere/elastic/v7@v7.0.6/nodes_info.go +pkg/mod/github.com/olivere/elastic/v7@v7.0.6/nodes_stats_test.go +pkg/mod/github.com/olivere/elastic/v7@v7.0.6/nodes_stats.go +pkg/mod/github.com/olivere/elastic/v7@v7.0.6/percolate_test.go +pkg/mod/github.com/olivere/elastic/v7@v7.0.6/ping_test.go +pkg/mod/github.com/olivere/elastic/v7@v7.0.6/ping.go +pkg/mod/github.com/olivere/elastic/v7@v7.0.6/plugins_test.go +pkg/mod/github.com/olivere/elastic/v7@v7.0.6/plugins.go +pkg/mod/github.com/olivere/elastic/v7@v7.0.6/query.go +pkg/mod/github.com/olivere/elastic/v7@v7.0.6/README.md +pkg/mod/github.com/olivere/elastic/v7@v7.0.6/reindex_test.go +pkg/mod/github.com/olivere/elastic/v7@v7.0.6/reindex.go +pkg/mod/github.com/olivere/elastic/v7@v7.0.6/request_test.go +pkg/mod/github.com/olivere/elastic/v7@v7.0.6/request.go +pkg/mod/github.com/olivere/elastic/v7@v7.0.6/rescore.go +pkg/mod/github.com/olivere/elastic/v7@v7.0.6/rescorer.go +pkg/mod/github.com/olivere/elastic/v7@v7.0.6/response_test.go +pkg/mod/github.com/olivere/elastic/v7@v7.0.6/response.go +pkg/mod/github.com/olivere/elastic/v7@v7.0.6/retrier_test.go +pkg/mod/github.com/olivere/elastic/v7@v7.0.6/retrier.go +pkg/mod/github.com/olivere/elastic/v7@v7.0.6/retry_test.go +pkg/mod/github.com/olivere/elastic/v7@v7.0.6/retry.go +pkg/mod/github.com/olivere/elastic/v7@v7.0.6/run-es.sh +pkg/mod/github.com/olivere/elastic/v7@v7.0.6/run-tests.sh +pkg/mod/github.com/olivere/elastic/v7@v7.0.6/script_delete_test.go +pkg/mod/github.com/olivere/elastic/v7@v7.0.6/script_delete.go +pkg/mod/github.com/olivere/elastic/v7@v7.0.6/script_get_test.go +pkg/mod/github.com/olivere/elastic/v7@v7.0.6/script_get.go +pkg/mod/github.com/olivere/elastic/v7@v7.0.6/script_put_test.go +pkg/mod/github.com/olivere/elastic/v7@v7.0.6/script_put.go +pkg/mod/github.com/olivere/elastic/v7@v7.0.6/script_test.go +pkg/mod/github.com/olivere/elastic/v7@v7.0.6/script.go +pkg/mod/github.com/olivere/elastic/v7@v7.0.6/scroll_test.go +pkg/mod/github.com/olivere/elastic/v7@v7.0.6/scroll.go +pkg/mod/github.com/olivere/elastic/v7@v7.0.6/search_aggs_bucket_adjacency_matrix_test.go +pkg/mod/github.com/olivere/elastic/v7@v7.0.6/search_aggs_bucket_adjacency_matrix.go +pkg/mod/github.com/olivere/elastic/v7@v7.0.6/search_aggs_bucket_auto_date_histogram_test.go +pkg/mod/github.com/olivere/elastic/v7@v7.0.6/search_aggs_bucket_auto_date_histogram.go +pkg/mod/github.com/olivere/elastic/v7@v7.0.6/search_aggs_bucket_children_test.go +pkg/mod/github.com/olivere/elastic/v7@v7.0.6/search_aggs_bucket_children.go +pkg/mod/github.com/olivere/elastic/v7@v7.0.6/search_aggs_bucket_composite_test.go +pkg/mod/github.com/olivere/elastic/v7@v7.0.6/search_aggs_bucket_composite.go +pkg/mod/github.com/olivere/elastic/v7@v7.0.6/search_aggs_bucket_count_thresholds.go +pkg/mod/github.com/olivere/elastic/v7@v7.0.6/search_aggs_bucket_date_histogram_test.go +pkg/mod/github.com/olivere/elastic/v7@v7.0.6/search_aggs_bucket_date_histogram.go +pkg/mod/github.com/olivere/elastic/v7@v7.0.6/search_aggs_bucket_date_range_test.go +pkg/mod/github.com/olivere/elastic/v7@v7.0.6/search_aggs_bucket_date_range.go +pkg/mod/github.com/olivere/elastic/v7@v7.0.6/search_aggs_bucket_diversified_sampler_test.go +pkg/mod/github.com/olivere/elastic/v7@v7.0.6/search_aggs_bucket_diversified_sampler.go +pkg/mod/github.com/olivere/elastic/v7@v7.0.6/search_aggs_bucket_filter_test.go +pkg/mod/github.com/olivere/elastic/v7@v7.0.6/search_aggs_bucket_filter.go +pkg/mod/github.com/olivere/elastic/v7@v7.0.6/search_aggs_bucket_filters_test.go +pkg/mod/github.com/olivere/elastic/v7@v7.0.6/search_aggs_bucket_filters.go +pkg/mod/github.com/olivere/elastic/v7@v7.0.6/search_aggs_bucket_geo_distance_test.go +pkg/mod/github.com/olivere/elastic/v7@v7.0.6/search_aggs_bucket_geo_distance.go +pkg/mod/github.com/olivere/elastic/v7@v7.0.6/search_aggs_bucket_geohash_grid_test.go +pkg/mod/github.com/olivere/elastic/v7@v7.0.6/search_aggs_bucket_geohash_grid.go +pkg/mod/github.com/olivere/elastic/v7@v7.0.6/search_aggs_bucket_global_test.go +pkg/mod/github.com/olivere/elastic/v7@v7.0.6/search_aggs_bucket_global.go +pkg/mod/github.com/olivere/elastic/v7@v7.0.6/search_aggs_bucket_histogram_test.go +pkg/mod/github.com/olivere/elastic/v7@v7.0.6/search_aggs_bucket_histogram.go +pkg/mod/github.com/olivere/elastic/v7@v7.0.6/search_aggs_bucket_ip_range_test.go +pkg/mod/github.com/olivere/elastic/v7@v7.0.6/search_aggs_bucket_ip_range.go +pkg/mod/github.com/olivere/elastic/v7@v7.0.6/search_aggs_bucket_missing_test.go +pkg/mod/github.com/olivere/elastic/v7@v7.0.6/search_aggs_bucket_missing.go +pkg/mod/github.com/olivere/elastic/v7@v7.0.6/search_aggs_bucket_nested_test.go +pkg/mod/github.com/olivere/elastic/v7@v7.0.6/search_aggs_bucket_nested.go +pkg/mod/github.com/olivere/elastic/v7@v7.0.6/search_aggs_bucket_range_test.go +pkg/mod/github.com/olivere/elastic/v7@v7.0.6/search_aggs_bucket_range.go +pkg/mod/github.com/olivere/elastic/v7@v7.0.6/search_aggs_bucket_reverse_nested_test.go +pkg/mod/github.com/olivere/elastic/v7@v7.0.6/search_aggs_bucket_reverse_nested.go +pkg/mod/github.com/olivere/elastic/v7@v7.0.6/search_aggs_bucket_sampler_test.go +pkg/mod/github.com/olivere/elastic/v7@v7.0.6/search_aggs_bucket_sampler.go +pkg/mod/github.com/olivere/elastic/v7@v7.0.6/search_aggs_bucket_significant_terms_test.go +pkg/mod/github.com/olivere/elastic/v7@v7.0.6/search_aggs_bucket_significant_terms.go +pkg/mod/github.com/olivere/elastic/v7@v7.0.6/search_aggs_bucket_significant_text_test.go +pkg/mod/github.com/olivere/elastic/v7@v7.0.6/search_aggs_bucket_significant_text.go +pkg/mod/github.com/olivere/elastic/v7@v7.0.6/search_aggs_bucket_terms_test.go +pkg/mod/github.com/olivere/elastic/v7@v7.0.6/search_aggs_bucket_terms.go +pkg/mod/github.com/olivere/elastic/v7@v7.0.6/search_aggs_matrix_stats_test.go +pkg/mod/github.com/olivere/elastic/v7@v7.0.6/search_aggs_matrix_stats.go +pkg/mod/github.com/olivere/elastic/v7@v7.0.6/search_aggs_metrics_avg_test.go +pkg/mod/github.com/olivere/elastic/v7@v7.0.6/search_aggs_metrics_avg.go +pkg/mod/github.com/olivere/elastic/v7@v7.0.6/search_aggs_metrics_cardinality_test.go +pkg/mod/github.com/olivere/elastic/v7@v7.0.6/search_aggs_metrics_cardinality.go +pkg/mod/github.com/olivere/elastic/v7@v7.0.6/search_aggs_metrics_extended_stats_test.go +pkg/mod/github.com/olivere/elastic/v7@v7.0.6/search_aggs_metrics_extended_stats.go +pkg/mod/github.com/olivere/elastic/v7@v7.0.6/search_aggs_metrics_geo_bounds_test.go +pkg/mod/github.com/olivere/elastic/v7@v7.0.6/search_aggs_metrics_geo_bounds.go +pkg/mod/github.com/olivere/elastic/v7@v7.0.6/search_aggs_metrics_geo_centroid_test.go +pkg/mod/github.com/olivere/elastic/v7@v7.0.6/search_aggs_metrics_geo_centroid.go +pkg/mod/github.com/olivere/elastic/v7@v7.0.6/search_aggs_metrics_max_test.go +pkg/mod/github.com/olivere/elastic/v7@v7.0.6/search_aggs_metrics_max.go +pkg/mod/github.com/olivere/elastic/v7@v7.0.6/search_aggs_metrics_min_test.go +pkg/mod/github.com/olivere/elastic/v7@v7.0.6/search_aggs_metrics_min.go +pkg/mod/github.com/olivere/elastic/v7@v7.0.6/search_aggs_metrics_percentile_ranks_test.go +pkg/mod/github.com/olivere/elastic/v7@v7.0.6/search_aggs_metrics_percentile_ranks.go +pkg/mod/github.com/olivere/elastic/v7@v7.0.6/search_aggs_metrics_percentiles_test.go +pkg/mod/github.com/olivere/elastic/v7@v7.0.6/search_aggs_metrics_percentiles.go +pkg/mod/github.com/olivere/elastic/v7@v7.0.6/search_aggs_metrics_scripted_metric_test.go +pkg/mod/github.com/olivere/elastic/v7@v7.0.6/search_aggs_metrics_scripted_metric.go +pkg/mod/github.com/olivere/elastic/v7@v7.0.6/search_aggs_metrics_stats_test.go +pkg/mod/github.com/olivere/elastic/v7@v7.0.6/search_aggs_metrics_stats.go +pkg/mod/github.com/olivere/elastic/v7@v7.0.6/search_aggs_metrics_sum_test.go +pkg/mod/github.com/olivere/elastic/v7@v7.0.6/search_aggs_metrics_sum.go +pkg/mod/github.com/olivere/elastic/v7@v7.0.6/search_aggs_metrics_top_hits_test.go +pkg/mod/github.com/olivere/elastic/v7@v7.0.6/search_aggs_metrics_top_hits.go +pkg/mod/github.com/olivere/elastic/v7@v7.0.6/search_aggs_metrics_value_count_test.go +pkg/mod/github.com/olivere/elastic/v7@v7.0.6/search_aggs_metrics_value_count.go +pkg/mod/github.com/olivere/elastic/v7@v7.0.6/search_aggs_metrics_weighted_avg_test.go +pkg/mod/github.com/olivere/elastic/v7@v7.0.6/search_aggs_metrics_weighted_avg.go +pkg/mod/github.com/olivere/elastic/v7@v7.0.6/search_aggs_pipeline_avg_bucket_test.go +pkg/mod/github.com/olivere/elastic/v7@v7.0.6/search_aggs_pipeline_avg_bucket.go +pkg/mod/github.com/olivere/elastic/v7@v7.0.6/search_aggs_pipeline_bucket_script_test.go +pkg/mod/github.com/olivere/elastic/v7@v7.0.6/search_aggs_pipeline_bucket_script.go +pkg/mod/github.com/olivere/elastic/v7@v7.0.6/search_aggs_pipeline_bucket_selector_test.go +pkg/mod/github.com/olivere/elastic/v7@v7.0.6/search_aggs_pipeline_bucket_selector.go +pkg/mod/github.com/olivere/elastic/v7@v7.0.6/search_aggs_pipeline_bucket_sort_test.go +pkg/mod/github.com/olivere/elastic/v7@v7.0.6/search_aggs_pipeline_bucket_sort.go +pkg/mod/github.com/olivere/elastic/v7@v7.0.6/search_aggs_pipeline_cumulative_sum_test.go +pkg/mod/github.com/olivere/elastic/v7@v7.0.6/search_aggs_pipeline_cumulative_sum.go +pkg/mod/github.com/olivere/elastic/v7@v7.0.6/search_aggs_pipeline_derivative_test.go +pkg/mod/github.com/olivere/elastic/v7@v7.0.6/search_aggs_pipeline_derivative.go +pkg/mod/github.com/olivere/elastic/v7@v7.0.6/search_aggs_pipeline_extended_stats_bucket_test.go +pkg/mod/github.com/olivere/elastic/v7@v7.0.6/search_aggs_pipeline_extended_stats_bucket.go +pkg/mod/github.com/olivere/elastic/v7@v7.0.6/search_aggs_pipeline_max_bucket_test.go +pkg/mod/github.com/olivere/elastic/v7@v7.0.6/search_aggs_pipeline_max_bucket.go +pkg/mod/github.com/olivere/elastic/v7@v7.0.6/search_aggs_pipeline_min_bucket_test.go +pkg/mod/github.com/olivere/elastic/v7@v7.0.6/search_aggs_pipeline_min_bucket.go +pkg/mod/github.com/olivere/elastic/v7@v7.0.6/search_aggs_pipeline_mov_avg_test.go +pkg/mod/github.com/olivere/elastic/v7@v7.0.6/search_aggs_pipeline_mov_avg.go +pkg/mod/github.com/olivere/elastic/v7@v7.0.6/search_aggs_pipeline_mov_fn_test.go +pkg/mod/github.com/olivere/elastic/v7@v7.0.6/search_aggs_pipeline_mov_fn.go +pkg/mod/github.com/olivere/elastic/v7@v7.0.6/search_aggs_pipeline_percentiles_bucket_test.go +pkg/mod/github.com/olivere/elastic/v7@v7.0.6/search_aggs_pipeline_percentiles_bucket.go +pkg/mod/github.com/olivere/elastic/v7@v7.0.6/search_aggs_pipeline_serial_diff_test.go +pkg/mod/github.com/olivere/elastic/v7@v7.0.6/search_aggs_pipeline_serial_diff.go +pkg/mod/github.com/olivere/elastic/v7@v7.0.6/search_aggs_pipeline_stats_bucket_test.go +pkg/mod/github.com/olivere/elastic/v7@v7.0.6/search_aggs_pipeline_stats_bucket.go +pkg/mod/github.com/olivere/elastic/v7@v7.0.6/search_aggs_pipeline_sum_bucket_test.go +pkg/mod/github.com/olivere/elastic/v7@v7.0.6/search_aggs_pipeline_sum_bucket.go +pkg/mod/github.com/olivere/elastic/v7@v7.0.6/search_aggs_pipeline_test.go +pkg/mod/github.com/olivere/elastic/v7@v7.0.6/search_aggs_test.go +pkg/mod/github.com/olivere/elastic/v7@v7.0.6/search_aggs.go +pkg/mod/github.com/olivere/elastic/v7@v7.0.6/search_collapse_builder_test.go +pkg/mod/github.com/olivere/elastic/v7@v7.0.6/search_collapse_builder.go +pkg/mod/github.com/olivere/elastic/v7@v7.0.6/search_queries_bool_test.go +pkg/mod/github.com/olivere/elastic/v7@v7.0.6/search_queries_bool.go +pkg/mod/github.com/olivere/elastic/v7@v7.0.6/search_queries_boosting_test.go +pkg/mod/github.com/olivere/elastic/v7@v7.0.6/search_queries_boosting.go +pkg/mod/github.com/olivere/elastic/v7@v7.0.6/search_queries_common_terms_test.go +pkg/mod/github.com/olivere/elastic/v7@v7.0.6/search_queries_common_terms.go +pkg/mod/github.com/olivere/elastic/v7@v7.0.6/search_queries_constant_score_test.go +pkg/mod/github.com/olivere/elastic/v7@v7.0.6/search_queries_constant_score.go +pkg/mod/github.com/olivere/elastic/v7@v7.0.6/search_queries_dis_max_test.go +pkg/mod/github.com/olivere/elastic/v7@v7.0.6/search_queries_dis_max.go +pkg/mod/github.com/olivere/elastic/v7@v7.0.6/search_queries_exists_test.go +pkg/mod/github.com/olivere/elastic/v7@v7.0.6/search_queries_exists.go +pkg/mod/github.com/olivere/elastic/v7@v7.0.6/search_queries_fsq_score_funcs.go +pkg/mod/github.com/olivere/elastic/v7@v7.0.6/search_queries_fsq_test.go +pkg/mod/github.com/olivere/elastic/v7@v7.0.6/search_queries_fsq.go +pkg/mod/github.com/olivere/elastic/v7@v7.0.6/search_queries_fuzzy_test.go +pkg/mod/github.com/olivere/elastic/v7@v7.0.6/search_queries_fuzzy.go +pkg/mod/github.com/olivere/elastic/v7@v7.0.6/search_queries_geo_bounding_box_test.go +pkg/mod/github.com/olivere/elastic/v7@v7.0.6/search_queries_geo_bounding_box.go +pkg/mod/github.com/olivere/elastic/v7@v7.0.6/search_queries_geo_distance_test.go +pkg/mod/github.com/olivere/elastic/v7@v7.0.6/search_queries_geo_distance.go +pkg/mod/github.com/olivere/elastic/v7@v7.0.6/search_queries_geo_polygon_test.go +pkg/mod/github.com/olivere/elastic/v7@v7.0.6/search_queries_geo_polygon.go +pkg/mod/github.com/olivere/elastic/v7@v7.0.6/search_queries_has_child_test.go +pkg/mod/github.com/olivere/elastic/v7@v7.0.6/search_queries_has_child.go +pkg/mod/github.com/olivere/elastic/v7@v7.0.6/search_queries_has_parent_test.go +pkg/mod/github.com/olivere/elastic/v7@v7.0.6/search_queries_has_parent.go +pkg/mod/github.com/olivere/elastic/v7@v7.0.6/search_queries_ids_test.go +pkg/mod/github.com/olivere/elastic/v7@v7.0.6/search_queries_ids.go +pkg/mod/github.com/olivere/elastic/v7@v7.0.6/search_queries_match_all_test.go +pkg/mod/github.com/olivere/elastic/v7@v7.0.6/search_queries_match_all.go +pkg/mod/github.com/olivere/elastic/v7@v7.0.6/search_queries_match_none_test.go +pkg/mod/github.com/olivere/elastic/v7@v7.0.6/search_queries_match_none.go +pkg/mod/github.com/olivere/elastic/v7@v7.0.6/search_queries_match_phrase_prefix_test.go +pkg/mod/github.com/olivere/elastic/v7@v7.0.6/search_queries_match_phrase_prefix.go +pkg/mod/github.com/olivere/elastic/v7@v7.0.6/search_queries_match_phrase_test.go +pkg/mod/github.com/olivere/elastic/v7@v7.0.6/search_queries_match_phrase.go +pkg/mod/github.com/olivere/elastic/v7@v7.0.6/search_queries_match_test.go +pkg/mod/github.com/olivere/elastic/v7@v7.0.6/search_queries_match.go +pkg/mod/github.com/olivere/elastic/v7@v7.0.6/search_queries_more_like_this_test.go +pkg/mod/github.com/olivere/elastic/v7@v7.0.6/search_queries_more_like_this.go +pkg/mod/github.com/olivere/elastic/v7@v7.0.6/search_queries_multi_match_test.go +pkg/mod/github.com/olivere/elastic/v7@v7.0.6/search_queries_multi_match.go +pkg/mod/github.com/olivere/elastic/v7@v7.0.6/search_queries_nested_test.go +pkg/mod/github.com/olivere/elastic/v7@v7.0.6/search_queries_nested.go +pkg/mod/github.com/olivere/elastic/v7@v7.0.6/search_queries_parent_id_test.go +pkg/mod/github.com/olivere/elastic/v7@v7.0.6/search_queries_parent_id.go +pkg/mod/github.com/olivere/elastic/v7@v7.0.6/search_queries_percolator_test.go +pkg/mod/github.com/olivere/elastic/v7@v7.0.6/search_queries_percolator.go +pkg/mod/github.com/olivere/elastic/v7@v7.0.6/search_queries_prefix_example_test.go +pkg/mod/github.com/olivere/elastic/v7@v7.0.6/search_queries_prefix_test.go +pkg/mod/github.com/olivere/elastic/v7@v7.0.6/search_queries_prefix.go +pkg/mod/github.com/olivere/elastic/v7@v7.0.6/search_queries_query_string_test.go +pkg/mod/github.com/olivere/elastic/v7@v7.0.6/search_queries_query_string.go +pkg/mod/github.com/olivere/elastic/v7@v7.0.6/search_queries_range_test.go +pkg/mod/github.com/olivere/elastic/v7@v7.0.6/search_queries_range.go +pkg/mod/github.com/olivere/elastic/v7@v7.0.6/search_queries_raw_string_test.go +pkg/mod/github.com/olivere/elastic/v7@v7.0.6/search_queries_raw_string.go +pkg/mod/github.com/olivere/elastic/v7@v7.0.6/search_queries_regexp_test.go +pkg/mod/github.com/olivere/elastic/v7@v7.0.6/search_queries_regexp.go +pkg/mod/github.com/olivere/elastic/v7@v7.0.6/search_queries_script_test.go +pkg/mod/github.com/olivere/elastic/v7@v7.0.6/search_queries_script.go +pkg/mod/github.com/olivere/elastic/v7@v7.0.6/search_queries_simple_query_string_test.go +pkg/mod/github.com/olivere/elastic/v7@v7.0.6/search_queries_simple_query_string.go +pkg/mod/github.com/olivere/elastic/v7@v7.0.6/search_queries_slice_test.go +pkg/mod/github.com/olivere/elastic/v7@v7.0.6/search_queries_slice.go +pkg/mod/github.com/olivere/elastic/v7@v7.0.6/search_queries_term_test.go +pkg/mod/github.com/olivere/elastic/v7@v7.0.6/search_queries_term.go +pkg/mod/github.com/olivere/elastic/v7@v7.0.6/search_queries_terms_set_test.go +pkg/mod/github.com/olivere/elastic/v7@v7.0.6/search_queries_terms_set.go +pkg/mod/github.com/olivere/elastic/v7@v7.0.6/search_queries_terms_test.go +pkg/mod/github.com/olivere/elastic/v7@v7.0.6/search_queries_terms.go +pkg/mod/github.com/olivere/elastic/v7@v7.0.6/search_queries_type_test.go +pkg/mod/github.com/olivere/elastic/v7@v7.0.6/search_queries_type.go +pkg/mod/github.com/olivere/elastic/v7@v7.0.6/search_queries_wildcard_test.go +pkg/mod/github.com/olivere/elastic/v7@v7.0.6/search_queries_wildcard.go +pkg/mod/github.com/olivere/elastic/v7@v7.0.6/search_queries_wrapper_integration_test.go +pkg/mod/github.com/olivere/elastic/v7@v7.0.6/search_queries_wrapper_test.go +pkg/mod/github.com/olivere/elastic/v7@v7.0.6/search_queries_wrapper.go +pkg/mod/github.com/olivere/elastic/v7@v7.0.6/search_request_test.go +pkg/mod/github.com/olivere/elastic/v7@v7.0.6/search_request.go +pkg/mod/github.com/olivere/elastic/v7@v7.0.6/search_shards_test.go +pkg/mod/github.com/olivere/elastic/v7@v7.0.6/search_shards.go +pkg/mod/github.com/olivere/elastic/v7@v7.0.6/search_source_test.go +pkg/mod/github.com/olivere/elastic/v7@v7.0.6/search_source.go +pkg/mod/github.com/olivere/elastic/v7@v7.0.6/search_suggester_test.go +pkg/mod/github.com/olivere/elastic/v7@v7.0.6/search_terms_lookup_test.go +pkg/mod/github.com/olivere/elastic/v7@v7.0.6/search_terms_lookup.go +pkg/mod/github.com/olivere/elastic/v7@v7.0.6/search_test.go +pkg/mod/github.com/olivere/elastic/v7@v7.0.6/search.go +pkg/mod/github.com/olivere/elastic/v7@v7.0.6/setup_test.go +pkg/mod/github.com/olivere/elastic/v7@v7.0.6/snapshot_create_repository_test.go +pkg/mod/github.com/olivere/elastic/v7@v7.0.6/snapshot_create_repository.go +pkg/mod/github.com/olivere/elastic/v7@v7.0.6/snapshot_create_test.go +pkg/mod/github.com/olivere/elastic/v7@v7.0.6/snapshot_create.go +pkg/mod/github.com/olivere/elastic/v7@v7.0.6/snapshot_delete_repository_test.go +pkg/mod/github.com/olivere/elastic/v7@v7.0.6/snapshot_delete_repository.go +pkg/mod/github.com/olivere/elastic/v7@v7.0.6/snapshot_delete_test.go +pkg/mod/github.com/olivere/elastic/v7@v7.0.6/snapshot_delete.go +pkg/mod/github.com/olivere/elastic/v7@v7.0.6/snapshot_get_repository_test.go +pkg/mod/github.com/olivere/elastic/v7@v7.0.6/snapshot_get_repository.go +pkg/mod/github.com/olivere/elastic/v7@v7.0.6/snapshot_get_test.go +pkg/mod/github.com/olivere/elastic/v7@v7.0.6/snapshot_get.go +pkg/mod/github.com/olivere/elastic/v7@v7.0.6/snapshot_restore_test.go +pkg/mod/github.com/olivere/elastic/v7@v7.0.6/snapshot_restore.go +pkg/mod/github.com/olivere/elastic/v7@v7.0.6/snapshot_verify_repository_test.go +pkg/mod/github.com/olivere/elastic/v7@v7.0.6/snapshot_verify_repository.go +pkg/mod/github.com/olivere/elastic/v7@v7.0.6/sort_test.go +pkg/mod/github.com/olivere/elastic/v7@v7.0.6/sort.go +pkg/mod/github.com/olivere/elastic/v7@v7.0.6/suggest_field_test.go +pkg/mod/github.com/olivere/elastic/v7@v7.0.6/suggest_field.go +pkg/mod/github.com/olivere/elastic/v7@v7.0.6/suggester_completion_test.go +pkg/mod/github.com/olivere/elastic/v7@v7.0.6/suggester_completion.go +pkg/mod/github.com/olivere/elastic/v7@v7.0.6/suggester_context_category_test.go +pkg/mod/github.com/olivere/elastic/v7@v7.0.6/suggester_context_category.go +pkg/mod/github.com/olivere/elastic/v7@v7.0.6/suggester_context_geo_test.go +pkg/mod/github.com/olivere/elastic/v7@v7.0.6/suggester_context_geo.go +pkg/mod/github.com/olivere/elastic/v7@v7.0.6/suggester_context_test.go +pkg/mod/github.com/olivere/elastic/v7@v7.0.6/suggester_context.go +pkg/mod/github.com/olivere/elastic/v7@v7.0.6/suggester_phrase_test.go +pkg/mod/github.com/olivere/elastic/v7@v7.0.6/suggester_phrase.go +pkg/mod/github.com/olivere/elastic/v7@v7.0.6/suggester_term_test.go +pkg/mod/github.com/olivere/elastic/v7@v7.0.6/suggester_term.go +pkg/mod/github.com/olivere/elastic/v7@v7.0.6/suggester.go +pkg/mod/github.com/olivere/elastic/v7@v7.0.6/tasks_cancel_test.go +pkg/mod/github.com/olivere/elastic/v7@v7.0.6/tasks_cancel.go +pkg/mod/github.com/olivere/elastic/v7@v7.0.6/tasks_get_task_test.go +pkg/mod/github.com/olivere/elastic/v7@v7.0.6/tasks_get_task.go +pkg/mod/github.com/olivere/elastic/v7@v7.0.6/tasks_list_test.go +pkg/mod/github.com/olivere/elastic/v7@v7.0.6/tasks_list.go +pkg/mod/github.com/olivere/elastic/v7@v7.0.6/termvectors_test.go +pkg/mod/github.com/olivere/elastic/v7@v7.0.6/termvectors.go +pkg/mod/github.com/olivere/elastic/v7@v7.0.6/update_by_query_test.go +pkg/mod/github.com/olivere/elastic/v7@v7.0.6/update_by_query.go +pkg/mod/github.com/olivere/elastic/v7@v7.0.6/update_integration_test.go +pkg/mod/github.com/olivere/elastic/v7@v7.0.6/update_test.go +pkg/mod/github.com/olivere/elastic/v7@v7.0.6/update.go +pkg/mod/github.com/olivere/elastic/v7@v7.0.6/validate_test.go +pkg/mod/github.com/olivere/elastic/v7@v7.0.6/validate.go +pkg/mod/github.com/olivere/elastic/v7@v7.0.6/xpack_ilm_delete_lifecycle.go +pkg/mod/github.com/olivere/elastic/v7@v7.0.6/xpack_ilm_get_lifecycle.go +pkg/mod/github.com/olivere/elastic/v7@v7.0.6/xpack_ilm_put_lifecycle.go +pkg/mod/github.com/olivere/elastic/v7@v7.0.6/xpack_ilm_test.go +pkg/mod/github.com/olivere/elastic/v7@v7.0.6/xpack_info_test.go +pkg/mod/github.com/olivere/elastic/v7@v7.0.6/xpack_info.go +pkg/mod/github.com/olivere/elastic/v7@v7.0.6/xpack_security_change_password_test.go +pkg/mod/github.com/olivere/elastic/v7@v7.0.6/xpack_security_change_password.go +pkg/mod/github.com/olivere/elastic/v7@v7.0.6/xpack_security_delete_role_mapping_test.go +pkg/mod/github.com/olivere/elastic/v7@v7.0.6/xpack_security_delete_role_mapping.go +pkg/mod/github.com/olivere/elastic/v7@v7.0.6/xpack_security_delete_role_test.go +pkg/mod/github.com/olivere/elastic/v7@v7.0.6/xpack_security_delete_role.go +pkg/mod/github.com/olivere/elastic/v7@v7.0.6/xpack_security_get_role_mapping_test.go +pkg/mod/github.com/olivere/elastic/v7@v7.0.6/xpack_security_get_role_mapping.go +pkg/mod/github.com/olivere/elastic/v7@v7.0.6/xpack_security_get_role_test.go +pkg/mod/github.com/olivere/elastic/v7@v7.0.6/xpack_security_get_role.go +pkg/mod/github.com/olivere/elastic/v7@v7.0.6/xpack_security_put_role_mapping_test.go +pkg/mod/github.com/olivere/elastic/v7@v7.0.6/xpack_security_put_role_mapping.go +pkg/mod/github.com/olivere/elastic/v7@v7.0.6/xpack_security_put_role_test.go +pkg/mod/github.com/olivere/elastic/v7@v7.0.6/xpack_security_put_role.go +pkg/mod/github.com/olivere/elastic/v7@v7.0.6/xpack_test.go +pkg/mod/github.com/olivere/elastic/v7@v7.0.6/xpack_watcher_ack_watch_test.go +pkg/mod/github.com/olivere/elastic/v7@v7.0.6/xpack_watcher_ack_watch.go +pkg/mod/github.com/olivere/elastic/v7@v7.0.6/xpack_watcher_activate_watch_test.go +pkg/mod/github.com/olivere/elastic/v7@v7.0.6/xpack_watcher_activate_watch.go +pkg/mod/github.com/olivere/elastic/v7@v7.0.6/xpack_watcher_deactivate_watch_test.go +pkg/mod/github.com/olivere/elastic/v7@v7.0.6/xpack_watcher_deactivate_watch.go +pkg/mod/github.com/olivere/elastic/v7@v7.0.6/xpack_watcher_delete_watch_test.go +pkg/mod/github.com/olivere/elastic/v7@v7.0.6/xpack_watcher_delete_watch.go +pkg/mod/github.com/olivere/elastic/v7@v7.0.6/xpack_watcher_execute_watch_test.go +pkg/mod/github.com/olivere/elastic/v7@v7.0.6/xpack_watcher_execute_watch.go +pkg/mod/github.com/olivere/elastic/v7@v7.0.6/xpack_watcher_get_watch_test.go +pkg/mod/github.com/olivere/elastic/v7@v7.0.6/xpack_watcher_get_watch.go +pkg/mod/github.com/olivere/elastic/v7@v7.0.6/xpack_watcher_put_watch_test.go +pkg/mod/github.com/olivere/elastic/v7@v7.0.6/xpack_watcher_put_watch.go +pkg/mod/github.com/olivere/elastic/v7@v7.0.6/xpack_watcher_start_test.go +pkg/mod/github.com/olivere/elastic/v7@v7.0.6/xpack_watcher_start.go +pkg/mod/github.com/olivere/elastic/v7@v7.0.6/xpack_watcher_stats_test.go +pkg/mod/github.com/olivere/elastic/v7@v7.0.6/xpack_watcher_stats.go +pkg/mod/github.com/olivere/elastic/v7@v7.0.6/xpack_watcher_stop_test.go +pkg/mod/github.com/olivere/elastic/v7@v7.0.6/xpack_watcher_stop.go +pkg/mod/github.com/olivere/elastic/v7@v7.0.6/aws/sign_v4_test.go +pkg/mod/github.com/olivere/elastic/v7@v7.0.6/aws/sign_v4.go +pkg/mod/github.com/olivere/elastic/v7@v7.0.6/aws/v4/aws_v4_test.go +pkg/mod/github.com/olivere/elastic/v7@v7.0.6/aws/v4/aws_v4.go +pkg/mod/github.com/olivere/elastic/v7@v7.0.6/aws/v4/CREDITS +pkg/mod/github.com/olivere/elastic/v7@v7.0.6/cluster-test/cluster-test.go +pkg/mod/github.com/olivere/elastic/v7@v7.0.6/cluster-test/Makefile +pkg/mod/github.com/olivere/elastic/v7@v7.0.6/cluster-test/README.md +pkg/mod/github.com/olivere/elastic/v7@v7.0.6/config/config_test.go +pkg/mod/github.com/olivere/elastic/v7@v7.0.6/config/config.go +pkg/mod/github.com/olivere/elastic/v7@v7.0.6/config/doc.go +pkg/mod/github.com/olivere/elastic/v7@v7.0.6/etc/elasticsearch.yml +pkg/mod/github.com/olivere/elastic/v7@v7.0.6/etc/jvm.options +pkg/mod/github.com/olivere/elastic/v7@v7.0.6/etc/log4j2.properties +pkg/mod/github.com/olivere/elastic/v7@v7.0.6/etc/ingest-geoip/.gitkeep +pkg/mod/github.com/olivere/elastic/v7@v7.0.6/etc/scripts/.gitkeep +pkg/mod/github.com/olivere/elastic/v7@v7.0.6/recipes/aws-connect/.gitignore +pkg/mod/github.com/olivere/elastic/v7@v7.0.6/recipes/aws-connect/main.go +pkg/mod/github.com/olivere/elastic/v7@v7.0.6/recipes/aws-connect-v4/.gitignore +pkg/mod/github.com/olivere/elastic/v7@v7.0.6/recipes/aws-connect-v4/main.go +pkg/mod/github.com/olivere/elastic/v7@v7.0.6/recipes/aws-mapping-v4/.gitignore +pkg/mod/github.com/olivere/elastic/v7@v7.0.6/recipes/aws-mapping-v4/main.go +pkg/mod/github.com/olivere/elastic/v7@v7.0.6/recipes/bulk_insert/.gitignore +pkg/mod/github.com/olivere/elastic/v7@v7.0.6/recipes/bulk_insert/bulk_insert.go +pkg/mod/github.com/olivere/elastic/v7@v7.0.6/recipes/bulk_processor/.gitignore +pkg/mod/github.com/olivere/elastic/v7@v7.0.6/recipes/bulk_processor/main.go +pkg/mod/github.com/olivere/elastic/v7@v7.0.6/recipes/connect/.gitignore +pkg/mod/github.com/olivere/elastic/v7@v7.0.6/recipes/connect/connect.go +pkg/mod/github.com/olivere/elastic/v7@v7.0.6/recipes/connect_with_config/.gitignore +pkg/mod/github.com/olivere/elastic/v7@v7.0.6/recipes/connect_with_config/connect_with_config.go +pkg/mod/github.com/olivere/elastic/v7@v7.0.6/recipes/go-modules/.gitignore +pkg/mod/github.com/olivere/elastic/v7@v7.0.6/recipes/go-modules/main.go +pkg/mod/github.com/olivere/elastic/v7@v7.0.6/recipes/mapping/.gitignore +pkg/mod/github.com/olivere/elastic/v7@v7.0.6/recipes/mapping/mapping.go +pkg/mod/github.com/olivere/elastic/v7@v7.0.6/recipes/middleware/.gitignore +pkg/mod/github.com/olivere/elastic/v7@v7.0.6/recipes/middleware/main.go +pkg/mod/github.com/olivere/elastic/v7@v7.0.6/recipes/scroll/.gitignore +pkg/mod/github.com/olivere/elastic/v7@v7.0.6/recipes/scroll/scroll.go +pkg/mod/github.com/olivere/elastic/v7@v7.0.6/recipes/sliced_scroll/.gitignore +pkg/mod/github.com/olivere/elastic/v7@v7.0.6/recipes/sliced_scroll/sliced_scroll.go +pkg/mod/github.com/olivere/elastic/v7@v7.0.6/recipes/suggesters/completion/.gitignore +pkg/mod/github.com/olivere/elastic/v7@v7.0.6/recipes/suggesters/completion/main.go +pkg/mod/github.com/olivere/elastic/v7@v7.0.6/recipes/text_vs_keyword/main.go +pkg/mod/github.com/olivere/elastic/v7@v7.0.6/recipes/tracing/.gitignore +pkg/mod/github.com/olivere/elastic/v7@v7.0.6/recipes/tracing/README.md +pkg/mod/github.com/olivere/elastic/v7@v7.0.6/recipes/tracing/run-tracer.sh +pkg/mod/github.com/olivere/elastic/v7@v7.0.6/recipes/tracing/tracing.go +pkg/mod/github.com/olivere/elastic/v7@v7.0.6/trace/opencensus/transport_test.go +pkg/mod/github.com/olivere/elastic/v7@v7.0.6/trace/opencensus/transport.go +pkg/mod/github.com/olivere/elastic/v7@v7.0.6/trace/opencensus/util.go +pkg/mod/github.com/olivere/elastic/v7@v7.0.6/trace/opentracing/transport_test.go +pkg/mod/github.com/olivere/elastic/v7@v7.0.6/trace/opentracing/transport.go +pkg/mod/github.com/olivere/elastic/v7@v7.0.6/trace/opentracing/util.go +pkg/mod/github.com/olivere/elastic/v7@v7.0.6/uritemplates/LICENSE +pkg/mod/github.com/olivere/elastic/v7@v7.0.6/uritemplates/uritemplates.go +pkg/mod/github.com/olivere/elastic/v7@v7.0.6/uritemplates/utils_test.go +pkg/mod/github.com/olivere/elastic/v7@v7.0.6/uritemplates/utils.go +pkg/mod/github.com/pkg/errors@v0.8.1/.gitignore +pkg/mod/github.com/pkg/errors@v0.8.1/.travis.yml +pkg/mod/github.com/pkg/errors@v0.8.1/appveyor.yml +pkg/mod/github.com/pkg/errors@v0.8.1/bench_test.go +pkg/mod/github.com/pkg/errors@v0.8.1/errors_test.go +pkg/mod/github.com/pkg/errors@v0.8.1/errors.go +pkg/mod/github.com/pkg/errors@v0.8.1/example_test.go +pkg/mod/github.com/pkg/errors@v0.8.1/format_test.go +pkg/mod/github.com/pkg/errors@v0.8.1/LICENSE +pkg/mod/github.com/pkg/errors@v0.8.1/README.md +pkg/mod/github.com/pkg/errors@v0.8.1/stack_test.go +pkg/mod/github.com/pkg/errors@v0.8.1/stack.go +pkg/mod/github.com/pmezard/go-difflib@v1.0.0/.travis.yml +pkg/mod/github.com/pmezard/go-difflib@v1.0.0/LICENSE +pkg/mod/github.com/pmezard/go-difflib@v1.0.0/README.md +pkg/mod/github.com/pmezard/go-difflib@v1.0.0/difflib/difflib_test.go +pkg/mod/github.com/pmezard/go-difflib@v1.0.0/difflib/difflib.go +pkg/mod/github.com/prometheus/client_golang@v1.5.1/.gitignore +pkg/mod/github.com/prometheus/client_golang@v1.5.1/.golangci.yml +pkg/mod/github.com/prometheus/client_golang@v1.5.1/.travis.yml +pkg/mod/github.com/prometheus/client_golang@v1.5.1/CHANGELOG.md +pkg/mod/github.com/prometheus/client_golang@v1.5.1/CONTRIBUTING.md +pkg/mod/github.com/prometheus/client_golang@v1.5.1/Dockerfile +pkg/mod/github.com/prometheus/client_golang@v1.5.1/go.mod +pkg/mod/github.com/prometheus/client_golang@v1.5.1/go.sum +pkg/mod/github.com/prometheus/client_golang@v1.5.1/LICENSE +pkg/mod/github.com/prometheus/client_golang@v1.5.1/MAINTAINERS.md +pkg/mod/github.com/prometheus/client_golang@v1.5.1/Makefile +pkg/mod/github.com/prometheus/client_golang@v1.5.1/Makefile.common +pkg/mod/github.com/prometheus/client_golang@v1.5.1/NOTICE +pkg/mod/github.com/prometheus/client_golang@v1.5.1/README.md +pkg/mod/github.com/prometheus/client_golang@v1.5.1/VERSION +pkg/mod/github.com/prometheus/client_golang@v1.5.1/.circleci/config.yml +pkg/mod/github.com/prometheus/client_golang@v1.5.1/.github/ISSUE_TEMPLATE.md +pkg/mod/github.com/prometheus/client_golang@v1.5.1/api/client_test.go +pkg/mod/github.com/prometheus/client_golang@v1.5.1/api/client.go +pkg/mod/github.com/prometheus/client_golang@v1.5.1/api/prometheus/v1/api_bench_test.go +pkg/mod/github.com/prometheus/client_golang@v1.5.1/api/prometheus/v1/api_test.go +pkg/mod/github.com/prometheus/client_golang@v1.5.1/api/prometheus/v1/api.go +pkg/mod/github.com/prometheus/client_golang@v1.5.1/api/prometheus/v1/example_test.go +pkg/mod/github.com/prometheus/client_golang@v1.5.1/examples/random/main.go +pkg/mod/github.com/prometheus/client_golang@v1.5.1/examples/simple/main.go +pkg/mod/github.com/prometheus/client_golang@v1.5.1/prometheus/.gitignore +pkg/mod/github.com/prometheus/client_golang@v1.5.1/prometheus/benchmark_test.go +pkg/mod/github.com/prometheus/client_golang@v1.5.1/prometheus/build_info_pre_1.12.go +pkg/mod/github.com/prometheus/client_golang@v1.5.1/prometheus/build_info.go +pkg/mod/github.com/prometheus/client_golang@v1.5.1/prometheus/collector_test.go +pkg/mod/github.com/prometheus/client_golang@v1.5.1/prometheus/collector.go +pkg/mod/github.com/prometheus/client_golang@v1.5.1/prometheus/counter_test.go +pkg/mod/github.com/prometheus/client_golang@v1.5.1/prometheus/counter.go +pkg/mod/github.com/prometheus/client_golang@v1.5.1/prometheus/desc_test.go +pkg/mod/github.com/prometheus/client_golang@v1.5.1/prometheus/desc.go +pkg/mod/github.com/prometheus/client_golang@v1.5.1/prometheus/doc.go +pkg/mod/github.com/prometheus/client_golang@v1.5.1/prometheus/example_clustermanager_test.go +pkg/mod/github.com/prometheus/client_golang@v1.5.1/prometheus/example_timer_complex_test.go +pkg/mod/github.com/prometheus/client_golang@v1.5.1/prometheus/example_timer_gauge_test.go +pkg/mod/github.com/prometheus/client_golang@v1.5.1/prometheus/example_timer_test.go +pkg/mod/github.com/prometheus/client_golang@v1.5.1/prometheus/examples_test.go +pkg/mod/github.com/prometheus/client_golang@v1.5.1/prometheus/expvar_collector_test.go +pkg/mod/github.com/prometheus/client_golang@v1.5.1/prometheus/expvar_collector.go +pkg/mod/github.com/prometheus/client_golang@v1.5.1/prometheus/fnv.go +pkg/mod/github.com/prometheus/client_golang@v1.5.1/prometheus/gauge_test.go +pkg/mod/github.com/prometheus/client_golang@v1.5.1/prometheus/gauge.go +pkg/mod/github.com/prometheus/client_golang@v1.5.1/prometheus/go_collector_test.go +pkg/mod/github.com/prometheus/client_golang@v1.5.1/prometheus/go_collector.go +pkg/mod/github.com/prometheus/client_golang@v1.5.1/prometheus/histogram_test.go +pkg/mod/github.com/prometheus/client_golang@v1.5.1/prometheus/histogram.go +pkg/mod/github.com/prometheus/client_golang@v1.5.1/prometheus/labels.go +pkg/mod/github.com/prometheus/client_golang@v1.5.1/prometheus/metric_test.go +pkg/mod/github.com/prometheus/client_golang@v1.5.1/prometheus/metric.go +pkg/mod/github.com/prometheus/client_golang@v1.5.1/prometheus/observer.go +pkg/mod/github.com/prometheus/client_golang@v1.5.1/prometheus/process_collector_other.go +pkg/mod/github.com/prometheus/client_golang@v1.5.1/prometheus/process_collector_test.go +pkg/mod/github.com/prometheus/client_golang@v1.5.1/prometheus/process_collector_windows_test.go +pkg/mod/github.com/prometheus/client_golang@v1.5.1/prometheus/process_collector_windows.go +pkg/mod/github.com/prometheus/client_golang@v1.5.1/prometheus/process_collector.go +pkg/mod/github.com/prometheus/client_golang@v1.5.1/prometheus/README.md +pkg/mod/github.com/prometheus/client_golang@v1.5.1/prometheus/registry_test.go +pkg/mod/github.com/prometheus/client_golang@v1.5.1/prometheus/registry.go +pkg/mod/github.com/prometheus/client_golang@v1.5.1/prometheus/summary_test.go +pkg/mod/github.com/prometheus/client_golang@v1.5.1/prometheus/summary.go +pkg/mod/github.com/prometheus/client_golang@v1.5.1/prometheus/timer_test.go +pkg/mod/github.com/prometheus/client_golang@v1.5.1/prometheus/timer.go +pkg/mod/github.com/prometheus/client_golang@v1.5.1/prometheus/untyped.go +pkg/mod/github.com/prometheus/client_golang@v1.5.1/prometheus/value_test.go +pkg/mod/github.com/prometheus/client_golang@v1.5.1/prometheus/value.go +pkg/mod/github.com/prometheus/client_golang@v1.5.1/prometheus/vec_test.go +pkg/mod/github.com/prometheus/client_golang@v1.5.1/prometheus/vec.go +pkg/mod/github.com/prometheus/client_golang@v1.5.1/prometheus/wrap_test.go +pkg/mod/github.com/prometheus/client_golang@v1.5.1/prometheus/wrap.go +pkg/mod/github.com/prometheus/client_golang@v1.5.1/prometheus/graphite/bridge_test.go +pkg/mod/github.com/prometheus/client_golang@v1.5.1/prometheus/graphite/bridge.go +pkg/mod/github.com/prometheus/client_golang@v1.5.1/prometheus/internal/metric.go +pkg/mod/github.com/prometheus/client_golang@v1.5.1/prometheus/promauto/auto.go +pkg/mod/github.com/prometheus/client_golang@v1.5.1/prometheus/promhttp/delegator.go +pkg/mod/github.com/prometheus/client_golang@v1.5.1/prometheus/promhttp/http_test.go +pkg/mod/github.com/prometheus/client_golang@v1.5.1/prometheus/promhttp/http.go +pkg/mod/github.com/prometheus/client_golang@v1.5.1/prometheus/promhttp/instrument_client_test.go +pkg/mod/github.com/prometheus/client_golang@v1.5.1/prometheus/promhttp/instrument_client.go +pkg/mod/github.com/prometheus/client_golang@v1.5.1/prometheus/promhttp/instrument_server_test.go +pkg/mod/github.com/prometheus/client_golang@v1.5.1/prometheus/promhttp/instrument_server.go +pkg/mod/github.com/prometheus/client_golang@v1.5.1/prometheus/push/example_add_from_gatherer_test.go +pkg/mod/github.com/prometheus/client_golang@v1.5.1/prometheus/push/examples_test.go +pkg/mod/github.com/prometheus/client_golang@v1.5.1/prometheus/push/push_test.go +pkg/mod/github.com/prometheus/client_golang@v1.5.1/prometheus/push/push.go +pkg/mod/github.com/prometheus/client_golang@v1.5.1/prometheus/testutil/testutil_test.go +pkg/mod/github.com/prometheus/client_golang@v1.5.1/prometheus/testutil/testutil.go +pkg/mod/github.com/prometheus/client_model@v0.2.0/.gitignore +pkg/mod/github.com/prometheus/client_model@v0.2.0/CONTRIBUTING.md +pkg/mod/github.com/prometheus/client_model@v0.2.0/go.mod +pkg/mod/github.com/prometheus/client_model@v0.2.0/go.sum +pkg/mod/github.com/prometheus/client_model@v0.2.0/LICENSE +pkg/mod/github.com/prometheus/client_model@v0.2.0/MAINTAINERS.md +pkg/mod/github.com/prometheus/client_model@v0.2.0/Makefile +pkg/mod/github.com/prometheus/client_model@v0.2.0/metrics.proto +pkg/mod/github.com/prometheus/client_model@v0.2.0/NOTICE +pkg/mod/github.com/prometheus/client_model@v0.2.0/README.md +pkg/mod/github.com/prometheus/client_model@v0.2.0/go/metrics.pb.go +pkg/mod/github.com/prometheus/common@v0.9.1/.gitignore +pkg/mod/github.com/prometheus/common@v0.9.1/.travis.yml +pkg/mod/github.com/prometheus/common@v0.9.1/CONTRIBUTING.md +pkg/mod/github.com/prometheus/common@v0.9.1/go.mod +pkg/mod/github.com/prometheus/common@v0.9.1/go.sum +pkg/mod/github.com/prometheus/common@v0.9.1/LICENSE +pkg/mod/github.com/prometheus/common@v0.9.1/MAINTAINERS.md +pkg/mod/github.com/prometheus/common@v0.9.1/Makefile +pkg/mod/github.com/prometheus/common@v0.9.1/Makefile.common +pkg/mod/github.com/prometheus/common@v0.9.1/NOTICE +pkg/mod/github.com/prometheus/common@v0.9.1/README.md +pkg/mod/github.com/prometheus/common@v0.9.1/config/config.go +pkg/mod/github.com/prometheus/common@v0.9.1/config/http_config_test.go +pkg/mod/github.com/prometheus/common@v0.9.1/config/http_config.go +pkg/mod/github.com/prometheus/common@v0.9.1/config/tls_config_test.go +pkg/mod/github.com/prometheus/common@v0.9.1/config/testdata/basic-auth-password +pkg/mod/github.com/prometheus/common@v0.9.1/config/testdata/bearer.token +pkg/mod/github.com/prometheus/common@v0.9.1/config/testdata/client-no-pass.key +pkg/mod/github.com/prometheus/common@v0.9.1/config/testdata/client.crt +pkg/mod/github.com/prometheus/common@v0.9.1/config/testdata/empty +pkg/mod/github.com/prometheus/common@v0.9.1/config/testdata/http.conf.basic-auth.good.yaml +pkg/mod/github.com/prometheus/common@v0.9.1/config/testdata/http.conf.basic-auth.no-password.yaml +pkg/mod/github.com/prometheus/common@v0.9.1/config/testdata/http.conf.basic-auth.no-username.yaml +pkg/mod/github.com/prometheus/common@v0.9.1/config/testdata/http.conf.basic-auth.too-much.bad.yaml +pkg/mod/github.com/prometheus/common@v0.9.1/config/testdata/http.conf.bearer-token-and-file-set.bad.yml +pkg/mod/github.com/prometheus/common@v0.9.1/config/testdata/http.conf.empty.bad.yml +pkg/mod/github.com/prometheus/common@v0.9.1/config/testdata/http.conf.good.yml +pkg/mod/github.com/prometheus/common@v0.9.1/config/testdata/http.conf.invalid-bearer-token-file.bad.yml +pkg/mod/github.com/prometheus/common@v0.9.1/config/testdata/self-signed-client.crt +pkg/mod/github.com/prometheus/common@v0.9.1/config/testdata/self-signed-client.key +pkg/mod/github.com/prometheus/common@v0.9.1/config/testdata/server.crt +pkg/mod/github.com/prometheus/common@v0.9.1/config/testdata/server.key +pkg/mod/github.com/prometheus/common@v0.9.1/config/testdata/tls_config.cert_no_key.bad.yml +pkg/mod/github.com/prometheus/common@v0.9.1/config/testdata/tls_config.empty.good.yml +pkg/mod/github.com/prometheus/common@v0.9.1/config/testdata/tls_config.insecure.good.yml +pkg/mod/github.com/prometheus/common@v0.9.1/config/testdata/tls_config.invalid_field.bad.yml +pkg/mod/github.com/prometheus/common@v0.9.1/config/testdata/tls_config.key_no_cert.bad.yml +pkg/mod/github.com/prometheus/common@v0.9.1/config/testdata/tls-ca-chain.pem +pkg/mod/github.com/prometheus/common@v0.9.1/expfmt/bench_test.go +pkg/mod/github.com/prometheus/common@v0.9.1/expfmt/decode_test.go +pkg/mod/github.com/prometheus/common@v0.9.1/expfmt/decode.go +pkg/mod/github.com/prometheus/common@v0.9.1/expfmt/encode_test.go +pkg/mod/github.com/prometheus/common@v0.9.1/expfmt/encode.go +pkg/mod/github.com/prometheus/common@v0.9.1/expfmt/expfmt.go +pkg/mod/github.com/prometheus/common@v0.9.1/expfmt/fuzz.go +pkg/mod/github.com/prometheus/common@v0.9.1/expfmt/openmetrics_create_test.go +pkg/mod/github.com/prometheus/common@v0.9.1/expfmt/openmetrics_create.go +pkg/mod/github.com/prometheus/common@v0.9.1/expfmt/text_create_test.go +pkg/mod/github.com/prometheus/common@v0.9.1/expfmt/text_create.go +pkg/mod/github.com/prometheus/common@v0.9.1/expfmt/text_parse_test.go +pkg/mod/github.com/prometheus/common@v0.9.1/expfmt/text_parse.go +pkg/mod/github.com/prometheus/common@v0.9.1/expfmt/fuzz/corpus/from_test_parse_0 +pkg/mod/github.com/prometheus/common@v0.9.1/expfmt/fuzz/corpus/from_test_parse_1 +pkg/mod/github.com/prometheus/common@v0.9.1/expfmt/fuzz/corpus/from_test_parse_2 +pkg/mod/github.com/prometheus/common@v0.9.1/expfmt/fuzz/corpus/from_test_parse_3 +pkg/mod/github.com/prometheus/common@v0.9.1/expfmt/fuzz/corpus/from_test_parse_4 +pkg/mod/github.com/prometheus/common@v0.9.1/expfmt/fuzz/corpus/from_test_parse_error_0 +pkg/mod/github.com/prometheus/common@v0.9.1/expfmt/fuzz/corpus/from_test_parse_error_1 +pkg/mod/github.com/prometheus/common@v0.9.1/expfmt/fuzz/corpus/from_test_parse_error_2 +pkg/mod/github.com/prometheus/common@v0.9.1/expfmt/fuzz/corpus/from_test_parse_error_3 +pkg/mod/github.com/prometheus/common@v0.9.1/expfmt/fuzz/corpus/from_test_parse_error_4 +pkg/mod/github.com/prometheus/common@v0.9.1/expfmt/fuzz/corpus/from_test_parse_error_5 +pkg/mod/github.com/prometheus/common@v0.9.1/expfmt/fuzz/corpus/from_test_parse_error_6 +pkg/mod/github.com/prometheus/common@v0.9.1/expfmt/fuzz/corpus/from_test_parse_error_7 +pkg/mod/github.com/prometheus/common@v0.9.1/expfmt/fuzz/corpus/from_test_parse_error_8 +pkg/mod/github.com/prometheus/common@v0.9.1/expfmt/fuzz/corpus/from_test_parse_error_9 +pkg/mod/github.com/prometheus/common@v0.9.1/expfmt/fuzz/corpus/from_test_parse_error_10 +pkg/mod/github.com/prometheus/common@v0.9.1/expfmt/fuzz/corpus/from_test_parse_error_11 +pkg/mod/github.com/prometheus/common@v0.9.1/expfmt/fuzz/corpus/from_test_parse_error_12 +pkg/mod/github.com/prometheus/common@v0.9.1/expfmt/fuzz/corpus/from_test_parse_error_13 +pkg/mod/github.com/prometheus/common@v0.9.1/expfmt/fuzz/corpus/from_test_parse_error_14 +pkg/mod/github.com/prometheus/common@v0.9.1/expfmt/fuzz/corpus/from_test_parse_error_15 +pkg/mod/github.com/prometheus/common@v0.9.1/expfmt/fuzz/corpus/from_test_parse_error_16 +pkg/mod/github.com/prometheus/common@v0.9.1/expfmt/fuzz/corpus/from_test_parse_error_17 +pkg/mod/github.com/prometheus/common@v0.9.1/expfmt/fuzz/corpus/from_test_parse_error_18 +pkg/mod/github.com/prometheus/common@v0.9.1/expfmt/fuzz/corpus/from_test_parse_error_19 +pkg/mod/github.com/prometheus/common@v0.9.1/expfmt/fuzz/corpus/minimal +pkg/mod/github.com/prometheus/common@v0.9.1/expfmt/testdata/json2 +pkg/mod/github.com/prometheus/common@v0.9.1/expfmt/testdata/json2_bad +pkg/mod/github.com/prometheus/common@v0.9.1/expfmt/testdata/protobuf +pkg/mod/github.com/prometheus/common@v0.9.1/expfmt/testdata/protobuf.gz +pkg/mod/github.com/prometheus/common@v0.9.1/expfmt/testdata/text +pkg/mod/github.com/prometheus/common@v0.9.1/expfmt/testdata/text.gz +pkg/mod/github.com/prometheus/common@v0.9.1/internal/bitbucket.org/ww/goautoneg/autoneg_test.go +pkg/mod/github.com/prometheus/common@v0.9.1/internal/bitbucket.org/ww/goautoneg/autoneg.go +pkg/mod/github.com/prometheus/common@v0.9.1/internal/bitbucket.org/ww/goautoneg/README.txt +pkg/mod/github.com/prometheus/common@v0.9.1/log/eventlog_formatter.go +pkg/mod/github.com/prometheus/common@v0.9.1/log/log_test.go +pkg/mod/github.com/prometheus/common@v0.9.1/log/log.go +pkg/mod/github.com/prometheus/common@v0.9.1/log/syslog_formatter_test.go +pkg/mod/github.com/prometheus/common@v0.9.1/log/syslog_formatter.go +pkg/mod/github.com/prometheus/common@v0.9.1/model/alert_test.go +pkg/mod/github.com/prometheus/common@v0.9.1/model/alert.go +pkg/mod/github.com/prometheus/common@v0.9.1/model/fingerprinting_test.go +pkg/mod/github.com/prometheus/common@v0.9.1/model/fingerprinting.go +pkg/mod/github.com/prometheus/common@v0.9.1/model/fnv.go +pkg/mod/github.com/prometheus/common@v0.9.1/model/labels_test.go +pkg/mod/github.com/prometheus/common@v0.9.1/model/labels.go +pkg/mod/github.com/prometheus/common@v0.9.1/model/labelset_test.go +pkg/mod/github.com/prometheus/common@v0.9.1/model/labelset.go +pkg/mod/github.com/prometheus/common@v0.9.1/model/metric_test.go +pkg/mod/github.com/prometheus/common@v0.9.1/model/metric.go +pkg/mod/github.com/prometheus/common@v0.9.1/model/model.go +pkg/mod/github.com/prometheus/common@v0.9.1/model/signature_test.go +pkg/mod/github.com/prometheus/common@v0.9.1/model/signature.go +pkg/mod/github.com/prometheus/common@v0.9.1/model/silence_test.go +pkg/mod/github.com/prometheus/common@v0.9.1/model/silence.go +pkg/mod/github.com/prometheus/common@v0.9.1/model/time_test.go +pkg/mod/github.com/prometheus/common@v0.9.1/model/time.go +pkg/mod/github.com/prometheus/common@v0.9.1/model/value_test.go +pkg/mod/github.com/prometheus/common@v0.9.1/model/value.go +pkg/mod/github.com/prometheus/common@v0.9.1/promlog/log.go +pkg/mod/github.com/prometheus/common@v0.9.1/promlog/flag/flag.go +pkg/mod/github.com/prometheus/common@v0.9.1/route/route_test.go +pkg/mod/github.com/prometheus/common@v0.9.1/route/route.go +pkg/mod/github.com/prometheus/common@v0.9.1/server/static_file_server_test.go +pkg/mod/github.com/prometheus/common@v0.9.1/server/static_file_server.go +pkg/mod/github.com/prometheus/common@v0.9.1/version/info.go +pkg/mod/github.com/prometheus/procfs@v0.0.8/.gitignore +pkg/mod/github.com/prometheus/procfs@v0.0.8/.golangci.yml +pkg/mod/github.com/prometheus/procfs@v0.0.8/arp_test.go +pkg/mod/github.com/prometheus/procfs@v0.0.8/arp.go +pkg/mod/github.com/prometheus/procfs@v0.0.8/buddyinfo_test.go +pkg/mod/github.com/prometheus/procfs@v0.0.8/buddyinfo.go +pkg/mod/github.com/prometheus/procfs@v0.0.8/CONTRIBUTING.md +pkg/mod/github.com/prometheus/procfs@v0.0.8/cpuinfo_test.go +pkg/mod/github.com/prometheus/procfs@v0.0.8/cpuinfo.go +pkg/mod/github.com/prometheus/procfs@v0.0.8/crypto_test.go +pkg/mod/github.com/prometheus/procfs@v0.0.8/crypto.go +pkg/mod/github.com/prometheus/procfs@v0.0.8/doc.go +pkg/mod/github.com/prometheus/procfs@v0.0.8/fixtures.ttar +pkg/mod/github.com/prometheus/procfs@v0.0.8/fs_test.go +pkg/mod/github.com/prometheus/procfs@v0.0.8/fs.go +pkg/mod/github.com/prometheus/procfs@v0.0.8/go.mod +pkg/mod/github.com/prometheus/procfs@v0.0.8/go.sum +pkg/mod/github.com/prometheus/procfs@v0.0.8/ipvs_test.go +pkg/mod/github.com/prometheus/procfs@v0.0.8/ipvs.go +pkg/mod/github.com/prometheus/procfs@v0.0.8/LICENSE +pkg/mod/github.com/prometheus/procfs@v0.0.8/MAINTAINERS.md +pkg/mod/github.com/prometheus/procfs@v0.0.8/Makefile +pkg/mod/github.com/prometheus/procfs@v0.0.8/Makefile.common +pkg/mod/github.com/prometheus/procfs@v0.0.8/mdstat_test.go +pkg/mod/github.com/prometheus/procfs@v0.0.8/mdstat.go +pkg/mod/github.com/prometheus/procfs@v0.0.8/meminfo_test.go +pkg/mod/github.com/prometheus/procfs@v0.0.8/meminfo.go +pkg/mod/github.com/prometheus/procfs@v0.0.8/mountinfo_test.go +pkg/mod/github.com/prometheus/procfs@v0.0.8/mountinfo.go +pkg/mod/github.com/prometheus/procfs@v0.0.8/mountstats_test.go +pkg/mod/github.com/prometheus/procfs@v0.0.8/mountstats.go +pkg/mod/github.com/prometheus/procfs@v0.0.8/net_dev_test.go +pkg/mod/github.com/prometheus/procfs@v0.0.8/net_dev.go +pkg/mod/github.com/prometheus/procfs@v0.0.8/net_sockstat_test.go +pkg/mod/github.com/prometheus/procfs@v0.0.8/net_sockstat.go +pkg/mod/github.com/prometheus/procfs@v0.0.8/net_softnet_test.go +pkg/mod/github.com/prometheus/procfs@v0.0.8/net_softnet.go +pkg/mod/github.com/prometheus/procfs@v0.0.8/net_unix_test.go +pkg/mod/github.com/prometheus/procfs@v0.0.8/net_unix.go +pkg/mod/github.com/prometheus/procfs@v0.0.8/NOTICE +pkg/mod/github.com/prometheus/procfs@v0.0.8/proc_environ_test.go +pkg/mod/github.com/prometheus/procfs@v0.0.8/proc_environ.go +pkg/mod/github.com/prometheus/procfs@v0.0.8/proc_fdinfo_test.go +pkg/mod/github.com/prometheus/procfs@v0.0.8/proc_fdinfo.go +pkg/mod/github.com/prometheus/procfs@v0.0.8/proc_io_test.go +pkg/mod/github.com/prometheus/procfs@v0.0.8/proc_io.go +pkg/mod/github.com/prometheus/procfs@v0.0.8/proc_limits_test.go +pkg/mod/github.com/prometheus/procfs@v0.0.8/proc_limits.go +pkg/mod/github.com/prometheus/procfs@v0.0.8/proc_ns_test.go +pkg/mod/github.com/prometheus/procfs@v0.0.8/proc_ns.go +pkg/mod/github.com/prometheus/procfs@v0.0.8/proc_psi_test.go +pkg/mod/github.com/prometheus/procfs@v0.0.8/proc_psi.go +pkg/mod/github.com/prometheus/procfs@v0.0.8/proc_stat_test.go +pkg/mod/github.com/prometheus/procfs@v0.0.8/proc_stat.go +pkg/mod/github.com/prometheus/procfs@v0.0.8/proc_status_test.go +pkg/mod/github.com/prometheus/procfs@v0.0.8/proc_status.go +pkg/mod/github.com/prometheus/procfs@v0.0.8/proc_test.go +pkg/mod/github.com/prometheus/procfs@v0.0.8/proc.go +pkg/mod/github.com/prometheus/procfs@v0.0.8/README.md +pkg/mod/github.com/prometheus/procfs@v0.0.8/schedstat_test.go +pkg/mod/github.com/prometheus/procfs@v0.0.8/schedstat.go +pkg/mod/github.com/prometheus/procfs@v0.0.8/stat_test.go +pkg/mod/github.com/prometheus/procfs@v0.0.8/stat.go +pkg/mod/github.com/prometheus/procfs@v0.0.8/ttar +pkg/mod/github.com/prometheus/procfs@v0.0.8/vm_test.go +pkg/mod/github.com/prometheus/procfs@v0.0.8/vm.go +pkg/mod/github.com/prometheus/procfs@v0.0.8/xfrm_test.go +pkg/mod/github.com/prometheus/procfs@v0.0.8/xfrm.go +pkg/mod/github.com/prometheus/procfs@v0.0.8/zoneinfo_test.go +pkg/mod/github.com/prometheus/procfs@v0.0.8/zoneinfo.go +pkg/mod/github.com/prometheus/procfs@v0.0.8/.circleci/config.yml +pkg/mod/github.com/prometheus/procfs@v0.0.8/bcache/bcache.go +pkg/mod/github.com/prometheus/procfs@v0.0.8/bcache/get_test.go +pkg/mod/github.com/prometheus/procfs@v0.0.8/bcache/get.go +pkg/mod/github.com/prometheus/procfs@v0.0.8/blockdevice/stats_test.go +pkg/mod/github.com/prometheus/procfs@v0.0.8/blockdevice/stats.go +pkg/mod/github.com/prometheus/procfs@v0.0.8/btrfs/btrfs.go +pkg/mod/github.com/prometheus/procfs@v0.0.8/btrfs/get_test.go +pkg/mod/github.com/prometheus/procfs@v0.0.8/btrfs/get.go +pkg/mod/github.com/prometheus/procfs@v0.0.8/internal/fs/fs_test.go +pkg/mod/github.com/prometheus/procfs@v0.0.8/internal/fs/fs.go +pkg/mod/github.com/prometheus/procfs@v0.0.8/internal/util/parse.go +pkg/mod/github.com/prometheus/procfs@v0.0.8/internal/util/readfile.go +pkg/mod/github.com/prometheus/procfs@v0.0.8/internal/util/sysreadfile_compat.go +pkg/mod/github.com/prometheus/procfs@v0.0.8/internal/util/sysreadfile.go +pkg/mod/github.com/prometheus/procfs@v0.0.8/internal/util/valueparser_test.go +pkg/mod/github.com/prometheus/procfs@v0.0.8/internal/util/valueparser.go +pkg/mod/github.com/prometheus/procfs@v0.0.8/iscsi/get_test.go +pkg/mod/github.com/prometheus/procfs@v0.0.8/iscsi/get.go +pkg/mod/github.com/prometheus/procfs@v0.0.8/iscsi/iscsi.go +pkg/mod/github.com/prometheus/procfs@v0.0.8/nfs/nfs.go +pkg/mod/github.com/prometheus/procfs@v0.0.8/nfs/parse_nfs_test.go +pkg/mod/github.com/prometheus/procfs@v0.0.8/nfs/parse_nfs.go +pkg/mod/github.com/prometheus/procfs@v0.0.8/nfs/parse_nfsd_test.go +pkg/mod/github.com/prometheus/procfs@v0.0.8/nfs/parse_nfsd.go +pkg/mod/github.com/prometheus/procfs@v0.0.8/nfs/parse.go +pkg/mod/github.com/prometheus/procfs@v0.0.8/scripts/check_license.sh +pkg/mod/github.com/prometheus/procfs@v0.0.8/sysfs/.gitignore +pkg/mod/github.com/prometheus/procfs@v0.0.8/sysfs/class_cooling_device_test.go +pkg/mod/github.com/prometheus/procfs@v0.0.8/sysfs/class_cooling_device.go +pkg/mod/github.com/prometheus/procfs@v0.0.8/sysfs/class_infiniband_test.go +pkg/mod/github.com/prometheus/procfs@v0.0.8/sysfs/class_infiniband.go +pkg/mod/github.com/prometheus/procfs@v0.0.8/sysfs/class_power_supply_test.go +pkg/mod/github.com/prometheus/procfs@v0.0.8/sysfs/class_power_supply.go +pkg/mod/github.com/prometheus/procfs@v0.0.8/sysfs/class_powercap_test.go +pkg/mod/github.com/prometheus/procfs@v0.0.8/sysfs/class_powercap.go +pkg/mod/github.com/prometheus/procfs@v0.0.8/sysfs/class_thermal_test.go +pkg/mod/github.com/prometheus/procfs@v0.0.8/sysfs/class_thermal.go +pkg/mod/github.com/prometheus/procfs@v0.0.8/sysfs/clocksource_test.go +pkg/mod/github.com/prometheus/procfs@v0.0.8/sysfs/clocksource.go +pkg/mod/github.com/prometheus/procfs@v0.0.8/sysfs/doc.go +pkg/mod/github.com/prometheus/procfs@v0.0.8/sysfs/fs_test.go +pkg/mod/github.com/prometheus/procfs@v0.0.8/sysfs/fs.go +pkg/mod/github.com/prometheus/procfs@v0.0.8/sysfs/net_class_test.go +pkg/mod/github.com/prometheus/procfs@v0.0.8/sysfs/net_class.go +pkg/mod/github.com/prometheus/procfs@v0.0.8/sysfs/system_cpu_test.go +pkg/mod/github.com/prometheus/procfs@v0.0.8/sysfs/system_cpu.go +pkg/mod/github.com/prometheus/procfs@v0.0.8/sysfs/vulnerability.go +pkg/mod/github.com/prometheus/procfs@v0.0.8/xfs/parse_test.go +pkg/mod/github.com/prometheus/procfs@v0.0.8/xfs/parse.go +pkg/mod/github.com/prometheus/procfs@v0.0.8/xfs/xfs_test.go +pkg/mod/github.com/prometheus/procfs@v0.0.8/xfs/xfs.go +pkg/mod/github.com/stretchr/testify@v1.5.1/.gitignore +pkg/mod/github.com/stretchr/testify@v1.5.1/.travis.gofmt.sh +pkg/mod/github.com/stretchr/testify@v1.5.1/.travis.gogenerate.sh +pkg/mod/github.com/stretchr/testify@v1.5.1/.travis.govet.sh +pkg/mod/github.com/stretchr/testify@v1.5.1/.travis.yml +pkg/mod/github.com/stretchr/testify@v1.5.1/CONTRIBUTING.md +pkg/mod/github.com/stretchr/testify@v1.5.1/doc.go +pkg/mod/github.com/stretchr/testify@v1.5.1/go.mod +pkg/mod/github.com/stretchr/testify@v1.5.1/go.sum +pkg/mod/github.com/stretchr/testify@v1.5.1/LICENSE +pkg/mod/github.com/stretchr/testify@v1.5.1/MAINTAINERS.md +pkg/mod/github.com/stretchr/testify@v1.5.1/package_test.go +pkg/mod/github.com/stretchr/testify@v1.5.1/README.md +pkg/mod/github.com/stretchr/testify@v1.5.1/assert/assertion_format.go +pkg/mod/github.com/stretchr/testify@v1.5.1/assert/assertion_format.go.tmpl +pkg/mod/github.com/stretchr/testify@v1.5.1/assert/assertion_forward.go +pkg/mod/github.com/stretchr/testify@v1.5.1/assert/assertion_forward.go.tmpl +pkg/mod/github.com/stretchr/testify@v1.5.1/assert/assertion_order_test.go +pkg/mod/github.com/stretchr/testify@v1.5.1/assert/assertion_order.go +pkg/mod/github.com/stretchr/testify@v1.5.1/assert/assertions_test.go +pkg/mod/github.com/stretchr/testify@v1.5.1/assert/assertions.go +pkg/mod/github.com/stretchr/testify@v1.5.1/assert/doc.go +pkg/mod/github.com/stretchr/testify@v1.5.1/assert/errors.go +pkg/mod/github.com/stretchr/testify@v1.5.1/assert/forward_assertions_test.go +pkg/mod/github.com/stretchr/testify@v1.5.1/assert/forward_assertions.go +pkg/mod/github.com/stretchr/testify@v1.5.1/assert/http_assertions_test.go +pkg/mod/github.com/stretchr/testify@v1.5.1/assert/http_assertions.go +pkg/mod/github.com/stretchr/testify@v1.5.1/http/doc.go +pkg/mod/github.com/stretchr/testify@v1.5.1/http/test_response_writer.go +pkg/mod/github.com/stretchr/testify@v1.5.1/http/test_round_tripper.go +pkg/mod/github.com/stretchr/testify@v1.5.1/mock/doc.go +pkg/mod/github.com/stretchr/testify@v1.5.1/mock/mock_test.go +pkg/mod/github.com/stretchr/testify@v1.5.1/mock/mock.go +pkg/mod/github.com/stretchr/testify@v1.5.1/require/doc.go +pkg/mod/github.com/stretchr/testify@v1.5.1/require/forward_requirements_test.go +pkg/mod/github.com/stretchr/testify@v1.5.1/require/forward_requirements.go +pkg/mod/github.com/stretchr/testify@v1.5.1/require/require_forward.go +pkg/mod/github.com/stretchr/testify@v1.5.1/require/require_forward.go.tmpl +pkg/mod/github.com/stretchr/testify@v1.5.1/require/require.go +pkg/mod/github.com/stretchr/testify@v1.5.1/require/require.go.tmpl +pkg/mod/github.com/stretchr/testify@v1.5.1/require/requirements_test.go +pkg/mod/github.com/stretchr/testify@v1.5.1/require/requirements.go +pkg/mod/github.com/stretchr/testify@v1.5.1/suite/doc.go +pkg/mod/github.com/stretchr/testify@v1.5.1/suite/interfaces.go +pkg/mod/github.com/stretchr/testify@v1.5.1/suite/suite_test.go +pkg/mod/github.com/stretchr/testify@v1.5.1/suite/suite.go +pkg/mod/github.com/thoas/go-funk@v0.7.0/.gitignore +pkg/mod/github.com/thoas/go-funk@v0.7.0/.travis.yml +pkg/mod/github.com/thoas/go-funk@v0.7.0/benchmark_test.go +pkg/mod/github.com/thoas/go-funk@v0.7.0/builder_test.go +pkg/mod/github.com/thoas/go-funk@v0.7.0/builder.go +pkg/mod/github.com/thoas/go-funk@v0.7.0/chain_builder_test.go +pkg/mod/github.com/thoas/go-funk@v0.7.0/chain_builder.go +pkg/mod/github.com/thoas/go-funk@v0.7.0/CHANGELOG.md +pkg/mod/github.com/thoas/go-funk@v0.7.0/compact_test.go +pkg/mod/github.com/thoas/go-funk@v0.7.0/compact.go +pkg/mod/github.com/thoas/go-funk@v0.7.0/example_presence_test.go +pkg/mod/github.com/thoas/go-funk@v0.7.0/fill_test.go +pkg/mod/github.com/thoas/go-funk@v0.7.0/fill.go +pkg/mod/github.com/thoas/go-funk@v0.7.0/funk_test.go +pkg/mod/github.com/thoas/go-funk@v0.7.0/go.mod +pkg/mod/github.com/thoas/go-funk@v0.7.0/go.sum +pkg/mod/github.com/thoas/go-funk@v0.7.0/helpers_test.go +pkg/mod/github.com/thoas/go-funk@v0.7.0/helpers.go +pkg/mod/github.com/thoas/go-funk@v0.7.0/intersection_test.go +pkg/mod/github.com/thoas/go-funk@v0.7.0/intersection.go +pkg/mod/github.com/thoas/go-funk@v0.7.0/join_primitives.go +pkg/mod/github.com/thoas/go-funk@v0.7.0/join_test.go +pkg/mod/github.com/thoas/go-funk@v0.7.0/join.go +pkg/mod/github.com/thoas/go-funk@v0.7.0/lazy_builder_test.go +pkg/mod/github.com/thoas/go-funk@v0.7.0/lazy_builder.go +pkg/mod/github.com/thoas/go-funk@v0.7.0/LICENSE +pkg/mod/github.com/thoas/go-funk@v0.7.0/Makefile +pkg/mod/github.com/thoas/go-funk@v0.7.0/map_test.go +pkg/mod/github.com/thoas/go-funk@v0.7.0/map.go +pkg/mod/github.com/thoas/go-funk@v0.7.0/max_test.go +pkg/mod/github.com/thoas/go-funk@v0.7.0/max.go +pkg/mod/github.com/thoas/go-funk@v0.7.0/min_test.go +pkg/mod/github.com/thoas/go-funk@v0.7.0/min.go +pkg/mod/github.com/thoas/go-funk@v0.7.0/operation_test.go +pkg/mod/github.com/thoas/go-funk@v0.7.0/operation.go +pkg/mod/github.com/thoas/go-funk@v0.7.0/presence_test.go +pkg/mod/github.com/thoas/go-funk@v0.7.0/presence.go +pkg/mod/github.com/thoas/go-funk@v0.7.0/README.rst +pkg/mod/github.com/thoas/go-funk@v0.7.0/reduce_test.go +pkg/mod/github.com/thoas/go-funk@v0.7.0/reduce.go +pkg/mod/github.com/thoas/go-funk@v0.7.0/retrieve_test.go +pkg/mod/github.com/thoas/go-funk@v0.7.0/retrieve.go +pkg/mod/github.com/thoas/go-funk@v0.7.0/scan_test.go +pkg/mod/github.com/thoas/go-funk@v0.7.0/scan.go +pkg/mod/github.com/thoas/go-funk@v0.7.0/subset_test.go +pkg/mod/github.com/thoas/go-funk@v0.7.0/subset.go +pkg/mod/github.com/thoas/go-funk@v0.7.0/subtraction_test.go +pkg/mod/github.com/thoas/go-funk@v0.7.0/subtraction.go +pkg/mod/github.com/thoas/go-funk@v0.7.0/transform_test.go +pkg/mod/github.com/thoas/go-funk@v0.7.0/transform.go +pkg/mod/github.com/thoas/go-funk@v0.7.0/typesafe_test.go +pkg/mod/github.com/thoas/go-funk@v0.7.0/typesafe.go +pkg/mod/github.com/thoas/go-funk@v0.7.0/utils_test.go +pkg/mod/github.com/thoas/go-funk@v0.7.0/utils.go +pkg/mod/github.com/thoas/go-funk@v0.7.0/without_test.go +pkg/mod/github.com/thoas/go-funk@v0.7.0/without.go +pkg/mod/github.com/thoas/go-funk@v0.7.0/zip_test.go +pkg/mod/github.com/thoas/go-funk@v0.7.0/zip.go +pkg/mod/github.com/valyala/bytebufferpool@v1.0.0/.travis.yml +pkg/mod/github.com/valyala/bytebufferpool@v1.0.0/bytebuffer_example_test.go +pkg/mod/github.com/valyala/bytebufferpool@v1.0.0/bytebuffer_test.go +pkg/mod/github.com/valyala/bytebufferpool@v1.0.0/bytebuffer_timing_test.go +pkg/mod/github.com/valyala/bytebufferpool@v1.0.0/bytebuffer.go +pkg/mod/github.com/valyala/bytebufferpool@v1.0.0/doc.go +pkg/mod/github.com/valyala/bytebufferpool@v1.0.0/LICENSE +pkg/mod/github.com/valyala/bytebufferpool@v1.0.0/pool_test.go +pkg/mod/github.com/valyala/bytebufferpool@v1.0.0/pool.go +pkg/mod/github.com/valyala/bytebufferpool@v1.0.0/README.md +pkg/mod/github.com/valyala/fasttemplate@v1.0.1/example_test.go +pkg/mod/github.com/valyala/fasttemplate@v1.0.1/go.mod +pkg/mod/github.com/valyala/fasttemplate@v1.0.1/go.sum +pkg/mod/github.com/valyala/fasttemplate@v1.0.1/LICENSE +pkg/mod/github.com/valyala/fasttemplate@v1.0.1/README.md +pkg/mod/github.com/valyala/fasttemplate@v1.0.1/template_test.go +pkg/mod/github.com/valyala/fasttemplate@v1.0.1/template_timing_test.go +pkg/mod/github.com/valyala/fasttemplate@v1.0.1/template.go +pkg/mod/github.com/valyala/fasttemplate@v1.0.1/unsafe_gae.go +pkg/mod/github.com/valyala/fasttemplate@v1.0.1/unsafe.go +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/.errcheck-excludes +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/.gitignore +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/.gitmodules +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/.lint-whitelist +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/CONTRIBUTING.md +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/Gopkg.lock +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/Gopkg.toml +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/LICENSE +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/Makefile +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/README.md +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/THIRD-PARTY-NOTICES +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/.evergreen/config.yml +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/.evergreen/krb5.config +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/benchmark/bson_document.go +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/benchmark/bson_map.go +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/benchmark/bson_struct.go +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/benchmark/bson_test.go +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/benchmark/bson_types.go +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/benchmark/bson.go +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/benchmark/canary_test.go +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/benchmark/canary.go +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/benchmark/harness_case.go +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/benchmark/harness_main.go +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/benchmark/harness_results.go +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/benchmark/harness.go +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/benchmark/multi_test.go +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/benchmark/multi.go +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/benchmark/single_test.go +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/benchmark/single.go +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/bson/benchmark_test.go +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/bson/bson_1_8.go +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/bson/bson_corpus_spec_test.go +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/bson/bson_test.go +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/bson/bson.go +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/bson/decoder_test.go +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/bson/decoder.go +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/bson/doc.go +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/bson/encoder_test.go +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/bson/encoder.go +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/bson/marshal_test.go +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/bson/marshal.go +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/bson/marshaling_cases_test.go +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/bson/primitive_codecs_test.go +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/bson/primitive_codecs.go +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/bson/raw_element.go +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/bson/raw_test.go +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/bson/raw_value_test.go +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/bson/raw_value.go +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/bson/raw.go +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/bson/registry.go +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/bson/truncation_test.go +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/bson/types.go +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/bson/unmarshal_test.go +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/bson/unmarshal.go +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/bson/unmarshaling_cases_test.go +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/bson/bsoncodec/bsoncodec_test.go +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/bson/bsoncodec/bsoncodec.go +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/bson/bsoncodec/default_value_decoders_test.go +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/bson/bsoncodec/default_value_decoders.go +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/bson/bsoncodec/default_value_encoders_test.go +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/bson/bsoncodec/default_value_encoders.go +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/bson/bsoncodec/doc.go +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/bson/bsoncodec/mode.go +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/bson/bsoncodec/pointer_codec.go +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/bson/bsoncodec/proxy.go +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/bson/bsoncodec/registry_test.go +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/bson/bsoncodec/registry.go +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/bson/bsoncodec/struct_codec_test.go +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/bson/bsoncodec/struct_codec.go +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/bson/bsoncodec/struct_tag_parser_test.go +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/bson/bsoncodec/struct_tag_parser.go +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/bson/bsoncodec/types.go +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/bson/bsonrw/bsonrw_test.go +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/bson/bsonrw/copier_test.go +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/bson/bsonrw/copier.go +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/bson/bsonrw/doc.go +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/bson/bsonrw/extjson_parser_test.go +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/bson/bsonrw/extjson_parser.go +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/bson/bsonrw/extjson_reader_test.go +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/bson/bsonrw/extjson_reader.go +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/bson/bsonrw/extjson_tables.go +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/bson/bsonrw/extjson_wrappers.go +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/bson/bsonrw/extjson_writer_test.go +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/bson/bsonrw/extjson_writer.go +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/bson/bsonrw/json_scanner_test.go +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/bson/bsonrw/json_scanner.go +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/bson/bsonrw/mode.go +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/bson/bsonrw/reader.go +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/bson/bsonrw/value_reader_test.go +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/bson/bsonrw/value_reader_writer_test.go +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/bson/bsonrw/value_reader.go +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/bson/bsonrw/value_writer_test.go +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/bson/bsonrw/value_writer.go +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/bson/bsonrw/writer.go +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/bson/bsonrw/bsonrwtest/bsonrwtest.go +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/bson/bsontype/bsontype_test.go +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/bson/bsontype/bsontype.go +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/bson/primitive/decimal.go +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/bson/primitive/objectid_test.go +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/bson/primitive/objectid.go +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/bson/primitive/primitive_test.go +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/bson/primitive/primitive.go +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/cmd/docbuilder/main.go +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/cmd/godriver-benchmark/main.go +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/cmd/operationgen/main.go +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/cmd/operationgen/README.md +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/data/array.json +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/data/binary.json +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/data/boolean.json +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/data/bsonview +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/data/code_w_scope.json +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/data/code.json +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/data/datetime.json +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/data/dbpointer.json +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/data/dbref.json +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/data/decimal128-1.json +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/data/decimal128-2.json +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/data/decimal128-3.json +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/data/decimal128-4.json +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/data/decimal128-5.json +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/data/decimal128-6.json +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/data/decimal128-7.json +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/data/document.json +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/data/double.json +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/data/int32.json +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/data/int64.json +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/data/maxkey.json +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/data/minkey.json +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/data/multi-type-deprecated.json +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/data/multi-type.json +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/data/null.json +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/data/oid.json +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/data/regex.json +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/data/string.json +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/data/symbol.json +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/data/timestamp.json +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/data/top.json +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/data/undefined.json +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/data/auth/connection-string.json +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/data/auth/connection-string.yml +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/data/auth/README.rst +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/data/bson-corpus/array.json +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/data/bson-corpus/binary.json +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/data/bson-corpus/boolean.json +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/data/bson-corpus/bsonview +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/data/bson-corpus/code_w_scope.json +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/data/bson-corpus/code.json +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/data/bson-corpus/datetime.json +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/data/bson-corpus/dbpointer.json +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/data/bson-corpus/dbref.json +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/data/bson-corpus/decimal128-1.json +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/data/bson-corpus/decimal128-2.json +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/data/bson-corpus/decimal128-3.json +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/data/bson-corpus/decimal128-4.json +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/data/bson-corpus/decimal128-5.json +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/data/bson-corpus/decimal128-6.json +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/data/bson-corpus/decimal128-7.json +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/data/bson-corpus/document.json +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/data/bson-corpus/double.json +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/data/bson-corpus/int32.json +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/data/bson-corpus/int64.json +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/data/bson-corpus/maxkey.json +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/data/bson-corpus/minkey.json +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/data/bson-corpus/multi-type-deprecated.json +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/data/bson-corpus/multi-type.json +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/data/bson-corpus/null.json +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/data/bson-corpus/oid.json +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/data/bson-corpus/regex.json +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/data/bson-corpus/string.json +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/data/bson-corpus/symbol.json +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/data/bson-corpus/timestamp.json +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/data/bson-corpus/top.json +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/data/bson-corpus/undefined.json +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/data/certificates/ca.pem +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/data/certificates/client.pem +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/data/certificates/server.pem +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/data/change-streams/change-streams-errors.json +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/data/change-streams/change-streams-errors.yml +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/data/change-streams/change-streams.json +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/data/change-streams/change-streams.yml +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/data/change-streams/README.rst +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/data/command-monitoring/bulkWrite.json +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/data/command-monitoring/bulkWrite.yml +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/data/command-monitoring/command.json +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/data/command-monitoring/command.yml +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/data/command-monitoring/deleteMany.json +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/data/command-monitoring/deleteMany.yml +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/data/command-monitoring/deleteOne.json +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/data/command-monitoring/deleteOne.yml +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/data/command-monitoring/find.json +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/data/command-monitoring/find.yml +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/data/command-monitoring/insertMany.json +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/data/command-monitoring/insertMany.yml +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/data/command-monitoring/insertOne.json +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/data/command-monitoring/insertOne.yml +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/data/command-monitoring/README.rst +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/data/command-monitoring/unacknowledgedBulkWrite.json +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/data/command-monitoring/unacknowledgedBulkWrite.yml +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/data/command-monitoring/updateMany.json +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/data/command-monitoring/updateMany.yml +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/data/command-monitoring/updateOne.json +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/data/command-monitoring/updateOne.yml +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/data/connection-monitoring-and-pooling/connection-monitoring-and-pooling.rst +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/data/connection-monitoring-and-pooling/connection-must-have-id.json +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/data/connection-monitoring-and-pooling/connection-must-have-id.yml +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/data/connection-monitoring-and-pooling/connection-must-order-ids.json +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/data/connection-monitoring-and-pooling/connection-must-order-ids.yml +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/data/connection-monitoring-and-pooling/pool-checkin-destroy-closed.json +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/data/connection-monitoring-and-pooling/pool-checkin-destroy-closed.yml +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/data/connection-monitoring-and-pooling/pool-checkin-destroy-stale.json +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/data/connection-monitoring-and-pooling/pool-checkin-destroy-stale.yml +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/data/connection-monitoring-and-pooling/pool-checkin-make-available.json +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/data/connection-monitoring-and-pooling/pool-checkin-make-available.yml +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/data/connection-monitoring-and-pooling/pool-checkin.json +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/data/connection-monitoring-and-pooling/pool-checkin.yml +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/data/connection-monitoring-and-pooling/pool-checkout-connection.json +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/data/connection-monitoring-and-pooling/pool-checkout-connection.yml +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/data/connection-monitoring-and-pooling/pool-checkout-error-closed.json +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/data/connection-monitoring-and-pooling/pool-checkout-error-closed.yml +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/data/connection-monitoring-and-pooling/pool-checkout-multiple.json +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/data/connection-monitoring-and-pooling/pool-checkout-multiple.yml +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/data/connection-monitoring-and-pooling/pool-checkout-no-idle.json +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/data/connection-monitoring-and-pooling/pool-checkout-no-idle.yml +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/data/connection-monitoring-and-pooling/pool-checkout-no-stale.json +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/data/connection-monitoring-and-pooling/pool-checkout-no-stale.yml +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/data/connection-monitoring-and-pooling/pool-close-destroy-conns.json +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/data/connection-monitoring-and-pooling/pool-close-destroy-conns.yml +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/data/connection-monitoring-and-pooling/pool-close.json +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/data/connection-monitoring-and-pooling/pool-close.yml +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/data/connection-monitoring-and-pooling/pool-create-max-size.json +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/data/connection-monitoring-and-pooling/pool-create-max-size.yml +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/data/connection-monitoring-and-pooling/pool-create-min-size.json +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/data/connection-monitoring-and-pooling/pool-create-min-size.yml +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/data/connection-monitoring-and-pooling/pool-create-with-options.json +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/data/connection-monitoring-and-pooling/pool-create-with-options.yml +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/data/connection-monitoring-and-pooling/pool-create.json +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/data/connection-monitoring-and-pooling/pool-create.yml +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/data/connection-monitoring-and-pooling/README.rst +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/data/connection-monitoring-and-pooling/wait-queue-fairness.json +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/data/connection-monitoring-and-pooling/wait-queue-fairness.yml +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/data/connection-monitoring-and-pooling/wait-queue-timeout.json +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/data/connection-monitoring-and-pooling/wait-queue-timeout.yml +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/data/connection-string/invalid-uris.json +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/data/connection-string/invalid-uris.yml +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/data/connection-string/README.rst +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/data/connection-string/valid-auth.json +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/data/connection-string/valid-auth.yml +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/data/connection-string/valid-db-with-dotted-name.json +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/data/connection-string/valid-db-with-dotted-name.yml +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/data/connection-string/valid-host_identifiers.json +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/data/connection-string/valid-host_identifiers.yml +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/data/connection-string/valid-options.json +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/data/connection-string/valid-options.yml +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/data/connection-string/valid-unix_socket-absolute.json +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/data/connection-string/valid-unix_socket-absolute.yml +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/data/connection-string/valid-unix_socket-relative.json +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/data/connection-string/valid-unix_socket-relative.yml +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/data/connection-string/valid-warnings.json +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/data/connection-string/valid-warnings.yml +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/data/convenient-transactions/callback-aborts.json +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/data/convenient-transactions/callback-aborts.yml +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/data/convenient-transactions/callback-commits.json +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/data/convenient-transactions/callback-commits.yml +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/data/convenient-transactions/callback-retry.json +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/data/convenient-transactions/callback-retry.yml +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/data/convenient-transactions/commit-retry.json +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/data/convenient-transactions/commit-retry.yml +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/data/convenient-transactions/commit-transienttransactionerror-4.2.json +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/data/convenient-transactions/commit-transienttransactionerror-4.2.yml +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/data/convenient-transactions/commit-transienttransactionerror.json +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/data/convenient-transactions/commit-transienttransactionerror.yml +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/data/convenient-transactions/commit-writeconcernerror.json +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/data/convenient-transactions/commit-writeconcernerror.yml +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/data/convenient-transactions/commit.json +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/data/convenient-transactions/commit.yml +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/data/convenient-transactions/README.rst +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/data/convenient-transactions/transaction-options.json +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/data/convenient-transactions/transaction-options.yml +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/data/crud/README.rst +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/data/crud/v1/read/aggregate-collation.json +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/data/crud/v1/read/aggregate-collation.yml +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/data/crud/v1/read/aggregate-out.json +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/data/crud/v1/read/aggregate-out.yml +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/data/crud/v1/read/aggregate.json +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/data/crud/v1/read/aggregate.yml +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/data/crud/v1/read/count-collation.json +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/data/crud/v1/read/count-collation.yml +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/data/crud/v1/read/count.json +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/data/crud/v1/read/count.yml +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/data/crud/v1/read/distinct-collation.json +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/data/crud/v1/read/distinct-collation.yml +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/data/crud/v1/read/distinct.json +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/data/crud/v1/read/distinct.yml +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/data/crud/v1/read/find-collation.json +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/data/crud/v1/read/find-collation.yml +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/data/crud/v1/read/find.json +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/data/crud/v1/read/find.yml +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/data/crud/v1/write/bulkWrite-arrayFilters.json +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/data/crud/v1/write/bulkWrite-arrayFilters.yml +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/data/crud/v1/write/bulkWrite-collation.json +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/data/crud/v1/write/bulkWrite-collation.yml +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/data/crud/v1/write/bulkWrite.json +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/data/crud/v1/write/bulkWrite.yml +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/data/crud/v1/write/deleteMany-collation.json +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/data/crud/v1/write/deleteMany-collation.yml +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/data/crud/v1/write/deleteMany.json +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/data/crud/v1/write/deleteMany.yml +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/data/crud/v1/write/deleteOne-collation.json +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/data/crud/v1/write/deleteOne-collation.yml +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/data/crud/v1/write/deleteOne.json +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/data/crud/v1/write/deleteOne.yml +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/data/crud/v1/write/findOneAndDelete-collation.json +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/data/crud/v1/write/findOneAndDelete-collation.yml +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/data/crud/v1/write/findOneAndDelete.json +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/data/crud/v1/write/findOneAndDelete.yml +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/data/crud/v1/write/findOneAndReplace-collation.json +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/data/crud/v1/write/findOneAndReplace-collation.yml +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/data/crud/v1/write/findOneAndReplace-upsert.json +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/data/crud/v1/write/findOneAndReplace-upsert.yml +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/data/crud/v1/write/findOneAndReplace.json +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/data/crud/v1/write/findOneAndReplace.yml +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/data/crud/v1/write/findOneAndUpdate-arrayFilters.json +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/data/crud/v1/write/findOneAndUpdate-arrayFilters.yml +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/data/crud/v1/write/findOneAndUpdate-collation.json +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/data/crud/v1/write/findOneAndUpdate-collation.yml +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/data/crud/v1/write/findOneAndUpdate.json +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/data/crud/v1/write/findOneAndUpdate.yml +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/data/crud/v1/write/insertMany.json +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/data/crud/v1/write/insertMany.yml +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/data/crud/v1/write/insertOne.json +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/data/crud/v1/write/insertOne.yml +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/data/crud/v1/write/replaceOne-collation.json +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/data/crud/v1/write/replaceOne-collation.yml +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/data/crud/v1/write/replaceOne.json +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/data/crud/v1/write/replaceOne.yml +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/data/crud/v1/write/updateMany-arrayFilters.json +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/data/crud/v1/write/updateMany-arrayFilters.yml +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/data/crud/v1/write/updateMany-collation.json +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/data/crud/v1/write/updateMany-collation.yml +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/data/crud/v1/write/updateMany.json +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/data/crud/v1/write/updateMany.yml +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/data/crud/v1/write/updateOne-arrayFilters.json +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/data/crud/v1/write/updateOne-arrayFilters.yml +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/data/crud/v1/write/updateOne-collation.json +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/data/crud/v1/write/updateOne-collation.yml +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/data/crud/v1/write/updateOne.json +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/data/crud/v1/write/updateOne.yml +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/data/crud/v2/aggregate-merge.json +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/data/crud/v2/aggregate-merge.yml +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/data/crud/v2/aggregate-out-readConcern.json +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/data/crud/v2/aggregate-out-readConcern.yml +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/data/crud/v2/db-aggregate.json +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/data/crud/v2/db-aggregate.yml +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/data/crud/v2/updateWithPipelines.json +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/data/crud/v2/updateWithPipelines.yml +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/data/extended_bson/deep_bson.json.gz +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/data/extended_bson/flat_bson.json.gz +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/data/extended_bson/full_bson.json.gz +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/data/gridfs/delete.json +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/data/gridfs/delete.yml +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/data/gridfs/download_by_name.json +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/data/gridfs/download_by_name.yml +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/data/gridfs/download.json +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/data/gridfs/download.yml +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/data/gridfs/upload.json +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/data/gridfs/upload.yml +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/data/initial-dns-seedlist-discovery/longer-parent-in-return.json +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/data/initial-dns-seedlist-discovery/longer-parent-in-return.yml +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/data/initial-dns-seedlist-discovery/misformatted-option.json +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/data/initial-dns-seedlist-discovery/misformatted-option.yml +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/data/initial-dns-seedlist-discovery/no-results.json +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/data/initial-dns-seedlist-discovery/no-results.yml +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/data/initial-dns-seedlist-discovery/not-enough-parts.json +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/data/initial-dns-seedlist-discovery/not-enough-parts.yml +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/data/initial-dns-seedlist-discovery/one-result-default-port.json +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/data/initial-dns-seedlist-discovery/one-result-default-port.yml +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/data/initial-dns-seedlist-discovery/one-txt-record-multiple-strings.json +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/data/initial-dns-seedlist-discovery/one-txt-record-multiple-strings.yml +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/data/initial-dns-seedlist-discovery/one-txt-record.json +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/data/initial-dns-seedlist-discovery/one-txt-record.yml +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/data/initial-dns-seedlist-discovery/parent-part-mismatch1.json +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/data/initial-dns-seedlist-discovery/parent-part-mismatch1.yml +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/data/initial-dns-seedlist-discovery/parent-part-mismatch2.json +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/data/initial-dns-seedlist-discovery/parent-part-mismatch2.yml +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/data/initial-dns-seedlist-discovery/parent-part-mismatch3.json +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/data/initial-dns-seedlist-discovery/parent-part-mismatch3.yml +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/data/initial-dns-seedlist-discovery/parent-part-mismatch4.json +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/data/initial-dns-seedlist-discovery/parent-part-mismatch4.yml +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/data/initial-dns-seedlist-discovery/parent-part-mismatch5.json +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/data/initial-dns-seedlist-discovery/parent-part-mismatch5.yml +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/data/initial-dns-seedlist-discovery/README.rst +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/data/initial-dns-seedlist-discovery/returned-parent-too-short.json +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/data/initial-dns-seedlist-discovery/returned-parent-too-short.yml +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/data/initial-dns-seedlist-discovery/returned-parent-wrong.json +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/data/initial-dns-seedlist-discovery/returned-parent-wrong.yml +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/data/initial-dns-seedlist-discovery/two-results-default-port.json +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/data/initial-dns-seedlist-discovery/two-results-default-port.yml +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/data/initial-dns-seedlist-discovery/two-results-nonstandard-port.json +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/data/initial-dns-seedlist-discovery/two-results-nonstandard-port.yml +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/data/initial-dns-seedlist-discovery/two-txt-records.json +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/data/initial-dns-seedlist-discovery/two-txt-records.yml +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/data/initial-dns-seedlist-discovery/txt-record-not-allowed-option.json +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/data/initial-dns-seedlist-discovery/txt-record-not-allowed-option.yml +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/data/initial-dns-seedlist-discovery/txt-record-with-overridden-ssl-option.json +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/data/initial-dns-seedlist-discovery/txt-record-with-overridden-ssl-option.yml +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/data/initial-dns-seedlist-discovery/txt-record-with-overridden-uri-option.json +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/data/initial-dns-seedlist-discovery/txt-record-with-overridden-uri-option.yml +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/data/initial-dns-seedlist-discovery/txt-record-with-unallowed-option.json +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/data/initial-dns-seedlist-discovery/txt-record-with-unallowed-option.yml +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/data/initial-dns-seedlist-discovery/uri-with-port.json +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/data/initial-dns-seedlist-discovery/uri-with-port.yml +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/data/initial-dns-seedlist-discovery/uri-with-two-hosts.json +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/data/initial-dns-seedlist-discovery/uri-with-two-hosts.yml +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/data/max-staleness/README.rst +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/data/max-staleness/ReplicaSetNoPrimary/DefaultNoMaxStaleness.json +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/data/max-staleness/ReplicaSetNoPrimary/DefaultNoMaxStaleness.yml +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/data/max-staleness/ReplicaSetNoPrimary/Incompatible.json +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/data/max-staleness/ReplicaSetNoPrimary/Incompatible.yml +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/data/max-staleness/ReplicaSetNoPrimary/LastUpdateTime.json +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/data/max-staleness/ReplicaSetNoPrimary/LastUpdateTime.yml +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/data/max-staleness/ReplicaSetNoPrimary/Nearest.json +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/data/max-staleness/ReplicaSetNoPrimary/Nearest.yml +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/data/max-staleness/ReplicaSetNoPrimary/Nearest2.json +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/data/max-staleness/ReplicaSetNoPrimary/Nearest2.yml +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/data/max-staleness/ReplicaSetNoPrimary/NoKnownServers.json +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/data/max-staleness/ReplicaSetNoPrimary/NoKnownServers.yml +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/data/max-staleness/ReplicaSetNoPrimary/PrimaryPreferred_tags.json +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/data/max-staleness/ReplicaSetNoPrimary/PrimaryPreferred_tags.yml +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/data/max-staleness/ReplicaSetNoPrimary/PrimaryPreferred.json +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/data/max-staleness/ReplicaSetNoPrimary/PrimaryPreferred.yml +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/data/max-staleness/ReplicaSetNoPrimary/Secondary.json +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/data/max-staleness/ReplicaSetNoPrimary/Secondary.yml +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/data/max-staleness/ReplicaSetNoPrimary/SecondaryPreferred_tags.json +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/data/max-staleness/ReplicaSetNoPrimary/SecondaryPreferred_tags.yml +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/data/max-staleness/ReplicaSetNoPrimary/SecondaryPreferred.json +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/data/max-staleness/ReplicaSetNoPrimary/SecondaryPreferred.yml +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/data/max-staleness/ReplicaSetNoPrimary/ZeroMaxStaleness.json +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/data/max-staleness/ReplicaSetNoPrimary/ZeroMaxStaleness.yml +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/data/max-staleness/ReplicaSetWithPrimary/DefaultNoMaxStaleness.json +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/data/max-staleness/ReplicaSetWithPrimary/DefaultNoMaxStaleness.yml +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/data/max-staleness/ReplicaSetWithPrimary/Incompatible.json +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/data/max-staleness/ReplicaSetWithPrimary/Incompatible.yml +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/data/max-staleness/ReplicaSetWithPrimary/LastUpdateTime.json +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/data/max-staleness/ReplicaSetWithPrimary/LastUpdateTime.yml +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/data/max-staleness/ReplicaSetWithPrimary/LongHeartbeat.json +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/data/max-staleness/ReplicaSetWithPrimary/LongHeartbeat.yml +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/data/max-staleness/ReplicaSetWithPrimary/LongHeartbeat2.json +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/data/max-staleness/ReplicaSetWithPrimary/LongHeartbeat2.yml +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/data/max-staleness/ReplicaSetWithPrimary/MaxStalenessTooSmall.json +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/data/max-staleness/ReplicaSetWithPrimary/MaxStalenessTooSmall.yml +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/data/max-staleness/ReplicaSetWithPrimary/MaxStalenessWithModePrimary.json +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/data/max-staleness/ReplicaSetWithPrimary/MaxStalenessWithModePrimary.yml +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/data/max-staleness/ReplicaSetWithPrimary/Nearest_tags.json +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/data/max-staleness/ReplicaSetWithPrimary/Nearest_tags.yml +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/data/max-staleness/ReplicaSetWithPrimary/Nearest.json +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/data/max-staleness/ReplicaSetWithPrimary/Nearest.yml +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/data/max-staleness/ReplicaSetWithPrimary/Nearest2.json +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/data/max-staleness/ReplicaSetWithPrimary/Nearest2.yml +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/data/max-staleness/ReplicaSetWithPrimary/PrimaryPreferred_incompatible.json +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/data/max-staleness/ReplicaSetWithPrimary/PrimaryPreferred_incompatible.yml +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/data/max-staleness/ReplicaSetWithPrimary/PrimaryPreferred.json +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/data/max-staleness/ReplicaSetWithPrimary/PrimaryPreferred.yml +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/data/max-staleness/ReplicaSetWithPrimary/Secondary_tags.json +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/data/max-staleness/ReplicaSetWithPrimary/Secondary_tags.yml +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/data/max-staleness/ReplicaSetWithPrimary/Secondary_tags2.json +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/data/max-staleness/ReplicaSetWithPrimary/Secondary_tags2.yml +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/data/max-staleness/ReplicaSetWithPrimary/SecondaryPreferred_tags.json +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/data/max-staleness/ReplicaSetWithPrimary/SecondaryPreferred_tags.yml +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/data/max-staleness/ReplicaSetWithPrimary/SecondaryPreferred_tags2.json +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/data/max-staleness/ReplicaSetWithPrimary/SecondaryPreferred_tags2.yml +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/data/max-staleness/ReplicaSetWithPrimary/SecondaryPreferred.json +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/data/max-staleness/ReplicaSetWithPrimary/SecondaryPreferred.yml +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/data/max-staleness/ReplicaSetWithPrimary/ZeroMaxStaleness.json +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/data/max-staleness/ReplicaSetWithPrimary/ZeroMaxStaleness.yml +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/data/max-staleness/Sharded/Incompatible.json +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/data/max-staleness/Sharded/Incompatible.yml +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/data/max-staleness/Sharded/SmallMaxStaleness.json +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/data/max-staleness/Sharded/SmallMaxStaleness.yml +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/data/max-staleness/Single/Incompatible.json +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/data/max-staleness/Single/Incompatible.yml +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/data/max-staleness/Single/SmallMaxStaleness.json +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/data/max-staleness/Single/SmallMaxStaleness.yml +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/data/max-staleness/Unknown/SmallMaxStaleness.json +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/data/max-staleness/Unknown/SmallMaxStaleness.yml +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/data/read-write-concern/README.rst +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/data/read-write-concern/connection-string/read-concern.json +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/data/read-write-concern/connection-string/read-concern.yml +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/data/read-write-concern/connection-string/write-concern.json +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/data/read-write-concern/connection-string/write-concern.yml +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/data/read-write-concern/document/read-concern.json +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/data/read-write-concern/document/read-concern.yml +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/data/read-write-concern/document/write-concern.json +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/data/read-write-concern/document/write-concern.yml +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/data/retryable-reads/aggregate-merge.json +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/data/retryable-reads/aggregate-merge.yml +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/data/retryable-reads/aggregate-serverErrors.json +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/data/retryable-reads/aggregate-serverErrors.yml +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/data/retryable-reads/aggregate.json +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/data/retryable-reads/aggregate.yml +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/data/retryable-reads/changeStreams-client.watch-serverErrors.json +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/data/retryable-reads/changeStreams-client.watch-serverErrors.yml +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/data/retryable-reads/changeStreams-client.watch.json +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/data/retryable-reads/changeStreams-client.watch.yml +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/data/retryable-reads/changeStreams-db.coll.watch-serverErrors.json +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/data/retryable-reads/changeStreams-db.coll.watch-serverErrors.yml +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/data/retryable-reads/changeStreams-db.coll.watch.json +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/data/retryable-reads/changeStreams-db.coll.watch.yml +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/data/retryable-reads/changeStreams-db.watch-serverErrors.json +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/data/retryable-reads/changeStreams-db.watch-serverErrors.yml +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/data/retryable-reads/changeStreams-db.watch.json +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/data/retryable-reads/changeStreams-db.watch.yml +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/data/retryable-reads/count-serverErrors.json +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/data/retryable-reads/count-serverErrors.yml +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/data/retryable-reads/count.json +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/data/retryable-reads/count.yml +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/data/retryable-reads/countDocuments-serverErrors.json +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/data/retryable-reads/countDocuments-serverErrors.yml +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/data/retryable-reads/countDocuments.json +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/data/retryable-reads/countDocuments.yml +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/data/retryable-reads/distinct-serverErrors.json +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/data/retryable-reads/distinct-serverErrors.yml +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/data/retryable-reads/distinct.json +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/data/retryable-reads/distinct.yml +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/data/retryable-reads/estimatedDocumentCount-serverErrors.json +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/data/retryable-reads/estimatedDocumentCount-serverErrors.yml +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/data/retryable-reads/estimatedDocumentCount.json +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/data/retryable-reads/estimatedDocumentCount.yml +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/data/retryable-reads/find-serverErrors.json +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/data/retryable-reads/find-serverErrors.yml +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/data/retryable-reads/find.json +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/data/retryable-reads/find.yml +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/data/retryable-reads/findOne-serverErrors.json +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/data/retryable-reads/findOne-serverErrors.yml +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/data/retryable-reads/findOne.json +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/data/retryable-reads/findOne.yml +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/data/retryable-reads/gridfs-download-serverErrors.json +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/data/retryable-reads/gridfs-download-serverErrors.yml +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/data/retryable-reads/gridfs-download.json +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/data/retryable-reads/gridfs-download.yml +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/data/retryable-reads/gridfs-downloadByName-serverErrors.json +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/data/retryable-reads/gridfs-downloadByName-serverErrors.yml +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/data/retryable-reads/gridfs-downloadByName.json +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/data/retryable-reads/gridfs-downloadByName.yml +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/data/retryable-reads/listCollectionNames-serverErrors.json +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/data/retryable-reads/listCollectionNames-serverErrors.yml +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/data/retryable-reads/listCollectionNames.json +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/data/retryable-reads/listCollectionNames.yml +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/data/retryable-reads/listCollectionObjects-serverErrors.json +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/data/retryable-reads/listCollectionObjects-serverErrors.yml +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/data/retryable-reads/listCollectionObjects.json +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/data/retryable-reads/listCollectionObjects.yml +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/data/retryable-reads/listCollections-serverErrors.json +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/data/retryable-reads/listCollections-serverErrors.yml +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/data/retryable-reads/listCollections.json +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/data/retryable-reads/listCollections.yml +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/data/retryable-reads/listDatabaseNames-serverErrors.json +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/data/retryable-reads/listDatabaseNames-serverErrors.yml +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/data/retryable-reads/listDatabaseNames.json +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/data/retryable-reads/listDatabaseNames.yml +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/data/retryable-reads/listDatabaseObjects-serverErrors.json +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/data/retryable-reads/listDatabaseObjects-serverErrors.yml +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/data/retryable-reads/listDatabaseObjects.json +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/data/retryable-reads/listDatabaseObjects.yml +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/data/retryable-reads/listDatabases-serverErrors.json +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/data/retryable-reads/listDatabases-serverErrors.yml +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/data/retryable-reads/listDatabases.json +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/data/retryable-reads/listDatabases.yml +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/data/retryable-reads/listIndexes-serverErrors.json +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/data/retryable-reads/listIndexes-serverErrors.yml +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/data/retryable-reads/listIndexes.json +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/data/retryable-reads/listIndexes.yml +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/data/retryable-reads/listIndexNames-serverErrors.json +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/data/retryable-reads/listIndexNames-serverErrors.yml +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/data/retryable-reads/listIndexNames.json +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/data/retryable-reads/listIndexNames.yml +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/data/retryable-reads/mapReduce.json +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/data/retryable-reads/mapReduce.yml +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/data/retryable-writes/bulkWrite-serverErrors.json +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/data/retryable-writes/bulkWrite-serverErrors.yml +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/data/retryable-writes/bulkWrite.json +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/data/retryable-writes/bulkWrite.yml +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/data/retryable-writes/deleteMany.json +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/data/retryable-writes/deleteMany.yml +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/data/retryable-writes/deleteOne-serverErrors.json +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/data/retryable-writes/deleteOne-serverErrors.yml +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/data/retryable-writes/deleteOne.json +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/data/retryable-writes/deleteOne.yml +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/data/retryable-writes/findOneAndDelete-serverErrors.json +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/data/retryable-writes/findOneAndDelete-serverErrors.yml +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/data/retryable-writes/findOneAndDelete.json +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/data/retryable-writes/findOneAndDelete.yml +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/data/retryable-writes/findOneAndReplace-serverErrors.json +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/data/retryable-writes/findOneAndReplace-serverErrors.yml +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/data/retryable-writes/findOneAndReplace.json +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/data/retryable-writes/findOneAndReplace.yml +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/data/retryable-writes/findOneAndUpdate-serverErrors.json +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/data/retryable-writes/findOneAndUpdate-serverErrors.yml +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/data/retryable-writes/findOneAndUpdate.json +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/data/retryable-writes/findOneAndUpdate.yml +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/data/retryable-writes/insertMany-serverErrors.json +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/data/retryable-writes/insertMany-serverErrors.yml +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/data/retryable-writes/insertMany.json +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/data/retryable-writes/insertMany.yml +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/data/retryable-writes/insertOne-serverErrors.json +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/data/retryable-writes/insertOne-serverErrors.yml +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/data/retryable-writes/insertOne.json +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/data/retryable-writes/insertOne.yml +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/data/retryable-writes/README.rst +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/data/retryable-writes/replaceOne-serverErrors.json +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/data/retryable-writes/replaceOne-serverErrors.yml +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/data/retryable-writes/replaceOne.json +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/data/retryable-writes/replaceOne.yml +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/data/retryable-writes/updateMany.json +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/data/retryable-writes/updateMany.yml +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/data/retryable-writes/updateOne-serverErrors.json +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/data/retryable-writes/updateOne-serverErrors.yml +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/data/retryable-writes/updateOne.json +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/data/retryable-writes/updateOne.yml +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/data/server-discovery-and-monitoring/README.rst +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/data/server-discovery-and-monitoring/monitoring/README.rst +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/data/server-discovery-and-monitoring/monitoring/replica_set_with_no_primary.json +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/data/server-discovery-and-monitoring/monitoring/replica_set_with_no_primary.yml +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/data/server-discovery-and-monitoring/monitoring/replica_set_with_primary.json +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/data/server-discovery-and-monitoring/monitoring/replica_set_with_primary.yml +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/data/server-discovery-and-monitoring/monitoring/replica_set_with_removal.json +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/data/server-discovery-and-monitoring/monitoring/replica_set_with_removal.yml +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/data/server-discovery-and-monitoring/monitoring/required_replica_set.json +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/data/server-discovery-and-monitoring/monitoring/required_replica_set.yml +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/data/server-discovery-and-monitoring/monitoring/standalone.json +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/data/server-discovery-and-monitoring/monitoring/standalone.yml +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/data/server-discovery-and-monitoring/rs/compatible_unknown.json +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/data/server-discovery-and-monitoring/rs/compatible_unknown.yml +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/data/server-discovery-and-monitoring/rs/compatible.json +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/data/server-discovery-and-monitoring/rs/compatible.yml +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/data/server-discovery-and-monitoring/rs/discover_arbiters.json +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/data/server-discovery-and-monitoring/rs/discover_arbiters.yml +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/data/server-discovery-and-monitoring/rs/discover_passives.json +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/data/server-discovery-and-monitoring/rs/discover_passives.yml +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/data/server-discovery-and-monitoring/rs/discover_primary.json +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/data/server-discovery-and-monitoring/rs/discover_primary.yml +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/data/server-discovery-and-monitoring/rs/discover_secondary.json +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/data/server-discovery-and-monitoring/rs/discover_secondary.yml +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/data/server-discovery-and-monitoring/rs/discovery.json +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/data/server-discovery-and-monitoring/rs/discovery.yml +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/data/server-discovery-and-monitoring/rs/equal_electionids.json +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/data/server-discovery-and-monitoring/rs/equal_electionids.yml +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/data/server-discovery-and-monitoring/rs/ghost_discovered.json +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/data/server-discovery-and-monitoring/rs/ghost_discovered.yml +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/data/server-discovery-and-monitoring/rs/hosts_differ_from_seeds.json +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/data/server-discovery-and-monitoring/rs/hosts_differ_from_seeds.yml +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/data/server-discovery-and-monitoring/rs/incompatible_arbiter.json +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/data/server-discovery-and-monitoring/rs/incompatible_arbiter.yml +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/data/server-discovery-and-monitoring/rs/incompatible_ghost.json +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/data/server-discovery-and-monitoring/rs/incompatible_ghost.yml +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/data/server-discovery-and-monitoring/rs/incompatible_other.json +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/data/server-discovery-and-monitoring/rs/incompatible_other.yml +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/data/server-discovery-and-monitoring/rs/ls_timeout.json +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/data/server-discovery-and-monitoring/rs/ls_timeout.yml +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/data/server-discovery-and-monitoring/rs/member_reconfig.json +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/data/server-discovery-and-monitoring/rs/member_reconfig.yml +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/data/server-discovery-and-monitoring/rs/member_standalone.json +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/data/server-discovery-and-monitoring/rs/member_standalone.yml +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/data/server-discovery-and-monitoring/rs/new_primary_new_electionid.json +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/data/server-discovery-and-monitoring/rs/new_primary_new_electionid.yml +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/data/server-discovery-and-monitoring/rs/new_primary_new_setversion.json +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/data/server-discovery-and-monitoring/rs/new_primary_new_setversion.yml +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/data/server-discovery-and-monitoring/rs/new_primary_wrong_set_name.json +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/data/server-discovery-and-monitoring/rs/new_primary_wrong_set_name.yml +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/data/server-discovery-and-monitoring/rs/new_primary.json +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/data/server-discovery-and-monitoring/rs/new_primary.yml +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/data/server-discovery-and-monitoring/rs/non_rs_member.json +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/data/server-discovery-and-monitoring/rs/non_rs_member.yml +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/data/server-discovery-and-monitoring/rs/normalize_case_me.json +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/data/server-discovery-and-monitoring/rs/normalize_case_me.yml +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/data/server-discovery-and-monitoring/rs/normalize_case.json +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/data/server-discovery-and-monitoring/rs/normalize_case.yml +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/data/server-discovery-and-monitoring/rs/null_election_id.json +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/data/server-discovery-and-monitoring/rs/null_election_id.yml +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/data/server-discovery-and-monitoring/rs/primary_becomes_ghost.json +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/data/server-discovery-and-monitoring/rs/primary_becomes_ghost.yml +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/data/server-discovery-and-monitoring/rs/primary_becomes_mongos.json +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/data/server-discovery-and-monitoring/rs/primary_becomes_mongos.yml +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/data/server-discovery-and-monitoring/rs/primary_becomes_standalone.json +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/data/server-discovery-and-monitoring/rs/primary_becomes_standalone.yml +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/data/server-discovery-and-monitoring/rs/primary_changes_set_name.json +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/data/server-discovery-and-monitoring/rs/primary_changes_set_name.yml +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/data/server-discovery-and-monitoring/rs/primary_disconnect_electionid.json +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/data/server-discovery-and-monitoring/rs/primary_disconnect_electionid.yml +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/data/server-discovery-and-monitoring/rs/primary_disconnect_setversion.json +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/data/server-discovery-and-monitoring/rs/primary_disconnect_setversion.yml +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/data/server-discovery-and-monitoring/rs/primary_disconnect.json +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/data/server-discovery-and-monitoring/rs/primary_disconnect.yml +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/data/server-discovery-and-monitoring/rs/primary_hint_from_secondary_with_mismatched_me.json +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/data/server-discovery-and-monitoring/rs/primary_hint_from_secondary_with_mismatched_me.yml +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/data/server-discovery-and-monitoring/rs/primary_mismatched_me.json +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/data/server-discovery-and-monitoring/rs/primary_mismatched_me.yml +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/data/server-discovery-and-monitoring/rs/primary_reports_new_member.json +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/data/server-discovery-and-monitoring/rs/primary_reports_new_member.yml +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/data/server-discovery-and-monitoring/rs/primary_to_no_primary_mismatched_me.json +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/data/server-discovery-and-monitoring/rs/primary_to_no_primary_mismatched_me.yml +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/data/server-discovery-and-monitoring/rs/primary_wrong_set_name.json +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/data/server-discovery-and-monitoring/rs/primary_wrong_set_name.yml +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/data/server-discovery-and-monitoring/rs/response_from_removed.json +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/data/server-discovery-and-monitoring/rs/response_from_removed.yml +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/data/server-discovery-and-monitoring/rs/rsother_discovered.json +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/data/server-discovery-and-monitoring/rs/rsother_discovered.yml +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/data/server-discovery-and-monitoring/rs/sec_not_auth.json +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/data/server-discovery-and-monitoring/rs/sec_not_auth.yml +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/data/server-discovery-and-monitoring/rs/secondary_ignore_ok_0.json +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/data/server-discovery-and-monitoring/rs/secondary_ignore_ok_0.yml +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/data/server-discovery-and-monitoring/rs/secondary_mismatched_me.json +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/data/server-discovery-and-monitoring/rs/secondary_mismatched_me.yml +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/data/server-discovery-and-monitoring/rs/secondary_wrong_set_name_with_primary.json +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/data/server-discovery-and-monitoring/rs/secondary_wrong_set_name_with_primary.yml +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/data/server-discovery-and-monitoring/rs/secondary_wrong_set_name.json +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/data/server-discovery-and-monitoring/rs/secondary_wrong_set_name.yml +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/data/server-discovery-and-monitoring/rs/setversion_without_electionid.json +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/data/server-discovery-and-monitoring/rs/setversion_without_electionid.yml +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/data/server-discovery-and-monitoring/rs/stepdown_change_set_name.json +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/data/server-discovery-and-monitoring/rs/stepdown_change_set_name.yml +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/data/server-discovery-and-monitoring/rs/too_new.json +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/data/server-discovery-and-monitoring/rs/too_new.yml +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/data/server-discovery-and-monitoring/rs/too_old.json +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/data/server-discovery-and-monitoring/rs/too_old.yml +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/data/server-discovery-and-monitoring/rs/unexpected_mongos.json +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/data/server-discovery-and-monitoring/rs/unexpected_mongos.yml +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/data/server-discovery-and-monitoring/rs/use_setversion_without_electionid.json +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/data/server-discovery-and-monitoring/rs/use_setversion_without_electionid.yml +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/data/server-discovery-and-monitoring/rs/wrong_set_name.json +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/data/server-discovery-and-monitoring/rs/wrong_set_name.yml +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/data/server-discovery-and-monitoring/sharded/compatible.json +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/data/server-discovery-and-monitoring/sharded/compatible.yml +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/data/server-discovery-and-monitoring/sharded/ls_timeout_mongos.json +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/data/server-discovery-and-monitoring/sharded/ls_timeout_mongos.yml +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/data/server-discovery-and-monitoring/sharded/mongos_disconnect.json +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/data/server-discovery-and-monitoring/sharded/mongos_disconnect.yml +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/data/server-discovery-and-monitoring/sharded/multiple_mongoses.json +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/data/server-discovery-and-monitoring/sharded/multiple_mongoses.yml +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/data/server-discovery-and-monitoring/sharded/non_mongos_removed.json +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/data/server-discovery-and-monitoring/sharded/non_mongos_removed.yml +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/data/server-discovery-and-monitoring/sharded/normalize_uri_case.json +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/data/server-discovery-and-monitoring/sharded/normalize_uri_case.yml +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/data/server-discovery-and-monitoring/sharded/too_new.json +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/data/server-discovery-and-monitoring/sharded/too_new.yml +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/data/server-discovery-and-monitoring/sharded/too_old.json +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/data/server-discovery-and-monitoring/sharded/too_old.yml +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/data/server-discovery-and-monitoring/single/compatible.json +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/data/server-discovery-and-monitoring/single/compatible.yml +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/data/server-discovery-and-monitoring/single/direct_connection_external_ip.json +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/data/server-discovery-and-monitoring/single/direct_connection_external_ip.yml +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/data/server-discovery-and-monitoring/single/direct_connection_mongos.json +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/data/server-discovery-and-monitoring/single/direct_connection_mongos.yml +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/data/server-discovery-and-monitoring/single/direct_connection_rsarbiter.json +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/data/server-discovery-and-monitoring/single/direct_connection_rsarbiter.yml +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/data/server-discovery-and-monitoring/single/direct_connection_rsprimary.json +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/data/server-discovery-and-monitoring/single/direct_connection_rsprimary.yml +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/data/server-discovery-and-monitoring/single/direct_connection_rssecondary.json +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/data/server-discovery-and-monitoring/single/direct_connection_rssecondary.yml +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/data/server-discovery-and-monitoring/single/direct_connection_slave.json +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/data/server-discovery-and-monitoring/single/direct_connection_slave.yml +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/data/server-discovery-and-monitoring/single/direct_connection_standalone.json +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/data/server-discovery-and-monitoring/single/direct_connection_standalone.yml +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/data/server-discovery-and-monitoring/single/ls_timeout_standalone.json +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/data/server-discovery-and-monitoring/single/ls_timeout_standalone.yml +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/data/server-discovery-and-monitoring/single/not_ok_response.json +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/data/server-discovery-and-monitoring/single/not_ok_response.yml +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/data/server-discovery-and-monitoring/single/standalone_removed.json +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/data/server-discovery-and-monitoring/single/standalone_removed.yml +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/data/server-discovery-and-monitoring/single/too_new.json +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/data/server-discovery-and-monitoring/single/too_new.yml +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/data/server-discovery-and-monitoring/single/too_old.json +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/data/server-discovery-and-monitoring/single/too_old.yml +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/data/server-discovery-and-monitoring/single/unavailable_seed.json +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/data/server-discovery-and-monitoring/single/unavailable_seed.yml +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/data/server-selection/README.rst +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/data/server-selection/rtt/first_value_zero.json +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/data/server-selection/rtt/first_value_zero.yml +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/data/server-selection/rtt/first_value.json +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/data/server-selection/rtt/first_value.yml +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/data/server-selection/rtt/value_test_1.json +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/data/server-selection/rtt/value_test_1.yml +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/data/server-selection/rtt/value_test_2.json +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/data/server-selection/rtt/value_test_2.yml +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/data/server-selection/rtt/value_test_3.json +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/data/server-selection/rtt/value_test_3.yml +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/data/server-selection/rtt/value_test_4.json +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/data/server-selection/rtt/value_test_4.yml +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/data/server-selection/rtt/value_test_5.json +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/data/server-selection/rtt/value_test_5.yml +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/data/server-selection/server_selection/ReplicaSetNoPrimary/read/Nearest_multiple.json +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/data/server-selection/server_selection/ReplicaSetNoPrimary/read/Nearest_multiple.yml +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/data/server-selection/server_selection/ReplicaSetNoPrimary/read/Nearest_non_matching.json +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/data/server-selection/server_selection/ReplicaSetNoPrimary/read/Nearest_non_matching.yml +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/data/server-selection/server_selection/ReplicaSetNoPrimary/read/Nearest.json +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/data/server-selection/server_selection/ReplicaSetNoPrimary/read/Nearest.yml +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/data/server-selection/server_selection/ReplicaSetNoPrimary/read/PossiblePrimary.json +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/data/server-selection/server_selection/ReplicaSetNoPrimary/read/PossiblePrimary.yml +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/data/server-selection/server_selection/ReplicaSetNoPrimary/read/PossiblePrimaryNearest.json +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/data/server-selection/server_selection/ReplicaSetNoPrimary/read/PossiblePrimaryNearest.yml +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/data/server-selection/server_selection/ReplicaSetNoPrimary/read/Primary.json +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/data/server-selection/server_selection/ReplicaSetNoPrimary/read/Primary.yml +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/data/server-selection/server_selection/ReplicaSetNoPrimary/read/PrimaryPreferred_non_matching.json +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/data/server-selection/server_selection/ReplicaSetNoPrimary/read/PrimaryPreferred_non_matching.yml +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/data/server-selection/server_selection/ReplicaSetNoPrimary/read/PrimaryPreferred.json +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/data/server-selection/server_selection/ReplicaSetNoPrimary/read/PrimaryPreferred.yml +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/data/server-selection/server_selection/ReplicaSetNoPrimary/read/Secondary_multi_tags.json +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/data/server-selection/server_selection/ReplicaSetNoPrimary/read/Secondary_multi_tags.yml +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/data/server-selection/server_selection/ReplicaSetNoPrimary/read/Secondary_multi_tags2.json +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/data/server-selection/server_selection/ReplicaSetNoPrimary/read/Secondary_multi_tags2.yml +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/data/server-selection/server_selection/ReplicaSetNoPrimary/read/Secondary_non_matching.json +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/data/server-selection/server_selection/ReplicaSetNoPrimary/read/Secondary_non_matching.yml +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/data/server-selection/server_selection/ReplicaSetNoPrimary/read/Secondary.json +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/data/server-selection/server_selection/ReplicaSetNoPrimary/read/Secondary.yml +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/data/server-selection/server_selection/ReplicaSetNoPrimary/read/SecondaryPreferred_non_matching.json +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/data/server-selection/server_selection/ReplicaSetNoPrimary/read/SecondaryPreferred_non_matching.yml +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/data/server-selection/server_selection/ReplicaSetNoPrimary/read/SecondaryPreferred.json +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/data/server-selection/server_selection/ReplicaSetNoPrimary/read/SecondaryPreferred.yml +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/data/server-selection/server_selection/ReplicaSetNoPrimary/write/SecondaryPreferred.json +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/data/server-selection/server_selection/ReplicaSetNoPrimary/write/SecondaryPreferred.yml +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/data/server-selection/server_selection/ReplicaSetWithPrimary/read/Nearest_multiple.json +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/data/server-selection/server_selection/ReplicaSetWithPrimary/read/Nearest_multiple.yml +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/data/server-selection/server_selection/ReplicaSetWithPrimary/read/Nearest_non_matching.json +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/data/server-selection/server_selection/ReplicaSetWithPrimary/read/Nearest_non_matching.yml +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/data/server-selection/server_selection/ReplicaSetWithPrimary/read/Nearest.json +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/data/server-selection/server_selection/ReplicaSetWithPrimary/read/Nearest.yml +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/data/server-selection/server_selection/ReplicaSetWithPrimary/read/Primary.json +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/data/server-selection/server_selection/ReplicaSetWithPrimary/read/Primary.yml +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/data/server-selection/server_selection/ReplicaSetWithPrimary/read/PrimaryPreferred_non_matching.json +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/data/server-selection/server_selection/ReplicaSetWithPrimary/read/PrimaryPreferred_non_matching.yml +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/data/server-selection/server_selection/ReplicaSetWithPrimary/read/PrimaryPreferred.json +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/data/server-selection/server_selection/ReplicaSetWithPrimary/read/PrimaryPreferred.yml +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/data/server-selection/server_selection/ReplicaSetWithPrimary/read/Secondary_non_matching.json +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/data/server-selection/server_selection/ReplicaSetWithPrimary/read/Secondary_non_matching.yml +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/data/server-selection/server_selection/ReplicaSetWithPrimary/read/Secondary.json +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/data/server-selection/server_selection/ReplicaSetWithPrimary/read/Secondary.yml +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/data/server-selection/server_selection/ReplicaSetWithPrimary/read/SecondaryPreferred_non_matching.json +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/data/server-selection/server_selection/ReplicaSetWithPrimary/read/SecondaryPreferred_non_matching.yml +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/data/server-selection/server_selection/ReplicaSetWithPrimary/read/SecondaryPreferred_tags.json +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/data/server-selection/server_selection/ReplicaSetWithPrimary/read/SecondaryPreferred_tags.yml +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/data/server-selection/server_selection/ReplicaSetWithPrimary/read/SecondaryPreferred.json +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/data/server-selection/server_selection/ReplicaSetWithPrimary/read/SecondaryPreferred.yml +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/data/server-selection/server_selection/ReplicaSetWithPrimary/write/SecondaryPreferred.json +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/data/server-selection/server_selection/ReplicaSetWithPrimary/write/SecondaryPreferred.yml +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/data/server-selection/server_selection/Sharded/read/SecondaryPreferred.json +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/data/server-selection/server_selection/Sharded/read/SecondaryPreferred.yml +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/data/server-selection/server_selection/Sharded/write/SecondaryPreferred.json +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/data/server-selection/server_selection/Sharded/write/SecondaryPreferred.yml +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/data/server-selection/server_selection/Single/read/SecondaryPreferred.json +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/data/server-selection/server_selection/Single/read/SecondaryPreferred.yml +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/data/server-selection/server_selection/Single/write/SecondaryPreferred.json +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/data/server-selection/server_selection/Single/write/SecondaryPreferred.yml +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/data/server-selection/server_selection/Unknown/read/SecondaryPreferred.json +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/data/server-selection/server_selection/Unknown/read/SecondaryPreferred.yml +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/data/server-selection/server_selection/Unknown/write/SecondaryPreferred.json +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/data/server-selection/server_selection/Unknown/write/SecondaryPreferred.yml +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/data/sessions/dirty-session-errors.json +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/data/sessions/dirty-session-errors.yml +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/data/sessions/README.rst +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/data/single_and_multi_document/large_doc.json.gz +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/data/single_and_multi_document/small_doc.json.gz +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/data/single_and_multi_document/tweet.json.gz +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/data/transactions/abort.json +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/data/transactions/abort.yml +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/data/transactions/bulk.json +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/data/transactions/bulk.yml +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/data/transactions/causal-consistency.json +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/data/transactions/causal-consistency.yml +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/data/transactions/commit.json +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/data/transactions/commit.yml +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/data/transactions/count.json +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/data/transactions/count.yml +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/data/transactions/delete.json +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/data/transactions/delete.yml +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/data/transactions/error-labels.json +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/data/transactions/error-labels.yml +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/data/transactions/errors.json +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/data/transactions/errors.yml +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/data/transactions/findOneAndDelete.json +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/data/transactions/findOneAndDelete.yml +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/data/transactions/findOneAndReplace.json +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/data/transactions/findOneAndReplace.yml +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/data/transactions/findOneAndUpdate.json +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/data/transactions/findOneAndUpdate.yml +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/data/transactions/insert.json +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/data/transactions/insert.yml +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/data/transactions/isolation.json +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/data/transactions/isolation.yml +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/data/transactions/mongos-pin-auto-tests.py +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/data/transactions/mongos-pin-auto.json +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/data/transactions/mongos-pin-auto.yml +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/data/transactions/mongos-recovery-token.json +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/data/transactions/mongos-recovery-token.yml +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/data/transactions/pin-mongos.json +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/data/transactions/pin-mongos.yml +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/data/transactions/read-concern.json +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/data/transactions/read-concern.yml +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/data/transactions/read-pref.json +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/data/transactions/read-pref.yml +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/data/transactions/README.rst +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/data/transactions/reads.json +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/data/transactions/reads.yml +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/data/transactions/retryable-abort.json +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/data/transactions/retryable-abort.yml +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/data/transactions/retryable-commit.json +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/data/transactions/retryable-commit.yml +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/data/transactions/retryable-writes.json +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/data/transactions/retryable-writes.yml +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/data/transactions/run-command.json +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/data/transactions/run-command.yml +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/data/transactions/transaction-options.json +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/data/transactions/transaction-options.yml +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/data/transactions/update.json +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/data/transactions/update.yml +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/data/transactions/write-concern.json +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/data/transactions/write-concern.yml +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/data/uri-options/auth-options.json +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/data/uri-options/auth-options.yml +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/data/uri-options/ca.pem +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/data/uri-options/cert.pem +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/data/uri-options/client.pem +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/data/uri-options/compression-options.json +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/data/uri-options/compression-options.yml +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/data/uri-options/concern-options.json +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/data/uri-options/concern-options.yml +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/data/uri-options/connection-options.json +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/data/uri-options/connection-options.yml +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/data/uri-options/connection-pool-options.json +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/data/uri-options/connection-pool-options.yml +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/data/uri-options/read-preference-options.json +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/data/uri-options/read-preference-options.yml +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/data/uri-options/README.rst +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/data/uri-options/single-threaded-options.json +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/data/uri-options/single-threaded-options.yml +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/data/uri-options/tls-options.json +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/data/uri-options/tls-options.yml +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/etc/add-license.sh +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/etc/check_env.sh +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/etc/check_fmt.sh +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/etc/generate-notices.pl +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/etc/lintscreen.pl +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/etc/list_pkgs.sh +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/etc/list_test_pkgs.sh +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/etc/update-spec-tests.sh +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/etc/assets/docs-mongodb-green.svg +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/etc/assets/godoc-bson-blue.svg +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/etc/assets/godoc-mongo-blue.svg +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/etc/assets/mongo-gopher.png +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/event/monitoring.go +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/examples/documentation_examples/examples_test.go +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/examples/documentation_examples/examples.go +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/internal/const.go +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/internal/error.go +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/internal/semaphore_test.go +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/internal/semaphore.go +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/internal/testutil/config.go +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/internal/testutil/ops.go +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/internal/testutil/helpers/helpers.go +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/internal/testutil/israce/norace.go +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/internal/testutil/israce/race.go +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/mongo/batch_cursor.go +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/mongo/bulk_write_models.go +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/mongo/bulk_write.go +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/mongo/causal_consistency_test.go +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/mongo/change_stream_spec_test.go +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/mongo/change_stream_test.go +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/mongo/change_stream.go +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/mongo/client_internal_test.go +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/mongo/client_options_test.go +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/mongo/client_test.go +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/mongo/client.go +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/mongo/collection_internal_test.go +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/mongo/collection.go +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/mongo/command_monitoring_test.go +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/mongo/crud_spec_test.go +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/mongo/crud_spec_v2_test.go +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/mongo/crud_util_test.go +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/mongo/cursor_test.go +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/mongo/cursor.go +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/mongo/database_internal_test.go +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/mongo/database_test.go +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/mongo/database.go +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/mongo/doc.go +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/mongo/errors.go +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/mongo/index_options_builder.go +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/mongo/index_view_internal_test.go +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/mongo/index_view.go +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/mongo/mongo_test.go +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/mongo/mongo.go +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/mongo/operation_legacy_test.go +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/mongo/primary_stepdown_test.go +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/mongo/read_write_concern_spec_test.go +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/mongo/results_test.go +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/mongo/results.go +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/mongo/retryable_reads_test.go +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/mongo/retryable_writes_test.go +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/mongo/session.go +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/mongo/sessions_test.go +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/mongo/single_result_test.go +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/mongo/single_result.go +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/mongo/transaction_mongos_pinning_test.go +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/mongo/transactions_test.go +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/mongo/util.go +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/mongo/with_transactions_test.go +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/mongo/gridfs/bucket.go +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/mongo/gridfs/download_stream.go +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/mongo/gridfs/gridfs_retryable_spec_test.go +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/mongo/gridfs/gridfs_spec_test.go +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/mongo/gridfs/gridfs_test.go +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/mongo/gridfs/gridfs_util_test.go +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/mongo/gridfs/upload_stream.go +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/mongo/options/aggregateoptions.go +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/mongo/options/bulkwriteoptions.go +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/mongo/options/changestreamoptions.go +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/mongo/options/clientoptions_1_9.go +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/mongo/options/clientoptions_1_10.go +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/mongo/options/clientoptions_test.go +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/mongo/options/clientoptions.go +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/mongo/options/collation_test.go +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/mongo/options/collectionoptions.go +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/mongo/options/countoptions.go +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/mongo/options/dboptions.go +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/mongo/options/deleteoptions.go +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/mongo/options/distinctoptions.go +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/mongo/options/estimatedcountoptions.go +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/mongo/options/findoptions.go +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/mongo/options/gridfsoptions.go +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/mongo/options/indexoptions.go +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/mongo/options/insertoptions.go +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/mongo/options/listcollectionsoptions.go +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/mongo/options/listdatabasesoptions.go +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/mongo/options/mongooptions.go +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/mongo/options/replaceoptions.go +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/mongo/options/runcmdoptions.go +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/mongo/options/sessionoptions.go +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/mongo/options/transactionoptions.go +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/mongo/options/updateoptions.go +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/mongo/options/testdata/ca-key.pem +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/mongo/options/testdata/ca.pem +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/mongo/options/testdata/cert.pem +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/mongo/options/testdata/certificate.pem +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/mongo/options/testdata/csr.json +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/mongo/options/testdata/key.pem +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/mongo/options/testdata/nopass/cert.pem +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/mongo/options/testdata/nopass/certificate.pem +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/mongo/options/testdata/nopass/key.pem +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/mongo/readconcern/readconcern.go +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/mongo/readpref/mode.go +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/mongo/readpref/options.go +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/mongo/readpref/readpref_test.go +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/mongo/readpref/readpref.go +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/mongo/testatlas/main.go +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/mongo/writeconcern/writeconcern_test.go +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/mongo/writeconcern/writeconcern.go +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/tag/tag_test.go +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/tag/tag.go +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/version/version.go +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/x/README.md +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/x/bsonx/array_test.go +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/x/bsonx/array.go +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/x/bsonx/bson_test.go +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/x/bsonx/constructor.go +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/x/bsonx/document_test.go +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/x/bsonx/document.go +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/x/bsonx/element_test.go +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/x/bsonx/element.go +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/x/bsonx/mdocument_test.go +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/x/bsonx/mdocument.go +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/x/bsonx/primitive_codecs_test.go +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/x/bsonx/primitive_codecs.go +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/x/bsonx/registry.go +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/x/bsonx/value_test.go +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/x/bsonx/value.go +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/x/bsonx/bsoncore/bsoncore_test.go +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/x/bsonx/bsoncore/bsoncore.go +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/x/bsonx/bsoncore/document_sequence_test.go +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/x/bsonx/bsoncore/document_sequence.go +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/x/bsonx/bsoncore/document_test.go +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/x/bsonx/bsoncore/document.go +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/x/bsonx/bsoncore/element_test.go +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/x/bsonx/bsoncore/element.go +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/x/bsonx/bsoncore/tables.go +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/x/bsonx/bsoncore/value_test.go +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/x/bsonx/bsoncore/value.go +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/x/mongo/driver/batch_cursor.go +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/x/mongo/driver/batches_test.go +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/x/mongo/driver/batches.go +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/x/mongo/driver/DESIGN.md +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/x/mongo/driver/driver.go +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/x/mongo/driver/errors.go +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/x/mongo/driver/address/addr_test.go +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/x/mongo/driver/address/addr.go +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/x/mongo/driver/auth/auth_spec_test.go +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/x/mongo/driver/auth/auth_test.go +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/x/mongo/driver/auth/auth.go +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/x/mongo/driver/auth/cred.go +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/x/mongo/driver/auth/default.go +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/x/mongo/driver/auth/doc.go +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/x/mongo/driver/auth/gssapi_not_enabled.go +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/x/mongo/driver/auth/gssapi_not_supported.go +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/x/mongo/driver/auth/gssapi_test.go +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/x/mongo/driver/auth/gssapi.go +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/x/mongo/driver/auth/mongodbcr_test.go +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/x/mongo/driver/auth/mongodbcr.go +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/x/mongo/driver/auth/plain_test.go +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/x/mongo/driver/auth/plain.go +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/x/mongo/driver/auth/sasl.go +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/x/mongo/driver/auth/scram.go +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/x/mongo/driver/auth/util.go +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/x/mongo/driver/auth/x509.go +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/x/mongo/driver/auth/internal/gssapi/gss_wrapper.c +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/x/mongo/driver/auth/internal/gssapi/gss_wrapper.h +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/x/mongo/driver/auth/internal/gssapi/gss.go +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/x/mongo/driver/auth/internal/gssapi/sspi_wrapper.c +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/x/mongo/driver/auth/internal/gssapi/sspi_wrapper.h +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/x/mongo/driver/auth/internal/gssapi/sspi.go +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/x/mongo/driver/connstring/connstring_spec_test.go +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/x/mongo/driver/connstring/connstring_test.go +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/x/mongo/driver/connstring/connstring.go +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/x/mongo/driver/description/description.go +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/x/mongo/driver/description/feature_test.go +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/x/mongo/driver/description/feature.go +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/x/mongo/driver/description/max_staleness_spec_test.go +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/x/mongo/driver/description/selector_spec_test.go +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/x/mongo/driver/description/selector_test.go +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/x/mongo/driver/description/selector_write_test.go +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/x/mongo/driver/description/server_kind.go +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/x/mongo/driver/description/server_selector.go +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/x/mongo/driver/description/server.go +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/x/mongo/driver/description/shared_spec_test.go +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/x/mongo/driver/description/topology_kind.go +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/x/mongo/driver/description/topology.go +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/x/mongo/driver/description/version_range_test.go +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/x/mongo/driver/description/version_range.go +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/x/mongo/driver/description/version_test.go +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/x/mongo/driver/description/version.go +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/x/mongo/driver/dns/dns.go +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/x/mongo/driver/drivergen/drivergen_test.go +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/x/mongo/driver/drivergen/drivergen.go +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/x/mongo/driver/drivergen/example.operation.toml +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/x/mongo/driver/drivergen/templates.go +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/x/mongo/driver/drivergen/templates/command_parameter.tmpl +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/x/mongo/driver/drivergen/templates/operation.tmpl +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/x/mongo/driver/drivergen/templates/response_field.tmpl +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/x/mongo/driver/drivertest/channel_conn.go +pkg/mod/go.mongodb.org/mongo-driver@v1.1.1/x/mongo/driver/drivertest/channel_netconn.go diff --git a/orc8r/cloud/deploy/orc8r_deployer/docker/Dockerfile b/orc8r/cloud/deploy/orc8r_deployer/docker/Dockerfile new file mode 100644 index 000000000000..ccc2d91e3b05 --- /dev/null +++ b/orc8r/cloud/deploy/orc8r_deployer/docker/Dockerfile @@ -0,0 +1,71 @@ +FROM python:3.9-slim-buster + +ARG HELM_VERSION="3.5.1" +ARG TERRAFORM_VERSION="0.14.7" +ARG KUBECTL_VERSION="1.20.2" +ARG ANSIBLE_VERSION="3.0.0" + +RUN apt-get update && \ + apt-get upgrade -y && \ + apt-get install -y \ + git \ + wget \ + unzip \ + curl \ + vim \ + jq \ + procps && \ + apt-get clean -y && \ + apt-get autoclean -y && \ + apt-get autoremove -y && \ + rm -rf /var/lib/apt/lists/* && \ + rm -rf /var/cache/apt/archives/* + +RUN pip3 install --no-cache-dir \ + boto \ + boto3 \ + ansible==${ANSIBLE_VERSION} \ + prettytable \ + requests \ + docker \ + pyOpenSSL \ + unittest2 \ + colorama +WORKDIR /root/download + +# Install aws cli +RUN wget "https://awscli.amazonaws.com/awscli-exe-linux-x86_64.zip" -O "/root/download/awscli2.zip" && \ + unzip awscli2.zip && \ + ./aws/install + +# Install aws iam authenticator +RUN wget https://amazon-eks.s3.us-west-2.amazonaws.com/1.18.9/2020-11-02/bin/linux/amd64/aws-iam-authenticator -O /usr/local/bin/aws-iam-authenticator && \ + chmod +x /usr/local/bin/aws-iam-authenticator + +# Install helm +RUN mkdir helm3 && \ + curl -SsL --retry 5 "https://get.helm.sh/helm-v$HELM_VERSION-linux-amd64.tar.gz" | tar xz -C ./helm3 && \ + cp /root/download/helm3/linux-amd64/helm /usr/local/bin/helm + +# Install terraform +RUN wget https://releases.hashicorp.com/terraform/${TERRAFORM_VERSION}/terraform\_${TERRAFORM_VERSION}\_linux_amd64.zip && \ + unzip ./terraform\_${TERRAFORM_VERSION}\_linux_amd64.zip -d terraform14_cli && \ + cp /root/download/terraform14_cli/terraform /usr/local/bin/terraform && \ + wget https://storage.googleapis.com/kubernetes-release/release/v$KUBECTL_VERSION/bin/linux/amd64/kubectl -O /usr/local/bin/kubectl && \ + chmod +x /usr/local/bin/kubectl && \ + rm -rf /root/download/* + +COPY root/ /root/ + +# Install the orc8r cli (orcl) +WORKDIR /root/scripts +RUN pip3 install . + +# Set these environment variables to ensure aws configuration remains +# in the deployment directory even when container is removed +ENV AWS_CONFIG_FILE=/root/project/.aws/config +ENV AWS_SHARED_CREDENTIALS_FILE=/root/project/.aws/credentials +ENV AWS_DEFAULT_OUTPUT=json + +WORKDIR /root/project +CMD ["/bin/bash"] diff --git a/orc8r/cloud/deploy/orc8r_deployer/docker/README.md b/orc8r/cloud/deploy/orc8r_deployer/docker/README.md new file mode 100644 index 000000000000..8fc92dbdeed0 --- /dev/null +++ b/orc8r/cloud/deploy/orc8r_deployer/docker/README.md @@ -0,0 +1,243 @@ +# Orcl + +Orcl is a Orchestrator CLI. It is used for managing Orc8r deployment. It provides following subcommands. + +- Configure +- Certs +- Install +- Upgrade +- Verify +- Cleanup +- Debug(perhaps in the future) + +[Image: image.png]Orcl is packaged within orc8r_deployer. Orc8r deployer is a docker image which contains all the necessary prerequisites to deploy orc8r. The only requirements for running orc8r_deployer is that the the host machine must have [docker engine installed](https://docs.docker.com/get-docker/). + +## Usage + +``` +./run_deployer runs the orc8r deployer container +orc8r deployer contains scripts which enable user to configure, run prechecks +install, upgrade, verify and cleanup an orc8r deployment +Usage: run_deployer [-deploy-dir|-root-dir|-build|-h] +options: +-h Print this Help +-deploy-dir deployment dir containing configs and secrets (mandatory) +-root-dir magma root directory +-build build the deployer container +example: ./run_deployer -deploy-dir /tmp/orc8r_14_deployment +``` + +Deployment directory will be used to maintain the configs, secrets and also contain the deployment related files such as main.tf etc. It is important to use the same deployment directory during the upgrade so that the configuration variables can be reused. + +In the following section, we will discuss each of the orcl commands in detail. + +## Configure + +Every orc8r deployment relies on several configuration attributes. For e.g cluster_name attribute is used to identify the orc8r kubernetes cluster, orc8r_tag provides the image tag to be used during the deployment. Configure command enables user to easily configure the mandatory configs necessary for the deployment. It additionally provides the ability to also configure optional attributes through the ***set*** subcommand. Configure command also has subcommands like ***info*** to show all the possible configuration attributes and ***show*** to display the current configuration. Finally configure also contains a subcommand ***check*** which provides the ability to check if all mandatory configs needed by the deployment have been successfully configured. + +``` +# orcl configure --help +Usage: orcl configure [OPTIONS] COMMAND [ARGS]... + + Configure orc8r deployment variables + +Options: + -c, --component [infra|platform|service] + --help Show this message and exit. + +Commands: + check Check if all mandatory configs are present + info Display all possible config options in detail + set Set enables user to configure any configuration option. + show Display the current configuration +``` + +Note: Currently we don’t have the capability to sanitize the inputs. This might be a feature to be added later + +Configure command relies mainly on a meta file `vars.yml`. This file contains all deployment variables with their name, type, description, default value, whether it is a required or an optional attribute and finally the apps which rely on this configuration attribute. +The configuration provided by the user is used to + +- set the aws configuration(specifically access_key, secret_key, region), +- build terraform.tfvars.json(which is automatically loaded by terraform and provided to the root module) +- build main.tf and vars.tf(to ensure that we set the vars in the root module for it to be correspondingly consumed by the orc8r and orc8r-app modules) + +For e.g. + +``` +module "orc8r" { + source = "/root/magma/orc8r/cloud/deploy/terraform/orc8r-aws" + cluster_name=var.cluster_name <-- generated from configured keys + cluster_version=var.cluster_version + orc8r_domain_name=var.orc8r_domain_name + region=var.region +``` + +## Certs + +Certs is a orcl command for managing certificates. Currently it has only one subcommand ***add*** which can be used to add application certs and self signed root certificates(optional). + +``` +# orcl certs --help +Usage: orcl certs [OPTIONS] COMMAND [ARGS]... + Manage certs in orc8r deployment + +Options: + --help Show this message and exit. + +Commands: + add Add creates application and self signed(optional) certs +``` + + +Certs relies on the orc8r configuration options being configured. Specifically it relies on `orc8r_domain_name` attribute to be configured. + +## Install + +Install command provides the ability to install an orc8r deployment based on provided configuration. Install command provides an optional ability to run prechecks prior to installation and also provides prechecks as a separate subcommand. + +``` +# orcl install --help +Usage: orcl install [OPTIONS] COMMAND [ARGS]... + + Deploy new instance of orc8r + +Options: + --help Show this message and exit. + +Commands: + precheck Performs various checks to ensure successful installation +``` + +Currently the installation process is dependent on terraform. Install command runs following subprocess commands + +``` +terraform init +terraform apply -target=module.orc8r -auto-approve +terraform apply -target=module.orc8r-app.null_resource.orc8r_seed_secrets -auto-approve +terraform apply + +``` + +Following prechecks are performed prior to the installation + +- Check if all mandatory configs are present +- Check if all secret files are present +- Check if we have atleast 3 availability zones in our deployment +- Check if secrets manager is already configured with the orc8r secrets id +- Check cloudwatch log group already exists +- In case of elastic search deployment, check if AWSServiceRoleForAmazonElasticsearchService IAM role already exists +- Check if orc8r deployment type is valid +- Check if image tag specified is present in image repository +- Check if helm chart specified is present in the helm repository + +## Upgrade + +Upgrade command provides the ability to install an orc8r deployment based on provided configuration. Upgrade command provides an optional ability to run prechecks prior to upgrade and also provides prechecks as a separate subcommand. +**Note: it is important to use the same deployment directory which was used during install. This is mainly to ensure that the terraform state is available and also reuse the configuration variables** + +``` +# orcl upgrade --help +Usage: orcl upgrade [OPTIONS] COMMAND [ARGS]... + + Upgrade existing orc8r deployment + +Options: + --help Show this message and exit. + +Commands: + precheck Precheck runs various checks to ensure successful upgrade +``` + +Upgrade command runs the following upgrade command + +``` +terraform apply +``` + +Following prechecks are performed prior to the upgrade + +- Check if terraform state exists +- Check if all mandatory configs are present +- Check if we have atleast 3 availability zones in our deployment +- Check if deployed EKS cluster version is greater than what’s specified in the configuration +- In case of elastic search deployment, check if AWSServiceRoleForAmazonElasticsearchService IAM role already exists +- Check if deployed RDS instance version is greater than what’s specified in the configuration +- Check if image tag specified is present in image repository +- Check if helm chart specified is present in the helm repository + +## Verify + +Verify command provides the ability to post deployment checks on orc8r. + +``` +Usage: orcl verify [OPTIONS] COMMAND [ARGS]... + + Run post deployment checks on orc8r + +Options: + --help Show this message and exit. + +Commands: + sanity +``` + +Currently the subcommand ***sanity*** runs the following checks + +- Check if all kubernetes pods are healthy and dump the logs of unhealthy pods +- Invoke get all networks API for generic and LTE networks(magma/v1/networks, magma/v1/lte) to ensure if the orchestrator and lte pods are working as expected + +## Cleanup + +Cleanup command provides the ability to cleanup all resources deployed during orc8r deployment + +``` +# orcl cleanup --help +Usage: orcl cleanup [OPTIONS] COMMAND [ARGS]... + + Removes resources deployed for orc8r + +Options: + --help Show this message and exit. + +Commands: + raw Individually cleans up resources deployed for orc8r +``` + +Cleanup runs + +``` +terraform destroy +``` + +to perform the cleanup. Unfortunately terraform cleanup hasn’t been very reliable in the past. So this command provides ability to also directly cleanup the underlying resources when the terraform destroy command fails. It is though the subcommand ***raw*** + +``` +Usage: orcl cleanup raw [OPTIONS] + + Individually cleans up resources deployed for orc8r + +Options: + --dryrun Show resources to be cleaned up during raw cleanup + --state TEXT Provide state file containing resource information e.g. + terraform.tfstate or terraform.tfstate.backup + + --help Show this message and exit. +``` + +Currently the raw cleanup performs cleanup of the following + +- Orc8r cloudwatch log group +- RDS instances +- Elastic search instance. +- EFS mount targets and volumes +- EKS cluster +- Autoscaling groups +- Elastic load balancers +- NAT gateways +- Internet gateways +- Subnets +- VPC +- Hosted records in hosted zone and hosted zone + +Note: The raw cleanup attempts to identify the above mentioned resources through `terraform show --json` +Sometimes terraform destroy might cause state to be cleaned up and we have no way to identify the state to be cleaned up. So there is an optional knob to specify state. This can be used to provide terraform tfstate backup file to identify the resources to be cleaned up. diff --git a/orc8r/cloud/deploy/orc8r_deployer/docker/root/config.yml b/orc8r/cloud/deploy/orc8r_deployer/docker/root/config.yml new file mode 100644 index 000000000000..29bab4f3896e --- /dev/null +++ b/orc8r/cloud/deploy/orc8r_deployer/docker/root/config.yml @@ -0,0 +1,16 @@ +project_dir: /root/project +config_dir: /root/project/configs +secret_dir: /root/project/secrets +scripts_dir: /root/scripts +tf_dir: /root/tf +main_tf: /root/project/main.tf +vars_tf: /root/project/vars.tf +auto_tf: /root/project/terraform.tfvars.json +data_dir: /root/data +magma_root: /root/magma +vars_definition: /root/data/vars.yml +playbooks: /root/scripts/playbooks +components: + - infra + - platform + - service \ No newline at end of file diff --git a/orc8r/cloud/deploy/orc8r_deployer/docker/root/data/vars.yml b/orc8r/cloud/deploy/orc8r_deployer/docker/root/data/vars.yml new file mode 100644 index 000000000000..9979a207a431 --- /dev/null +++ b/orc8r/cloud/deploy/orc8r_deployer/docker/root/data/vars.yml @@ -0,0 +1,506 @@ +infra: + aws_access_key_id: + Description: AWS access key id. + Type: string + Required: true + ConfigApps: + -awscli + aws_secret_access_key: + Description: AWS access secret. + Type: string + Required: true + ConfigApps: + -awscli + region: + Description: AWS region to deploy Orchestrator components into. The chosen region must provide EKS. + Type: string + Required: true + ConfigApps: + -awscli + -tf + cluster_name: + Description: Name for the Orchestrator EKS cluster. + Type: string + Required: false + Default: "orc8r" + ConfigApps: + -tf + cluster_version: + Description: Kubernetes version for the EKS cluster + Type: string + Required: false + Default: "1.17" + ConfigApps: + -tf + eks_map_roles: + Description: EKS IAM role mapping. Note that by default, the creator of the cluster will be in the system:master group. + Type: list(object({rolearn = string, username = string, groups = list(string), })) + Required: false + ConfigApps: + -tf + eks_map_users: + Description: Additional IAM users to add to the aws-auth ConfigMap. + Type: list(object({userarn = string, username = string, groups = list(string)})) + Required: false + ConfigApps: + -tf + eks_worker_additional_policy_arns: + Description: Additional IAM policy ARNs to attach to EKS worker nodes. + Type: list(string) + Required: false + ConfigApps: + -tf + eks_worker_additional_sg_ids: + Description: Additional security group IDs to attach to EKS worker nodes. + Type: list(string) + Required: false + ConfigApps: + -tf + eks_worker_group_key: + Description: If specified, the worker nodes for EKS will use this EC2 keypair. + Type: string + Required: false + ConfigApps: + -tf + eks_worker_groups: + Description: Worker group configuration for EKS. Default value is 1 worker group consisting of 3 t3.large instances. + Type: any + Required: false + ConfigApps: + -tf + vpc_cidr: + Description: CIDR block for the VPC. + Type: string + Required: false + ConfigApps: + -tf + vpc_database_subnets: + Description: ' CIDR blocks for the VPC''s database subnets. ' + Type: list(string) + Required: false + ConfigApps: + -tf + vpc_extra_tags: + Description: Tags to add to the VPC. + Type: map + Required: false + ConfigApps: + -tf + vpc_name: + Description: Name for the VPC that will contain all the Orchestrator components. + Type: string + Required: false + ConfigApps: + -tf + vpc_private_subnets: + Description: ' CIDR blocks for the VPC''s private subnets. ' + Type: list(string) + Required: false + ConfigApps: + -tf + vpc_public_subnets: + Description: ' CIDR blocks for the VPC''s public subnets. ' + Type: list(string) + Required: false + ConfigApps: + -tf + orc8r_domain_name: + Description: Base domain name for AWS Route 53 hosted zone. + Type: string + Required: true + ConfigApps: + -tf + secretsmanager_orc8r_secret: + Description: AWS Secret Manager secret to store Orchestrator secrets. + Type: string + Required: true + ConfigApps: + -tf + +platform: + deploy_elasticsearch: + Default: true + Description: Flag to deploy AWS Elasticsearch service as the datasink for aggregated logs. + Type: bool + Required: false + ConfigApps: + -tf + deploy_elasticsearch_service_linked_role: + Default: true + Description: ' Flag to deploy AWS Elasticsearch service linked role with cluster. If you''ve already created an ES service linked role for another cluster, you should set this to false. ' + Type: bool + Required: false + ConfigApps: + -tf + efs_project_name: + Description: Project name for EFS file system + Type: string + Required: false + ConfigApps: + -tf + elasticsearch_az_count: + Default: 2 + Description: AZ count for ES. + Type: number + Required: false + ConfigApps: + -tf + elasticsearch_dedicated_master_count: + Description: Number of dedicated ES master nodes. + Type: number + Required: false + ConfigApps: + -tf + elasticsearch_dedicated_master_enabled: + Description: Enable/disable dedicated master nodes for ES. + Type: bool + Required: false + ConfigApps: + -tf + elasticsearch_dedicated_master_type: + Description: Instance type for ES dedicated master nodes. + Type: string + Required: false + ConfigApps: + -tf + elasticsearch_domain_name: + Default: "orc8r-es" + Description: Name for the ES domain. + Type: string + Required: false + ConfigApps: + -tf + elasticsearch_domain_tags: + Description: Extra tags for the ES domain. + Type: map + Required: false + ConfigApps: + -tf + elasticsearch_ebs_enabled: + Default: true + Description: Use EBS for ES storage. + Type: bool + Required: false + ConfigApps: + -tf + elasticsearch_ebs_iops: + Description: IOPS for ES EBS volumes. + Type: number + Required: false + ConfigApps: + -tf + elasticsearch_ebs_volume_size: + Default: 32 + Description: Size in GB to allocate for ES EBS data volumes. + Type: number + Required: false + ConfigApps: + -tf + elasticsearch_ebs_volume_type: + Default: gp2 + Description: EBS volume type for ES data volumes. + Type: string + Required: false + ConfigApps: + -tf + elasticsearch_instance_count: + Default: 2 + Description: Number of instances to allocate for ES domain. + Type: number + Required: false + ConfigApps: + -tf + elasticsearch_instance_type: + Default: "t2.medium.elasticsearch" + Description: AWS instance type for ES domain. + Type: string + Required: false + ConfigApps: + -tf + elasticsearch_version: + Default: "7.7" + Description: ES version for ES domain. + Type: string + Required: false + ConfigApps: + -tf + global_tags: + Description: n/a + Type: map + Required: false + ConfigApps: + -tf + orc8r_db_engine_version: + Default: "9.6.15" + Description: Postgres engine version for Orchestrator DB. + Type: string + Required: false + ConfigApps: + -tf + orc8r_db_identifier: + Default: "orc8rdb" + Description: Identifier for the RDS instance for Orchestrator. + Type: string + Required: false + ConfigApps: + -tf + orc8r_db_instance_class: + Description: RDS instance type for Orchestrator DB. + Type: string + Required: false + ConfigApps: + -tf + orc8r_db_name: + Description: DB name for Orchestrator RDS instance. + Type: string + Required: false + ConfigApps: + -tf + orc8r_db_password: + Description: Password for the Orchestrator DB. + Type: string + Required: true + ConfigApps: + -tf + orc8r_db_storage_gb: + Description: Capacity in GB to allocate for Orchestrator RDS instance. + Type: number + Required: false + ConfigApps: + -tf + orc8r_db_username: + Description: Username for default DB user for Orchestrator DB. + Type: string + Required: false + ConfigApps: + -tf +service: + cwf_orc8r_chart_version: + Description: Version of the Orchestrator cwf module Helm chart to install. + Type: string + Required: false + ConfigApps: + -tf + Default: "0.2.1" + deploy_nms: + Description: Flag to deploy NMS. + Type: bool + Required: false + ConfigApps: + -tf + deploy_openvpn: + Description: Flag to deploy OpenVPN server into cluster. This is useful if you want to remotely access AGWs. + Type: bool + Required: false + ConfigApps: + -tf + docker_pass: + Description: Docker registry password. + Type: string + Required: true + ConfigApps: + -tf + docker_registry: + Description: Docker registry to pull Orchestrator containers from. + Type: string + Required: true + ConfigApps: + -tf + docker_user: + Description: Docker username to login to registry with. + Type: string + Required: true + ConfigApps: + -tf + efs_file_system_id: + Description: ID of the EFS file system to use for K8s persistent volumes. + Type: string + Required: false + ConfigApps: + -tf + efs_provisioner_role_arn: + Description: ARN of the IAM role for the EFS provisioner. + Type: string + Required: false + ConfigApps: + -tf + eks_cluster_id: + Description: EKS cluster ID for the K8s cluster. + Type: string + Required: false + ConfigApps: + -tf + elasticsearch_endpoint: + Description: Endpoint of the Elasticsearch datasink for aggregated logs and events. + Type: string + Required: false + ConfigApps: + -tf + elasticsearch_retention_days: + Description: Retention period in days of Elasticsearch indices. + Type: number + Required: false + ConfigApps: + -tf + existing_tiller_service_account_name: + Description: Name of existing Tiller service account to use for Helm. + Type: string + Required: false + ConfigApps: + -tf + external_dns_role_arn: + Description: IAM role ARN for ExternalDNS. + Type: string + Required: false + ConfigApps: + -tf + fbinternal_orc8r_chart_version: + Description: Version of the Orchestrator fbinternal module Helm chart to install. + Type: string + Required: false + ConfigApps: + -tf + Default: "0.2.1" + feg_orc8r_chart_version: + Description: Version of the Orchestrator feg module Helm chart to install. + Type: string + Required: false + ConfigApps: + -tf + Default: "0.2.2" + helm_deployment_name: + Description: Name for the Helm release. + Type: string + Required: false + ConfigApps: + -tf + helm_pass: + Description: Helm repository password. + Type: string + Required: true + ConfigApps: + -tf + helm_repo: + Description: Helm repository URL for Orchestrator charts. + Type: string + Required: true + ConfigApps: + -tf + helm_user: + Description: Helm username to login to repository with. + Type: string + Required: true + ConfigApps: + -tf + install_tiller: + Description: Install Tiller in the cluster or not. + Type: bool + Required: false + ConfigApps: + -tf + lte_orc8r_chart_version: + Description: Version of the Orchestrator lte module Helm chart to install. + Type: string + Required: false + ConfigApps: + -tf + Default: "0.2.4" + monitoring_kubernetes_namespace: + Description: K8s namespace to install Orchestrator monitoring components into. + Type: string + Required: false + ConfigApps: + -tf + orc8r_chart_version: + Description: Version of the core Orchestrator Helm chart to install. + Type: string + Required: false + ConfigApps: + -tf + Default: "1.5.19" + orc8r_controller_replicas: + Description: Replica count for Orchestrator controller pods. + Type: number + Required: false + ConfigApps: + -tf + orc8r_db_port: + Description: DB port for Orchestrator database connection. + Type: number + Required: false + ConfigApps: + -tf + orc8r_deployment_type: + Description: Deployment type of Orchestrator (fwa, federated fwa(ffwa), all). + Type: string + Required: true + ConfigApps: + -tf + orc8r_kubernetes_namespace: + Description: K8s namespace to install main Orchestrator components into. + Type: string + Required: false + ConfigApps: + -tf + orc8r_proxy_replicas: + Description: Replica count for Orchestrator proxy pods. + Type: number + Required: false + ConfigApps: + -tf + orc8r_route53_zone_id: + Description: Route53 zone ID of Orchestrator domain name for ExternalDNS. + Type: string + Required: false + ConfigApps: + -tf + orc8r_tag: + Description: Image tag for Orchestrator components. + Type: string + Required: true + ConfigApps: + -tf + secretsmanager_orc8r_name: + Description: Name of the AWS Secrets Manager secret where Orchestrator deployment secrets will be stored. + Type: string + Required: false + ConfigApps: + -tf + seed_certs_dir: + Description: Directory on LOCAL disk where Orchestrator certificates are stored to seed Secrets Manager values. Home directory and env vars will be expanded. + Type: string + Required: false + ConfigApps: + -tf + state_backend: + Description: State backend for terraform (e.g. s3, local). + Type: string + Required: false + ConfigApps: + -tf + state_config: + Description: Optional config for state backend. The object type will depend on your backend. + Type: any + Required: false + ConfigApps: + -tf + tiller_namespace: + Description: Namespace where Tiller is installed or should be installed into. + Type: string + Required: false + ConfigApps: + -tf + wifi_orc8r_chart_version: + Description: Version of the Orchestrator wifi module Helm chart to install. + Type: string + Required: false + ConfigApps: + -tf + Default: "0.2.1" + seed_certs_dir: + Description: Directory on LOCAL disk where Orchestrator certificates are stored to seed Secrets Manager values. Home directory and env vars will be expanded. + Type : string + Required: false + ConfigApps: + -tf + Default : "/root/project/secrets" \ No newline at end of file diff --git a/orc8r/cloud/deploy/orc8r_deployer/docker/root/scripts/cli/__init__.py b/orc8r/cloud/deploy/orc8r_deployer/docker/root/scripts/cli/__init__.py new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/orc8r/cloud/deploy/orc8r_deployer/docker/root/scripts/cli/certs.py b/orc8r/cloud/deploy/orc8r_deployer/docker/root/scripts/cli/certs.py new file mode 100644 index 000000000000..ce6b124e5301 --- /dev/null +++ b/orc8r/cloud/deploy/orc8r_deployer/docker/root/scripts/cli/certs.py @@ -0,0 +1,55 @@ +""" +Copyright 2021 The Magma Authors. + +This source code is licensed under the BSD-style license found in the +LICENSE file in the root directory of this source tree. + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +""" +import click + +from .common import ( + run_command, + run_playbook, + print_error_msg, + print_success_msg) + +@click.group() +@click.pass_context +def certs(ctx): + """ + Manage certs in orc8r deployment + """ + pass + +@certs.command() +@click.pass_context +@click.option('--self-signed', is_flag=True) +def add(ctx, self_signed): + """ + Add creates application and self signed(optional) certs + """ + if self_signed: + run_playbook([ + "ansible-playbook", + "-v", + "-e", + "@/root/config.yml", + "-t", + "addcerts", + "%s/certs.yml" % ctx.obj["playbooks"]]) + else: + run_playbook([ + "ansible-playbook", + "-v", + "-e", + "@/root/config.yml", + "-t", + "addcerts", + "--skip-tags", + "self_signed", + "%s/certs.yml" % ctx.obj["playbooks"]]) \ No newline at end of file diff --git a/orc8r/cloud/deploy/orc8r_deployer/docker/root/scripts/cli/cleanup.py b/orc8r/cloud/deploy/orc8r_deployer/docker/root/scripts/cli/cleanup.py new file mode 100644 index 000000000000..f009db2c4a82 --- /dev/null +++ b/orc8r/cloud/deploy/orc8r_deployer/docker/root/scripts/cli/cleanup.py @@ -0,0 +1,96 @@ +""" +Copyright 2021 The Magma Authors. + +This source code is licensed under the BSD-style license found in the +LICENSE file in the root directory of this source tree. + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +""" +import os +import sys +import json + +import click +from boto3 import Session + +from .common import ( + run_command, + run_playbook, + print_error_msg, + print_success_msg) + +def setup_aws_creds(): + session = Session() + creds = session.get_credentials() + if not creds: + print_error_msg(''' +AWS credentials not configured. +configure through awscli or through orcl +orcl configure set -k aws_access_key_id -v +orcl configure set -k aws_secret_access_key -v +orcl configure set -k region -v +''') + sys.exit(1) + + frozen_creds = creds.get_frozen_credentials() + os.environ["AWS_ACCESS_KEY_ID"] = frozen_creds.access_key + os.environ["AWS_SECRET_ACCESS_KEY"] = frozen_creds.secret_key + + +@click.group(invoke_without_command=True) +@click.pass_context +def cleanup(ctx): + """ + Removes resources deployed for orc8r + """ + tf_destroy = [ "terraform", "destroy", "-auto-approve"] + + if ctx.invoked_subcommand is None: + cmd = " ".join(tf_destroy) + click.echo(f"Following commands will be run during cleanup\n{cmd}") + click.confirm('Do you want to continue with cleanup?', abort=True) + click.echo(f"Running {cmd}") + rc = run_command(tf_destroy) + if rc != 0: + print_error_msg("Destroy Failed!!! Attempt cleaning up individual resources using 'orcl cleanup raw' subcommand") + return + +@cleanup.command() +@click.pass_context +@click.option('--dryrun', default=False, is_flag=True, help='Show resources to be cleaned up during raw cleanup') +@click.option('--state', help='Provide state file containing resource information e.g. terraform.tfstate or terraform.tfstate.backup') +@click.option('--values', multiple=True, help='Key value pairs. for e.g. cluster_name,orc8r. Can be used multiple times') +def raw(ctx, dryrun, state, values): + """ + Individually cleans up resources deployed for orc8r + """ + if state: + ctx.obj['cleanup_state'] = state + + # add additional items + for config_items in values: + k, v = config_items.split(",") + ctx.obj[k] = v + + extra_vars = json.dumps(ctx.obj) + cleanup_playbook = "%s/cleanup.yml" % ctx.obj["playbooks"] + playbook_args = [ "ansible-playbook", "-v", "-e", extra_vars] + + # Few boto dependent modules in ansible require these values to be + # setup as environment variables. Hence setting these up. + setup_aws_creds() + + if dryrun: + tag_args = ["-t", "cleanup_dryrun"] + else: + tag_args = ["-t", "cleanup"] + + rc = run_playbook(playbook_args + tag_args + [cleanup_playbook]) + if rc != 0: + print_error_msg("Failed cleaning up resources!!!") + sys.exit(1) + print_success_msg("Successfully cleaned up underlying resources") \ No newline at end of file diff --git a/orc8r/cloud/deploy/orc8r_deployer/docker/root/scripts/cli/common.py b/orc8r/cloud/deploy/orc8r_deployer/docker/root/scripts/cli/common.py new file mode 100644 index 000000000000..a72b72235725 --- /dev/null +++ b/orc8r/cloud/deploy/orc8r_deployer/docker/root/scripts/cli/common.py @@ -0,0 +1,40 @@ +""" +Copyright 2021 The Magma Authors. + +This source code is licensed under the BSD-style license found in the +LICENSE file in the root directory of this source tree. + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +""" +import subprocess + +import click +from ansible.cli.playbook import PlaybookCLI + +def run_playbook(args): + pb_cli = PlaybookCLI(args) + pb_cli.parse() + return pb_cli.run() + +def run_command(cmd): + with subprocess.Popen(cmd, stdout=subprocess.PIPE) as p: + for output in p.stdout: + click.echo(output, nl=False) + return p.wait() + return 1 + +def print_error_msg(msg): + click.echo(click.style(msg, fg='red')) + +def print_success_msg(msg): + click.echo(click.style(msg, fg='green')) + +def print_warning_msg(msg): + click.echo(click.style(msg, fg='yellow')) + +def print_info_msg(msg): + click.echo(click.style(msg, fg='blue')) \ No newline at end of file diff --git a/orc8r/cloud/deploy/orc8r_deployer/docker/root/scripts/cli/configlib.py b/orc8r/cloud/deploy/orc8r_deployer/docker/root/scripts/cli/configlib.py new file mode 100644 index 000000000000..c843618f92f5 --- /dev/null +++ b/orc8r/cloud/deploy/orc8r_deployer/docker/root/scripts/cli/configlib.py @@ -0,0 +1,223 @@ +""" +Copyright 2021 The Magma Authors. + +This source code is licensed under the BSD-style license found in the +LICENSE file in the root directory of this source tree. + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +""" + +import os +import json +import yaml +from subprocess import check_call, CalledProcessError + +from jinja2 import Environment, FileSystemLoader +from prettytable import PrettyTable +import click + +def get_input(text, default_val): + if default_val: + resp = click.prompt(text, default=default_val) + else: + resp = click.prompt(text, default=default_val, show_default=False) + + # strip any quotes + resp = resp.strip("\'").strip("\"") + return resp + +def get_json(fn: str) -> dict: + try: + with open(fn) as f: + return json.load(f) + except OSError: + pass + return {} + +def put_json(fn: str, cfgs: dict): + with open(fn, 'w') as outfile: + json.dump(cfgs, outfile) + +def add_pretty_table(fields, items): + table = PrettyTable() + table.field_names = fields + for field in fields: + table.align[field] = 'l' + + for item in items: + table.add_row(item) + return table + +def render_j2_template(src_dir, dst_fn, cfg): + env = Environment(loader=FileSystemLoader(searchpath=src_dir)) + fn = os.path.basename(dst_fn) + template = env.get_template(f'{fn}.j2') + try: + with open(dst_fn, "w") as f: + f.write(template.render(cfg=cfg)) + except Exception as err: + click.echo(f"Error: {fn} rendering {err!r} file ") + +class ConfigManager(object): + ''' + ConfigManager manages the orcl configuration. It reads the variable + definitions during init and uses the information parsed to configure + the various component attributes. + Currently the variables are used to configure aws cli and + terraform. + ''' + def _get_config_fn(self, component: str): + config_dir = self.constants['config_dir'] + return f"{config_dir}/{component}.tfvars.json" + + def set(self, component: str, key: str, value: str): + click.echo(f"Setting key {key} value {value} for component {component}") + cfgs = get_json(self._get_config_fn(component)) + config_vars = self.config_vars[component] + + if not config_vars.get(key): + click.echo("not a valid key") + return + cfgs[key] = value + + self._configure_aws(component, cfgs) + self._configure_tf(component, cfgs) + put_json(self._get_config_fn(component), cfgs) + + def configure(self, component: str): + click.echo(click.style(f"\nConfiguring {component} deployment variables ", underline=True)) + cfgs = self.configs[component] + # TODO: use a different yaml loader to ensure we load inorder + # sort the variables to group the inputs together + config_vars = self.config_vars[component].items() + for config_key, config_info in sorted(config_vars, key=lambda s: s[0]): + config_description = config_info.get('Description', config_key).strip('.') + defaultValue = config_info.get('Default') + # add defaults to the json configs to ensure we can run prechecks + if defaultValue: + cfgs[config_key] = defaultValue + continue + + if not config_info['Required']: + continue + + v = cfgs.get(config_key) + if v: + inp = get_input(f"{config_key}({config_description})" , v) + else: + inp = get_input(f"{config_key}({config_description})" , v) + + # strip quotes from input + if inp: + cfgs[config_key] = inp + else: + if v is None: + if click.confirm("press 'y' to set empty string and " + "'n' to skip", prompt_suffix=': '): + cfgs[config_key] = "" + + self.configs[component] = cfgs + self._configure_aws(component, cfgs) + self._configure_tf(component, cfgs) + put_json(self._get_config_fn(component), cfgs) + + def _configure_aws(self, component: str, cfgs: dict): + ''' configures aws cli with configuration ''' + for k, v in cfgs.items(): + if k in self.aws_vars: + check_call(["aws", "configure", "set", k, v]) + + def _configure_tf(self, component: str, cfgs: dict): + ''' updates the terraform auto configuration and main.tf ''' + auto_tf = self.constants['auto_tf'] + auto_cfgs = get_json(auto_tf) + for k, v in cfgs.items(): + if k in self.tf_vars: + auto_cfgs[k] = v + put_json(auto_tf, auto_cfgs) + + # render main.tf with terraform variables alone + tf_cfgs = {} + for component in self.configs: + tf_cfgs[component] = {} + for k, v in self.configs[component].items(): + if k in self.tf_vars: + tf_cfgs[component][k] = v + + render_j2_template( + self.constants['tf_dir'], + self.constants['main_tf'], + tf_cfgs) + render_j2_template( + self.constants['tf_dir'], + self.constants['vars_tf'], + self.tf_vars) + + + def check(self, component: str) -> bool: + ''' check if all mandatory options of a specific component is set ''' + cfgs = self.configs[component] + valid = True + missing_cfgs = [] + for k, v in self.config_vars[component].items(): + if v['Required'] and cfgs.get(k) is None: + missing_cfgs.append(k) + valid = False + if missing_cfgs: + click.echo(f"Missing {missing_cfgs!r} configs for {component} component") + else: + click.echo(f"All mandatory configs for {component} has been configured") + return valid + + def info(self, component: str): + ''' pretty click.echo vars yml ''' + click.echo (f"{component} Configuration Options") + fields = ["Name", "Description", "Type", "Required", "Used By"] + items = [] + for k, v in self.config_vars[component].items(): + items.append([ + k, + v["Description"], + v["Type"], + v["Required"], + v["ConfigApps"] + ]) + click.echo(add_pretty_table(fields, items)) + + def show(self, component: str): + ''' pretty click.echo existing configuration ''' + click.echo (f"{component} Configuration") + items = [[k, v] for k, v in self.configs[component].items()] + fields = ["Name", "Configuration"] + + click.echo(add_pretty_table(fields, items)) + + def __init__(self, constants: dict): + self.config_vars = {} + self.configs = {} + self.tf_vars = set() + self.aws_vars = set() + self.constants = constants + vars_fn = constants['vars_definition'] + try: + with open(vars_fn) as f: + self.config_vars = yaml.load(f, Loader=yaml.FullLoader) + except OSError: + click.echo(f"Failed opening vars file {vars_fn}") + + # read all configs + for component in constants['components']: + try: + fn = self._get_config_fn(component) + self.configs[component] = get_json(fn) + for k, v in self.config_vars[component].items(): + if 'tf' in v["ConfigApps"]: + self.tf_vars.add(k) + if 'awscli' in v["ConfigApps"]: + self.aws_vars.add(k) + except OSError: + pass diff --git a/orc8r/cloud/deploy/orc8r_deployer/docker/root/scripts/cli/configlib_test.py b/orc8r/cloud/deploy/orc8r_deployer/docker/root/scripts/cli/configlib_test.py new file mode 100644 index 000000000000..df24e3ade951 --- /dev/null +++ b/orc8r/cloud/deploy/orc8r_deployer/docker/root/scripts/cli/configlib_test.py @@ -0,0 +1,242 @@ +""" +Copyright 2021 The Magma Authors. + +This source code is licensed under the BSD-style license found in the +LICENSE file in the root directory of this source tree. + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +""" + +import os +import shutil +import yaml +from tempfile import mkdtemp + +from unittest import TestCase, main +from unittest.mock import patch, Mock + +import configlib + + +class TestConfigManager(TestCase): + def setUp(self): + self.root_dir = mkdtemp() + self.constants = { + 'project_dir': self.root_dir, + 'config_dir': '%s/configs' % self.root_dir, + 'vars_definition': '%s/vars.yml' % self.root_dir, + 'components': ['infra', 'platform', 'service'], + 'auto_tf': self.root_dir + '/terraform.tfvars.json', + 'tf_dir': self.root_dir, + 'main_tf': self.root_dir + '/main.tf', + 'vars_tf': self.root_dir + '/vars.tf' + } + # add config directory + os.makedirs(self.constants["config_dir"]) + test_vars = { + "infra": { + "aws_access_key_id": { + "Required": True, + "ConfigApps": ["awscli"] + }, + "aws_secret_access_key": { + "Required": True, + "ConfigApps": ["awscli"] + }, + "cluster_name": { + "Required": False, + "ConfigApps": ["tf"] + }, + "secretsmanager_orc8r_secret": { + "Required": True, + "ConfigApps": ["tf"] + } + }, + "platform": { + "deploy_elastic": { + "Required": False, + "ConfigApps": ["tf"] + }, + "nms_db_password": { + "Required": True, + "ConfigApps": ["tf"] + } + }, + "service": { + "lte_orc8r_chart_version": { + "Required": False, + "Default": "0.2.4", + "ConfigApps": ["tf"] + }, + } + } + with open(self.constants['vars_definition'], 'w') as f: + yaml.dump(test_vars, f) + + # write a simple jinja template file + jinja_template = ( +''' +{% for k in cfg['infra'] %} +{{k}}=var.{{k}}{% endfor %} +''') + with open(self.constants['tf_dir'] + '/main.tf.j2', 'w') as f: + f.write(jinja_template) + + jinja_template = ( +''' +{% for k in cfg %} +variable "{{k}}" {}{% endfor %} +''') + with open(self.constants['tf_dir'] + '/vars.tf.j2', 'w') as f: + f.write(jinja_template) + + def tearDown(self): + try: + shutil.rmtree(self.root_dir) + except: + pass + + @patch("configlib.get_input") + @patch("configlib.check_call") + def test_configure_sanity(self, mock_check_call, mock_get_input): + mock_vals = { + 'aws_access_key_id': 'foo', + 'aws_secret_access_key': 'bar', + 'secretsmanager_orc8r_secret': 'jar', + } + mock_get_input.side_effect = [ + mock_vals['aws_access_key_id'], + mock_vals['aws_secret_access_key'], + mock_vals['secretsmanager_orc8r_secret'], + ] + # verify if components tfvars json is created + mgr = configlib.ConfigManager(self.constants) + mgr.configure('infra') + + # verify if configs are set in infra tfvars json + fn = "%s/infra.tfvars.json" % self.constants['config_dir'] + cfg = configlib.get_json(fn) + self.assertEqual(len(cfg.keys()), 3) + self.assertEqual(cfg['aws_access_key_id'], "foo") + self.assertEqual(cfg['aws_secret_access_key'], "bar") + self.assertEqual(cfg['secretsmanager_orc8r_secret'], "jar") + + # check if aws configs are set + aws_config_cmd = ['aws', 'configure', 'set'] + mock_check_call.assert_any_call( + aws_config_cmd + ['aws_access_key_id', 'foo']) + mock_check_call.assert_any_call( + aws_config_cmd + ['aws_secret_access_key', 'bar']) + + # verify that platform tfvars json file isn't present + fn = "%s/platform.tfvars.json" % self.constants['config_dir'] + self.assertEqual(os.path.isfile(fn), False) + + # reset mocks + mock_get_input.reset_mock() + mock_check_call.reset_mock() + + mock_vals = { + 'nms_db_password': 'foo', + } + mock_get_input.side_effect = [ + mock_vals['nms_db_password'], + ] + + # configure platform + mgr.configure('platform') + + # verify that no aws call was invoked + self.assertEqual(mock_check_call.call_count, 0) + + # check if we only invoked input for nms_db_password + self.assertEqual(mock_get_input.call_count, 1) + fn = "%s/platform.tfvars.json" % self.constants['config_dir'] + cfg = configlib.get_json(fn) + self.assertEqual(len(cfg.keys()), 1) + self.assertEqual(cfg['nms_db_password'], "foo") + + + # verify that service tfvars json file isn't present + fn = "%s/service.tfvars.json" % self.constants['config_dir'] + self.assertEqual(os.path.isfile(fn), False) + + # reset mocks + mock_get_input.reset_mock() + mock_check_call.reset_mock() + + # configure service + mgr.configure('service') + + # verify that no input or aws call was invoked + self.assertEqual(mock_check_call.call_count, 0) + self.assertEqual(mock_get_input.call_count, 0) + + fn = "%s/service.tfvars.json" % self.constants['config_dir'] + cfg = configlib.get_json(fn) + # verify that default value was set + self.assertEqual(len(cfg.keys()), 1) + self.assertEqual(cfg['lte_orc8r_chart_version'], "0.2.4") + + # finally verify if all configs required by tf is present + cfg = configlib.get_json(self.constants['auto_tf']) + self.assertEqual(len(cfg.keys()), 3) + self.assertEqual(cfg['secretsmanager_orc8r_secret'], "jar") + self.assertEqual(cfg['nms_db_password'], "foo") + self.assertEqual(cfg['lte_orc8r_chart_version'], "0.2.4") + + # verify if jinja template has been rendered accordingly + with open(self.constants['main_tf']) as f: + jinja_cfg = dict(ln.split('=') for ln in f.readlines() if ln.strip()) + + # all infra terraform keys should be present in the jinja template + self.assertEqual( + set(jinja_cfg.keys()), + set(['secretsmanager_orc8r_secret'])) + + # variable tf is of the form variable "var_name" {} + # get the middle element and remove the quotes + with open(self.constants['vars_tf']) as f: + jinja_cfg = [ln.split()[1][1:-1] for ln in f.readlines() if ln.strip()] + + # all infra terraform keys should be present in the jinja template + self.assertEqual(set(jinja_cfg), set(mgr.tf_vars)) + + @patch("configlib.get_input") + @patch("configlib.check_call") + def test_configure_set(self, mock_check_call, mock_get_input): + mock_vals = { + 'nms_db_password': 'foo', + } + mock_get_input.side_effect = [ + mock_vals['nms_db_password'], + ] + + # configure platform + mgr = configlib.ConfigManager(self.constants) + mgr.configure('platform') + + # check if we only invoked input for nms_db_password + self.assertEqual(mock_get_input.call_count, 1) + fn = "%s/platform.tfvars.json" % self.constants['config_dir'] + cfg = configlib.get_json(fn) + self.assertEqual(len(cfg.keys()), 1) + self.assertEqual(cfg['nms_db_password'], "foo") + + mgr.set('platform', 'deploy_elastic', 'true') + cfg = configlib.get_json(fn) + self.assertEqual(len(cfg.keys()), 2) + self.assertEqual(cfg['deploy_elastic'], "true") + + # finally verify if all configs required by tf is present + cfg = configlib.get_json(self.constants['auto_tf']) + self.assertEqual(len(cfg.keys()), 2) + self.assertEqual(cfg['nms_db_password'], "foo") + self.assertEqual(cfg['deploy_elastic'], "true") + +if __name__ == '__main__': + main() \ No newline at end of file diff --git a/orc8r/cloud/deploy/orc8r_deployer/docker/root/scripts/cli/configure.py b/orc8r/cloud/deploy/orc8r_deployer/docker/root/scripts/cli/configure.py new file mode 100644 index 000000000000..115c08bea10d --- /dev/null +++ b/orc8r/cloud/deploy/orc8r_deployer/docker/root/scripts/cli/configure.py @@ -0,0 +1,93 @@ +""" +Copyright 2021 The Magma Authors. + +This source code is licensed under the BSD-style license found in the +LICENSE file in the root directory of this source tree. + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +""" +import sys + +import click + +from .configlib import ConfigManager + +def get_component_choices(): + return ['infra', 'platform', 'service'] + +@click.group(invoke_without_command=True) +@click.option('-c', '--component', + type=click.Choice(get_component_choices()), + multiple=True, + default=get_component_choices()) +@click.pass_context +def configure(ctx, component): + """ + Configure orc8r deployment variables + """ + if ctx.invoked_subcommand is None: + mgr = ConfigManager(ctx.obj) + for c in component: + mgr.configure(c) + +@configure.command() +@click.pass_context +@click.option('-c', '--component', + type=click.Choice(get_component_choices()), + multiple=True, + default=get_component_choices()) +def show(ctx, component): + """ + Display the current configuration + """ + mgr = ConfigManager(ctx.obj) + for c in component: + mgr.show(c) + +@configure.command() +@click.option('-c', '--component', + type=click.Choice(get_component_choices()), + multiple=True, + default=get_component_choices()) +@click.pass_context +def info(ctx, component): + """ + Display all possible config options in detail + """ + mgr = ConfigManager(ctx.obj) + for c in component: + mgr.info(c) + +@configure.command() +@click.option('-c', '--component', + type=click.Choice(get_component_choices()), + multiple=True, + default=get_component_choices()) +@click.pass_context +def check(ctx, component): + """ + Check if all mandatory configs are present + """ + mgr = ConfigManager(ctx.obj) + valid = True + for c in component: + valid = mgr.check(c) + if not valid: + sys.exit(1) + +@configure.command() +@click.option('-c', '--component', + type=click.Choice(get_component_choices()), + prompt='select component') +@click.option('-k', '--key', prompt='name of the variable') +@click.option('-v', '--value', prompt='value of the variable') +@click.pass_context +def set(ctx, component, key, value): + """ + Set a specific configuration attribute + """ + ConfigManager(ctx.obj).set(component, key, value) \ No newline at end of file diff --git a/orc8r/cloud/deploy/orc8r_deployer/docker/root/scripts/cli/install.py b/orc8r/cloud/deploy/orc8r_deployer/docker/root/scripts/cli/install.py new file mode 100644 index 000000000000..1f7130b49a06 --- /dev/null +++ b/orc8r/cloud/deploy/orc8r_deployer/docker/root/scripts/cli/install.py @@ -0,0 +1,91 @@ +""" +Copyright 2021 The Magma Authors. + +This source code is licensed under the BSD-style license found in the +LICENSE file in the root directory of this source tree. + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +""" +import os +import sys +import glob + +import click + +from .common import ( + run_command, + run_playbook, + print_error_msg, + print_success_msg, + print_warning_msg, + print_info_msg, +) + +@click.group(invoke_without_command=True) +@click.pass_context +def install(ctx): + """ + Deploy new instance of orc8r + """ + constants = ctx.obj + + tf_init = ["terraform", "init"] + tf_orc8r = [ "terraform", "apply", "-target=module.orc8r", "-auto-approve"] + tf_secrets = [ "terraform", "apply", "-target=module.orc8r-app.null_resource.orc8r_seed_secrets", "-auto-approve"] + tf_orc8r_app = [ "terraform", "apply", "-auto-approve"] + + if ctx.invoked_subcommand is None: + if click.confirm('Do you want to run installation prechecks?'): + ctx.invoke(precheck) + else: + print_warning_msg(f"Skipping installation prechecks") + + for tf_cmd in [tf_init, tf_orc8r, tf_secrets, tf_orc8r_app]: + cmd = " ".join(tf_cmd) + if click.confirm(f'Do you want to continue with {cmd}?'): + rc = run_command(tf_cmd) + if rc != 0: + print_error_msg(f"Install failed when running {cmd} !!!") + return + + # set the kubectl after bringing up the infra + if tf_cmd == tf_orc8r or tf_orc8r_app: + kubeconfigs = glob.glob(constants['project_dir'] + "/kubeconfig_*") + if len(kubeconfigs) != 1: + if len(kubeconfigs) == 0: + print_success_msg('No kubeconfig found!!!') + else: + print_error_msg("multiple kubeconfigs found %s!!!" % repr(kubeconfigs)) + return + kubeconfig = kubeconfigs[0] + os.environ['KUBECONFIG'] = kubeconfig + print_info_msg(f'For accessing kubernetes cluster, set `export KUBECONFIG={kubeconfig}`') + print_success_msg(f"Command {cmd} ran successfully") + + else: + print_warning_msg(f"Skipping Command {cmd}") + + + +@install.command() +@click.pass_context +def precheck(ctx): + """ + Performs various checks to ensure successful installation + """ + rc = run_playbook([ + "ansible-playbook", + "-v", + "-e", + "@/root/config.yml", + "-t", + "install_precheck", + "%s/main.yml" % ctx.obj["playbooks"]]) + if rc != 0: + print_error_msg("Install prechecks failed!!!") + sys.exit(1) + print_success_msg("Install prechecks ran successfully") diff --git a/orc8r/cloud/deploy/orc8r_deployer/docker/root/scripts/cli/orcl.py b/orc8r/cloud/deploy/orc8r_deployer/docker/root/scripts/cli/orcl.py new file mode 100755 index 000000000000..d525804e2364 --- /dev/null +++ b/orc8r/cloud/deploy/orc8r_deployer/docker/root/scripts/cli/orcl.py @@ -0,0 +1,62 @@ +""" +Copyright 2021 The Magma Authors. + +This source code is licensed under the BSD-style license found in the +LICENSE file in the root directory of this source tree. + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +""" +from typing import List +import os +import sys +import argparse +import subprocess +import pprint +import yaml +import pathlib + +import click + +from .configure import configure +from .install import install +from .upgrade import upgrade +from .verify import verify +from .certs import certs +from .cleanup import cleanup + +def init(): + constants = None + try: + with open("/root/config.yml") as f: + constants = yaml.load(f, Loader=yaml.FullLoader) + except OSError: + click.echo("Failed opening config.yml file") + + dirnames = (constants["config_dir"], constants["secret_dir"]) + for dirname in dirnames: + try: + pathlib.Path(dirname).mkdir(parents=True, exist_ok=True) + except OSError as error: + click.echo(f"failed creating dir {dirname} error {error}") + sys.exit(1) + return constants + +@click.group() +@click.version_option() +@click.pass_context +def cli(ctx): + """ + Orchestrator Deployment CLI + """ + ctx.obj = init() + +cli.add_command(configure) +cli.add_command(certs) +cli.add_command(install) +cli.add_command(upgrade) +cli.add_command(verify) +cli.add_command(cleanup) diff --git a/orc8r/cloud/deploy/orc8r_deployer/docker/root/scripts/cli/upgrade.py b/orc8r/cloud/deploy/orc8r_deployer/docker/root/scripts/cli/upgrade.py new file mode 100644 index 000000000000..4bc2fbec059d --- /dev/null +++ b/orc8r/cloud/deploy/orc8r_deployer/docker/root/scripts/cli/upgrade.py @@ -0,0 +1,71 @@ +""" +Copyright 2021 The Magma Authors. + +This source code is licensed under the BSD-style license found in the +LICENSE file in the root directory of this source tree. + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +""" +import sys + +import click + +from .common import ( + run_command, + run_playbook, + print_error_msg, + print_success_msg, + print_warning_msg +) + +@click.group(invoke_without_command=True) +@click.pass_context +def upgrade(ctx): + """ + Upgrade existing orc8r deployment + """ + tf_cmds = [ + ["terraform", "init", "--upgrade"], + ["terraform", "refresh"], + [ "terraform", "apply", "-auto-approve"] + ] + + if ctx.invoked_subcommand is None: + if click.confirm('Do you want to run upgrade prechecks?'): + ctx.invoke(precheck) + else: + print_warning_msg(f"Skipping upgrade prechecks") + + click.echo( + "Following commands will be run during upgrade\n%s" % ( + "\n".join((map(" ".join, tf_cmds))) + )) + for cmd in tf_cmds: + if click.confirm('Do you want to continue with %s?' % " ".join(cmd)): + rc = run_command(cmd) + if rc != 0: + print_error_msg("Upgrade Failed!!!") + return + +@upgrade.command() +@click.pass_context +def precheck(ctx): + """ + Precheck runs various checks to ensure successful upgrade + """ + rc = run_playbook([ + "ansible-playbook", + "-v", + "-e", + "@/root/config.yml", + "-t", + "upgrade_precheck", + "%s/main.yml" % ctx.obj["playbooks"]]) + if rc != 0: + print_error_msg("Upgrade prechecks failed!!!") + sys.exit(1) + print_success_msg("Upgrade prechecks ran successfully") \ No newline at end of file diff --git a/orc8r/cloud/deploy/orc8r_deployer/docker/root/scripts/cli/verify.py b/orc8r/cloud/deploy/orc8r_deployer/docker/root/scripts/cli/verify.py new file mode 100644 index 000000000000..5f5296d4f779 --- /dev/null +++ b/orc8r/cloud/deploy/orc8r_deployer/docker/root/scripts/cli/verify.py @@ -0,0 +1,65 @@ +""" +Copyright 2021 The Magma Authors. + +This source code is licensed under the BSD-style license found in the +LICENSE file in the root directory of this source tree. + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +""" +import os +import sys +import glob + +import click + +from .common import ( + run_playbook, + print_error_msg, + print_success_msg) + +@click.group() +@click.pass_context +def verify(ctx): + """ + Run post deployment checks on orc8r + """ + pass + +@verify.command('sanity') +@click.pass_context +def verify_sanity(ctx): + # check if KUBECONFIG is set else find kubeconfig file and set the + # environment variable + constants = ctx.obj + + # set kubeconfig + kubeconfig = os.environ.get('KUBECONFIG') + if not kubeconfig: + kubeconfigs = glob.glob(constants['project_dir'] + "/kubeconfig_*") + if len(kubeconfigs) != 1: + if len(kubeconfigs) == 0: + print_success_msg('No kubeconfig found!!!') + else: + print_error_msg("multiple kubeconfigs found %s!!!" % repr(kubeconfigs)) + return + kubeconfig = kubeconfigs[0] + + os.environ["KUBECONFIG"] = kubeconfig + os.environ["K8S_AUTH_KUBECONFIG"] = kubeconfig + + rc = run_playbook([ + "ansible-playbook", + "-v", + "-e", + "@/root/config.yml", + "-t", + "verify_sanity", + "%s/main.yml" % constants["playbooks"]]) + if rc != 0: + print_error_msg("Post deployment verification checks failed!!!") + sys.exit(1) + print_success_msg("Post deployment verification ran successfully") \ No newline at end of file diff --git a/orc8r/cloud/deploy/orc8r_deployer/docker/root/scripts/playbooks/certs.yml b/orc8r/cloud/deploy/orc8r_deployer/docker/root/scripts/playbooks/certs.yml new file mode 100644 index 000000000000..3acb3c6c0735 --- /dev/null +++ b/orc8r/cloud/deploy/orc8r_deployer/docker/root/scripts/playbooks/certs.yml @@ -0,0 +1,46 @@ +- hosts: localhost + tags: addcerts + tasks: + - name: Open configuration file + shell: "jq .orc8r_domain_name {{ config_dir }}/infra.tfvars.json" + ignore_errors: true + register: result + + - name: Check for a valid orc8r domain name + assert: + that: + - result.rc == 0 + - result.stdout != 'null' + fail_msg: + - "Failed reading valid orc8r domain name. " + - "Configure orc8r domain name 'orcl configure set -k orc8r_domain_name -v " + + - name: Set orc8r domain name + set_fact: + orc8r_domain_name: "{{ result.stdout | from_json }}" + + - name: Create certs directory + file: + name: "{{ secret_dir }}" + state: directory + mode: '0775' + + - name: Generate self-signed certs + command: "{{ magma_root }}/orc8r/cloud/deploy/scripts/self_sign_certs.sh {{ orc8r_domain_name }}" + args: + chdir: "{{ secret_dir }}" + tags: self_signed + + - name: Generate application certs + command: "{{ magma_root }}/orc8r/cloud/deploy/scripts/create_application_certs.sh {{ orc8r_domain_name }}" + args: + chdir: "{{ secret_dir }}" + + - name: Generate admin operator certs as pfx bundle + openssl_pkcs12: + action: export + path: "{{ secret_dir }}/admin_operator.pfx" + friendly_name: orc8rpfx + privatekey_path: "{{ secret_dir }}/admin_operator.key.pem" + certificate_path: "{{ secret_dir }}/admin_operator.pem" + state: present \ No newline at end of file diff --git a/orc8r/cloud/deploy/orc8r_deployer/docker/root/scripts/playbooks/cleanup.yml b/orc8r/cloud/deploy/orc8r_deployer/docker/root/scripts/playbooks/cleanup.yml new file mode 100644 index 000000000000..e30019c98f4c --- /dev/null +++ b/orc8r/cloud/deploy/orc8r_deployer/docker/root/scripts/playbooks/cleanup.yml @@ -0,0 +1,10 @@ +- hosts: localhost + roles: + - role: deploy + - role: services/orc8r + - role: services + - role: platform + - role: infra/aws + vars_files: + - roles/deploy/vars/all.yml + - roles/services/vars/all.yml \ No newline at end of file diff --git a/orc8r/cloud/deploy/orc8r_deployer/docker/root/scripts/playbooks/main.yml b/orc8r/cloud/deploy/orc8r_deployer/docker/root/scripts/playbooks/main.yml new file mode 100644 index 000000000000..179c60371f9d --- /dev/null +++ b/orc8r/cloud/deploy/orc8r_deployer/docker/root/scripts/playbooks/main.yml @@ -0,0 +1,10 @@ +- hosts: localhost + roles: + - role: deploy + - role: infra/aws + - role: platform + - role: services + - role: services/orc8r + vars_files: + - roles/deploy/vars/all.yml + - roles/services/vars/all.yml diff --git a/orc8r/cloud/deploy/orc8r_deployer/docker/root/scripts/playbooks/roles/deploy/tasks/main.yml b/orc8r/cloud/deploy/orc8r_deployer/docker/root/scripts/playbooks/roles/deploy/tasks/main.yml new file mode 100644 index 000000000000..8be463433194 --- /dev/null +++ b/orc8r/cloud/deploy/orc8r_deployer/docker/root/scripts/playbooks/roles/deploy/tasks/main.yml @@ -0,0 +1,5 @@ +- name: Secrets tasks + import_tasks: secrets.yml + +- name: Terraform tasks + import_tasks: tf.yml diff --git a/orc8r/cloud/deploy/orc8r_deployer/docker/root/scripts/playbooks/roles/deploy/tasks/secrets.yml b/orc8r/cloud/deploy/orc8r_deployer/docker/root/scripts/playbooks/roles/deploy/tasks/secrets.yml new file mode 100644 index 000000000000..b4d880982be2 --- /dev/null +++ b/orc8r/cloud/deploy/orc8r_deployer/docker/root/scripts/playbooks/roles/deploy/tasks/secrets.yml @@ -0,0 +1,20 @@ +- name: Get information from secrets directory + stat: + path: "{{secret_dir}}/{{item}}" + register: st_info + with_items: "{{ ORC8R_CERTS + FLUENTD_CERTS + ADMIN_CERTS}}" + tags: + - install_precheck + +- name: Debug secrets + debug: msg="{{st_info}}" + tags: + - install_precheck + +- name: Check if secrets are missing + assert: + that: "{{item.stat.exists}}" + fail_msg: "Secret file {{item.item}} does not exist" + with_items: "{{st_info.results}}" + tags: + - install_precheck \ No newline at end of file diff --git a/orc8r/cloud/deploy/orc8r_deployer/docker/root/scripts/playbooks/roles/deploy/tasks/tf.yml b/orc8r/cloud/deploy/orc8r_deployer/docker/root/scripts/playbooks/roles/deploy/tasks/tf.yml new file mode 100644 index 000000000000..089224bbbbf3 --- /dev/null +++ b/orc8r/cloud/deploy/orc8r_deployer/docker/root/scripts/playbooks/roles/deploy/tasks/tf.yml @@ -0,0 +1,287 @@ +- name: Initialize terraform + ansible.builtin.command: terraform init + args: + chdir: "{{project_dir}}" + tags: + - install_precheck + - upgrade_precheck + - verify_sanity + - cleanup_dryrun + - cleanup + +- name: Get terraform state facts + ansible.builtin.command: "{% if cleanup_state is defined %} terraform show -json {{cleanup_state}} {% else %} terraform show -json {% endif %}" + register: result + args: + chdir: "{{project_dir}}" + tags: + - install_precheck + - upgrade_precheck + - verify_sanity + - cleanup_dryrun + - cleanup + +- name: Check if terraform state command errors out + assert: + that: + - result.rc == 0 + msg: [ + "Error attempting to run 'terraform show' command", + "Terraform state not found", + "Check if the deployment directory {{project_dir}} contains terraform files" + ] + tags: + - install_precheck + - upgrade_precheck + - verify_sanity + - cleanup_dryrun + - cleanup + +- name: Check if any resources are present in terraform state + assert: + that: + - "{{ (result.stdout | from_json) | json_query('values') != None }}" + msg: [ + "No resources found in terraform state" + ] + tags: + - upgrade_precheck + - verify_sanity + +- name: Check if no resources are present in terraform state + assert: + that: + - "{{ (result.stdout | from_json) | json_query('values') == None }}" + fail_msg: "Prior terraform state present. Cleanup terraform.tfstate files or backend s3 state if necessary" + tags: + - install_precheck + +- name: Set terraform state fact + set_fact: + terraform_state: "{{result.stdout | from_json}}" + ignore_errors: true + tags: + - upgrade_precheck + - verify_sanity + - cleanup_dryrun + - cleanup + +- name: Set orc8r namespace + set_fact: + orc8r_namespace: "{{lookup('flattened', result.stdout | from_json | json_query(ORC8R_NAMESPACE_QUERY))}}" + when: orc8r_namespace is undefined + ignore_errors: true + tags: + - upgrade_precheck + - verify_sanity + - cleanup_dryrun + - cleanup + +- name: Debug orc8r namespace + debug: + msg: "orc8r_namespace : {{orc8r_namespace}}" + ignore_errors: true + tags: + - upgrade_precheck + - verify_sanity + - cleanup_dryrun + - cleanup + +- name: Set orc8r secrets + set_fact: + orc8r_secrets: "{{lookup('flattened', result.stdout | from_json | json_query(ORC8R_AWS_SECRETS_QUERY))}}" + ignore_errors: true + when: orc8r_secrets is undefined + tags: + - upgrade_precheck + - verify_sanity + - cleanup_dryrun + - cleanup + +- name: Debug orc8r secrets + debug: + msg: "orc8r_secrets : {{orc8r_secrets}}" + tags: + - upgrade_precheck + - verify_sanity + - cleanup_dryrun + - cleanup + +- name: Set orc8r ES domain + set_fact: + orc8r_es_domain: "{{lookup('flattened', result.stdout | from_json | json_query(ORC8R_ES_DOMAIN_QUERY))}}" + ignore_errors: true + when: orc8r_es_domain is undefined + tags: + - upgrade_precheck + - verify_sanity + - cleanup_dryrun + - cleanup + +- name: Debug orc8r es domain + debug: + msg: "orc8r_es_domain : {{orc8r_es_domain}}" + tags: + - upgrade_precheck + - verify_sanity + - cleanup_dryrun + - cleanup + +- name: Set orc8r cluster name + set_fact: + orc8r_cluster_name: "{{lookup('flattened', result.stdout | from_json | json_query(ORC8R_CLUSTER_NAME_QUERY))}}" + when: orc8r_cluster_name is undefined + ignore_errors: true + tags: + - upgrade_precheck + - verify_sanity + - cleanup_dryrun + - cleanup + +- name: Debug orc8r cluster name + debug: + msg: "orc8r_cluster_name : {{orc8r_cluster_name}}" + tags: + - upgrade_precheck + - verify_sanity + - cleanup_dryrun + - cleanup + +- name: Set orc8r db instance id + set_fact: + orc8r_db_id: "{{lookup('flattened', result.stdout | from_json | json_query(ORC8R_DB_ID_QUERY))}}" + when: orc8r_db_id is undefined + ignore_errors: true + tags: + - upgrade_precheck + - verify_sanity + - cleanup_dryrun + - cleanup + +- name: Debug orc8r db instance + debug: + msg: "orc8r_db_id : {{orc8r_db_id}}" + tags: + - upgrade_precheck + - verify_sanity + - cleanup_dryrun + - cleanup + +- name: Set orc8r db subnet + set_fact: + orc8r_db_subnet: "{{lookup('flattened', result.stdout | from_json | json_query(ORC8R_DB_SUBNET_QUERY))}}" + ignore_errors: true + tags: + - upgrade_precheck + - verify_sanity + - cleanup_dryrun + - cleanup + +- name: Debug orc8r db subnet + debug: + msg: "orc8r_db_subnet : {{orc8r_db_subnet}}" + tags: + - upgrade_precheck + - verify_sanity + - cleanup_dryrun + - cleanup + +- name: Set EFS mount targets + set_fact: + efs_mount_targets: "{{lookup('flattened', result.stdout | from_json | json_query(EFS_MOUNT_TARGET_ID_QUERY), wantlist=True)}}" + ignore_errors: true + when: efs_mount_targets is undefined + tags: + - upgrade_precheck + - verify_sanity + - cleanup_dryrun + - cleanup + +- name: Debug EFS mount targets + debug: + msg: "efs_mount_targets : {{efs_mount_targets}}" + tags: + - upgrade_precheck + - verify_sanity + - cleanup_dryrun + - cleanup + +- name: Set EFS FS targets + set_fact: + efs_fs_targets: "{{lookup('flattened', result.stdout | from_json | json_query(EFS_FILE_SYSTEM_ID_QUERY), wantlist=True)}}" + ignore_errors: true + when: efs_fs_targets is undefined + tags: + - upgrade_precheck + - verify_sanity + - cleanup_dryrun + - cleanup + +- name: Debug EFS FS targets + debug: + msg: "efs_fs_targets : {{efs_fs_targets}}" + tags: + - upgrade_precheck + - verify_sanity + - cleanup_dryrun + - cleanup + +- name: Set vpc + set_fact: + vpc: "{{lookup('flattened', result.stdout | from_json | json_query(VPC_QUERY))}}" + ignore_errors: true + when: vpc is undefined + tags: + - upgrade_precheck + - verify_sanity + - cleanup_dryrun + - cleanup + +- name: Debug vpc + debug: + msg: "vpc : {{vpc}}" + tags: + - upgrade_precheck + - verify_sanity + - cleanup_dryrun + - cleanup + +- name: Set hosted zone + set_fact: + hosted_zone: "{{lookup('flattened', result.stdout | from_json | json_query(HOSTED_ZONE_QUERY))}}" + ignore_errors: true + when: hosted_zone is undefined + tags: + - upgrade_precheck + - verify_sanity + - cleanup_dryrun + - cleanup + +- name: Debug hosted zone + debug: + msg: "hosted_zone : {{hosted_zone}}" + tags: + - upgrade_precheck + - verify_sanity + - cleanup_dryrun + - cleanup + +- name: Set region name + set_fact: + region_name: "{{lookup('flattened', result.stdout | from_json | json_query(REGION_QUERY) | first)}}" + ignore_errors: true + when: region_name is undefined + tags: + - upgrade_precheck + - verify_sanity + - cleanup_dryrun + - cleanup + +- name: Debug region name + debug: + msg: "region_name : {{region_name}}" + tags: + - upgrade_precheck + - verify_sanity + - cleanup_dryrun + - cleanup \ No newline at end of file diff --git a/orc8r/cloud/deploy/orc8r_deployer/docker/root/scripts/playbooks/roles/deploy/vars/all.yml b/orc8r/cloud/deploy/orc8r_deployer/docker/root/scripts/playbooks/roles/deploy/vars/all.yml new file mode 100644 index 000000000000..0d23222e6edd --- /dev/null +++ b/orc8r/cloud/deploy/orc8r_deployer/docker/root/scripts/playbooks/roles/deploy/vars/all.yml @@ -0,0 +1,29 @@ +ORC8R_NAMESPACE_QUERY: "values.root_module.child_modules[*].resources[?address=='module.orc8r-app.kubernetes_namespace.orc8r'].values.metadata[*].name" +ORC8R_AWS_SECRETS_QUERY: "values.root_module.child_modules[*].resources[?address=='module.orc8r.aws_secretsmanager_secret.orc8r_secrets'].values.name" +ORC8R_ES_DOMAIN_QUERY: "values.root_module.child_modules[*].resources[?type=='aws_elasticsearch_domain'].values.domain_name" +ORC8R_CLUSTER_NAME_QUERY: "values.root_module.child_modules[*].resources[?address=='module.orc8r-app.data.aws_eks_cluster.cluster'].values.name" +ORC8R_DB_ID_QUERY: "values.root_module.child_modules[*].resources[?address=='module.orc8r.aws_db_instance.default'].values.id" +ORC8R_DB_SUBNET_QUERY: "values.root_module.child_modules[*].resources[?address=='module.orc8r.aws_db_instance.default'].values.db_subnet_group_name" +NMS_DB_ID_QUERY: "values.root_module.child_modules[*].resources[?address=='module.orc8r.aws_db_instance.nms'].values.id" +EFS_MOUNT_TARGET_ID_QUERY: "values.root_module.child_modules[*].resources[?type=='aws_efs_mount_target'].values.id" +EFS_FILE_SYSTEM_ID_QUERY: "values.root_module.child_modules[*].resources[?type=='aws_efs_mount_target'].values.file_system_id" +HOSTED_ZONE_QUERY: "values.root_module.child_modules[*].resources[?address=='module.orc8r.aws_route53_zone.orc8r'].values" +VPC_QUERY: "values.root_module.child_modules[*].child_modules[*].resources[?type=='aws_vpc'].values" +REGION_QUERY: "values.root_module.child_modules[*].resources[?type=='aws_availability_zones'].values.id" + +ORC8R_CERTS: + - 'rootCA.pem' + - 'rootCA.key' + - 'controller.key' + - 'controller.crt' + - 'certifier.key' + - 'certifier.pem' + - 'bootstrapper.key' + +FLUENTD_CERTS: + - 'fluentd.key' + - 'fluentd.pem' + +ADMIN_CERTS: + - 'admin_operator.pem' + - 'admin_operator.key.pem' \ No newline at end of file diff --git a/orc8r/cloud/deploy/orc8r_deployer/docker/root/scripts/playbooks/roles/infra/aws/tasks/az.yml b/orc8r/cloud/deploy/orc8r_deployer/docker/root/scripts/playbooks/roles/infra/aws/tasks/az.yml new file mode 100644 index 000000000000..f16364ee7bfd --- /dev/null +++ b/orc8r/cloud/deploy/orc8r_deployer/docker/root/scripts/playbooks/roles/infra/aws/tasks/az.yml @@ -0,0 +1,15 @@ +- name: Check if we have atleast 3 availability zones in our deployment + amazon.aws.aws_az_info: + register: aws_az_info + failed_when: aws_az_info.availability_zones | length < 3 + tags: + - install_precheck + - upgrade_precheck + +- name: Debug availability zones present in our region + amazon.aws.aws_az_info: + register: aws_az_info + failed_when: aws_az_info.availability_zones | length < 3 + tags: + - install_precheck + - upgrade_precheck \ No newline at end of file diff --git a/orc8r/cloud/deploy/orc8r_deployer/docker/root/scripts/playbooks/roles/infra/aws/tasks/efs.yml b/orc8r/cloud/deploy/orc8r_deployer/docker/root/scripts/playbooks/roles/infra/aws/tasks/efs.yml new file mode 100644 index 000000000000..d5122e7cc7f6 --- /dev/null +++ b/orc8r/cloud/deploy/orc8r_deployer/docker/root/scripts/playbooks/roles/infra/aws/tasks/efs.yml @@ -0,0 +1,19 @@ +- name: Delete efs mount targets + command: aws efs delete-mount-target --mount-target-id "{{ item }}" + with_items: "{{ efs_mount_targets }}" + ignore_errors: true + tags: cleanup + +- name: Sleep for a while before deleting filesystem + pause: + minutes: 2 + when: efs_mount_targets + tags: cleanup + +- name: Delete efs volumes + command: aws efs delete-file-system --file-system-id "{{ item }}" + with_items: "{{ efs_fs_targets }}" + ignore_errors: true + when: efs_fs_targets + tags: cleanup + diff --git a/orc8r/cloud/deploy/orc8r_deployer/docker/root/scripts/playbooks/roles/infra/aws/tasks/eks.yml b/orc8r/cloud/deploy/orc8r_deployer/docker/root/scripts/playbooks/roles/infra/aws/tasks/eks.yml new file mode 100644 index 000000000000..5b5eaa6914c0 --- /dev/null +++ b/orc8r/cloud/deploy/orc8r_deployer/docker/root/scripts/playbooks/roles/infra/aws/tasks/eks.yml @@ -0,0 +1,56 @@ +- name: Get EKS cluster information + ansible.builtin.shell: aws eks describe-cluster --name {{ infra_configs.cluster_name | quote }} || /bin/true + register: result + tags: upgrade_precheck + +- name: Check if deployed EKS cluster version is greater than version specified in values + assert: + that: "{{ infra_configs.cluster_version is version((result.stdout | from_json).cluster.version, '>=') }}" + msg: + - "deployed version higher than to be configured version" + - "Configure the cluster_version to be >= {{(result.stdout | from_json).cluster.version}}" + - "For e.g: orcl configure set -c infra -k cluster_version -v {{(result.stdout | from_json).cluster.version}}" + when: '"ResourceNotFoundException" not in result.stderr' + tags: upgrade_precheck + +- name: Cleanup eks cluster + aws_eks_cluster: + name: "{{ orc8r_cluster_name }}" + wait: yes + state: absent + ignore_errors: true + tags: + - cleanup + +- name: Get autoscaling groups + ec2_asg_info: + tags: + k8s.io/cluster-autoscaler/orc8r: "{{ orc8r_cluster_name }}" + register: asg_cluster_info + ignore_errors: true + tags: + - cleanup + - cleanup_dryrun + +- name: Debug autoscaling groups + debug: + msg: "{{ asg_cluster_info }}" + ignore_errors: true + tags: + - cleanup + - cleanup_dryrun + +- name: Set autoscaling group fact + set_fact: + asg_name: "{{ asg_cluster_info.results[0].auto_scaling_group_name }}" + ignore_errors: true + when: asg_cluster_info.results | length + tags: + - cleanup + +- name: Delete autoscaling group + command: aws autoscaling delete-auto-scaling-group --auto-scaling-group-name "{{ asg_name }}" --force-delete + ignore_errors: true + when: asg_cluster_info.results | length + tags: + - cleanup diff --git a/orc8r/cloud/deploy/orc8r_deployer/docker/root/scripts/playbooks/roles/infra/aws/tasks/elb.yml b/orc8r/cloud/deploy/orc8r_deployer/docker/root/scripts/playbooks/roles/infra/aws/tasks/elb.yml new file mode 100644 index 000000000000..3c19a19e6c5a --- /dev/null +++ b/orc8r/cloud/deploy/orc8r_deployer/docker/root/scripts/playbooks/roles/infra/aws/tasks/elb.yml @@ -0,0 +1,33 @@ +- name: Collect all elb information + ec2_elb_info: + region: "{{ region_name }}" + register: elb_info + ignore_errors: true + when: region_name + tags: + - cleanup_dryrun + - cleanup + +- name: Debug elb information + debug: + msg: "{{item}}" + ignore_errors: true + when: region_name and vpc and item.vpc_id == vpc.id + with_items: + - "{{ elb_info.elbs }}" + tags: + - cleanup_dryrun + - cleanup + +- name: Delete all elbs + ec2_elb_lb: + state: absent + name: "{{ item.name }}" + region: "{{ region_name }}" + wait: yes + when: region_name and vpc and item.vpc_id == vpc.id + ignore_errors: true + with_items: + - "{{ elb_info.elbs }}" + tags: + - cleanup diff --git a/orc8r/cloud/deploy/orc8r_deployer/docker/root/scripts/playbooks/roles/infra/aws/tasks/main.yml b/orc8r/cloud/deploy/orc8r_deployer/docker/root/scripts/playbooks/roles/infra/aws/tasks/main.yml new file mode 100644 index 000000000000..c901d4108220 --- /dev/null +++ b/orc8r/cloud/deploy/orc8r_deployer/docker/root/scripts/playbooks/roles/infra/aws/tasks/main.yml @@ -0,0 +1,42 @@ +- name: Check if required infra variables are set + ansible.builtin.command: "orcl configure check -c infra" + register: result + failed_when: result.rc != 0 + tags: + - install_precheck + - upgrade_precheck + +- name: Open configuration file + shell: "cat {{config_dir}}/infra.tfvars.json" + register: result + tags: + - install_precheck + - upgrade_precheck + +- name: Set Infra Configs + set_fact: + infra_configs: "{{ result.stdout | from_json }}" + tags: + - install_precheck + - upgrade_precheck + +- name: Availability zone tasks + import_tasks: az.yml + +- name: Secret manager tasks + import_tasks: secrets.yml + +- name: EKS tasks + import_tasks: eks.yml + +- name: ELB tasks + import_tasks: elb.yml + +- name: EFS tasks + import_tasks: efs.yml + +- name: VPC tasks + import_tasks: vpc.yml + +- name: Route53 tasks + import_tasks: route53.yml \ No newline at end of file diff --git a/orc8r/cloud/deploy/orc8r_deployer/docker/root/scripts/playbooks/roles/infra/aws/tasks/route53.yml b/orc8r/cloud/deploy/orc8r_deployer/docker/root/scripts/playbooks/roles/infra/aws/tasks/route53.yml new file mode 100644 index 000000000000..1e8d4319f288 --- /dev/null +++ b/orc8r/cloud/deploy/orc8r_deployer/docker/root/scripts/playbooks/roles/infra/aws/tasks/route53.yml @@ -0,0 +1,109 @@ +- name: Get all txt resource record sets + community.aws.route53: + command: get + zone: "{{hosted_zone.name}}" + record: "{{item[0]}}.{{hosted_zone.name}}" + type: "TXT" + register: current_dns_records + ignore_errors: true + with_items: ['bootstrapper-controller', 'controller', '*.nms', 'fluentd', 'api'] + tags: + - cleanup + - cleanup_dryrun + +- name: Debug all txt resource record sets + debug: msg="{{current_dns_records}}" + tags: + - cleanup_dryrun + - cleanup + +# HACK - deleting a record fails due to some oddities in value. Hence +# setting to a empty value and deleting it. +- name: Change all txt resource record sets + community.aws.route53: + state: present + overwrite: true + zone: "{{ hosted_zone.name }}" + record: "{{item.item}}.{{hosted_zone.name}}" + ttl: "{{ item.set.ttl | default('300') }}" + type: "TXT" + value: "{{ value | to_json }}" + wait: yes + loop: "{{ current_dns_records.results }}" + ignore_errors: true + vars: + value: "" + tags: cleanup + +- name: Delete all txt resource record sets + community.aws.route53: + state: absent + overwrite: true + zone: "{{ hosted_zone.name }}" + record: "{{item.item}}.{{hosted_zone.name}}" + ttl: "{{ item.set.ttl | default('300') }}" + type: "TXT" + value: "{{ value | to_json }}" + wait: yes + vars: + value: "" + loop: "{{ current_dns_records.results }}" + ignore_errors: true + tags: cleanup + +- name: Get all alias resource record sets + community.aws.route53: + command: get + zone: "{{hosted_zone.name}}" + record: "{{item[0]}}.{{hosted_zone.name}}" + type: "A" + register: current_dns_records + ignore_errors: true + with_items: ['bootstrapper-controller', 'controller', '*.nms', 'fluentd', 'api'] + tags: + - cleanup + - cleanup_dryrun + +- name: Debug all alias resource record sets + debug: msg="{{current_dns_records}}" + tags: + - cleanup_dryrun + - cleanup + +# HACK - deleting a record fails due to some oddities in value. Hence +# setting to a empty value and deleting it. +- name: Change all alias resource record sets + community.aws.route53: + state: present + overwrite: true + zone: "{{ hosted_zone.name }}" + record: "{{item.item}}.{{hosted_zone.name}}" + ttl: "{{ item.set.ttl | default('300') }}" + type: "A" + value: 0.0.0.0 + wait: yes + loop: "{{ current_dns_records.results }}" + ignore_errors: true + tags: cleanup + +- name: Delete all alias resource record sets + community.aws.route53: + state: absent + overwrite: true + zone: "{{ hosted_zone.name }}" + record: "{{item.item}}.{{hosted_zone.name}}" + ttl: "{{ item.set.ttl | default('300') }}" + type: "A" + value: 0.0.0.0 + wait: yes + loop: "{{ current_dns_records.results }}" + ignore_errors: true + tags: cleanup + +- name: Delete hosted zones for orc8r domain + community.aws.route53_zone: + zone: "{{hosted_zone.name}}" + hosted_zone_id: "{{hosted_zone.zone_id}}" + state: absent + ignore_errors: true + tags: cleanup \ No newline at end of file diff --git a/orc8r/cloud/deploy/orc8r_deployer/docker/root/scripts/playbooks/roles/infra/aws/tasks/secrets.yml b/orc8r/cloud/deploy/orc8r_deployer/docker/root/scripts/playbooks/roles/infra/aws/tasks/secrets.yml new file mode 100644 index 000000000000..622ccfeb1fd0 --- /dev/null +++ b/orc8r/cloud/deploy/orc8r_deployer/docker/root/scripts/playbooks/roles/infra/aws/tasks/secrets.yml @@ -0,0 +1,19 @@ +- name: Query secretsmanager + ansible.builtin.shell: aws secretsmanager list-secret-version-ids --secret-id {{ infra_configs.secretsmanager_orc8r_secret | quote }} || /bin/true + register: result + tags: install_precheck + +- name: Verify if secrets already present + assert: + that: '"ResourceNotFoundException" in result.stderr' + fail_msg: + - "secret {{ infra_configs.secretsmanager_orc8r_secret}} already present" + - "Remove secrets 'aws secretsmanager delete-secret --force-delete --secret-id {{ infra_configs.secretsmanager_orc8r_secret | quote }}'" + tags: install_precheck + +- name: Delete secretsmanager forcefully + command: aws secretsmanager delete-secret --force-delete --secret-id "{{ orc8r_secrets }}" + when: orc8r_secrets + ignore_errors: true + tags: + - cleanup diff --git a/orc8r/cloud/deploy/orc8r_deployer/docker/root/scripts/playbooks/roles/infra/aws/tasks/vpc.yml b/orc8r/cloud/deploy/orc8r_deployer/docker/root/scripts/playbooks/roles/infra/aws/tasks/vpc.yml new file mode 100644 index 000000000000..87b0b3202847 --- /dev/null +++ b/orc8r/cloud/deploy/orc8r_deployer/docker/root/scripts/playbooks/roles/infra/aws/tasks/vpc.yml @@ -0,0 +1,157 @@ +- name: Get all NAT gateways for this VPC + ec2_vpc_nat_gateway_info: + filters: + vpc-id: "{{ vpc.id }}" + register: natgw_info + when: vpc + ignore_errors: true + tags: + - cleanup + - cleanup_dryrun + +- name: Debug NAT gateways for this vpc + when: vpc + debug: msg="{{natgw_info}}" + ignore_errors: true + tags: + - cleanup + - cleanup_dryrun + +- name: Delete all nat gateways + ec2_vpc_nat_gateway: + state: absent + nat_gateway_id: "{{ item.nat_gateway_id }}" + release_eip: true + wait: yes + when: vpc + with_items: "{{ natgw_info.result }}" + ignore_errors: true + tags: cleanup + +- name: Get all internet gateways attached to this VPC + ec2_vpc_igw_info: + filters: + "tag:Name": "{{ vpc.tags['Name'] }}" + "attachment.state": "available" + register: igw_info + when: vpc + ignore_errors: true + tags: + - cleanup + - cleanup_dryrun + +- name: Debug internet gateways for this vpc + debug: msg="{{igw_info}}" + when: vpc + ignore_errors: true + tags: + - cleanup + - cleanup_dryrun + +- name: Detach internet gateway from VPC + command: aws ec2 detach-internet-gateway --internet-gateway-id "{{ item.internet_gateway_id }}" --vpc-id "{{ vpc.id }}" + ignore_errors: true + when: vpc + with_items: "{{igw_info.internet_gateways}}" + tags: cleanup + +- name: Delete internet gateway + command: aws ec2 delete-internet-gateway --internet-gateway-id "{{ item.internet_gateway_id }}" + ignore_errors: true + when: vpc + with_items: "{{igw_info.internet_gateways}}" + tags: cleanup + +- name: Find subnets of this vpc + ec2_vpc_subnet_info: + filters: + vpc-id: "{{ vpc.id }}" + when: vpc + register: subnet_info + ignore_errors: true + tags: + - cleanup + - cleanup_dryrun + +- name: Debug all subnets + debug: + msg: "{{subnet_info}}" + when: vpc + tags: + - cleanup + - cleanup_dryrun + +- name: Delete all subnets + ec2_vpc_subnet: + state: absent + vpc_id: "{{ item.vpc_id }}" + cidr: "{{ item.cidr_block }}" + when: item.default_for_az == false + with_items: "{{ subnet_info.subnets }}" + ignore_errors: true + tags: cleanup + +- name: Get all route tables for this subnet + ec2_vpc_route_table_info: + filters: + vpc_id: "{{ vpc.id }}" + region: "{{ region_name }}" + register: rt_info + when: vpc + ignore_errors: true + tags: + - cleanup + - cleanup_dryrun + +- name: Debug routing info + debug: + msg: "{{ rt_info }}" + ignore_errors: true + tags: + - cleanup + - cleanup_dryrun + +- name: Delete all route tables for this vpc + command: aws ec2 delete-route-table --route-table-id "{{ item.id }}" + when: item.associations[0] is not defined + with_items: + - "{{ rt_info.route_tables }}" + ignore_errors: true + tags: cleanup + +- name: Get all security groups for this vpc + ec2_group_info: + filters: + vpc_id: "{{ vpc.id }}" + register: sec_info + when: vpc + ignore_errors: true + tags: + - cleanup + - cleanup_dryrun + +- name: Debug security groups + debug: + msg: "{{ sec_info }}" + tags: + - cleanup + - cleanup_dryrun + +- name: Delete all security groups in this VPC + ec2_group: + group_id: "{{ item.group_id }}" + state: absent + when: vpc and item.group_name != "default" + with_items: + - "{{ sec_info.security_groups }}" + ignore_errors: true + tags: cleanup + +- name: Delete vpc + ec2_vpc_net: + state: absent + name: "{{ vpc.tags['Name'] }}" + cidr_block: "{{ vpc.cidr_block }}" + ignore_errors: true + when: vpc + tags: cleanup \ No newline at end of file diff --git a/orc8r/cloud/deploy/orc8r_deployer/docker/root/scripts/playbooks/roles/platform/tasks/cloudwatch.yml b/orc8r/cloud/deploy/orc8r_deployer/docker/root/scripts/playbooks/roles/platform/tasks/cloudwatch.yml new file mode 100644 index 000000000000..e88866af55d2 --- /dev/null +++ b/orc8r/cloud/deploy/orc8r_deployer/docker/root/scripts/playbooks/roles/platform/tasks/cloudwatch.yml @@ -0,0 +1,33 @@ +--- +- name: Check if required infra variables are set + ansible.builtin.command: "orcl configure check -c infra" + register: result + failed_when: result.rc != 0 + tags: install_precheck + +- name: Open configuration file + shell: "cat {{ config_dir }}/infra.tfvars.json" + register: result + tags: install_precheck + +- name: Set Infra Configs + set_fact: + infra_configs: "{{ result.stdout | from_json }}" + tags: install_precheck + +- name: Get all cloudwatch log groups + ansible.builtin.shell: aws logs describe-log-groups --output json + register: result + tags: install_precheck + +- name: Check if orc8r cloudwatch log group exists + fail: + msg: "Cloudwatch Log group still exists" + when: '"eks/{{infra_configs.cluster_name}}/cluster" in item.logGroupName' + with_items: "{{ (result.stdout|from_json).logGroups }}" + tags: install_precheck + +- name: Delete orchestrator cloudwatch group + command: aws logs delete-log-group --log-group-name "/aws/eks/{{ orc8r_cluster_name }}/cluster" + ignore_errors: true + tags: cleanup \ No newline at end of file diff --git a/orc8r/cloud/deploy/orc8r_deployer/docker/root/scripts/playbooks/roles/platform/tasks/elastic.yml b/orc8r/cloud/deploy/orc8r_deployer/docker/root/scripts/playbooks/roles/platform/tasks/elastic.yml new file mode 100644 index 000000000000..693a66403a23 --- /dev/null +++ b/orc8r/cloud/deploy/orc8r_deployer/docker/root/scripts/playbooks/roles/platform/tasks/elastic.yml @@ -0,0 +1,30 @@ +- name: Get AWS IAM roles information + ansible.builtin.shell: aws iam list-roles || /bin/true + register: result + when: platform_configs.deploy_elasticsearch + tags: + - install_precheck + - upgrade_precheck + +- name: Check if AWSServiceRoleForAmazonElasticsearchService is present already + assert: + that: + - "{{ platform_configs.deploy_elasticsearch_service_linked_role not in (true, 'true') or item.RoleName != 'AWSServiceRoleForAmazonElasticsearchService' }}" + msg: + - "IAM role AWSServiceRoleForAmazonElasticsearchService already exists" + - "Configure deploy_elasticsearch_service_linked_role to be false" + - "For e.g: orcl configure set -c platform -k deploy_elasticsearch_service_linked_role -v false" + when: platform_configs.deploy_elasticsearch + with_items: "{{(result.stdout | from_json).Roles}}" + tags: + - install_precheck + - upgrade_precheck + +- name: Cleanup elastic search instance + command: aws es delete-elasticsearch-domain --domain-name "{{ orc8r_es_domain }}" + ignore_errors: true + when: orc8r_es_domain + tags: + - cleanup + + diff --git a/orc8r/cloud/deploy/orc8r_deployer/docker/root/scripts/playbooks/roles/platform/tasks/main.yml b/orc8r/cloud/deploy/orc8r_deployer/docker/root/scripts/playbooks/roles/platform/tasks/main.yml new file mode 100644 index 000000000000..81e55380925d --- /dev/null +++ b/orc8r/cloud/deploy/orc8r_deployer/docker/root/scripts/playbooks/roles/platform/tasks/main.yml @@ -0,0 +1,30 @@ +- name: Check if required platform variables are set + ansible.builtin.command: "orcl configure check -c platform" + register: result + failed_when: result.rc != 0 + tags: + - install_precheck + - upgrade_precheck + +- name: Open configuration file + shell: "cat {{config_dir}}/platform.tfvars.json" + register: result + tags: + - install_precheck + - upgrade_precheck + +- name: Set platform Configs + set_fact: + platform_configs: "{{ result.stdout | from_json }}" + tags: + - install_precheck + - upgrade_precheck + +- name: Cloudwatch tasks + import_tasks: cloudwatch.yml + +- name: RDS tasks + import_tasks: rds.yml + +- name: Elastic tasks + import_tasks: elastic.yml diff --git a/orc8r/cloud/deploy/orc8r_deployer/docker/root/scripts/playbooks/roles/platform/tasks/rds.yml b/orc8r/cloud/deploy/orc8r_deployer/docker/root/scripts/playbooks/roles/platform/tasks/rds.yml new file mode 100644 index 000000000000..3222022c2f6c --- /dev/null +++ b/orc8r/cloud/deploy/orc8r_deployer/docker/root/scripts/playbooks/roles/platform/tasks/rds.yml @@ -0,0 +1,39 @@ +- name: Get RDS information + ansible.builtin.shell: aws rds describe-db-instances || /bin/true + register: result + tags: upgrade_precheck + +- name: Check if deployed RDS db instance version is greater than version specified in values + assert: + that: "{{ platform_configs.orc8r_db_engine_version is version(item.EngineVersion, '>=') }}" + msg: + - "deployed version higher than to be configured version" + - "Configure the db engine version to be >= {{item.EngineVersion}}" + - "For e.g: orcl configure set -c infra -k orc8r_db_engine_version -v {{item.EngineVersion}}" + when: item.DBInstanceIdentifier == platform_configs.orc8r_db_identifier + with_items: "{{(result.stdout | from_json).DBInstances}}" + tags: upgrade_precheck + +- name: Delete rds instances + command: aws rds delete-db-instance --db-instance-identifier "{{ item }}" --skip-final-snapshot + when: item + with_items: + - "{{ orc8r_db_id }}" + # - "{{ nms_db_id }}" + ignore_errors: true + tags: cleanup + +- name: Wait for database deletion before deleting subnet group + command: aws rds wait db-instance-deleted --db-instance-identifier "{{ item }}" + when: item + with_items: + - "{{ orc8r_db_id }}" + # - "{{ nms_db_id }}" + ignore_errors: true + tags: cleanup + +- name: Delete rds subnet group + command: aws rds delete-db-subnet-group --db-subnet-group-name "{{ orc8r_db_subnet }}" + ignore_errors: true + when: orc8r_db_subnet + tags: cleanup \ No newline at end of file diff --git a/orc8r/cloud/deploy/orc8r_deployer/docker/root/scripts/playbooks/roles/services/orc8r/tasks/main.yml b/orc8r/cloud/deploy/orc8r_deployer/docker/root/scripts/playbooks/roles/services/orc8r/tasks/main.yml new file mode 100644 index 000000000000..9cb1f5aa1335 --- /dev/null +++ b/orc8r/cloud/deploy/orc8r_deployer/docker/root/scripts/playbooks/roles/services/orc8r/tasks/main.yml @@ -0,0 +1,82 @@ +- name: Gather kubernetes pod status for orc8r namespace + ansible.builtin.shell: kubectl get pods -o json -n "{{orc8r_namespace}}" + register: kubectl_output + tags: verify_sanity + +- name: Check if any pods are unhealthy + assert: + that: + - "{{pod_status | length == 0}}" + fail_msg: "found pods {{pod_status}} not in running state" + vars: + status_query: "items[?status.phase != 'Running'].metadata.name" + pod_status: "{{kubectl_output.stdout | from_json | json_query(status_query)}}" + tags: verify_sanity + +- name: Get the logs for pods in unhealthy state + ansible.builtin.shell: kubectl -n "{{orc8r_namespace}}" logs --tail=50 "{{item}}" + register: result + vars: + status_query: "items[?status.phase != 'Running'].metadata.name" + pod_status: "{{kubectl_output.stdout | from_json | json_query(status_query)}}" + with_items: "{{pod_status}}" + tags: verify_sanity + +- name: Dump the logs for pods in unhealthy state + debug: msg="Logs for {{item.item}} \n {{item.stdout_lines}}" + with_items: "{{result.results}}" + tags: verify_sanity + +- name: Get orchestrator pod name + ansible.builtin.shell: kubectl get pod -n "{{orc8r_namespace}}" -l app.kubernetes.io/component=orchestrator -o json + register: result + tags: verify_sanity + +- name: Set orchestrator pod name + set_fact: + orc_pod: "{{result.stdout | from_json | json_query(pod_query)}}" + vars: + pod_query: "items[0].metadata.name" + + tags: + - 'verify_sanity' + +- name: Get NMS pod name + ansible.builtin.shell: kubectl get pod -n "{{orc8r_namespace}}" -l app.kubernetes.io/component=magmalte -o json + register: result + tags: verify_sanity + +- name: Set orchestrator pod name + set_fact: + nms_pod: "{{result.stdout | from_json | json_query(pod_query)}}" + api_host: "{{lookup('flattened', result.stdout | from_json | json_query(api_host_query))}}" + api_cert: "{{lookup('flattened', result.stdout | from_json | json_query(api_cert_query))}}" + api_key: "{{lookup('flattened', result.stdout | from_json | json_query(api_key_query))}}" + vars: + pod_query: "items[0].metadata.name" + api_host_query: "items[0].spec.containers[*].env[?name == 'API_HOST'].value" + api_cert_query: "items[0].spec.containers[*].env[?name == 'API_CERT_FILENAME'].value" + api_key_query: "items[0].spec.containers[*].env[?name == 'API_PRIVATE_KEY_FILENAME'].value" + tags: + - 'verify_sanity' + +- name: Add admin cert to orchestrator pod + ansible.builtin.shell: kubectl -n "{{orc8r_namespace}}" exec "{{orc_pod}}" -- envdir /var/opt/magma/envdir /var/opt/magma/bin/accessc add-existing -admin -cert /var/opt/magma/certs/admin_operator.pem admin_operator + register: result + failed_when: result.rc != 0 and 'AlreadyExists' not in result.stderr + tags: verify_sanity + + +- name: List admin certs in orchestrator pod + ansible.builtin.shell: kubectl -n "{{orc8r_namespace}}" exec "{{orc_pod}}" -- envdir /var/opt/magma/envdir /var/opt/magma/bin/accessc list-certs + register: result + failed_when: result.rc != 0 or 'admin_operator' not in result.stdout + tags: verify_sanity + +- name: Get all configured networks from NMS pod + ansible.builtin.shell: kubectl -n "{{orc8r_namespace}}" exec "{{nms_pod}}" -- curl --cert "{{api_cert}}" --key "{{api_key}}" -k -X GET https://"{{api_host}}"/"{{item}}" + register: result + tags: verify_sanity + with_items: + - magma/v1/networks + - magma/v1/lte \ No newline at end of file diff --git a/orc8r/cloud/deploy/orc8r_deployer/docker/root/scripts/playbooks/roles/services/tasks/docker.yml b/orc8r/cloud/deploy/orc8r_deployer/docker/root/scripts/playbooks/roles/services/tasks/docker.yml new file mode 100644 index 000000000000..9df271e97135 --- /dev/null +++ b/orc8r/cloud/deploy/orc8r_deployer/docker/root/scripts/playbooks/roles/services/tasks/docker.yml @@ -0,0 +1,17 @@ +- name: Verify image credentials + docker_login: + registry: "{{service_configs.docker_registry}}" + username: "{{service_configs.docker_user}}" + password: "{{service_configs.docker_pass}}" + reauthorize: yes + tags: install_precheck + +- name: Attempting to pull the images + docker_image: + tag: "{{service_configs.orc8r_tag}}" + name: "{{service_configs.docker_registry}}/{{item}}" + force_source: true + source: pull + tags: install_precheck + with_items: + - "{{vars.ORC8R_IMAGES}}" diff --git a/orc8r/cloud/deploy/orc8r_deployer/docker/root/scripts/playbooks/roles/services/tasks/helm.yml b/orc8r/cloud/deploy/orc8r_deployer/docker/root/scripts/playbooks/roles/services/tasks/helm.yml new file mode 100644 index 000000000000..85258c4f39a8 --- /dev/null +++ b/orc8r/cloud/deploy/orc8r_deployer/docker/root/scripts/playbooks/roles/services/tasks/helm.yml @@ -0,0 +1,90 @@ +- name: Set helm repo credentials + set_fact: + helm_cred_options: "" + when: service_configs.helm_user + tags: + - install_precheck + - upgrade_precheck + +- name: Verify the Helm repo + ansible.builtin.shell: "helm repo add test {{service_configs.helm_repo}} {% if service_configs.helm_user != '' %} --username {{service_configs.helm_user}} --password {{service_configs.helm_pass}} {% endif %}" + tags: + - install_precheck + - upgrade_precheck + +- name: Get all charts present in the Helm repo + ansible.builtin.shell: helm search repo test -o json + register: helm_chart_info + tags: + - install_precheck + - upgrade_precheck + +- name: Get chart name and version from Helm list + set_fact: + chart_version_map: "{{ chart_version_map | default({}) | combine({item.name.split('/')[1]: item.version}) }}" + with_items: "{{ helm_chart_info.stdout }}" + tags: + - install_precheck + - upgrade_precheck + +- name: Set chart version map + set_fact: + exp_chart_version_map: "{{ exp_chart_version_map | default({}) | combine({item.name: item.version}) }}" + with_items: + - { name: orc8r, version: "{{service_configs.orc8r_chart_version}}"} + - { name: lte-orc8r, version: "{{service_configs.lte_orc8r_chart_version}}" } + - { name: feg-orc8r, version: "{{service_configs.feg_orc8r_chart_version}}" } + - { name: cwf-orc8r, version: "{{service_configs.cwf_orc8r_chart_version}}" } + - { name: fbinternal-orc8r, version: "{{service_configs.fbinternal_orc8r_chart_version}}" } + - { name: wifi-orc8r, version: "{{service_configs.wifi_orc8r_chart_version}}" } + tags: + - install_precheck + - upgrade_precheck + +- name: Deployment to module mapping + set_fact: + deployment_module_map: "{{ deployment_module_map | default({}) | combine({item.name: item.modules}) }}" + with_items: + - { name: fwa, modules: ['orc8r', 'lte-orc8r']} + - { name: ffwa, modules: ['orc8r', 'lte-orc8r', 'feg-orc8r'] } + - { name: all, modules: ['orc8r', 'lte-orc8r', 'feg-orc8r', 'cwf-orc8r', 'fbinternal-orc8r', 'wifi-orc8r'] } + tags: + - install_precheck + - upgrade_precheck + +- name: Validate if necessary charts are present for deployment + assert: + that: item in chart_version_map + fail_msg: "{{item}} chart not found" + with_items: "{{deployment_module_map[service_configs.orc8r_deployment_type]}}" + tags: + - install_precheck + - upgrade_precheck + +- name: Validate if chart versions present match the expected version + assert: + that: chart_version_map[item] == exp_chart_version_map[item] + fail_msg: "{{item}} chart version mismatch expected version {{exp_chart_version_map[item]}} found version {{chart_version_map[item]}}" + with_items: "{{deployment_module_map[service_configs.orc8r_deployment_type]}}" + tags: + - install_precheck + - upgrade_precheck + +- name: Remove the test repo + ansible.builtin.shell: helm repo remove test + ignore_errors: yes + tags: + - install_precheck + - upgrade_precheck + +- name: Get list of installed charts + ansible.builtin.shell: helm list -o json -n "{{orc8r_namespace}}" + register: deployed_chart_info + tags: verify_sanity + +- name: Verify if charts have been deployed + assert: + that: item.status == "deployed" + fail_msg: "chart {{item.name}} status {{item.status}} not deployed successfully" + tags: verify_sanity + with_items: "{{deployed_chart_info.stdout}}" diff --git a/orc8r/cloud/deploy/orc8r_deployer/docker/root/scripts/playbooks/roles/services/tasks/main.yml b/orc8r/cloud/deploy/orc8r_deployer/docker/root/scripts/playbooks/roles/services/tasks/main.yml new file mode 100644 index 000000000000..aebaa60812fe --- /dev/null +++ b/orc8r/cloud/deploy/orc8r_deployer/docker/root/scripts/playbooks/roles/services/tasks/main.yml @@ -0,0 +1,27 @@ +- name: Check if required services variables are set + ansible.builtin.command: "orcl configure check -c service" + register: result + failed_when: result.rc != 0 + tags: install_precheck + +- name: Open configuration file + shell: "cat {{ config_dir }}/service.tfvars.json" + register: result + tags: install_precheck + +- name: Set Service Configs + set_fact: + service_configs: "{{ result.stdout | from_json }}" + tags: install_precheck + +- name: Validate Orc8r deployment type + assert: + that: service_configs.orc8r_deployment_type in ['fwa', 'ffwa', 'all'] + fail_msg: 'orc8r_deployment_type can only be one of these options [fwa, ffwa, all]' + tags: install_precheck + +- name: Image tasks + import_tasks: docker.yml + +- name: Helm tasks + import_tasks: helm.yml diff --git a/orc8r/cloud/deploy/orc8r_deployer/docker/root/scripts/playbooks/roles/services/vars/all.yml b/orc8r/cloud/deploy/orc8r_deployer/docker/root/scripts/playbooks/roles/services/vars/all.yml new file mode 100644 index 000000000000..94189cc57776 --- /dev/null +++ b/orc8r/cloud/deploy/orc8r_deployer/docker/root/scripts/playbooks/roles/services/vars/all.yml @@ -0,0 +1,4 @@ +ORC8R_IMAGES: + - nginx + - controller + - magmalte \ No newline at end of file diff --git a/orc8r/cloud/deploy/orc8r_deployer/docker/root/scripts/setup.py b/orc8r/cloud/deploy/orc8r_deployer/docker/root/scripts/setup.py new file mode 100644 index 000000000000..6e1af6f997b2 --- /dev/null +++ b/orc8r/cloud/deploy/orc8r_deployer/docker/root/scripts/setup.py @@ -0,0 +1,28 @@ +""" +Copyright 2021 The Magma Authors. + +This source code is licensed under the BSD-style license found in the +LICENSE file in the root directory of this source tree. + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +""" + +from setuptools import setup, find_packages + +setup( + name='orcl', + version='0.1', + packages=['cli'], + include_package_data=True, + install_requires=[ + 'Click', + ], + entry_points=''' + [console_scripts] + orcl=cli.orcl:cli + ''', +) \ No newline at end of file diff --git a/orc8r/cloud/deploy/orc8r_deployer/docker/root/tf/main.tf.j2 b/orc8r/cloud/deploy/orc8r_deployer/docker/root/tf/main.tf.j2 new file mode 100644 index 000000000000..e2c131375f2a --- /dev/null +++ b/orc8r/cloud/deploy/orc8r_deployer/docker/root/tf/main.tf.j2 @@ -0,0 +1,54 @@ +################################################################################ +# Copyright 2020 The Magma Authors. + +# This source code is licensed under the BSD-style license found in the +# LICENSE file in the root directory of this source tree. + +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +################################################################################ + +module "orc8r" { + source = "/root/magma/orc8r/cloud/deploy/terraform/orc8r-aws" + + {% for k in cfg['infra'] %} + {{k}}=var.{{k}} + {% endfor %} + + {% for k in cfg['platform'] %} + {{k}}=var.{{k}} + {% endfor %} +} + +module "orc8r-app" { + source = "/root/magma/orc8r/cloud/deploy/terraform/orc8r-helm-aws" + + {% for k in cfg['service'] %} + {{k}}=var.{{k}} + {% endfor %} + + region = var.region + orc8r_domain_name = module.orc8r.orc8r_domain_name + orc8r_route53_zone_id = module.orc8r.route53_zone_id + external_dns_role_arn = module.orc8r.external_dns_role_arn + secretsmanager_orc8r_name = module.orc8r.secretsmanager_secret_name + + orc8r_db_host = module.orc8r.orc8r_db_host + orc8r_db_port = module.orc8r.orc8r_db_port + orc8r_db_name = module.orc8r.orc8r_db_name + orc8r_db_user = module.orc8r.orc8r_db_user + orc8r_db_pass = module.orc8r.orc8r_db_pass + orc8r_db_dialect = module.orc8r.orc8r_db_dialect + + eks_cluster_id = module.orc8r.eks_cluster_id + efs_file_system_id = module.orc8r.efs_file_system_id + efs_provisioner_role_arn = module.orc8r.efs_provisioner_role_arn + elasticsearch_endpoint = module.orc8r.es_endpoint +} + +output "nameservers" { + value = module.orc8r.route53_nameservers +} diff --git a/orc8r/cloud/deploy/orc8r_deployer/docker/root/tf/vars.tf.j2 b/orc8r/cloud/deploy/orc8r_deployer/docker/root/tf/vars.tf.j2 new file mode 100644 index 000000000000..52603af3d558 --- /dev/null +++ b/orc8r/cloud/deploy/orc8r_deployer/docker/root/tf/vars.tf.j2 @@ -0,0 +1,4 @@ +{% for k in cfg %} +variable "{{k}}" { + default = {} +}{% endfor %} diff --git a/orc8r/cloud/deploy/orc8r_deployer/docker/run_deployer.bash b/orc8r/cloud/deploy/orc8r_deployer/docker/run_deployer.bash new file mode 100755 index 000000000000..c8fa5845fa82 --- /dev/null +++ b/orc8r/cloud/deploy/orc8r_deployer/docker/run_deployer.bash @@ -0,0 +1,75 @@ +#!/usr/bin/env bash +set -euf -o pipefail + +print_help() +{ + echo " +./run_deployer runs the orc8r deployer container +orc8r deployer contains scripts which enable user to configure, run prechecks +install, upgrade, verify and cleanup an orc8r deployment +Usage: run_deployer [-deploy-dir|-root-dir|-build|-h] +options: +-h Print this Help +-deploy-dir deployment dir containing configs and secrets (mandatory) +-root-dir magma root directory +-build build the deployer container +example: ./run_deployer.bash -deploy-dir /tmp/orc8r_14_deployment" +} + +if (( $# < 2 )); then + print_help + exit 1 +fi + +DOCKER_BUILD=false +DEPLOY_WORKDIR= +MAGMA_ROOT= +while [ -n "${1-}" ]; do + case "$1" in + -deploy-dir) + DEPLOY_WORKDIR="$2" + shift + ;; + -root-dir) + MAGMA_ROOT="$2" + shift + ;; + -build) + DOCKER_BUILD=true + ;; + -h) + print_help + exit;; + *) echo "Option $1 not recognized" + print_help + exit;; + esac + shift +done + +echo "Build $DOCKER_BUILD" + +echo "Deploy workddir $DEPLOY_WORKDIR" +if [[ ! -d $DEPLOY_WORKDIR ]]; then + echo "${DEPLOY_WORKDIR} does not exist. Creating a new directory" + mkdir -p $DEPLOY_WORKDIR +fi + +if [ -z $MAGMA_ROOT ]; then + SCRIPT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" &> /dev/null && pwd )" + PATTERN='\/orc8r\/cloud\/deploy\/orc8r_deployer\/docker' + MAGMA_ROOT=${SCRIPT_DIR%%$PATTERN} + echo "Warning: inferring magma root($MAGMA_ROOT) from run_deployer script directory" +fi + +if $DOCKER_BUILD; then + docker build -t orc8r_deployer:latest . +fi + +if [[ $? -eq 0 ]]; then + docker run -it \ + -v "${DEPLOY_WORKDIR}":/root/project \ + -v "${MAGMA_ROOT}":/root/magma \ + -v /var/run/docker.sock:/var/run/docker.sock \ + --rm orc8r_deployer:latest bash +fi From 1d57e1da104b593e788f0de5e36f785fdef1526c Mon Sep 17 00:00:00 2001 From: Mykola Yurchenko Date: Thu, 8 Apr 2021 12:24:02 -0400 Subject: [PATCH 60/91] [Pipelined] Remove outdated rule_ids proto field processing (#6037) Signed-off-by: Nick Yurchenko --- lte/gateway/python/magma/pipelined/rpc_servicer.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/lte/gateway/python/magma/pipelined/rpc_servicer.py b/lte/gateway/python/magma/pipelined/rpc_servicer.py index 563be7495d7f..fc014a2074f3 100644 --- a/lte/gateway/python/magma/pipelined/rpc_servicer.py +++ b/lte/gateway/python/magma/pipelined/rpc_servicer.py @@ -416,15 +416,17 @@ def _deactivate_flows_gx(self, request, ip_address: IPAddress): if request.remove_default_drop_flows: self._enforcement_stats.deactivate_default_flow(request.sid.id, ip_address) + rule_ids = [policy.rule_id for policy in request.policies] self._enforcer_app.deactivate_rules(request.sid.id, ip_address, - request.rule_ids) + rule_ids) def _deactivate_flows_gy(self, request, ip_address: IPAddress): logging.debug('Deactivating GY flows for %s', request.sid.id) # Only deactivate requested rules here to not affect GX self._remove_version(request, ip_address) + rule_ids = [policy.rule_id for policy in request.policies] self._gy_app.deactivate_rules(request.sid.id, ip_address, - request.rule_ids) + rule_ids) def GetPolicyUsage(self, request, context): """ From 961f123d8fb7d89b2e1f4bf652e2b7b9307e962c Mon Sep 17 00:00:00 2001 From: Matthew Mosesohn Date: Thu, 8 Apr 2021 19:50:06 +0300 Subject: [PATCH 61/91] [bare-metal] Update readme for bare-metal-ansible deployment (#6041) --- .../cloud/deploy/bare-metal-ansible/README.md | 27 +++++++------------ 1 file changed, 10 insertions(+), 17 deletions(-) diff --git a/orc8r/cloud/deploy/bare-metal-ansible/README.md b/orc8r/cloud/deploy/bare-metal-ansible/README.md index 4dcfe2a9648d..e39b41cde794 100644 --- a/orc8r/cloud/deploy/bare-metal-ansible/README.md +++ b/orc8r/cloud/deploy/bare-metal-ansible/README.md @@ -9,12 +9,12 @@ hide_title: true This page walks through a full, vanilla Orchestrator install using Ansible on bare metal or a virtual machine. If you want to install a specific release version, see the notes in the -[deployment intro](./deploy_intro.md). +[deployment intro](https://docs.magmacore.org/docs/orc8r/deploy_intro). ## Prerequisites We assume `MAGMA_ROOT` is set as described in the -[deployment intro](./deploy_intro.md). +[deployment intro](https://docs.magmacore.org/docs/orc8r/deploy_intro). This walkthrough assumes you already have the following @@ -128,25 +128,18 @@ upstream_dns_servers: # orc8r_nms_db_pass: # orc8r_nms_admin_pass: ``` -Once you've configured Ansible it's time to run the deployment. + +Once you've configured the Ansible vars file, it's time to run the deployment. + ## Deploy the Orchestrator -We're almost ready to deploy the orchestrator. First edit the deployment script: -```bash -vi $MAGMA_ROOT/orc8r/cloud/deploy/bare-metal-ansible/deploy.sh -``` -Search for a line that reads: +We're almost ready to deploy the orchestrator. The last step is to set the IP for the target host(s), as in: ```bash -# Copy ``inventory/sample`` as ``inventory/$CLUSTER_NAME`` -``` -and edit the script to read: -```bash -mkdir -p ../inventory/$CLUSTER_NAME -# Copy ``inventory/sample`` as ``inventory/$CLUSTER_NAME`` +export IPS=192.168.1.180 ``` -The last step is to set the IP for the target host, as in: +or if you have multiple hosts: ```bash -export IPS=192.168.1.180 +export IPS=192.168.1.180 192.168.181 192.168.1.182 ``` Now you're ready to run the deployment. From the `$MAGMA_ROOT/orc8r/cloud/deploy/bare-metal-ansible' directory, execute: @@ -158,6 +151,6 @@ Now you're ready to run the deployment. From the `$MAGMA_ROOT/orc8r/cloud/deplo Once you've begun a deployment, if you find that you need to go back and start again, it's best to completely reset the environment with: ```bash -cd $MAGMA_ROOT +cd $MAGMA_ROOT/orc8r/cloud/deploy/bare-metal-ansible/ ansible-playbook -i inventory/cluster.local/hosts.yaml -b kubespray/reset.yml ``` From 4b8ffbe2e1737727a11be05c044efe798e502db6 Mon Sep 17 00:00:00 2001 From: Pravin Shelar Date: Thu, 8 Apr 2021 22:29:48 +0530 Subject: [PATCH 62/91] AGW: Ubuntu upgrade proposal. (#5497) Attached is a proposal for transitioning to Ubuntu for the AGW deployments. Signed-off-by: Pravin B Shelar --- docs/readmes/proposals/p013_Ubuntu_upgrade.md | 91 +++++++++++++++++++ 1 file changed, 91 insertions(+) create mode 100644 docs/readmes/proposals/p013_Ubuntu_upgrade.md diff --git a/docs/readmes/proposals/p013_Ubuntu_upgrade.md b/docs/readmes/proposals/p013_Ubuntu_upgrade.md new file mode 100644 index 000000000000..9a32d675ba89 --- /dev/null +++ b/docs/readmes/proposals/p013_Ubuntu_upgrade.md @@ -0,0 +1,91 @@ +--- +id: p013_Ubuntu_upgrade +title: AGW Ubuntu upgrade +hide_title: true +--- + +# Overview + +*Status: Accepted*\ +*Author: @pshelar*\ +*Last Updated: 03/15*\ +*Targeted Release: 1.5*\ +*Feedback requested from: @arunuke* + +This document cover AGW ubuntu upgrade path + +## Goal +Add support for Ubuntu based AGW. + +## Introduction + +There are multiple reasons to update base OS from current Debian to ubuntu distribution + +1. Python 3.5 is EOL on September 13th, 2020. Current Debian does not support the latest python release. pip 21.0 + dropped support for Python 3.5 in January 2021. +2. Newer kernel is required for enabling next generation datapath performance. +3. Orc8r is already on Ubuntu. This would converge the magma platform on ubuntu distribution +4. With newer distribution we can relax AGW kernel dependency using *DKMS packages for OVS kernel module* + +## Deployment scenarios for 1.5 + +### *Fresh install on Ubuntu:* + +This is going to be default deployment OS for AGW from 1.5 onwards. Using ubuntu base OS for AGW 1.5 would also result +in smoother AGW updates for future releases. + +### *Debian based AGW 1.4 to Debian based AGW 1.5:* + +Current production deployment can use current Debian upgrade path for upgrading magma from 1.4 to 1.5. Existing workflow +remains same. + +### *Debian based AGW 1.4 to Ubuntu based AGW 1.5:* + +This deployment involves two steps first, upgrading underlying OS. OS upgrade can be performed either +inplace upgrade or re-install of Ubuntu OS. Second step installing AGW packages. +There is separate section below to discuss it in details. + +## Debian depreciation: + +Debian support will be deprecated on AGW 1.5. Default OS distribution for magma is going to be ubuntu. There would be +support for Debian based deployment. +This is subject to change depending on stability of ubuntu support for AGW 1.5. + +## Drop Debian support: + +Debian OS support would be dropped in AGW 1.6 + +## Extended AGW 1.5 release cycle: + +To ease urgency of ubuntu upgrade, magma community could provide extra minor releases for 1.5. This way existing +Debian based production deployment can continue to use debian based AGW and receive bug fixes for 1.5 release. + +## Debian to ubuntu upgrade solutions: + +### *Zero downtime:* + +1. Setup HA pair for AGW, either on-premises or on cloud, there is separate HA document for details. +2. Fail-over all UEs to the new standby AGW. +3. Follow steps in "Upgrade with single AGW node". +4. All UEs would fallback to updated AGW 1.5. +5. At this point the stand-by AGW can be removed. + +### *Upgrade with single AGW node:* + +This type of deployment results in downtime. + +1. Bring down AGW services +2. Update base OS on AGW from Debian to ubuntu +3. Deploy AGW 1.5 +4. Configure AGW +5. Start AGW services + +## Major changes for Ubuntu upgrade. +1. Update Python based services to Python 3.8 +2. Update OVS binaries from 2.8 to 2.14 +3. Use OVS DKMS kernel module package for GTP tunneling to support range of kernels, as of now it supports 4.9.214, + 4.14.171, 4.19.110, 5.4.50 and 5.6.19 +4. Add packaging scripts for Ubuntu +5. Update magma artifactory to host packages for Ubuntu +6. Add deployment script to deploy AGW on Ubuntu +7. Add CI pipeline for Ubuntu based AGW From f036d4aaf65caaf5c2236f3a0c87069b15c099c5 Mon Sep 17 00:00:00 2001 From: Karthik Subraveti Date: Thu, 8 Apr 2021 10:13:31 -0700 Subject: [PATCH 63/91] Fix minor issues in terraform found during orc8r install (#6034) Signed-off-by: Karthik Subraveti --- orc8r/cloud/deploy/terraform/orc8r-helm-aws/main.tf | 1 + .../deploy/terraform/orc8r-helm-aws/templates/orc8r-values.tpl | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/orc8r/cloud/deploy/terraform/orc8r-helm-aws/main.tf b/orc8r/cloud/deploy/terraform/orc8r-helm-aws/main.tf index 8424b7fc14e2..20470e35940b 100644 --- a/orc8r/cloud/deploy/terraform/orc8r-helm-aws/main.tf +++ b/orc8r/cloud/deploy/terraform/orc8r-helm-aws/main.tf @@ -178,6 +178,7 @@ data "template_file" "orc8r_values" { orc8r_db_port = var.orc8r_db_port orc8r_db_dialect = var.orc8r_db_dialect orc8r_db_user = var.orc8r_db_user + orc8r_db_pass = var.orc8r_db_pass deploy_nms = var.deploy_nms diff --git a/orc8r/cloud/deploy/terraform/orc8r-helm-aws/templates/orc8r-values.tpl b/orc8r/cloud/deploy/terraform/orc8r-helm-aws/templates/orc8r-values.tpl index b6d9eb054689..505cf89af8e9 100644 --- a/orc8r/cloud/deploy/terraform/orc8r-helm-aws/templates/orc8r-values.tpl +++ b/orc8r/cloud/deploy/terraform/orc8r-helm-aws/templates/orc8r-values.tpl @@ -188,7 +188,7 @@ nms: mysql_host: ${orc8r_db_host} mysql_port: ${orc8r_db_port} mysql_user: ${orc8r_db_user} - mysql_pass: ${orc8r_db_password} + mysql_pass: ${orc8r_db_pass} grafana_address: ${user_grafana_hostname} nginx: From 3a1998086e8056586a0670ea2972ca3570bed5da Mon Sep 17 00:00:00 2001 From: Scott Moeller Date: Thu, 8 Apr 2021 15:04:52 -0400 Subject: [PATCH 64/91] [DevExp] Fixup hadolint YML and change filter mode (#6043) Signed-off-by: Scott Harrison Moeller Co-authored-by: Scott Harrison Moeller --- .github/workflows/hadolint.yml | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/.github/workflows/hadolint.yml b/.github/workflows/hadolint.yml index 75d8f198349a..0296e6d89fba 100644 --- a/.github/workflows/hadolint.yml +++ b/.github/workflows/hadolint.yml @@ -1,5 +1,6 @@ +--- name: Dockerfile Linting -on: [pull_request] +on: [pull_request] # yamllint disable-line rule:truthy jobs: hadolint: name: Dockerfile Lint @@ -11,7 +12,7 @@ jobs: uses: reviewdog/action-hadolint@v1 with: github_token: ${{ secrets.github_token }} - reporter: github-pr-review # Default is github-pr-check + reporter: github-pr-review # Default is github-pr-check # Ignore DL3005-"Do not use apt-get upgrade or dist-upgrade" hadolint_ignore: DL3005 - filter_mode: file + filter_mode: added # All added or modified lines From 164a4b321701fdbdf4e1dce6f9efa19373e70691 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 8 Apr 2021 15:26:03 -0700 Subject: [PATCH 65/91] Bump babel-jest from 24.9.0 to 26.6.3 in /nms/app (#5096) Bumps [babel-jest](https://github.com/facebook/jest/tree/HEAD/packages/babel-jest) from 24.9.0 to 26.6.3. - [Release notes](https://github.com/facebook/jest/releases) - [Changelog](https://github.com/facebook/jest/blob/master/CHANGELOG.md) - [Commits](https://github.com/facebook/jest/commits/v26.6.3/packages/babel-jest) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- nms/app/package.json | 2 +- nms/app/yarn.lock | 329 +------------------------------------------ 2 files changed, 8 insertions(+), 323 deletions(-) diff --git a/nms/app/package.json b/nms/app/package.json index e94200fc22f1..afab322fb821 100644 --- a/nms/app/package.json +++ b/nms/app/package.json @@ -24,7 +24,7 @@ }, "devDependencies": { "babel-eslint": "^10.1.0", - "babel-jest": "^24.9.0", + "babel-jest": "^26.6.3", "eslint": "^6.0.1", "eslint-config-fb-strict": "^24.3.0", "eslint-config-fbcnms": "^0.2.1", diff --git a/nms/app/yarn.lock b/nms/app/yarn.lock index 6d8e4dad4f08..32f0c93cd47f 100644 --- a/nms/app/yarn.lock +++ b/nms/app/yarn.lock @@ -96,7 +96,7 @@ semver "^5.4.1" source-map "^0.5.0" -"@babel/generator@^7.0.0", "@babel/generator@^7.4.0": +"@babel/generator@^7.0.0": version "7.10.2" resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-7.10.2.tgz#0fa5b5b2389db8bfdfcc3492b551ee20f5dd69a9" integrity sha512-AxfBNHNu99DTMvlUPlt1h2+Hn7knPpH5ayJ8OqDWSeLld+Fi2AYBTC/IejWDM9Edcii4UzZRCsbUt0WlSDsDsA== @@ -359,7 +359,7 @@ regenerator-runtime "^0.13.4" v8flags "^3.1.1" -"@babel/parser@^7.0.0", "@babel/parser@^7.4.3", "@babel/parser@^7.7.0": +"@babel/parser@^7.0.0", "@babel/parser@^7.7.0": version "7.10.2" resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.10.2.tgz#871807f10442b92ff97e4783b9b54f6a0ca812d0" integrity sha512-PApSXlNMJyB4JiGVhCOlzKIif+TKFTvu0aQAhnTvfP/z3vVSN6ZypH5bfUNwFXXjRQtUEBNFd2PtmCmG2Py3qQ== @@ -1216,15 +1216,6 @@ "@babel/parser" "^7.12.7" "@babel/types" "^7.12.7" -"@babel/template@^7.4.0": - version "7.10.1" - resolved "https://registry.yarnpkg.com/@babel/template/-/template-7.10.1.tgz#e167154a94cb5f14b28dc58f5356d2162f539811" - integrity sha512-OQDg6SqvFSsc9A0ej6SKINWrpJiNonRIniYondK2ViKhB06i3c0s+76XUft71iqBEe9S1OKsHwPAjfHnuvnCig== - dependencies: - "@babel/code-frame" "^7.10.1" - "@babel/parser" "^7.10.1" - "@babel/types" "^7.10.1" - "@babel/traverse@^7.1.0", "@babel/traverse@^7.10.4", "@babel/traverse@^7.12.1", "@babel/traverse@^7.12.10", "@babel/traverse@^7.12.5", "@babel/traverse@^7.12.9": version "7.12.10" resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.12.10.tgz#2d1f4041e8bf42ea099e5b2dc48d6a594c00017a" @@ -1240,7 +1231,7 @@ globals "^11.1.0" lodash "^4.17.19" -"@babel/traverse@^7.4.3", "@babel/traverse@^7.7.0": +"@babel/traverse@^7.7.0": version "7.10.1" resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.10.1.tgz#bbcef3031e4152a6c0b50147f4958df54ca0dd27" integrity sha512-C/cTuXeKt85K+p08jN6vMDz8vSV0vZcI0wmQ36o6mjbuo++kPMdpOYw23W2XH04dbRt9/nMEfA4W3eR21CD+TQ== @@ -1264,7 +1255,7 @@ lodash "^4.17.19" to-fast-properties "^2.0.0" -"@babel/types@^7.0.0-beta.49", "@babel/types@^7.10.2", "@babel/types@^7.4.0", "@babel/types@^7.6.1", "@babel/types@^7.7.0", "@babel/types@^7.9.6": +"@babel/types@^7.0.0-beta.49", "@babel/types@^7.10.2", "@babel/types@^7.6.1", "@babel/types@^7.7.0", "@babel/types@^7.9.6": version "7.11.5" resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.11.5.tgz#d9de577d01252d77c6800cee039ee64faf75662d" integrity sha512-bvM7Qz6eKnJVFIn+1LPtjlBFPVN5jNDc1XmN15vWe7Q3DPBufWWsLiIvUu7xW87uTG6QoggpIDnUgLQvPheU+Q== @@ -1626,15 +1617,6 @@ resolved "https://registry.yarnpkg.com/@istanbuljs/schema/-/schema-0.1.2.tgz#26520bf09abe4a5644cd5414e37125a8954241dd" integrity sha512-tsAQNx32a8CoFhjhijUIhI4kccIAgmGhy8LZMZgGfmXcpMbPRUqn5LWmgRttILi6yeGmBJd2xsPkFMs0PzgPCw== -"@jest/console@^24.9.0": - version "24.9.0" - resolved "https://registry.yarnpkg.com/@jest/console/-/console-24.9.0.tgz#79b1bc06fb74a8cfb01cbdedf945584b1b9707f0" - integrity sha512-Zuj6b8TnKXi3q4ymac8EQfc3ea/uhLeCGThFqXeC8H9/raaH8ARPUTdId+XyGd03Z4In0/VjD2OYFcBF09fNLQ== - dependencies: - "@jest/source-map" "^24.9.0" - chalk "^2.0.1" - slash "^2.0.0" - "@jest/console@^26.6.2": version "26.6.2" resolved "https://registry.yarnpkg.com/@jest/console/-/console-26.6.2.tgz#4e04bc464014358b03ab4937805ee36a0aeb98f2" @@ -1691,15 +1673,6 @@ "@types/node" "*" jest-mock "^26.6.2" -"@jest/fake-timers@^24.9.0": - version "24.9.0" - resolved "https://registry.yarnpkg.com/@jest/fake-timers/-/fake-timers-24.9.0.tgz#ba3e6bf0eecd09a636049896434d306636540c93" - integrity sha512-eWQcNa2YSwzXWIMC5KufBh3oWRIijrQFROsIqt6v/NS9Io/gknw1jsAC9c+ih/RQX4A3O7SeWAhQeN0goKhT9A== - dependencies: - "@jest/types" "^24.9.0" - jest-message-util "^24.9.0" - jest-mock "^24.9.0" - "@jest/fake-timers@^26.6.2": version "26.6.2" resolved "https://registry.yarnpkg.com/@jest/fake-timers/-/fake-timers-26.6.2.tgz#459c329bcf70cee4af4d7e3f3e67848123535aad" @@ -1753,15 +1726,6 @@ optionalDependencies: node-notifier "^8.0.0" -"@jest/source-map@^24.9.0": - version "24.9.0" - resolved "https://registry.yarnpkg.com/@jest/source-map/-/source-map-24.9.0.tgz#0e263a94430be4b41da683ccc1e6bffe2a191714" - integrity sha512-/Xw7xGlsZb4MJzNDgB7PW5crou5JqWiBQaz6xyPd3ArOg2nfn/PunV8+olXbbEZzNl591o5rWKE9BRDaFAuIBg== - dependencies: - callsites "^3.0.0" - graceful-fs "^4.1.15" - source-map "^0.6.0" - "@jest/source-map@^26.6.2": version "26.6.2" resolved "https://registry.yarnpkg.com/@jest/source-map/-/source-map-26.6.2.tgz#29af5e1e2e324cafccc936f218309f54ab69d535" @@ -1771,15 +1735,6 @@ graceful-fs "^4.2.4" source-map "^0.6.0" -"@jest/test-result@^24.9.0": - version "24.9.0" - resolved "https://registry.yarnpkg.com/@jest/test-result/-/test-result-24.9.0.tgz#11796e8aa9dbf88ea025757b3152595ad06ba0ca" - integrity sha512-XEFrHbBonBJ8dGp2JmF8kP/nQI/ImPpygKHwQ/SY+es59Z3L5PI4Qb9TQQMAEeYsThG1xF0k6tmG0tIKATNiiA== - dependencies: - "@jest/console" "^24.9.0" - "@jest/types" "^24.9.0" - "@types/istanbul-lib-coverage" "^2.0.0" - "@jest/test-result@^26.6.2": version "26.6.2" resolved "https://registry.yarnpkg.com/@jest/test-result/-/test-result-26.6.2.tgz#55da58b62df134576cc95476efa5f7949e3f5f18" @@ -1801,28 +1756,6 @@ jest-runner "^26.6.3" jest-runtime "^26.6.3" -"@jest/transform@^24.9.0": - version "24.9.0" - resolved "https://registry.yarnpkg.com/@jest/transform/-/transform-24.9.0.tgz#4ae2768b296553fadab09e9ec119543c90b16c56" - integrity sha512-TcQUmyNRxV94S0QpMOnZl0++6RMiqpbH/ZMccFB/amku6Uwvyb1cjYX7xkp5nGNkbX4QPH/FcB6q1HBTHynLmQ== - dependencies: - "@babel/core" "^7.1.0" - "@jest/types" "^24.9.0" - babel-plugin-istanbul "^5.1.0" - chalk "^2.0.1" - convert-source-map "^1.4.0" - fast-json-stable-stringify "^2.0.0" - graceful-fs "^4.1.15" - jest-haste-map "^24.9.0" - jest-regex-util "^24.9.0" - jest-util "^24.9.0" - micromatch "^3.1.10" - pirates "^4.0.1" - realpath-native "^1.1.0" - slash "^2.0.0" - source-map "^0.6.1" - write-file-atomic "2.4.1" - "@jest/transform@^26.6.2": version "26.6.2" resolved "https://registry.yarnpkg.com/@jest/transform/-/transform-26.6.2.tgz#5ac57c5fa1ad17b2aae83e73e45813894dcf2e4b" @@ -2411,17 +2344,6 @@ "@types/babel__template" "*" "@types/babel__traverse" "*" -"@types/babel__core@^7.1.0": - version "7.1.9" - resolved "https://registry.yarnpkg.com/@types/babel__core/-/babel__core-7.1.9.tgz#77e59d438522a6fb898fa43dc3455c6e72f3963d" - integrity sha512-sY2RsIJ5rpER1u3/aQ8OFSI7qGIy8o1NEEbgb2UaJcvOtXOMpd39ko723NBpjQFg9SIX7TXtjejZVGeIMLhoOw== - dependencies: - "@babel/parser" "^7.1.0" - "@babel/types" "^7.0.0" - "@types/babel__generator" "*" - "@types/babel__template" "*" - "@types/babel__traverse" "*" - "@types/babel__generator@*": version "7.6.2" resolved "https://registry.yarnpkg.com/@types/babel__generator/-/babel__generator-7.6.2.tgz#f3d71178e187858f7c45e30380f8f1b7415a12d8" @@ -2601,11 +2523,6 @@ "@types/prop-types" "*" csstype "^3.0.2" -"@types/stack-utils@^1.0.1": - version "1.0.1" - resolved "https://registry.yarnpkg.com/@types/stack-utils/-/stack-utils-1.0.1.tgz#0a851d3bd96498fa25c33ab7278ed3bd65f06c3e" - integrity sha512-l42BggppR6zLmpfU6fq9HEa2oGPEI8yrSPL3GITjfRInppYFahObbIQOQK3UGxEnyQpltZLaPe75046NOZQikw== - "@types/stack-utils@^2.0.0": version "2.0.0" resolved "https://registry.yarnpkg.com/@types/stack-utils/-/stack-utils-2.0.0.tgz#7036640b4e21cc2f259ae826ce843d277dad8cff" @@ -3337,19 +3254,6 @@ babel-eslint@^10.1.0: eslint-visitor-keys "^1.0.0" resolve "^1.12.0" -babel-jest@^24.9.0: - version "24.9.0" - resolved "https://registry.yarnpkg.com/babel-jest/-/babel-jest-24.9.0.tgz#3fc327cb8467b89d14d7bc70e315104a783ccd54" - integrity sha512-ntuddfyiN+EhMw58PTNL1ph4C9rECiQXjI4nMMBKBaNjXvqLdkXpPRcMSr4iyBrJg/+wz9brFUD6RhOAT6r4Iw== - dependencies: - "@jest/transform" "^24.9.0" - "@jest/types" "^24.9.0" - "@types/babel__core" "^7.1.0" - babel-plugin-istanbul "^5.1.0" - babel-preset-jest "^24.9.0" - chalk "^2.4.2" - slash "^2.0.0" - babel-jest@^26.6.3: version "26.6.3" resolved "https://registry.yarnpkg.com/babel-jest/-/babel-jest-26.6.3.tgz#d87d25cb0037577a0c89f82e5755c5d293c01056" @@ -3420,16 +3324,6 @@ babel-plugin-fbt@^0.10.4: shelljs "^0.7.8" yargs "^9.0.0" -babel-plugin-istanbul@^5.1.0: - version "5.2.0" - resolved "https://registry.yarnpkg.com/babel-plugin-istanbul/-/babel-plugin-istanbul-5.2.0.tgz#df4ade83d897a92df069c4d9a25cf2671293c854" - integrity sha512-5LphC0USA8t4i1zCtjbbNb6jJj/9+X6P37Qfirc/70EQ34xKlMW+a1RHGwxGI+SwWpNwZ27HqvzAobeqaXwiZw== - dependencies: - "@babel/helper-plugin-utils" "^7.0.0" - find-up "^3.0.0" - istanbul-lib-instrument "^3.3.0" - test-exclude "^5.2.3" - babel-plugin-istanbul@^6.0.0: version "6.0.0" resolved "https://registry.yarnpkg.com/babel-plugin-istanbul/-/babel-plugin-istanbul-6.0.0.tgz#e159ccdc9af95e0b570c75b4573b7c34d671d765" @@ -3441,13 +3335,6 @@ babel-plugin-istanbul@^6.0.0: istanbul-lib-instrument "^4.0.0" test-exclude "^6.0.0" -babel-plugin-jest-hoist@^24.9.0: - version "24.9.0" - resolved "https://registry.yarnpkg.com/babel-plugin-jest-hoist/-/babel-plugin-jest-hoist-24.9.0.tgz#4f837091eb407e01447c8843cbec546d0002d756" - integrity sha512-2EMA2P8Vp7lG0RAzr4HXqtYwacfMErOuv1U3wrvxHX6rD1sV6xS3WXG3r8TRQ2r6w8OhvSdWt+z41hQNwNm3Xw== - dependencies: - "@types/babel__traverse" "^7.0.6" - babel-plugin-jest-hoist@^26.6.2: version "26.6.2" resolved "https://registry.yarnpkg.com/babel-plugin-jest-hoist/-/babel-plugin-jest-hoist-26.6.2.tgz#8185bd030348d254c6d7dd974355e6a28b21e62d" @@ -3539,14 +3426,6 @@ babel-preset-fbjs@^3.2.0: "@babel/plugin-transform-template-literals" "^7.0.0" babel-plugin-syntax-trailing-function-commas "^7.0.0-beta.0" -babel-preset-jest@^24.9.0: - version "24.9.0" - resolved "https://registry.yarnpkg.com/babel-preset-jest/-/babel-preset-jest-24.9.0.tgz#192b521e2217fb1d1f67cf73f70c336650ad3cdc" - integrity sha512-izTUuhE4TMfTRPF92fFwD2QfdXaZW08qvWTFCI51V8rW5x00UuPgc3ajRoWofXOuxjfcOM5zzSYsQS3H8KGCAg== - dependencies: - "@babel/plugin-syntax-object-rest-spread" "^7.0.0" - babel-plugin-jest-hoist "^24.9.0" - babel-preset-jest@^26.6.2: version "26.6.2" resolved "https://registry.yarnpkg.com/babel-preset-jest/-/babel-preset-jest-26.6.2.tgz#747872b1171df032252426586881d62d31798fee" @@ -5696,23 +5575,6 @@ es-abstract@^1.17.0-next.1, es-abstract@^1.17.5: string.prototype.trimend "^1.0.1" string.prototype.trimstart "^1.0.1" -es-abstract@^1.17.2: - version "1.17.6" - resolved "https://registry.yarnpkg.com/es-abstract/-/es-abstract-1.17.6.tgz#9142071707857b2cacc7b89ecb670316c3e2d52a" - integrity sha512-Fr89bON3WFyUi5EvAeI48QTWX0AyekGgLA8H+c+7fbfCkJwRWRMLd8CQedNEyJuoYYhmtEqY92pgte1FAhBlhw== - dependencies: - es-to-primitive "^1.2.1" - function-bind "^1.1.1" - has "^1.0.3" - has-symbols "^1.0.1" - is-callable "^1.2.0" - is-regex "^1.1.0" - object-inspect "^1.7.0" - object-keys "^1.1.1" - object.assign "^4.1.0" - string.prototype.trimend "^1.0.1" - string.prototype.trimstart "^1.0.1" - es-abstract@^1.18.0-next.0, es-abstract@^1.18.0-next.1: version "1.18.0-next.1" resolved "https://registry.yarnpkg.com/es-abstract/-/es-abstract-1.18.0-next.1.tgz#6e3a0a4bda717e5023ab3b8e90bec36108d22c68" @@ -7863,7 +7725,7 @@ is-buffer@^2.0.2: resolved "https://registry.yarnpkg.com/is-buffer/-/is-buffer-2.0.4.tgz#3e572f23c8411a5cfd9557c849e3665e0b290623" integrity sha512-Kq1rokWXOPXWuaMAqZiJW4XxsmD9zGx9q4aePabbn3qCRGedtH7Cm+zV8WETitMfu1wdh+Rvd6w5egwSngUX2A== -is-callable@^1.1.4, is-callable@^1.2.0, is-callable@^1.2.2: +is-callable@^1.1.4, is-callable@^1.2.2: version "1.2.2" resolved "https://registry.yarnpkg.com/is-callable/-/is-callable-1.2.2.tgz#c7c6715cd22d4ddb48d3e19970223aceabb080d9" integrity sha512-dnMqspv5nU3LoewK2N/y7KLtxtakvTuaCsU9FU50/QDmdbHNy/4/JuRtMHqRU22o3q+W89YQndQEeCVwK+3qrA== @@ -8157,7 +8019,7 @@ is-regex@^1.0.3, is-regex@^1.0.4: dependencies: has-symbols "^1.0.1" -is-regex@^1.1.0, is-regex@^1.1.1: +is-regex@^1.1.1: version "1.1.1" resolved "https://registry.yarnpkg.com/is-regex/-/is-regex-1.1.1.tgz#c6f98aacc546f6cec5468a07b7b153ab564a57b9" integrity sha512-1+QkEcxiLlB7VEyFtyBg94e08OAsvq7FUBgApTq/w2ymCLyKJgDPsybBENVtA7XCQEgEXxKPonG+mvYRxh/LIg== @@ -8278,29 +8140,11 @@ isstream@~0.1.2: resolved "https://registry.yarnpkg.com/isstream/-/isstream-0.1.2.tgz#47e63f7af55afa6f92e1500e690eb8b8529c099a" integrity sha1-R+Y/evVa+m+S4VAOaQ64uFKcCZo= -istanbul-lib-coverage@^2.0.5: - version "2.0.5" - resolved "https://registry.yarnpkg.com/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.5.tgz#675f0ab69503fad4b1d849f736baaca803344f49" - integrity sha512-8aXznuEPCJvGnMSRft4udDRDtb1V3pkQkMMI5LI+6HuQz5oQ4J2UFn1H82raA3qJtyOLkkwVqICBQkjnGtn5mA== - istanbul-lib-coverage@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/istanbul-lib-coverage/-/istanbul-lib-coverage-3.0.0.tgz#f5944a37c70b550b02a78a5c3b2055b280cec8ec" integrity sha512-UiUIqxMgRDET6eR+o5HbfRYP1l0hqkWOs7vNxC/mggutCMUIhWMm8gAHb8tHlyfD3/l6rlgNA5cKdDzEAf6hEg== -istanbul-lib-instrument@^3.3.0: - version "3.3.0" - resolved "https://registry.yarnpkg.com/istanbul-lib-instrument/-/istanbul-lib-instrument-3.3.0.tgz#a5f63d91f0bbc0c3e479ef4c5de027335ec6d630" - integrity sha512-5nnIN4vo5xQZHdXno/YDXJ0G+I3dAm4XgzfSVTPLQpj/zAV2dV6Juy0yaf10/zrJOJeHoN3fraFe+XRq2bFVZA== - dependencies: - "@babel/generator" "^7.4.0" - "@babel/parser" "^7.4.3" - "@babel/template" "^7.4.0" - "@babel/traverse" "^7.4.3" - "@babel/types" "^7.4.0" - istanbul-lib-coverage "^2.0.5" - semver "^6.0.0" - istanbul-lib-instrument@^4.0.0, istanbul-lib-instrument@^4.0.3: version "4.0.3" resolved "https://registry.yarnpkg.com/istanbul-lib-instrument/-/istanbul-lib-instrument-4.0.3.tgz#873c6fff897450118222774696a3f28902d77c1d" @@ -8491,25 +8335,6 @@ jest-get-type@^26.3.0: resolved "https://registry.yarnpkg.com/jest-get-type/-/jest-get-type-26.3.0.tgz#e97dc3c3f53c2b406ca7afaed4493b1d099199e0" integrity sha512-TpfaviN1R2pQWkIihlfEanwOXK0zcxrKEE4MlU6Tn7keoXdN6/3gK/xl0yEh8DOunn5pOVGKf8hB4R9gVh04ig== -jest-haste-map@^24.9.0: - version "24.9.0" - resolved "https://registry.yarnpkg.com/jest-haste-map/-/jest-haste-map-24.9.0.tgz#b38a5d64274934e21fa417ae9a9fbeb77ceaac7d" - integrity sha512-kfVFmsuWui2Sj1Rp1AJ4D9HqJwE4uwTlS/vO+eRUaMmd54BFpli2XhMQnPC2k4cHFVbB2Q2C+jtI1AGLgEnCjQ== - dependencies: - "@jest/types" "^24.9.0" - anymatch "^2.0.0" - fb-watchman "^2.0.0" - graceful-fs "^4.1.15" - invariant "^2.2.4" - jest-serializer "^24.9.0" - jest-util "^24.9.0" - jest-worker "^24.9.0" - micromatch "^3.1.10" - sane "^4.0.3" - walker "^1.0.7" - optionalDependencies: - fsevents "^1.2.7" - jest-haste-map@^26.6.2: version "26.6.2" resolved "https://registry.yarnpkg.com/jest-haste-map/-/jest-haste-map-26.6.2.tgz#dd7e60fe7dc0e9f911a23d79c5ff7fb5c2cafeaa" @@ -8583,20 +8408,6 @@ jest-matcher-utils@^26.6.2: jest-get-type "^26.3.0" pretty-format "^26.6.2" -jest-message-util@^24.9.0: - version "24.9.0" - resolved "https://registry.yarnpkg.com/jest-message-util/-/jest-message-util-24.9.0.tgz#527f54a1e380f5e202a8d1149b0ec872f43119e3" - integrity sha512-oCj8FiZ3U0hTP4aSui87P4L4jC37BtQwUMqk+zk/b11FR19BJDeZsZAvIHutWnmtw7r85UmR3CEWZ0HWU2mAlw== - dependencies: - "@babel/code-frame" "^7.0.0" - "@jest/test-result" "^24.9.0" - "@jest/types" "^24.9.0" - "@types/stack-utils" "^1.0.1" - chalk "^2.0.1" - micromatch "^3.1.10" - slash "^2.0.0" - stack-utils "^1.0.1" - jest-message-util@^26.6.2: version "26.6.2" resolved "https://registry.yarnpkg.com/jest-message-util/-/jest-message-util-26.6.2.tgz#58173744ad6fc0506b5d21150b9be56ef001ca07" @@ -8612,13 +8423,6 @@ jest-message-util@^26.6.2: slash "^3.0.0" stack-utils "^2.0.2" -jest-mock@^24.9.0: - version "24.9.0" - resolved "https://registry.yarnpkg.com/jest-mock/-/jest-mock-24.9.0.tgz#c22835541ee379b908673ad51087a2185c13f1c6" - integrity sha512-3BEYN5WbSq9wd+SyLDES7AHnjH9A/ROBwmz7l2y+ol+NtSFO8DYiEBzoO1CeFc9a8DYy10EO4dDFVv/wN3zl1w== - dependencies: - "@jest/types" "^24.9.0" - jest-mock@^26.6.2: version "26.6.2" resolved "https://registry.yarnpkg.com/jest-mock/-/jest-mock-26.6.2.tgz#d6cb712b041ed47fe0d9b6fc3474bc6543feb302" @@ -8632,11 +8436,6 @@ jest-pnp-resolver@^1.2.2: resolved "https://registry.yarnpkg.com/jest-pnp-resolver/-/jest-pnp-resolver-1.2.2.tgz#b704ac0ae028a89108a4d040b3f919dfddc8e33c" integrity sha512-olV41bKSMm8BdnuMsewT4jqlZ8+3TCARAXjZGT9jcoSnrfUnRCqnMoF9XEeoWjbzObpqF9dRhHQj0Xb9QdF6/w== -jest-regex-util@^24.9.0: - version "24.9.0" - resolved "https://registry.yarnpkg.com/jest-regex-util/-/jest-regex-util-24.9.0.tgz#c13fb3380bde22bf6575432c493ea8fe37965636" - integrity sha512-05Cmb6CuxaA+Ys6fjr3PhvV3bGQmO+2p2La4hFbU+W5uOc479f7FdLXUWXw4pYMAhhSZIuKHwSXSu6CsSBAXQA== - jest-regex-util@^26.0.0: version "26.0.0" resolved "https://registry.yarnpkg.com/jest-regex-util/-/jest-regex-util-26.0.0.tgz#d25e7184b36e39fd466c3bc41be0971e821fee28" @@ -8724,11 +8523,6 @@ jest-runtime@^26.6.3: strip-bom "^4.0.0" yargs "^15.4.1" -jest-serializer@^24.9.0: - version "24.9.0" - resolved "https://registry.yarnpkg.com/jest-serializer/-/jest-serializer-24.9.0.tgz#e6d7d7ef96d31e8b9079a714754c5d5c58288e73" - integrity sha512-DxYipDr8OvfrKH3Kel6NdED3OXxjvxXZ1uIY2I9OFbGg+vUkkg7AGvi65qbhbWNPvDckXmzMPbK3u3HaDO49bQ== - jest-serializer@^26.6.2: version "26.6.2" resolved "https://registry.yarnpkg.com/jest-serializer/-/jest-serializer-26.6.2.tgz#d139aafd46957d3a448f3a6cdabe2919ba0742d1" @@ -8759,24 +8553,6 @@ jest-snapshot@^26.6.2: pretty-format "^26.6.2" semver "^7.3.2" -jest-util@^24.9.0: - version "24.9.0" - resolved "https://registry.yarnpkg.com/jest-util/-/jest-util-24.9.0.tgz#7396814e48536d2e85a37de3e4c431d7cb140162" - integrity sha512-x+cZU8VRmOJxbA1K5oDBdxQmdq0OIdADarLxk0Mq+3XS4jgvhG/oKGWcIDCtPG0HgjxOYvF+ilPJQsAyXfbNOg== - dependencies: - "@jest/console" "^24.9.0" - "@jest/fake-timers" "^24.9.0" - "@jest/source-map" "^24.9.0" - "@jest/test-result" "^24.9.0" - "@jest/types" "^24.9.0" - callsites "^3.0.0" - chalk "^2.0.1" - graceful-fs "^4.1.15" - is-ci "^2.0.0" - mkdirp "^0.5.1" - slash "^2.0.0" - source-map "^0.6.0" - jest-util@^26.6.2: version "26.6.2" resolved "https://registry.yarnpkg.com/jest-util/-/jest-util-26.6.2.tgz#907535dbe4d5a6cb4c47ac9b926f6af29576cbc1" @@ -8814,14 +8590,6 @@ jest-watcher@^26.6.2: jest-util "^26.6.2" string-length "^4.0.1" -jest-worker@^24.9.0: - version "24.9.0" - resolved "https://registry.yarnpkg.com/jest-worker/-/jest-worker-24.9.0.tgz#5dbfdb5b2d322e98567898238a9697bcce67b3e5" - integrity sha512-51PE4haMSXcHohnSMdM42anbvZANYTqMrr52tVKPqqsPJMzoP6FYYDVqahX/HrAoKEKz3uUPzSvKs9A3qR4iVw== - dependencies: - merge-stream "^2.0.0" - supports-color "^6.1.0" - jest-worker@^26.6.2: version "26.6.2" resolved "https://registry.yarnpkg.com/jest-worker/-/jest-worker-26.6.2.tgz#7f72cbc4d643c365e27b9fd775f9d0eaa9c7a8ed" @@ -9276,16 +9044,6 @@ load-json-file@^2.0.0: pify "^2.0.0" strip-bom "^3.0.0" -load-json-file@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/load-json-file/-/load-json-file-4.0.0.tgz#2f5f45ab91e33216234fd53adab668eb4ec0993b" - integrity sha1-L19Fq5HjMhYjT9U62rZo607AmTs= - dependencies: - graceful-fs "^4.1.2" - parse-json "^4.0.0" - pify "^3.0.0" - strip-bom "^3.0.0" - loader-runner@^2.4.0: version "2.4.0" resolved "https://registry.yarnpkg.com/loader-runner/-/loader-runner-2.4.0.tgz#ed47066bfe534d7e84c4c7b9998c2a75607d9357" @@ -10360,7 +10118,7 @@ object-copy@^0.1.0: define-property "^0.2.5" kind-of "^3.0.3" -object-inspect@^1.7.0, object-inspect@^1.8.0: +object-inspect@^1.8.0: version "1.9.0" resolved "https://registry.yarnpkg.com/object-inspect/-/object-inspect-1.9.0.tgz#c90521d74e1127b67266ded3394ad6116986533a" integrity sha512-i3Bp9iTqwhaLZBxGkRfo5ZbE07BQRT7MGu8+nNgwW9ItGp1TzCTw2DLEoWwjClxBjOFI/hWljTAmYGCEwmtnOw== @@ -10424,14 +10182,6 @@ object.getownpropertydescriptors@^2.0.3: define-properties "^1.1.3" es-abstract "^1.18.0-next.1" -object.getownpropertydescriptors@^2.1.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/object.getownpropertydescriptors/-/object.getownpropertydescriptors-2.1.0.tgz#369bf1f9592d8ab89d712dced5cb81c7c5352649" - integrity sha512-Z53Oah9A3TdLoblT7VKJaTDdXdT+lQO+cNpKVnya5JDe9uLvzu1YyY1yFDFrcxrlRgWrEFH0jJtD/IbuwjcEVg== - dependencies: - define-properties "^1.1.3" - es-abstract "^1.17.0-next.1" - object.pick@^1.3.0: version "1.3.0" resolved "https://registry.yarnpkg.com/object.pick/-/object.pick-1.3.0.tgz#87a10ac4c1694bd2e1cbf53591a66141fb5dd747" @@ -10888,13 +10638,6 @@ path-type@^2.0.0: dependencies: pify "^2.0.0" -path-type@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/path-type/-/path-type-3.0.0.tgz#cef31dc8e0a1a3bb0d105c0cd97cf3bf47f4e36f" - integrity sha512-T2ZUsdZFHgA3u4e5PfPbjd7HDDpxPnQb5jN0SrDsjNSuVXHJqtwTnWqG0B1jZrgmJ/7lj1EmVIByWt1gxGkWvg== - dependencies: - pify "^3.0.0" - path-type@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/path-type/-/path-type-4.0.0.tgz#84ed01c0a7ba380afe09d90a8c180dcd9d03043b" @@ -12083,14 +11826,6 @@ read-pkg-up@^2.0.0: find-up "^2.0.0" read-pkg "^2.0.0" -read-pkg-up@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/read-pkg-up/-/read-pkg-up-4.0.0.tgz#1b221c6088ba7799601c808f91161c66e58f8978" - integrity sha512-6etQSH7nJGsK0RbG/2TeDzZFa8shjQ1um+SwQQ5cwKy0dhSXdOncEhb1CPpvQG4h7FyOV6EB6YlV0yJvZQNAkA== - dependencies: - find-up "^3.0.0" - read-pkg "^3.0.0" - read-pkg-up@^7.0.1: version "7.0.1" resolved "https://registry.yarnpkg.com/read-pkg-up/-/read-pkg-up-7.0.1.tgz#f3a6135758459733ae2b95638056e1854e7ef507" @@ -12109,15 +11844,6 @@ read-pkg@^2.0.0: normalize-package-data "^2.3.2" path-type "^2.0.0" -read-pkg@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/read-pkg/-/read-pkg-3.0.0.tgz#9cbc686978fee65d16c00e2b19c237fcf6e38389" - integrity sha1-nLxoaXj+5l0WwA4rGcI3/Pbjg4k= - dependencies: - load-json-file "^4.0.0" - normalize-package-data "^2.3.2" - path-type "^3.0.0" - read-pkg@^5.2.0: version "5.2.0" resolved "https://registry.yarnpkg.com/read-pkg/-/read-pkg-5.2.0.tgz#7bf295438ca5a33e56cd30e053b34ee7250c93cc" @@ -12183,13 +11909,6 @@ readdirp@~3.5.0: dependencies: picomatch "^2.2.1" -realpath-native@^1.1.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/realpath-native/-/realpath-native-1.1.0.tgz#2003294fea23fb0672f2476ebe22fcf498a2d65c" - integrity sha512-wlgPA6cCIIg9gKz0fgAPjnzh4yR/LnXovwuo9hvyGvx3h8nX4+/iLZplfUWasXpqD8BdnGnP5njOFjkUwPzvjA== - dependencies: - util.promisify "^1.0.0" - rebass@^4.0.7: version "4.0.7" resolved "https://registry.yarnpkg.com/rebass/-/rebass-4.0.7.tgz#0a84e5558750c1f416c3baf41ec4c7fc8d64a98a" @@ -13279,11 +12998,6 @@ stack-trace@0.0.x: resolved "https://registry.yarnpkg.com/stack-trace/-/stack-trace-0.0.10.tgz#547c70b347e8d32b4e108ea1a2a159e5fdde19c0" integrity sha1-VHxws0fo0ytOEI6hoqFZ5f3eGcA= -stack-utils@^1.0.1: - version "1.0.2" - resolved "https://registry.yarnpkg.com/stack-utils/-/stack-utils-1.0.2.tgz#33eba3897788558bebfc2db059dc158ec36cebb8" - integrity sha512-MTX+MeG5U994cazkjd/9KNAapsHnibjMLnfXodlkXw76JEea0UiNzrqidzo1emMwk7w5Qhc9jd4Bn9TBb1MFwA== - stack-utils@^2.0.2: version "2.0.3" resolved "https://registry.yarnpkg.com/stack-utils/-/stack-utils-2.0.3.tgz#cd5f030126ff116b78ccb3c027fe302713b61277" @@ -13763,16 +13477,6 @@ terser@^4.1.2: source-map "~0.6.1" source-map-support "~0.5.12" -test-exclude@^5.2.3: - version "5.2.3" - resolved "https://registry.yarnpkg.com/test-exclude/-/test-exclude-5.2.3.tgz#c3d3e1e311eb7ee405e092dac10aefd09091eac0" - integrity sha512-M+oxtseCFO3EDtAaGH7iiej3CBkzXqFMbzqYAACdzKui4eZA+pq3tZEwChvOdNfa7xxy8BfbmgJSIr43cC/+2g== - dependencies: - glob "^7.1.3" - minimatch "^3.0.4" - read-pkg-up "^4.0.0" - require-main-filename "^2.0.0" - test-exclude@^6.0.0: version "6.0.0" resolved "https://registry.yarnpkg.com/test-exclude/-/test-exclude-6.0.0.tgz#04a8698661d805ea6fa293b6cb9e63ac044ef15e" @@ -14431,16 +14135,6 @@ util-deprecate@^1.0.1, util-deprecate@^1.0.2, util-deprecate@~1.0.1: resolved "https://registry.yarnpkg.com/util-deprecate/-/util-deprecate-1.0.2.tgz#450d4dc9fa70de732762fbd2d4a28981419a0ccf" integrity sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8= -util.promisify@^1.0.0: - version "1.0.1" - resolved "https://registry.yarnpkg.com/util.promisify/-/util.promisify-1.0.1.tgz#6baf7774b80eeb0f7520d8b81d07982a59abbaee" - integrity sha512-g9JpC/3He3bm38zsLupWryXHoEcS22YHthuPQSJdMy6KNrzIRzWqcsHzD/WUnqe45whVou4VIsPew37DoXWNrA== - dependencies: - define-properties "^1.1.3" - es-abstract "^1.17.2" - has-symbols "^1.0.1" - object.getownpropertydescriptors "^2.1.0" - util@0.10.3: version "0.10.3" resolved "https://registry.yarnpkg.com/util/-/util-0.10.3.tgz#7afb1afe50805246489e3db7fe0ed379336ac0f9" @@ -14924,15 +14618,6 @@ wrappy@1: resolved "https://registry.yarnpkg.com/wrappy/-/wrappy-1.0.2.tgz#b5243d8f3ec1aa35f1364605bc0d1036e30ab69f" integrity sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8= -write-file-atomic@2.4.1: - version "2.4.1" - resolved "https://registry.yarnpkg.com/write-file-atomic/-/write-file-atomic-2.4.1.tgz#d0b05463c188ae804396fd5ab2a370062af87529" - integrity sha512-TGHFeZEZMnv+gBFRfjAcxL5bPHrsGKtnb4qsFAws7/vlh+QfwAaySIw4AXP9ZskTTh5GWu3FLuJhsWVdiJPGvg== - dependencies: - graceful-fs "^4.1.11" - imurmurhash "^0.1.4" - signal-exit "^3.0.2" - write-file-atomic@^2.0.0: version "2.4.3" resolved "https://registry.yarnpkg.com/write-file-atomic/-/write-file-atomic-2.4.3.tgz#1fd2e9ae1df3e75b8d8c367443c692d4ca81f481" From a597d4306b44d013e86256368542bdfef503928c Mon Sep 17 00:00:00 2001 From: Oriol Batalla Date: Thu, 8 Apr 2021 15:35:39 -0700 Subject: [PATCH 66/91] [feg] Add feature to send multiple AIR request through s6a_cli (#5925) Signed-off-by: Oriol Batalla --- feg/gateway/tools/s6a_cli/main.go | 88 ++++++++++++++++++++++++------- 1 file changed, 69 insertions(+), 19 deletions(-) diff --git a/feg/gateway/tools/s6a_cli/main.go b/feg/gateway/tools/s6a_cli/main.go index 9d764e81d9bf..89fba08ec030 100644 --- a/feg/gateway/tools/s6a_cli/main.go +++ b/feg/gateway/tools/s6a_cli/main.go @@ -24,6 +24,7 @@ import ( "path/filepath" "strconv" "strings" + "sync" "time" "magma/feg/cloud/go/protos" @@ -62,6 +63,8 @@ var ( eutranVectors int = 3 utranVectors int = 0 useMconfig bool + imsiRange uint64 = 1 + rate int = 0 ) type s6aCli interface { @@ -119,6 +122,9 @@ func init() { f.IntVar(&utranVectors, "utran_num", utranVectors, "Number of UTRAN vectors to request") f.BoolVar(&useMconfig, "use_mconfig", false, "Use local gateway.mconfig configuration for local proxy (if set - starts local s6a proxy)") + f.Uint64Var(&imsiRange, "range", imsiRange, "Send multiple request with consecutive imsis") + f.IntVar(&rate, "rate", rate, "Request per second (to be used with range)") + } // AIR Handler @@ -232,28 +238,72 @@ func air(cmd *commands.Command, args []string) int { } peerAddr = proxyAddr } - req := &protos.AuthenticationInformationRequest{ - UserName: imsi, - VisitedPlmn: plmnId[:], - NumRequestedEutranVectors: uint32(eutranVectors), - ImmediateResponsePreferred: true, - NumRequestedUtranGeranVectors: uint32(utranVectors), - } - // AIR - json, err := orcprotos.MarshalIntern(req) - fmt.Printf("Sending AIR to %s:\n%s\n%+#v\n\n", peerAddr, json, *req) - r, err := cli.AuthenticationInformation(req) - if err != nil || r == nil { - log.Printf("GRPC AIR Error: %v", err) - return 8 + + errChann := make(chan error) + airErrors := make([]error, 0) + done := make(chan struct{}) + lenOfImsi := len(imsi) + wg := sync.WaitGroup{} + // run all the producers in parallel + fmt.Printf("Start sending requests %d\n", imsiRange) + for i := uint64(0); i < imsiRange; i++ { + wg.Add(1) + iShadow := i + // wait to adjust the rate + if rate > 0 && i != 0 && i%uint64(rate) == 0 { + fmt.Printf("\nWait 1 sec to send next group of %d request\n\n", rate) + time.Sleep(time.Second) + } + go func() { + defer wg.Done() + req := &protos.AuthenticationInformationRequest{ + UserName: fmt.Sprintf("%0*d", lenOfImsi, imsiNum+iShadow), + VisitedPlmn: plmnId[:], + NumRequestedEutranVectors: uint32(eutranVectors), + ImmediateResponsePreferred: true, + NumRequestedUtranGeranVectors: uint32(utranVectors), + } + // AIR + json, err := orcprotos.MarshalIntern(req) + fmt.Printf("Sending AIR to %s:\n%s\n%+#v\n\n", peerAddr, json, *req) + r, err := cli.AuthenticationInformation(req) + if err != nil || r == nil { + err2 := fmt.Errorf("GRPC AIR Error: %v", err) + log.Print(err2) + errChann <- err2 + return + } + json, err = orcprotos.MarshalIntern(r) + if err != nil { + err2 := fmt.Errorf("Marshal Error %v for result: %+v", err, *r) + errChann <- err2 + return + } + fmt.Printf("Received AIA:\n%s\n%+v\n", json, *r) + }() } - json, err = orcprotos.MarshalIntern(r) - if err != nil { - log.Printf("Marshal Error %v for result: %+v", err, *r) + + // go routine to collect the errors + go func() { + for err2 := range errChann { + airErrors = append(airErrors, err2) + } + done <- struct{}{} + }() + + // wait untill all air request are done + wg.Wait() + close(errChann) + // wait until all the errors are processed + <-done + close(done) + + // check if errors + if len(airErrors) != 0 { + log.Printf("Errors found: %d request failed out of %d\n", len(airErrors), imsiRange) return 9 } - fmt.Printf("Received AIA:\n%s\n%+v\n", json, *r) - + log.Printf("\nAll request (%d) got a response\n", imsiRange) return 0 } From 751d074ccf9e31f815ffe7563eed972fbc54c7c1 Mon Sep 17 00:00:00 2001 From: Marie <37634144+themarwhal@users.noreply.github.com> Date: Thu, 8 Apr 2021 17:50:14 -0500 Subject: [PATCH 67/91] [SessionD][Sentry] Add commit hash as release version when initializing sentry (#6047) Signed-off-by: Marie Bremner --- lte/gateway/c/session_manager/sessiond_main.cpp | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/lte/gateway/c/session_manager/sessiond_main.cpp b/lte/gateway/c/session_manager/sessiond_main.cpp index 2fa737780f06..1c368a33bb18 100644 --- a/lte/gateway/c/session_manager/sessiond_main.cpp +++ b/lte/gateway/c/session_manager/sessiond_main.cpp @@ -11,6 +11,7 @@ * limitations under the License. */ +#include #include #include @@ -47,15 +48,23 @@ extern "C" void __gcov_flush(void); #if SENTRY_ENABLED #include "sentry.h" +#define COMMIT_HASH_ENV "COMMIT_HASH" +#define CONTROL_PROXY_SERVICE_NAME "control_proxy" +#define SENTRY_URL "sentry_url" + void initialize_sentry() { auto control_proxy_config = - magma::ServiceConfigLoader{}.load_service_config("control_proxy"); - if (control_proxy_config["sentry_url"].IsDefined()) { + magma::ServiceConfigLoader{}.load_service_config(CONTROL_PROXY_SERVICE_NAME); + if (control_proxy_config[SENTRY_URL].IsDefined()) { const std::string sentry_dns = - control_proxy_config["sentry_url"].as(); + control_proxy_config[SENTRY_URL].as(); sentry_options_t* options = sentry_options_new(); sentry_options_set_dsn(options, sentry_dns.c_str()); + if (const char* commit_hash_p = std::getenv(COMMIT_HASH_ENV)) { + sentry_options_set_release(options, commit_hash_p); + } + sentry_init(options); sentry_capture_event(sentry_value_new_message_event( SENTRY_LEVEL_INFO, "", "Starting SessionD with Sentry!")); From fd15469014f19cc2b2d455aabfe957ac618d4576 Mon Sep 17 00:00:00 2001 From: Oriol Batalla Date: Thu, 8 Apr 2021 16:06:49 -0700 Subject: [PATCH 68/91] [docs][feg] Add Readmes to feg documentation (#5865) Signed-off-by: Oriol Batalla --- docs/readmes/feg/docker.md | 63 ++++++++++++++++++++ docs/readmes/feg/session_proxy.md | 49 +++++++++++++++ feg/gateway/docker/README.md | 2 +- feg/gateway/services/session_proxy/README.md | 50 +--------------- 4 files changed, 114 insertions(+), 50 deletions(-) create mode 100644 docs/readmes/feg/docker.md create mode 100644 docs/readmes/feg/session_proxy.md mode change 100644 => 120000 feg/gateway/services/session_proxy/README.md diff --git a/docs/readmes/feg/docker.md b/docs/readmes/feg/docker.md new file mode 100644 index 000000000000..b7cd35e82c52 --- /dev/null +++ b/docs/readmes/feg/docker.md @@ -0,0 +1,63 @@ +--- +id: docker_setup +title: FeG Docker Setup +hide_title: true +--- +# FeG Docker Setup + +The FeG runs each service in its own Docker container. +Production services are defined in `docker-compose.yml`. +Development services are defined in `docker-compose.override.yml`. +The development `test` service is used to run unit tests and regenerate Swagger/Protobuf code. +The development `test` service can also be used to perform other development-related procedures. + +## Requirements + +To run the FeG with docker, both docker and docker compose must be installed. +* Follow [these steps](https://docs.docker.com/install/) to install docker +* Follow [these steps](https://docs.docker.com/compose/install/) to install docker compose + +NOTE: If you are running the FeG on Mac, you will need to increase the memory +limit of the docker daemon to at least 4GB to build the images. Otherwise, +when building the Go image, you may see an error message similar to this: +`/usr/local/go/pkg/tool/linux_amd64/link: signal: killed`. + +The `rootCA.pem` certificate must be located in the `.cache/test_certs` folder, +so that it can be mounted into the appropriate containers from there. + +## Development + +Follow these steps to run the FeG services: +1. `cd magma/feg/gateway/docker` +2. `docker-compose build` +3. `docker-compose up -d` + +Each service should now be running in each of its containers. +By default, both production and development services should be running. +To place a shell into the test container, run the command: + +`docker-compose exec test /bin/bash` + +The test container contains the mounted source code and configuration settings. +The mounted source code and configuration settings can be changed externally +and the changes will be reflected inside the test container. +Run the command `make precommit` in the container before submitting a patch. + +To make changes to currently running FeG services, the containers must be rebuilt and restarted: +1. `docker-compose down` +2. `docker-compose build` +3. `docker-compose up -d` + +To manage the containers, the following commands are useful: +* `docker-compose ps` (get status of each container) +* `docker-compose logs -f` (tail logs of all containers) +* `docker-compose logs -f ` (tail logs of a particular service) +* `docker-compose down` (stop all services) + +## Publishing the images + +To push production images to a private docker registry, use the following script: +``` +[/magma/feg/gateway/docker]$ ../../../orc8r/tools/docker/publish.sh -r -i gateway_python +[/magma/feg/gateway/docker]$ ../../../orc8r/tools/docker/publish.sh -r -i gateway_go +``` diff --git a/docs/readmes/feg/session_proxy.md b/docs/readmes/feg/session_proxy.md new file mode 100644 index 000000000000..2274613409f1 --- /dev/null +++ b/docs/readmes/feg/session_proxy.md @@ -0,0 +1,49 @@ +# Session Proxy + +## Overview of Session Proxy +session_proxy is a service from the FEG that interacts with PCRF and OCS. +It's main function is to translate GRPC messages from sessiond into Diameter protocol back and forth. + +## Interfaces +1. Session Manager (sessiond)
+Magma PCEF is implemented by Session Manager (or sessiond). Session Proxy receive the GRPC messages +translates from sessiond and translates them into diameter messages for creation (CCR-I and CCA-I), +update (CCR-U and CCA-U) and termination (CCR-T and CCA-T). + +2. PCRF (Gx)
+session_proxy implements Gx interface and will translate policy related calls into diameter AVPs to +be sent to PCRF. It supports some events like monitoring tracking, time based rules, +and PCRF initiated messages like reauthentication + +3. OCS (Gy)
+session_proxy implements Gy interface and will translate charging related calls into diameter AVPs to +be sent to OCS. It supports charging reporting and OCS initiated messages like reauthentication + +4. PoicyDb
+session_proxy will get static rules and omnipresent rules from policyDb to inject +them to the responses back to SessionD + + +##Session Proxy Configuration +Configuration of Session Proxy can be done through NMS or Swagger API. In both +cases the configuration of Session Proxy is through Gx and Gy labels/tabs. + + +## Magma PCRF-less configuration +Omnipresent rules can be used as a way to achieve a PCRF-less (Gx) configuration while +maintaining charging (Gy) capabilities. Omnipresent rules are configured on Orc8r and injected +by session proxy to any subscriber that creates a new session. So if those omnipresent rules define a +Rating Group (or charging key), that key will be used by to charge the user and will +be communicated through Gy. Note that **all** subscribers will need to have that Rating Group +configured on OCS so the reporting is tracked. + +###Configure Magma PCRF-less: +- Create omnipresent rules:
+On NMS, create a static rule. In case you want to use charging (Gy) for that rule add a Rating Group +Then check the box that says `omnipresent`. Once this is enable, any subscriber that attaches to the +network will get that rule installed. Remember to check that subscriber on OCS too. + +- Disable Gx (optional):
+If there is not a PCRF to connect to, you can disable Gx so your session proxy doesn't try to connect to a +non-existing PCRF. To do so go to swagger API `/feg/{network_id}/gateways/{gateway_id}` +search for your Federated Gateway(using `feg network` and `feg gateway`) and modify `disableGx` under `Gx` key. diff --git a/feg/gateway/docker/README.md b/feg/gateway/docker/README.md index a3ba02fdbcd4..14d1681bfcab 120000 --- a/feg/gateway/docker/README.md +++ b/feg/gateway/docker/README.md @@ -1 +1 @@ -../../../docs/readmes/feg/legacy/docker_setup.md \ No newline at end of file +../../../docs/readmes/feg/docker.md \ No newline at end of file diff --git a/feg/gateway/services/session_proxy/README.md b/feg/gateway/services/session_proxy/README.md deleted file mode 100644 index 214e2ec61064..000000000000 --- a/feg/gateway/services/session_proxy/README.md +++ /dev/null @@ -1,49 +0,0 @@ -# Session Proxy - -## Overview of Session Proxy -Session_proxy is a service from the FEG that interacts with PCRF and OCS. It's main function is to -translate GRPC messages from sessiond into Diameter protocol back and forth. - -## Interfaces -1. Session Manager (sessiond)
-Magma PCEF is implemented by Session Manager (or sessiond). Session Proxy receive the GRPC messages -translates from sessiond and translates them into diameter messages for creation (CCR-I and CCA-I), -update (CCR-U and CCA-U) and termination (CCR-T and CCA-T). - -2. PCRF (Gx)
-Session proxy implements Gx interface and will translate policy related calls into diameter AVPs to -be sent to PCRF. It supports some events like monitoring tracking, time based rules, -and PCRF initiated messages like reauthentication - -3. OCS (Gy)
-Session proxy implements Gy interface and will translate charging related calls into diameter AVPs to -be sent to OCS. It supports charging reporting and OCS initiated messages like reauthentication - -4. PoicyDb
-Session_proxy will get static rules and omnipresent rules from policyDb to inject -them to the responses back to SessionD - - -##Session Proxy Configuration -Configuration of Session Proxy can be done through NMS or Swagger API. In both -cases the configuration of Session Proxy is through Gx and Gy labels/tabs. - - -## Magma PCRF-less configuration -Omnipresent rules can be used as a way to achieve a PCRF-less (Gx) configuration while -maintaining charging (Gy) capabilities. Omnipresent rules are configured on Orc8r and injected -by session proxy to any subscriber that creates a new session. So if those omnipresent rules define a -Rating Group (or charging key), that key will be used by to charge the user and will -be communicated through Gy. Note that **all** subscribers will need to have that Rating Group -configured on OCS so the reporting is tracked. - -###Configure Magma PCRF-less: -- Create omnipresent rules:
-On NMS, create a static rule. In case you want to use charging (Gy) for that rule add a Rating Group -Then check the box that says `omnipresent`. Once this is enable, any subscriber that attaches to the -network will get that rule installed. Remember to check that subscriber on OCS too. - -- Disable Gx (optional):
-If there is not a PCRF to connect to, you can disable Gx so your session proxy doesn't try to connect to a -non-existing PCRF. To do so go to swagger API `/feg/{network_id}/gateways/{gateway_id}` -search for your Federated Gateway(using `feg network` and `feg gateway`) and modify `disableGx` under `Gx` key. \ No newline at end of file diff --git a/feg/gateway/services/session_proxy/README.md b/feg/gateway/services/session_proxy/README.md new file mode 120000 index 000000000000..acabc6089776 --- /dev/null +++ b/feg/gateway/services/session_proxy/README.md @@ -0,0 +1 @@ +../../../../docs/readmes/feg/session_proxy.md \ No newline at end of file From 6749ceccb9cb0b0022364f06337bf4a88d34bc4e Mon Sep 17 00:00:00 2001 From: Marie <37634144+themarwhal@users.noreply.github.com> Date: Thu, 8 Apr 2021 18:33:51 -0500 Subject: [PATCH 69/91] =?UTF-8?q?[SessionD]=20Remove=20non-versioned=20pol?= =?UTF-8?q?icies=20from=20Activate/Deactivate=20req=E2=80=A6=20(#6035)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Marie Bremner --- lte/gateway/c/session_manager/PipelinedClient.cpp | 9 --------- lte/gateway/c/session_manager/test/Matchers.h | 5 ++--- 2 files changed, 2 insertions(+), 12 deletions(-) diff --git a/lte/gateway/c/session_manager/PipelinedClient.cpp b/lte/gateway/c/session_manager/PipelinedClient.cpp index c59ed534866e..4b813a03e099 100644 --- a/lte/gateway/c/session_manager/PipelinedClient.cpp +++ b/lte/gateway/c/session_manager/PipelinedClient.cpp @@ -62,10 +62,6 @@ magma::DeactivateFlowsRequest create_deactivate_req( req.set_uplink_tunnel(teids.agw_teid()); req.set_remove_default_drop_flows(remove_default_drop_rules); req.mutable_request_origin()->set_type(origin_type); - auto ids = req.mutable_rule_ids(); - for (const auto& rule : to_process.rules) { - ids->Add()->assign(rule.id()); - } auto mut_versioned_rules = req.mutable_policies(); for (uint index = 0; index < to_process.rules.size(); ++index) { auto versioned_policy = mut_versioned_rules->Add(); @@ -93,11 +89,6 @@ magma::ActivateFlowsRequest create_activate_req( if (ambr) { req.mutable_apn_ambr()->CopyFrom(*ambr); } - // TODO depracate dynamic rules fields - auto mut_dyn_rules = req.mutable_dynamic_rules(); - for (const auto& dyn_rule : to_process.rules) { - mut_dyn_rules->Add()->CopyFrom(dyn_rule); - } auto mut_versioned_rules = req.mutable_policies(); for (uint index = 0; index < to_process.rules.size(); ++index) { auto versioned_policy = mut_versioned_rules->Add(); diff --git a/lte/gateway/c/session_manager/test/Matchers.h b/lte/gateway/c/session_manager/test/Matchers.h index 8af5fbcd6903..1134cf09f3e9 100644 --- a/lte/gateway/c/session_manager/test/Matchers.h +++ b/lte/gateway/c/session_manager/test/Matchers.h @@ -248,13 +248,12 @@ MATCHER_P6( CheckActivateFlowsForTunnIds, imsi, ipv4, ipv6, enb_teid, agw_teid, rule_count, "") { auto request = static_cast(arg); - std::cerr << "Got dynamic size : " << request->dynamic_rules_size() - << std::endl; + std::cerr << "Got " << request->policies_size() << " rules" << std::endl; auto res = request->sid().id() == imsi && request->ip_addr() == ipv4 && request->ipv6_addr() == ipv6 && request->uplink_tunnel() == agw_teid && request->downlink_tunnel() == enb_teid && - request->dynamic_rules_size() == rule_count; + request->policies_size() == rule_count; return res; } From 730422313501bb5970d37bad51dd27085ac9b6f2 Mon Sep 17 00:00:00 2001 From: Marie <37634144+themarwhal@users.noreply.github.com> Date: Thu, 8 Apr 2021 18:44:17 -0500 Subject: [PATCH 70/91] [AGW][PyFormat] Apply isort to PipelineD - round 2 (#5941) Signed-off-by: Marie Bremner --- .../magma/pipelined/app/access_control.py | 5 +- lte/gateway/python/magma/pipelined/app/arp.py | 13 ++-- .../python/magma/pipelined/app/base.py | 23 ++++--- .../python/magma/pipelined/app/check_quota.py | 26 ++++---- .../python/magma/pipelined/app/classifier.py | 18 +++--- .../python/magma/pipelined/app/conntrack.py | 8 +-- lte/gateway/python/magma/pipelined/app/dpi.py | 17 +++-- .../python/magma/pipelined/app/enforcement.py | 20 +++--- .../magma/pipelined/app/enforcement_stats.py | 52 +++++++++------ lte/gateway/python/magma/pipelined/app/gy.py | 13 ++-- lte/gateway/python/magma/pipelined/app/he.py | 44 ++++++++----- .../python/magma/pipelined/app/inout.py | 43 +++++++------ .../python/magma/pipelined/app/ipfix.py | 9 +-- .../magma/pipelined/app/ipv6_solicitation.py | 15 ++--- .../python/magma/pipelined/app/li_mirror.py | 10 ++- .../python/magma/pipelined/app/ng_services.py | 3 +- .../magma/pipelined/app/policy_mixin.py | 35 ++++++---- .../magma/pipelined/app/restart_mixin.py | 3 +- .../magma/pipelined/app/startup_flows.py | 14 ++-- .../python/magma/pipelined/app/testing.py | 6 +- .../magma/pipelined/app/tunnel_learn.py | 4 +- .../python/magma/pipelined/app/ue_mac.py | 25 ++++---- .../magma/pipelined/app/uplink_bridge.py | 3 +- .../python/magma/pipelined/app/vlan_learn.py | 14 ++-- .../magma/pipelined/app/xwf_passthru.py | 3 +- .../python/magma/pipelined/bridge_util.py | 6 +- .../magma/pipelined/directoryd_client.py | 11 ++-- .../python/magma/pipelined/encoding.py | 8 +-- .../python/magma/pipelined/envoy_client.py | 15 +++-- .../magma/pipelined/gtp_stats_collector.py | 13 ++-- lte/gateway/python/magma/pipelined/ifaces.py | 2 +- .../magma/pipelined/ipv6_prefix_store.py | 8 ++- lte/gateway/python/magma/pipelined/main.py | 24 +++---- lte/gateway/python/magma/pipelined/metrics.py | 1 - .../magma/pipelined/mobilityd_client.py | 7 +- .../ng_manager/node_state_manager.py | 16 +++-- .../ng_manager/session_state_manager.py | 27 ++++---- .../ng_manager/session_state_manager_util.py | 7 +- .../python/magma/pipelined/openflow/flows.py | 2 +- .../magma/pipelined/openflow/magma_match.py | 17 +++-- .../magma/pipelined/openflow/messages.py | 11 ++-- .../python/magma/pipelined/openflow/meters.py | 1 + .../magma/pipelined/openflow/registers.py | 1 + .../pipelined/openflow/tests/test_msg_hub.py | 5 +- .../magma/pipelined/policy_converters.py | 12 ++-- .../python/magma/pipelined/qos/common.py | 23 ++++--- .../magma/pipelined/qos/qos_meter_impl.py | 6 +- .../python/magma/pipelined/qos/qos_tc_impl.py | 10 +-- .../python/magma/pipelined/qos/tc_ops.py | 8 ++- .../python/magma/pipelined/qos/tc_ops_cmd.py | 5 +- .../magma/pipelined/qos/tc_ops_pyroute2.py | 6 +- .../python/magma/pipelined/qos/utils.py | 7 +- .../python/magma/pipelined/redirect.py | 20 ++++-- .../python/magma/pipelined/rpc_servicer.py | 54 +++++++++------- .../python/magma/pipelined/rule_mappers.py | 11 ++-- .../python/magma/pipelined/service_manager.py | 58 +++++++++-------- .../magma/pipelined/set_interface_client.py | 6 +- .../magma/pipelined/tests/app/flow_query.py | 1 - .../pipelined/tests/app/ng_set_session_msg.py | 28 ++++---- .../pipelined/tests/app/packet_builder.py | 17 ++++- .../pipelined/tests/app/packet_injector.py | 3 +- .../pipelined/tests/app/start_pipelined.py | 15 +++-- .../magma/pipelined/tests/app/subscriber.py | 13 ++-- .../pipelined/tests/app/table_isolation.py | 21 +++--- .../pipelined/tests/ng_node_rpc_servicer.py | 38 +++++------ .../tests/old_tests/test_controller.py | 21 +++--- .../pipelined/tests/old_tests/test_inout.py | 7 +- .../pipelined/tests/pipelined_test_util.py | 29 +++++---- .../pipelined/tests/test_access_control.py | 37 +++++++---- .../python/magma/pipelined/tests/test_arp.py | 38 ++++++----- .../magma/pipelined/tests/test_arp_non_nat.py | 41 ++++++++---- .../magma/pipelined/tests/test_check_quota.py | 8 +-- .../magma/pipelined/tests/test_classifier.py | 25 ++++---- .../tests/test_classifier_traffic.py | 26 ++++---- .../magma/pipelined/tests/test_conntrack.py | 31 ++++++--- .../tests/test_cwf_restart_resilience.py | 30 ++++++--- .../python/magma/pipelined/tests/test_dpi.py | 14 ++-- .../magma/pipelined/tests/test_encoding.py | 8 ++- .../magma/pipelined/tests/test_enforcement.py | 62 +++++++++++------- .../pipelined/tests/test_enforcement_stats.py | 51 ++++++++++----- .../python/magma/pipelined/tests/test_gy.py | 54 +++++++++++----- .../python/magma/pipelined/tests/test_he.py | 55 +++++++++------- .../pipelined/tests/test_imsi_encoding.py | 2 +- .../magma/pipelined/tests/test_inout.py | 10 +-- .../pipelined/tests/test_inout_non_nat.py | 22 +++---- .../tests/test_internal_pkt_ipfix_export.py | 19 ++++-- .../tests/test_ipv6_prefix_mapper.py | 7 +- .../pipelined/tests/test_ipv6_solicitation.py | 47 +++++++++----- .../magma/pipelined/tests/test_li_mirror.py | 13 ++-- .../pipelined/tests/test_ng_servicer_node.py | 33 +++++----- .../tests/test_ng_servicer_session.py | 18 +++--- .../python/magma/pipelined/tests/test_qos.py | 18 ++++-- .../pipelined/tests/test_qos_pyroute2.py | 16 ++--- .../magma/pipelined/tests/test_redirect.py | 46 ++++++++----- .../tests/test_restart_resilience.py | 64 +++++++++++++------ .../pipelined/tests/test_rule_mappers.py | 6 +- .../pipelined/tests/test_service_manager.py | 14 ++-- .../pipelined/tests/test_tunnel_learn.py | 27 +++++--- .../magma/pipelined/tests/test_ue_mac.py | 17 +++-- .../pipelined/tests/test_ue_passthrough.py | 32 +++++----- .../pipelined/tests/test_uplink_bridge.py | 18 +++--- .../magma/pipelined/tests/test_vlan_learn.py | 11 ++-- .../python/magma/pipelined/tunnel_id_store.py | 6 +- lte/gateway/python/magma/pipelined/utils.py | 2 + setup.cfg | 4 ++ 105 files changed, 1130 insertions(+), 829 deletions(-) diff --git a/lte/gateway/python/magma/pipelined/app/access_control.py b/lte/gateway/python/magma/pipelined/app/access_control.py index 745a2f61ab32..ad49e74af0e9 100644 --- a/lte/gateway/python/magma/pipelined/app/access_control.py +++ b/lte/gateway/python/magma/pipelined/app/access_control.py @@ -14,12 +14,11 @@ import ipaddress from collections import namedtuple -from ryu.lib.packet import ether_types - +from magma.pipelined.app.base import ControllerType, MagmaController from magma.pipelined.openflow import flows from magma.pipelined.openflow.magma_match import MagmaMatch from magma.pipelined.openflow.registers import Direction -from magma.pipelined.app.base import MagmaController, ControllerType +from ryu.lib.packet import ether_types class AccessControlController(MagmaController): diff --git a/lte/gateway/python/magma/pipelined/app/arp.py b/lte/gateway/python/magma/pipelined/app/arp.py index ce510ace8a1c..21e7dd1c6f48 100644 --- a/lte/gateway/python/magma/pipelined/app/arp.py +++ b/lte/gateway/python/magma/pipelined/app/arp.py @@ -11,23 +11,20 @@ limitations under the License. """ -import netifaces import ipaddress - from collections import namedtuple +import netifaces from lte.protos.pipelined_pb2 import SetupFlowsResult, SetupUEMacRequest - from magma.common.misc_utils import cidr_to_ip_netmask_tuple -from magma.pipelined.app.base import MagmaController, ControllerType +from magma.pipelined.app.base import ControllerType, MagmaController +from magma.pipelined.directoryd_client import get_all_records +from magma.pipelined.mobilityd_client import mobilityd_list_ip_blocks from magma.pipelined.openflow import flows from magma.pipelined.openflow.magma_match import MagmaMatch from magma.pipelined.openflow.registers import Direction, load_passthrough -from magma.pipelined.directoryd_client import get_all_records -from magma.pipelined.mobilityd_client import mobilityd_list_ip_blocks - from ryu.controller import dpset -from ryu.lib.packet import ether_types, arp +from ryu.lib.packet import arp, ether_types # This is used to determine valid ip-blocks. MAX_SUBNET_PREFIX_LEN = 31 diff --git a/lte/gateway/python/magma/pipelined/app/base.py b/lte/gateway/python/magma/pipelined/app/base.py index 07e6e512bd1e..92620723e5f3 100644 --- a/lte/gateway/python/magma/pipelined/app/base.py +++ b/lte/gateway/python/magma/pipelined/app/base.py @@ -10,24 +10,23 @@ See the License for the specific language governing permissions and limitations under the License. """ -from enum import Enum import time - -from ryu import utils -from ryu.base import app_manager -from ryu.controller import dpset -from ryu.controller import ofp_event -from ryu.controller.handler import CONFIG_DISPATCHER -from ryu.controller.handler import MAIN_DISPATCHER -from ryu.controller.handler import HANDSHAKE_DISPATCHER -from ryu.controller.handler import set_ev_cls -from ryu.ofproto import ofproto_v1_4 +from enum import Enum from lte.protos.pipelined_pb2 import SetupFlowsResult from magma.pipelined.bridge_util import BridgeTools, DatapathLookupError from magma.pipelined.metrics import OPENFLOW_ERROR_MSG from magma.pipelined.openflow.exceptions import MagmaOFError - +from ryu import utils +from ryu.base import app_manager +from ryu.controller import dpset, ofp_event +from ryu.controller.handler import ( + CONFIG_DISPATCHER, + HANDSHAKE_DISPATCHER, + MAIN_DISPATCHER, + set_ev_cls, +) +from ryu.ofproto import ofproto_v1_4 global_epoch = int(time.time()) diff --git a/lte/gateway/python/magma/pipelined/app/check_quota.py b/lte/gateway/python/magma/pipelined/app/check_quota.py index b407373c92ae..43be21b22628 100644 --- a/lte/gateway/python/magma/pipelined/app/check_quota.py +++ b/lte/gateway/python/magma/pipelined/app/check_quota.py @@ -10,24 +10,26 @@ See the License for the specific language governing permissions and limitations under the License. """ -import netifaces import ipaddress -from typing import NamedTuple, Dict, List - -from ryu.lib.packet import ether_types -from ryu.ofproto.inet import IPPROTO_TCP -from ryu.controller.controller import Datapath -from ryu.ofproto.ofproto_v1_4 import OFPP_LOCAL +from typing import Dict, List, NamedTuple -from lte.protos.pipelined_pb2 import SubscriberQuotaUpdate, SetupFlowsResult -from magma.pipelined.app.base import MagmaController, ControllerType -from magma.pipelined.app.inout import INGRESS, EGRESS +import netifaces +from lte.protos.pipelined_pb2 import SetupFlowsResult, SubscriberQuotaUpdate +from magma.pipelined.app.base import ControllerType, MagmaController +from magma.pipelined.app.inout import EGRESS, INGRESS from magma.pipelined.app.ue_mac import UEMacAddressController from magma.pipelined.imsi import encode_imsi from magma.pipelined.openflow import flows from magma.pipelined.openflow.magma_match import MagmaMatch -from magma.pipelined.openflow.registers import Direction, IMSI_REG, \ - DIRECTION_REG +from magma.pipelined.openflow.registers import ( + DIRECTION_REG, + IMSI_REG, + Direction, +) +from ryu.controller.controller import Datapath +from ryu.lib.packet import ether_types +from ryu.ofproto.inet import IPPROTO_TCP +from ryu.ofproto.ofproto_v1_4 import OFPP_LOCAL class CheckQuotaController(MagmaController): diff --git a/lte/gateway/python/magma/pipelined/app/classifier.py b/lte/gateway/python/magma/pipelined/app/classifier.py index c0ef438d6be3..ab2b04d566bf 100644 --- a/lte/gateway/python/magma/pipelined/app/classifier.py +++ b/lte/gateway/python/magma/pipelined/app/classifier.py @@ -10,23 +10,21 @@ See the License for the specific language governing permissions and limitations under the License. """ -import subprocess import ipaddress import socket +import subprocess from collections import namedtuple -from ryu.ofproto.ofproto_v1_4 import OFPP_LOCAL -from .base import MagmaController +from lte.protos.mobilityd_pb2 import IPAddress +from magma.pipelined.app.base import ControllerType, MagmaController +from magma.pipelined.app.inout import INGRESS from magma.pipelined.openflow import flows from magma.pipelined.openflow.magma_match import MagmaMatch -from magma.pipelined.app.inout import INGRESS -from ryu.lib.packet import ether_types -from magma.pipelined.app.base import MagmaController, ControllerType +from magma.pipelined.openflow.registers import TUN_PORT_REG, Direction +from magma.pipelined.policy_converters import get_eth_type, get_ue_ip_match_args from magma.pipelined.utils import Utils -from magma.pipelined.openflow.registers import TUN_PORT_REG -from lte.protos.mobilityd_pb2 import IPAddress -from magma.pipelined.policy_converters import get_ue_ip_match_args, get_eth_type -from magma.pipelined.openflow.registers import Direction +from ryu.lib.packet import ether_types +from ryu.ofproto.ofproto_v1_4 import OFPP_LOCAL GTP_PORT_MAC = "02:00:00:00:00:01" TUNNEL_OAM_FLAG = 1 diff --git a/lte/gateway/python/magma/pipelined/app/conntrack.py b/lte/gateway/python/magma/pipelined/app/conntrack.py index e29f5c295c79..276990ee2436 100644 --- a/lte/gateway/python/magma/pipelined/app/conntrack.py +++ b/lte/gateway/python/magma/pipelined/app/conntrack.py @@ -10,13 +10,11 @@ See the License for the specific language governing permissions and limitations under the License. """ -from ryu.lib.packet import ether_types -from ryu.ofproto.nicira_ext import ofs_nbits - - -from .base import MagmaController, ControllerType +from magma.pipelined.app.base import ControllerType, MagmaController from magma.pipelined.openflow import flows from magma.pipelined.openflow.magma_match import MagmaMatch +from ryu.lib.packet import ether_types +from ryu.ofproto.nicira_ext import ofs_nbits class ConntrackController(MagmaController): diff --git a/lte/gateway/python/magma/pipelined/app/dpi.py b/lte/gateway/python/magma/pipelined/app/dpi.py index a571f6bc92f1..716b61a48a82 100644 --- a/lte/gateway/python/magma/pipelined/app/dpi.py +++ b/lte/gateway/python/magma/pipelined/app/dpi.py @@ -10,19 +10,22 @@ See the License for the specific language governing permissions and limitations under the License. """ +import logging import shlex import subprocess -import logging -from magma.pipelined.openflow import flows -from magma.pipelined.bridge_util import BridgeTools -from magma.pipelined.app.base import MagmaController, ControllerType +from lte.protos.pipelined_pb2 import FlowRequest +from magma.pipelined.app.base import ControllerType, MagmaController from magma.pipelined.app.ipfix import IPFIXController +from magma.pipelined.bridge_util import BridgeTools +from magma.pipelined.openflow import flows from magma.pipelined.openflow.magma_match import MagmaMatch from magma.pipelined.openflow.registers import DPI_REG -from magma.pipelined.policy_converters import FlowMatchError, \ - flow_match_to_magma_match, flip_flow_match -from lte.protos.pipelined_pb2 import FlowRequest +from magma.pipelined.policy_converters import ( + FlowMatchError, + flip_flow_match, + flow_match_to_magma_match, +) # TODO might move to config file # Current classification will finalize if found in APP_PROTOS, if found in diff --git a/lte/gateway/python/magma/pipelined/app/enforcement.py b/lte/gateway/python/magma/pipelined/app/enforcement.py index 360539b2e05d..26ad42a65b55 100644 --- a/lte/gateway/python/magma/pipelined/app/enforcement.py +++ b/lte/gateway/python/magma/pipelined/app/enforcement.py @@ -11,27 +11,27 @@ limitations under the License. """ from lte.protos.pipelined_pb2 import RuleModResult - -from magma.pipelined.app.base import MagmaController, ControllerType +from magma.pipelined.app.base import ControllerType, MagmaController from magma.pipelined.app.enforcement_stats import EnforcementStatsController from magma.pipelined.app.policy_mixin import PolicyMixin -from magma.pipelined.app.restart_mixin import RestartMixin, DefaultMsgsMap - +from magma.pipelined.app.restart_mixin import DefaultMsgsMap, RestartMixin from magma.pipelined.imsi import encode_imsi from magma.pipelined.openflow import flows +from magma.pipelined.openflow.exceptions import MagmaDPDisconnectedError from magma.pipelined.openflow.magma_match import MagmaMatch from magma.pipelined.openflow.messages import MessageHub from magma.pipelined.openflow.registers import Direction -from magma.pipelined.policy_converters import FlowMatchError, \ - get_ue_ip_match_args, get_eth_type -from magma.pipelined.redirect import RedirectionManager, RedirectException +from magma.pipelined.policy_converters import ( + FlowMatchError, + get_eth_type, + get_ue_ip_match_args, +) from magma.pipelined.qos.common import QosManager from magma.pipelined.qos.qos_meter_impl import MeterManager - +from magma.pipelined.redirect import RedirectException, RedirectionManager +from magma.pipelined.utils import Utils from ryu.controller import ofp_event from ryu.controller.handler import MAIN_DISPATCHER, set_ev_cls -from magma.pipelined.utils import Utils -from magma.pipelined.openflow.exceptions import MagmaDPDisconnectedError class EnforcementController(PolicyMixin, RestartMixin, MagmaController): diff --git a/lte/gateway/python/magma/pipelined/app/enforcement_stats.py b/lte/gateway/python/magma/pipelined/app/enforcement_stats.py index f69948ccf894..50cf21e73714 100644 --- a/lte/gateway/python/magma/pipelined/app/enforcement_stats.py +++ b/lte/gateway/python/magma/pipelined/app/enforcement_stats.py @@ -13,34 +13,44 @@ import os from collections import defaultdict -from lte.protos.pipelined_pb2 import RuleModResult from lte.protos.mobilityd_pb2 import IPAddress +from lte.protos.pipelined_pb2 import RuleModResult from lte.protos.policydb_pb2 import FlowDescription -from lte.protos.session_manager_pb2 import RuleRecord, \ - RuleRecordTable +from lte.protos.session_manager_pb2 import RuleRecord, RuleRecordTable +from magma.pipelined.app.base import ( + ControllerType, + MagmaController, + global_epoch, +) +from magma.pipelined.app.policy_mixin import ( + DROP_FLOW_STATS, + IGNORE_STATS, + PROCESS_STATS, + PolicyMixin, +) +from magma.pipelined.app.restart_mixin import DefaultMsgsMap, RestartMixin +from magma.pipelined.imsi import decode_imsi, encode_imsi +from magma.pipelined.openflow import flows, messages +from magma.pipelined.openflow.exceptions import ( + MagmaDPDisconnectedError, + MagmaOFError, +) +from magma.pipelined.openflow.magma_match import MagmaMatch +from magma.pipelined.openflow.messages import MessageHub, MsgChannel +from magma.pipelined.openflow.registers import ( + DIRECTION_REG, + IMSI_REG, + RULE_VERSION_REG, + SCRATCH_REGS, + Direction, +) +from magma.pipelined.policy_converters import get_eth_type, get_ue_ip_match_args +from magma.pipelined.utils import Utils from ryu.controller import dpset, ofp_event from ryu.controller.handler import MAIN_DISPATCHER, set_ev_cls from ryu.lib import hub from ryu.ofproto.ofproto_v1_4 import OFPMPF_REPLY_MORE -from magma.pipelined.app.base import MagmaController, ControllerType, \ - global_epoch -from magma.pipelined.app.policy_mixin import PolicyMixin, IGNORE_STATS, \ - PROCESS_STATS, DROP_FLOW_STATS -from magma.pipelined.app.restart_mixin import RestartMixin, DefaultMsgsMap -from magma.pipelined.policy_converters import get_ue_ip_match_args, \ - get_eth_type -from magma.pipelined.openflow import messages, flows -from magma.pipelined.openflow.exceptions import MagmaOFError -from magma.pipelined.imsi import decode_imsi, encode_imsi -from magma.pipelined.openflow.magma_match import MagmaMatch -from magma.pipelined.openflow.messages import MsgChannel, MessageHub -from magma.pipelined.utils import Utils -from magma.pipelined.openflow.registers import Direction, DIRECTION_REG, \ - IMSI_REG, RULE_VERSION_REG, SCRATCH_REGS -from magma.pipelined.openflow.exceptions import MagmaDPDisconnectedError - - ETH_FRAME_SIZE_BYTES = 14 diff --git a/lte/gateway/python/magma/pipelined/app/gy.py b/lte/gateway/python/magma/pipelined/app/gy.py index dbe6c8364a1c..d19d84157530 100644 --- a/lte/gateway/python/magma/pipelined/app/gy.py +++ b/lte/gateway/python/magma/pipelined/app/gy.py @@ -11,25 +11,22 @@ limitations under the License. """ from lte.protos.pipelined_pb2 import RuleModResult - -from magma.pipelined.app.base import MagmaController, ControllerType +from magma.pipelined.app.base import ControllerType, MagmaController from magma.pipelined.app.enforcement_stats import EnforcementStatsController +from magma.pipelined.app.inout import EGRESS from magma.pipelined.app.policy_mixin import PolicyMixin -from magma.pipelined.app.restart_mixin import RestartMixin, DefaultMsgsMap - +from magma.pipelined.app.restart_mixin import DefaultMsgsMap, RestartMixin from magma.pipelined.imsi import encode_imsi from magma.pipelined.openflow import flows from magma.pipelined.openflow.magma_match import MagmaMatch from magma.pipelined.openflow.messages import MessageHub from magma.pipelined.policy_converters import FlowMatchError -from magma.pipelined.redirect import RedirectionManager, RedirectException -from magma.pipelined.app.inout import EGRESS from magma.pipelined.qos.common import QosManager from magma.pipelined.qos.qos_meter_impl import MeterManager - +from magma.pipelined.redirect import RedirectException, RedirectionManager +from magma.pipelined.utils import Utils from ryu.controller import ofp_event from ryu.controller.handler import MAIN_DISPATCHER, set_ev_cls -from magma.pipelined.utils import Utils class GYController(PolicyMixin, RestartMixin, MagmaController): diff --git a/lte/gateway/python/magma/pipelined/app/he.py b/lte/gateway/python/magma/pipelined/app/he.py index 32d9dfb641cc..d8d0d95002f0 100644 --- a/lte/gateway/python/magma/pipelined/app/he.py +++ b/lte/gateway/python/magma/pipelined/app/he.py @@ -10,29 +10,39 @@ See the License for the specific language governing permissions and limitations under the License. """ +import logging from collections import namedtuple from threading import Lock from typing import List -from ryu.lib.packet import ether_types -from ryu.lib.packet.in_proto import IPPROTO_TCP - -from .base import MagmaController, ControllerType -from magma.pipelined.openflow import flows -from magma.pipelined.openflow.magma_match import MagmaMatch -from magma.pipelined.openflow.registers import load_direction, Direction, \ - load_passthrough, set_proxy_tag, set_in_port, load_imsi, \ - PROXY_TAG_TO_PROXY, set_tun_id -from magma.pipelined.envoy_client import activate_he_urls_for_ue, \ - deactivate_he_urls_for_ue -from magma.pipelined.encoding import encrypt_str, get_hash, encode_str - from lte.protos.mobilityd_pb2 import IPAddress +from magma.pipelined.app.base import ControllerType, MagmaController from magma.pipelined.bridge_util import BridgeTools, DatapathLookupError -from magma.pipelined.policy_converters import get_ue_ip_match_args, \ - get_eth_type, convert_ipv4_str_to_ip_proto, ipv4_address_to_str - -import logging +from magma.pipelined.encoding import encode_str, encrypt_str, get_hash +from magma.pipelined.envoy_client import ( + activate_he_urls_for_ue, + deactivate_he_urls_for_ue, +) +from magma.pipelined.openflow import flows +from magma.pipelined.openflow.magma_match import MagmaMatch +from magma.pipelined.openflow.registers import ( + PROXY_TAG_TO_PROXY, + Direction, + load_direction, + load_imsi, + load_passthrough, + set_in_port, + set_proxy_tag, + set_tun_id, +) +from magma.pipelined.policy_converters import ( + convert_ipv4_str_to_ip_proto, + get_eth_type, + get_ue_ip_match_args, + ipv4_address_to_str, +) +from ryu.lib.packet import ether_types +from ryu.lib.packet.in_proto import IPPROTO_TCP PROXY_PORT_NAME = 'proxy_port' HTTP_PORT = 80 diff --git a/lte/gateway/python/magma/pipelined/app/inout.py b/lte/gateway/python/magma/pipelined/app/inout.py index 237f9aa84b54..4b582628c5a1 100644 --- a/lte/gateway/python/magma/pipelined/app/inout.py +++ b/lte/gateway/python/magma/pipelined/app/inout.py @@ -12,35 +12,38 @@ """ import ipaddress import threading - from collections import namedtuple -from ryu.ofproto.ofproto_v1_4 import OFPP_LOCAL - -from scapy.arch import get_if_hwaddr, get_if_addr -from scapy.data import ETHER_BROADCAST, ETH_P_ALL -from scapy.error import Scapy_Exception -from scapy.layers.l2 import ARP, Ether, Dot1Q -from scapy.sendrecv import srp1 - -from .base import MagmaController -from magma.pipelined.mobilityd_client import get_mobilityd_gw_info, \ - set_mobilityd_gw_info from lte.protos.mobilityd_pb2 import IPAddress - +from magma.pipelined.app.base import MagmaController from magma.pipelined.app.li_mirror import LIMirrorController -from magma.pipelined.openflow import flows +from magma.pipelined.app.restart_mixin import DefaultMsgsMap, RestartMixin from magma.pipelined.bridge_util import BridgeTools, DatapathLookupError +from magma.pipelined.mobilityd_client import ( + get_mobilityd_gw_info, + set_mobilityd_gw_info, +) +from magma.pipelined.openflow import flows from magma.pipelined.openflow.magma_match import MagmaMatch from magma.pipelined.openflow.messages import MessageHub, MsgChannel -from magma.pipelined.openflow.registers import load_direction, Direction, \ - PASSTHROUGH_REG_VAL, TUN_PORT_REG, PROXY_TAG_TO_PROXY, REG_ZERO_VAL -from magma.pipelined.app.restart_mixin import RestartMixin, DefaultMsgsMap - -from ryu.lib import hub -from ryu.lib.packet import ether_types +from magma.pipelined.openflow.registers import ( + PASSTHROUGH_REG_VAL, + PROXY_TAG_TO_PROXY, + REG_ZERO_VAL, + TUN_PORT_REG, + Direction, + load_direction, +) from ryu.controller import ofp_event from ryu.controller.handler import MAIN_DISPATCHER, set_ev_cls +from ryu.lib import hub +from ryu.lib.packet import ether_types +from ryu.ofproto.ofproto_v1_4 import OFPP_LOCAL +from scapy.arch import get_if_addr, get_if_hwaddr +from scapy.data import ETH_P_ALL, ETHER_BROADCAST +from scapy.error import Scapy_Exception +from scapy.layers.l2 import ARP, Dot1Q, Ether +from scapy.sendrecv import srp1 # ingress and egress service names -- used by other controllers diff --git a/lte/gateway/python/magma/pipelined/app/ipfix.py b/lte/gateway/python/magma/pipelined/app/ipfix.py index 6d783ced0102..51ef465ee877 100644 --- a/lte/gateway/python/magma/pipelined/app/ipfix.py +++ b/lte/gateway/python/magma/pipelined/app/ipfix.py @@ -12,13 +12,14 @@ """ import shlex import subprocess -from typing import NamedTuple, Dict +from typing import Dict, NamedTuple -from magma.pipelined.app.base import MagmaController, ControllerType +from magma.pipelined.app.base import ControllerType, MagmaController +from magma.pipelined.imsi import encode_imsi from magma.pipelined.openflow import flows -from ryu.controller.controller import Datapath from magma.pipelined.openflow.magma_match import MagmaMatch -from magma.pipelined.imsi import encode_imsi +from ryu.controller.controller import Datapath + class IPFIXController(MagmaController): """ diff --git a/lte/gateway/python/magma/pipelined/app/ipv6_solicitation.py b/lte/gateway/python/magma/pipelined/app/ipv6_solicitation.py index c46d007c651e..cb2effbf0e70 100644 --- a/lte/gateway/python/magma/pipelined/app/ipv6_solicitation.py +++ b/lte/gateway/python/magma/pipelined/app/ipv6_solicitation.py @@ -12,18 +12,15 @@ """ from collections import namedtuple -from ryu.controller import ofp_event -from ryu.controller.handler import MAIN_DISPATCHER, set_ev_cls -from magma.pipelined.app.base import MagmaController, ControllerType +from magma.pipelined.app.base import ControllerType, MagmaController +from magma.pipelined.ipv6_prefix_store import get_ipv6_interface_id from magma.pipelined.openflow import flows from magma.pipelined.openflow.magma_match import MagmaMatch -from magma.pipelined.ipv6_prefix_store import get_ipv6_interface_id -from magma.pipelined.openflow.registers import Direction, DIRECTION_REG - -from ryu.controller import dpset +from magma.pipelined.openflow.registers import DIRECTION_REG, Direction +from ryu.controller import dpset, ofp_event +from ryu.controller.handler import MAIN_DISPATCHER, set_ev_cls +from ryu.lib.packet import ether_types, ethernet, icmpv6, in_proto, ipv6, packet from ryu.ofproto.inet import IPPROTO_ICMPV6 -from ryu.lib.packet import packet, ethernet, ether_types, icmpv6, ipv6, \ - in_proto class IPV6SolicitationController(MagmaController): diff --git a/lte/gateway/python/magma/pipelined/app/li_mirror.py b/lte/gateway/python/magma/pipelined/app/li_mirror.py index e6fbe2f542bc..e06ef2018c73 100644 --- a/lte/gateway/python/magma/pipelined/app/li_mirror.py +++ b/lte/gateway/python/magma/pipelined/app/li_mirror.py @@ -10,16 +10,14 @@ See the License for the specific language governing permissions and limitations under the License. """ -from magma.pipelined.openflow import flows -from magma.pipelined.bridge_util import BridgeTools -from magma.pipelined.app.base import MagmaController, ControllerType +from lte.protos.mconfig import mconfigs_pb2 from magma.configuration.mconfig_managers import load_service_mconfig - +from magma.pipelined.app.base import ControllerType, MagmaController +from magma.pipelined.bridge_util import BridgeTools from magma.pipelined.imsi import encode_imsi +from magma.pipelined.openflow import flows from magma.pipelined.openflow.magma_match import MagmaMatch from magma.pipelined.openflow.registers import Direction -from lte.protos.mconfig import mconfigs_pb2 - from ryu.lib import hub from ryu.lib.packet import ether_types diff --git a/lte/gateway/python/magma/pipelined/app/ng_services.py b/lte/gateway/python/magma/pipelined/app/ng_services.py index 5b4f30871a2c..ba876ab43d05 100644 --- a/lte/gateway/python/magma/pipelined/app/ng_services.py +++ b/lte/gateway/python/magma/pipelined/app/ng_services.py @@ -10,10 +10,11 @@ See the License for the specific language governing permissions and limitations under the License. """ -from .base import MagmaController, ControllerType +from magma.pipelined.app.base import ControllerType, MagmaController from magma.pipelined.ng_manager.node_state_manager import NodeStateManager from magma.pipelined.ng_manager.session_state_manager import SessionStateManager + class NGServiceController(MagmaController): """ This class is intended to be a place holder for diff --git a/lte/gateway/python/magma/pipelined/app/policy_mixin.py b/lte/gateway/python/magma/pipelined/app/policy_mixin.py index bd63c8360703..21c766bdf78f 100644 --- a/lte/gateway/python/magma/pipelined/app/policy_mixin.py +++ b/lte/gateway/python/magma/pipelined/app/policy_mixin.py @@ -10,24 +10,33 @@ See the License for the specific language governing permissions and limitations under the License. """ -from typing import List from abc import ABCMeta, abstractmethod +from typing import List -from lte.protos.pipelined_pb2 import RuleModResult, ActivateFlowsResult, \ - ActivateFlowsRequest -from magma.pipelined.openflow import flows -from magma.pipelined.openflow.registers import SCRATCH_REGS, RULE_VERSION_REG, \ - RULE_NUM_REG -from magma.pipelined.openflow.messages import MsgChannel - +from lte.protos.mobilityd_pb2 import IPAddress +from lte.protos.pipelined_pb2 import ( + ActivateFlowsRequest, + ActivateFlowsResult, + RuleModResult, +) from lte.protos.policydb_pb2 import PolicyRule from magma.pipelined.app.dpi import UNCLASSIFIED_PROTO_ID, get_app_id from magma.pipelined.imsi import encode_imsi -from magma.pipelined.policy_converters import get_direction_for_match, \ - flow_match_to_magma_match, get_flow_ip_dst, ipv4_address_to_str, \ - FlowMatchError, convert_ipv4_str_to_ip_proto -from lte.protos.mobilityd_pb2 import IPAddress - +from magma.pipelined.openflow import flows +from magma.pipelined.openflow.messages import MsgChannel +from magma.pipelined.openflow.registers import ( + RULE_NUM_REG, + RULE_VERSION_REG, + SCRATCH_REGS, +) +from magma.pipelined.policy_converters import ( + FlowMatchError, + convert_ipv4_str_to_ip_proto, + flow_match_to_magma_match, + get_direction_for_match, + get_flow_ip_dst, + ipv4_address_to_str, +) from magma.pipelined.qos.types import QosInfo from magma.pipelined.utils import Utils diff --git a/lte/gateway/python/magma/pipelined/app/restart_mixin.py b/lte/gateway/python/magma/pipelined/app/restart_mixin.py index a83dbaaa3d3a..55968c041052 100644 --- a/lte/gateway/python/magma/pipelined/app/restart_mixin.py +++ b/lte/gateway/python/magma/pipelined/app/restart_mixin.py @@ -10,14 +10,13 @@ See the License for the specific language governing permissions and limitations under the License. """ -from typing import List, Dict from abc import ABCMeta, abstractmethod +from typing import Dict, List from lte.protos.pipelined_pb2 import SetupFlowsResult from magma.pipelined.app.base import ControllerNotReadyException from magma.pipelined.openflow import flows from magma.pipelined.policy_converters import ovs_flow_match_to_magma_match - from ryu.ofproto.ofproto_v1_4_parser import OFPFlowStats DefaultMsgsMap = Dict[int, List[OFPFlowStats]] diff --git a/lte/gateway/python/magma/pipelined/app/startup_flows.py b/lte/gateway/python/magma/pipelined/app/startup_flows.py index 7e3b53652303..86def2443c23 100644 --- a/lte/gateway/python/magma/pipelined/app/startup_flows.py +++ b/lte/gateway/python/magma/pipelined/app/startup_flows.py @@ -11,16 +11,18 @@ limitations under the License. """ -from ryu.lib import hub +from magma.pipelined.app.base import ( + ControllerNotReadyException, + ControllerType, + MagmaController, +) +from magma.pipelined.openflow import messages +from magma.pipelined.openflow.exceptions import MagmaOFError from ryu.controller import ofp_event from ryu.controller.handler import MAIN_DISPATCHER, set_ev_cls +from ryu.lib import hub from ryu.ofproto.ofproto_v1_4 import OFPMPF_REPLY_MORE -from magma.pipelined.app.base import ControllerNotReadyException, \ - MagmaController, ControllerType -from magma.pipelined.openflow import messages -from magma.pipelined.openflow.exceptions import MagmaOFError - class StartupFlows(MagmaController): """ diff --git a/lte/gateway/python/magma/pipelined/app/testing.py b/lte/gateway/python/magma/pipelined/app/testing.py index e6fea1073b70..b47150fef17f 100644 --- a/lte/gateway/python/magma/pipelined/app/testing.py +++ b/lte/gateway/python/magma/pipelined/app/testing.py @@ -11,14 +11,12 @@ limitations under the License. """ -from magma.pipelined.openflow import flows from magma.pipelined.app.base import MagmaController -from magma.pipelined.openflow import messages +from magma.pipelined.openflow import flows, messages from magma.pipelined.openflow.exceptions import MagmaOFError - from ryu.controller import ofp_event -from ryu.lib.ofctl_v1_4 import to_instructions from ryu.controller.handler import MAIN_DISPATCHER, set_ev_cls +from ryu.lib.ofctl_v1_4 import to_instructions class TestingController(MagmaController): diff --git a/lte/gateway/python/magma/pipelined/app/tunnel_learn.py b/lte/gateway/python/magma/pipelined/app/tunnel_learn.py index 897f90b1cba1..ac06cd37a980 100644 --- a/lte/gateway/python/magma/pipelined/app/tunnel_learn.py +++ b/lte/gateway/python/magma/pipelined/app/tunnel_learn.py @@ -11,10 +11,10 @@ limitations under the License. """ -from .base import MagmaController, ControllerType +from magma.pipelined.app.base import ControllerType, MagmaController from magma.pipelined.openflow import flows from magma.pipelined.openflow.magma_match import MagmaMatch -from magma.pipelined.openflow.registers import Direction, DIRECTION_REG +from magma.pipelined.openflow.registers import DIRECTION_REG, Direction class TunnelLearnController(MagmaController): diff --git a/lte/gateway/python/magma/pipelined/app/ue_mac.py b/lte/gateway/python/magma/pipelined/app/ue_mac.py index 43cc42c25c83..b1a69ed8a908 100644 --- a/lte/gateway/python/magma/pipelined/app/ue_mac.py +++ b/lte/gateway/python/magma/pipelined/app/ue_mac.py @@ -13,24 +13,25 @@ import threading from typing import List -from ryu.controller import ofp_event -from ryu.controller.handler import MAIN_DISPATCHER, set_ev_cls -from ryu.lib.packet import packet -from ryu.lib.packet import ether_types, dhcp -from ryu.ofproto.inet import IPPROTO_TCP, IPPROTO_UDP - -from lte.protos.pipelined_pb2 import FlowResponse, SetupFlowsResult, \ - UEMacFlowRequest -from magma.pipelined.app.base import MagmaController, ControllerType +from lte.protos.pipelined_pb2 import ( + FlowResponse, + SetupFlowsResult, + UEMacFlowRequest, +) +from magma.pipelined.app.base import ControllerType, MagmaController from magma.pipelined.app.inout import INGRESS -from magma.pipelined.directoryd_client import update_record -from magma.pipelined.imsi import encode_imsi, decode_imsi -from magma.pipelined.openflow import flows from magma.pipelined.app.ipfix import IPFIXController from magma.pipelined.bridge_util import BridgeTools +from magma.pipelined.directoryd_client import update_record +from magma.pipelined.imsi import decode_imsi, encode_imsi +from magma.pipelined.openflow import flows from magma.pipelined.openflow.exceptions import MagmaOFError from magma.pipelined.openflow.magma_match import MagmaMatch from magma.pipelined.openflow.registers import IMSI_REG, load_passthrough +from ryu.controller import ofp_event +from ryu.controller.handler import MAIN_DISPATCHER, set_ev_cls +from ryu.lib.packet import dhcp, ether_types, packet +from ryu.ofproto.inet import IPPROTO_TCP, IPPROTO_UDP class UEMacAddressController(MagmaController): diff --git a/lte/gateway/python/magma/pipelined/app/uplink_bridge.py b/lte/gateway/python/magma/pipelined/app/uplink_bridge.py index ca0d039c9dfa..e2516a076de2 100644 --- a/lte/gateway/python/magma/pipelined/app/uplink_bridge.py +++ b/lte/gateway/python/magma/pipelined/app/uplink_bridge.py @@ -16,8 +16,7 @@ import netaddr import netifaces - -from magma.pipelined.app.base import MagmaController, ControllerType +from magma.pipelined.app.base import ControllerType, MagmaController from magma.pipelined.bridge_util import BridgeTools from magma.pipelined.openflow import flows diff --git a/lte/gateway/python/magma/pipelined/app/vlan_learn.py b/lte/gateway/python/magma/pipelined/app/vlan_learn.py index a99597f2223e..7b099be711d9 100644 --- a/lte/gateway/python/magma/pipelined/app/vlan_learn.py +++ b/lte/gateway/python/magma/pipelined/app/vlan_learn.py @@ -11,13 +11,17 @@ limitations under the License. """ -from .base import MagmaController, ControllerType -from ryu.ofproto import ether -from magma.pipelined.openflow import flows +from magma.pipelined.app.base import ControllerType, MagmaController from magma.pipelined.imsi import encode_imsi +from magma.pipelined.openflow import flows from magma.pipelined.openflow.magma_match import MagmaMatch -from magma.pipelined.openflow.registers import Direction, DIRECTION_REG, \ - IMSI_REG, VLAN_TAG_REG +from magma.pipelined.openflow.registers import ( + DIRECTION_REG, + IMSI_REG, + VLAN_TAG_REG, + Direction, +) +from ryu.ofproto import ether class VlanLearnController(MagmaController): diff --git a/lte/gateway/python/magma/pipelined/app/xwf_passthru.py b/lte/gateway/python/magma/pipelined/app/xwf_passthru.py index 95ad87668161..f8ffd3b10d77 100644 --- a/lte/gateway/python/magma/pipelined/app/xwf_passthru.py +++ b/lte/gateway/python/magma/pipelined/app/xwf_passthru.py @@ -10,12 +10,11 @@ See the License for the specific language governing permissions and limitations under the License. """ +from magma.pipelined.app.base import ControllerType, MagmaController from magma.pipelined.app.inout import INGRESS from magma.pipelined.openflow import flows from magma.pipelined.openflow.magma_match import MagmaMatch -from .base import MagmaController, ControllerType - class XWFPassthruController(MagmaController): diff --git a/lte/gateway/python/magma/pipelined/bridge_util.py b/lte/gateway/python/magma/pipelined/bridge_util.py index 69424854eb5d..45e2d3e329b6 100644 --- a/lte/gateway/python/magma/pipelined/bridge_util.py +++ b/lte/gateway/python/magma/pipelined/bridge_util.py @@ -11,11 +11,11 @@ limitations under the License. """ import binascii -from collections import defaultdict -import re import logging +import re import subprocess -from typing import Optional, Dict, List, TYPE_CHECKING +from collections import defaultdict +from typing import TYPE_CHECKING, Dict, List, Optional # Prevent circular import if TYPE_CHECKING: diff --git a/lte/gateway/python/magma/pipelined/directoryd_client.py b/lte/gateway/python/magma/pipelined/directoryd_client.py index 0aab6ff7a6a6..bae19cc060e8 100644 --- a/lte/gateway/python/magma/pipelined/directoryd_client.py +++ b/lte/gateway/python/magma/pipelined/directoryd_client.py @@ -10,16 +10,17 @@ See the License for the specific language governing permissions and limitations under the License. """ -import grpc import logging -from ryu.lib import hub - +import grpc from magma.common.service_registry import ServiceRegistry from orc8r.protos.common_pb2 import Void -from orc8r.protos.directoryd_pb2 import UpdateRecordRequest, \ - GetDirectoryFieldRequest +from orc8r.protos.directoryd_pb2 import ( + GetDirectoryFieldRequest, + UpdateRecordRequest, +) from orc8r.protos.directoryd_pb2_grpc import GatewayDirectoryServiceStub +from ryu.lib import hub DIRECTORYD_SERVICE_NAME = "directoryd" DEFAULT_GRPC_TIMEOUT = 10 diff --git a/lte/gateway/python/magma/pipelined/encoding.py b/lte/gateway/python/magma/pipelined/encoding.py index 796b9973862a..b4a273a1ad14 100644 --- a/lte/gateway/python/magma/pipelined/encoding.py +++ b/lte/gateway/python/magma/pipelined/encoding.py @@ -11,12 +11,12 @@ limitations under the License. """ -import logging import codecs -import hashlib import gzip -from Crypto.Cipher import ARC4 -from Crypto.Cipher import AES +import hashlib +import logging + +from Crypto.Cipher import AES, ARC4 from Crypto.Hash import HMAC from Crypto.Random import get_random_bytes from lte.protos.mconfig.mconfigs_pb2 import PipelineD diff --git a/lte/gateway/python/magma/pipelined/envoy_client.py b/lte/gateway/python/magma/pipelined/envoy_client.py index f874bcd85c7b..4cf323642dd4 100644 --- a/lte/gateway/python/magma/pipelined/envoy_client.py +++ b/lte/gateway/python/magma/pipelined/envoy_client.py @@ -10,17 +10,20 @@ See the License for the specific language governing permissions and limitations under the License. """ +import logging from typing import List import grpc -import logging - -from magma.common.service_registry import ServiceRegistry +from feg.protos.envoy_controller_pb2 import ( + AddUEHeaderEnrichmentRequest, + AddUEHeaderEnrichmentResult, + DeactivateUEHeaderEnrichmentRequest, + DeactivateUEHeaderEnrichmentResult, + Header, +) from feg.protos.envoy_controller_pb2_grpc import EnvoyControllerStub -from feg.protos.envoy_controller_pb2 import AddUEHeaderEnrichmentRequest, \ - DeactivateUEHeaderEnrichmentRequest, Header, AddUEHeaderEnrichmentResult, \ - DeactivateUEHeaderEnrichmentResult from lte.protos.mobilityd_pb2 import IPAddress +from magma.common.service_registry import ServiceRegistry SERVICE_NAME = "envoy_controller" IMSI_HDR = 'imsi' diff --git a/lte/gateway/python/magma/pipelined/gtp_stats_collector.py b/lte/gateway/python/magma/pipelined/gtp_stats_collector.py index f6fd98b67bc3..62f7cfafccfd 100644 --- a/lte/gateway/python/magma/pipelined/gtp_stats_collector.py +++ b/lte/gateway/python/magma/pipelined/gtp_stats_collector.py @@ -11,18 +11,17 @@ limitations under the License. """ -import logging - import asyncio -from typing import List, NamedTuple, Optional - +import logging import re +from typing import List, NamedTuple, Optional from magma.common.job import Job from magma.magmad.check import subprocess_workflow - -from magma.pipelined.metrics import GTP_PORT_USER_PLANE_DL_BYTES, \ - GTP_PORT_USER_PLANE_UL_BYTES +from magma.pipelined.metrics import ( + GTP_PORT_USER_PLANE_DL_BYTES, + GTP_PORT_USER_PLANE_UL_BYTES, +) OVSDBDumpCommandParams = NamedTuple('OVSDBCommandParams', [('table', str), ('columns', List[str])]) diff --git a/lte/gateway/python/magma/pipelined/ifaces.py b/lte/gateway/python/magma/pipelined/ifaces.py index 559c62d2da91..be9e39d3ad07 100644 --- a/lte/gateway/python/magma/pipelined/ifaces.py +++ b/lte/gateway/python/magma/pipelined/ifaces.py @@ -11,8 +11,8 @@ limitations under the License. """ import asyncio -import netifaces +import netifaces from magma.pipelined.metrics import NETWORK_IFACE_STATUS POLL_INTERVAL_SECONDS = 3 diff --git a/lte/gateway/python/magma/pipelined/ipv6_prefix_store.py b/lte/gateway/python/magma/pipelined/ipv6_prefix_store.py index 6e198097ccf1..e36a2238b7a7 100644 --- a/lte/gateway/python/magma/pipelined/ipv6_prefix_store.py +++ b/lte/gateway/python/magma/pipelined/ipv6_prefix_store.py @@ -10,13 +10,15 @@ See the License for the specific language governing permissions and limitations under the License. """ -import threading import ipaddress +import threading from magma.common.redis.client import get_default_client from magma.common.redis.containers import RedisHashDict -from magma.common.redis.serializers import get_json_deserializer, \ - get_json_serializer +from magma.common.redis.serializers import ( + get_json_deserializer, + get_json_serializer, +) class InterfaceIDToPrefixMapper: diff --git a/lte/gateway/python/magma/pipelined/main.py b/lte/gateway/python/magma/pipelined/main.py index 1c493d04d827..d2563a54f5ba 100644 --- a/lte/gateway/python/magma/pipelined/main.py +++ b/lte/gateway/python/magma/pipelined/main.py @@ -20,26 +20,26 @@ import threading import aioeventlet -from ryu import cfg -from ryu.base.app_manager import AppManager -from scapy.arch import get_if_hwaddr -from ryu.ofproto.ofproto_v1_4 import OFPP_LOCAL - +from lte.protos.mconfig import mconfigs_pb2 from magma.common.misc_utils import call_process, get_ip_from_if from magma.common.sentry import sentry_init from magma.common.service import MagmaService from magma.configuration import environment from magma.pipelined.app import of_rest_server +from magma.pipelined.app.he import PROXY_PORT_NAME +from magma.pipelined.bridge_util import BridgeTools from magma.pipelined.check_quota_server import run_flask -from magma.pipelined.service_manager import ServiceManager +from magma.pipelined.gtp_stats_collector import ( + MIN_OVSDB_DUMP_POLLING_INTERVAL, + GTPStatsCollector, +) from magma.pipelined.ifaces import monitor_ifaces from magma.pipelined.rpc_servicer import PipelinedRpcServicer -from magma.pipelined.gtp_stats_collector import GTPStatsCollector, \ - MIN_OVSDB_DUMP_POLLING_INTERVAL - -from magma.pipelined.app.he import PROXY_PORT_NAME -from magma.pipelined.bridge_util import BridgeTools -from lte.protos.mconfig import mconfigs_pb2 +from magma.pipelined.service_manager import ServiceManager +from ryu import cfg +from ryu.base.app_manager import AppManager +from ryu.ofproto.ofproto_v1_4 import OFPP_LOCAL +from scapy.arch import get_if_hwaddr def main(): diff --git a/lte/gateway/python/magma/pipelined/metrics.py b/lte/gateway/python/magma/pipelined/metrics.py index efbb9ddd5c42..6e6fa61fcf08 100644 --- a/lte/gateway/python/magma/pipelined/metrics.py +++ b/lte/gateway/python/magma/pipelined/metrics.py @@ -13,7 +13,6 @@ from prometheus_client import Counter, Gauge - DP_SEND_MSG_ERROR = Counter('dp_send_msg_error', 'Total datapath message send errors', ['cause']) ARP_DEFAULT_GW_MAC_ERROR = Counter('arp_default_gw_mac_error', diff --git a/lte/gateway/python/magma/pipelined/mobilityd_client.py b/lte/gateway/python/magma/pipelined/mobilityd_client.py index ec6188f14ec7..723adada3add 100644 --- a/lte/gateway/python/magma/pipelined/mobilityd_client.py +++ b/lte/gateway/python/magma/pipelined/mobilityd_client.py @@ -10,15 +10,14 @@ See the License for the specific language governing permissions and limitations under the License. """ +import logging from typing import List import grpc -import logging - +from lte.protos.mobilityd_pb2 import GWInfo, IPAddress +from lte.protos.mobilityd_pb2_grpc import MobilityServiceStub from magma.common.service_registry import ServiceRegistry from orc8r.protos.common_pb2 import Void -from lte.protos.mobilityd_pb2_grpc import MobilityServiceStub -from lte.protos.mobilityd_pb2 import IPAddress, GWInfo SERVICE_NAME = "mobilityd" IPV4_ADDR_KEY = "ipv4_addr" diff --git a/lte/gateway/python/magma/pipelined/ng_manager/node_state_manager.py b/lte/gateway/python/magma/pipelined/ng_manager/node_state_manager.py index 88965a7a98e6..c3ad9ef4bd94 100644 --- a/lte/gateway/python/magma/pipelined/ng_manager/node_state_manager.py +++ b/lte/gateway/python/magma/pipelined/ng_manager/node_state_manager.py @@ -12,17 +12,19 @@ """ import logging -import netifaces -from typing import NamedTuple, Dict +from typing import Dict, NamedTuple +import netifaces +from google.protobuf.timestamp_pb2 import Timestamp from lte.protos.session_manager_pb2 import ( - UPFNodeState, UPFAssociationState, UPFFeatureSet, - UserPlaneIPResourceSchema) - -from google.protobuf.timestamp_pb2 import Timestamp -from magma.pipelined.set_interface_client import send_node_state_association_request + UPFNodeState, + UserPlaneIPResourceSchema, +) +from magma.pipelined.set_interface_client import ( + send_node_state_association_request, +) from ryu.lib import hub EXP_BASE = 3 diff --git a/lte/gateway/python/magma/pipelined/ng_manager/session_state_manager.py b/lte/gateway/python/magma/pipelined/ng_manager/session_state_manager.py index b821991e8f95..103132d5e9ef 100644 --- a/lte/gateway/python/magma/pipelined/ng_manager/session_state_manager.py +++ b/lte/gateway/python/magma/pipelined/ng_manager/session_state_manager.py @@ -11,26 +11,23 @@ limitations under the License. """ -from typing import ( - NamedTuple, - Optional) - from enum import Enum -from magma.pipelined.set_interface_client import ( - send_periodic_session_update) - -from magma.pipelined.ng_manager.session_state_manager_util import ( - pdr_create_rule_entry) - -from lte.protos.session_manager_pb2 import ( - UPFSessionConfigState, - UPFSessionState) +from typing import NamedTuple, Optional from lte.protos.pipelined_pb2 import ( + CauseIE, + OffendingIE, PdrState, UPFSessionContextState, - OffendingIE, - CauseIE) +) +from lte.protos.session_manager_pb2 import ( + UPFSessionConfigState, + UPFSessionState, +) +from magma.pipelined.ng_manager.session_state_manager_util import ( + pdr_create_rule_entry, +) +from magma.pipelined.set_interface_client import send_periodic_session_update # Help to build failure report MsgParseOutput = NamedTuple( diff --git a/lte/gateway/python/magma/pipelined/ng_manager/session_state_manager_util.py b/lte/gateway/python/magma/pipelined/ng_manager/session_state_manager_util.py index 709e55e9db6a..2121c9eb28d1 100644 --- a/lte/gateway/python/magma/pipelined/ng_manager/session_state_manager_util.py +++ b/lte/gateway/python/magma/pipelined/ng_manager/session_state_manager_util.py @@ -11,8 +11,11 @@ limitations under the License. """ from typing import NamedTuple -from lte.protos.pipelined_pb2 import ActivateFlowsRequest, \ - DeactivateFlowsRequest + +from lte.protos.pipelined_pb2 import ( + ActivateFlowsRequest, + DeactivateFlowsRequest, +) FARRuleEntry = NamedTuple( 'FARRuleEntry', diff --git a/lte/gateway/python/magma/pipelined/openflow/flows.py b/lte/gateway/python/magma/pipelined/openflow/flows.py index 19687ad86bf6..082dde241ba8 100644 --- a/lte/gateway/python/magma/pipelined/openflow/flows.py +++ b/lte/gateway/python/magma/pipelined/openflow/flows.py @@ -14,7 +14,7 @@ from magma.pipelined.openflow import messages from magma.pipelined.openflow.magma_match import MagmaMatch -from magma.pipelined.openflow.registers import SCRATCH_REGS, REG_ZERO_VAL +from magma.pipelined.openflow.registers import REG_ZERO_VAL, SCRATCH_REGS from ryu.ofproto.nicira_ext import ofs_nbits logger = logging.getLogger(__name__) diff --git a/lte/gateway/python/magma/pipelined/openflow/magma_match.py b/lte/gateway/python/magma/pipelined/openflow/magma_match.py index ee3ae5946885..42c3f591de3d 100644 --- a/lte/gateway/python/magma/pipelined/openflow/magma_match.py +++ b/lte/gateway/python/magma/pipelined/openflow/magma_match.py @@ -12,9 +12,18 @@ """ from typing import Optional -from magma.pipelined.openflow.registers import IMSI_REG, DIRECTION_REG, \ - is_valid_direction, Direction, RULE_VERSION_REG, PASSTHROUGH_REG, \ - VLAN_TAG_REG, DPI_REG, RULE_NUM_REG, PROXY_TAG_REG +from magma.pipelined.openflow.registers import ( + DIRECTION_REG, + DPI_REG, + IMSI_REG, + PASSTHROUGH_REG, + PROXY_TAG_REG, + RULE_NUM_REG, + RULE_VERSION_REG, + VLAN_TAG_REG, + Direction, + is_valid_direction, +) class MagmaMatch(object): @@ -77,4 +86,4 @@ def _check_args(self): if k == DIRECTION_REG and self.direction: raise Exception("Register %s should not be directly set" % k) if k == IMSI_REG and self.imsi: - raise Exception("Register %s should not be directly set" % k) \ No newline at end of file + raise Exception("Register %s should not be directly set" % k) diff --git a/lte/gateway/python/magma/pipelined/openflow/messages.py b/lte/gateway/python/magma/pipelined/openflow/messages.py index a87707be7b35..5cd79caade80 100644 --- a/lte/gateway/python/magma/pipelined/openflow/messages.py +++ b/lte/gateway/python/magma/pipelined/openflow/messages.py @@ -15,15 +15,16 @@ # there's a cyclic dependency in ryu import ryu.base.app_manager # pylint: disable=unused-import +from magma.pipelined.metrics import DP_SEND_MSG_ERROR +from magma.pipelined.openflow.exceptions import ( + MagmaDPDisconnectedError, + MagmaOFError, +) +from magma.pipelined.policy_converters import MATCH_ATTRIBUTES from ryu.controller.controller import Datapath from ryu.lib import hub from ryu.ofproto.ofproto_parser import MsgBase -from magma.pipelined.openflow.exceptions import MagmaOFError,\ - MagmaDPDisconnectedError -from magma.pipelined.metrics import DP_SEND_MSG_ERROR -from magma.pipelined.policy_converters import MATCH_ATTRIBUTES - logger = logging.getLogger(__name__) DEFAULT_TIMEOUT_SEC = 10 diff --git a/lte/gateway/python/magma/pipelined/openflow/meters.py b/lte/gateway/python/magma/pipelined/openflow/meters.py index 1ddb62405a7f..dcf7f1f574ff 100644 --- a/lte/gateway/python/magma/pipelined/openflow/meters.py +++ b/lte/gateway/python/magma/pipelined/openflow/meters.py @@ -11,6 +11,7 @@ limitations under the License. """ import logging + from magma.pipelined.openflow import messages LOG = logging.getLogger('openflow.meters') diff --git a/lte/gateway/python/magma/pipelined/openflow/registers.py b/lte/gateway/python/magma/pipelined/openflow/registers.py index 9d92d6b197e9..1fed5d906487 100644 --- a/lte/gateway/python/magma/pipelined/openflow/registers.py +++ b/lte/gateway/python/magma/pipelined/openflow/registers.py @@ -11,6 +11,7 @@ limitations under the License. """ from enum import IntEnum + from magma.pipelined.imsi import encode_imsi # Register names diff --git a/lte/gateway/python/magma/pipelined/openflow/tests/test_msg_hub.py b/lte/gateway/python/magma/pipelined/openflow/tests/test_msg_hub.py index 10cebc1afc67..6864e2981a71 100644 --- a/lte/gateway/python/magma/pipelined/openflow/tests/test_msg_hub.py +++ b/lte/gateway/python/magma/pipelined/openflow/tests/test_msg_hub.py @@ -13,11 +13,10 @@ import logging import unittest -from unittest.mock import Mock, MagicMock - -from ryu.lib import hub +from unittest.mock import MagicMock, Mock from magma.pipelined.openflow.messages import MessageHub +from ryu.lib import hub class MockBarrierRequest(object): diff --git a/lte/gateway/python/magma/pipelined/policy_converters.py b/lte/gateway/python/magma/pipelined/policy_converters.py index 762e49e95049..2c6cacf648dc 100644 --- a/lte/gateway/python/magma/pipelined/policy_converters.py +++ b/lte/gateway/python/magma/pipelined/policy_converters.py @@ -12,14 +12,16 @@ """ import ipaddress -from lte.protos.policydb_pb2 import FlowMatch from lte.protos.mobilityd_pb2 import IPAddress +from lte.protos.policydb_pb2 import FlowMatch from magma.pipelined.openflow.magma_match import MagmaMatch -from magma.pipelined.openflow.registers import Direction, load_direction, \ - DPI_REG +from magma.pipelined.openflow.registers import ( + DPI_REG, + Direction, + load_direction, +) from ryu.lib.packet import ether_types - MATCH_ATTRIBUTES = ['metadata', 'reg0', 'reg1', 'reg2', 'reg3', 'reg4', 'reg5', 'reg6', 'reg8', 'reg9', 'reg10', 'in_port', 'dl_vlan', 'vlan_tci', @@ -262,4 +264,4 @@ def ovs_flow_match_to_magma_match(flow): val = flow.match.get(a, None) if val: attribute_dict[a] = val - return MagmaMatch(**attribute_dict) \ No newline at end of file + return MagmaMatch(**attribute_dict) diff --git a/lte/gateway/python/magma/pipelined/qos/common.py b/lte/gateway/python/magma/pipelined/qos/common.py index beed77221aae..be7bfc676fef 100644 --- a/lte/gateway/python/magma/pipelined/qos/common.py +++ b/lte/gateway/python/magma/pipelined/qos/common.py @@ -10,20 +10,27 @@ See the License for the specific language governing permissions and limitations under the License. """ -from typing import List, Dict # noqa - -import logging import json +import logging +import threading +import traceback from enum import Enum +from typing import Dict, List # noqa + from lte.protos.policydb_pb2 import FlowMatch +from magma.configuration.service_configs import load_service_config from magma.pipelined.qos.qos_meter_impl import MeterManager from magma.pipelined.qos.qos_tc_impl import TCManager, TrafficClass -from magma.pipelined.qos.types import QosInfo, get_key_json, get_key, get_subscriber_key,\ - get_subscriber_data, get_data_json, get_data +from magma.pipelined.qos.types import ( + QosInfo, + get_data, + get_data_json, + get_key, + get_key_json, + get_subscriber_data, + get_subscriber_key, +) from magma.pipelined.qos.utils import QosStore -from magma.configuration.service_configs import load_service_config -import traceback -import threading LOG = logging.getLogger("pipelined.qos.common") # LOG.setLevel(logging.DEBUG) diff --git a/lte/gateway/python/magma/pipelined/qos/qos_meter_impl.py b/lte/gateway/python/magma/pipelined/qos/qos_meter_impl.py index a33fbf82f6b1..07e4f8f496db 100644 --- a/lte/gateway/python/magma/pipelined/qos/qos_meter_impl.py +++ b/lte/gateway/python/magma/pipelined/qos/qos_meter_impl.py @@ -11,10 +11,12 @@ limitations under the License. """ import logging +import subprocess + from magma.pipelined.openflow.meters import MeterClass -from .utils import IdManager + from .types import QosInfo -import subprocess +from .utils import IdManager LOG = logging.getLogger('pipelined.qos.qos_meter_impl') BROKEN_KERN_ERROR_MSG = "kernel module has a broken meter implementation" diff --git a/lte/gateway/python/magma/pipelined/qos/qos_tc_impl.py b/lte/gateway/python/magma/pipelined/qos/qos_tc_impl.py index 8f8235140783..80a116124e83 100644 --- a/lte/gateway/python/magma/pipelined/qos/qos_tc_impl.py +++ b/lte/gateway/python/magma/pipelined/qos/qos_tc_impl.py @@ -10,14 +10,16 @@ See the License for the specific language governing permissions and limitations under the License. """ -from typing import Optional # noqa -import subprocess import logging +import subprocess +from typing import Optional # noqa + from lte.protos.policydb_pb2 import FlowMatch + +from .tc_ops_cmd import TcOpsCmd, argSplit, run_cmd +from .tc_ops_pyroute2 import TcOpsPyRoute2 from .types import QosInfo from .utils import IdManager -from .tc_ops_cmd import run_cmd, TcOpsCmd, argSplit -from .tc_ops_pyroute2 import TcOpsPyRoute2 LOG = logging.getLogger('pipelined.qos.qos_tc_impl') # LOG.setLevel(logging.DEBUG) diff --git a/lte/gateway/python/magma/pipelined/qos/tc_ops.py b/lte/gateway/python/magma/pipelined/qos/tc_ops.py index 9c6565098442..ce6a4c03daae 100644 --- a/lte/gateway/python/magma/pipelined/qos/tc_ops.py +++ b/lte/gateway/python/magma/pipelined/qos/tc_ops.py @@ -11,8 +11,12 @@ limitations under the License. """ -from __future__ import absolute_import, division, print_function, \ - unicode_literals +from __future__ import ( + absolute_import, + division, + print_function, + unicode_literals, +) from abc import ABC, abstractmethod diff --git a/lte/gateway/python/magma/pipelined/qos/tc_ops_cmd.py b/lte/gateway/python/magma/pipelined/qos/tc_ops_cmd.py index e1d848e9a696..9dfc238deb9c 100644 --- a/lte/gateway/python/magma/pipelined/qos/tc_ops_cmd.py +++ b/lte/gateway/python/magma/pipelined/qos/tc_ops_cmd.py @@ -12,12 +12,13 @@ """ -from .tc_ops import TcOpsBase +import logging import os import shlex import subprocess from typing import List # noqa -import logging + +from .tc_ops import TcOpsBase LOG = logging.getLogger('pipelined.qos.tc_cmd') diff --git a/lte/gateway/python/magma/pipelined/qos/tc_ops_pyroute2.py b/lte/gateway/python/magma/pipelined/qos/tc_ops_pyroute2.py index 8ad170da32d8..d5a0bbce7f3e 100644 --- a/lte/gateway/python/magma/pipelined/qos/tc_ops_pyroute2.py +++ b/lte/gateway/python/magma/pipelined/qos/tc_ops_pyroute2.py @@ -12,11 +12,13 @@ """ -from .tc_ops import TcOpsBase import logging -from pyroute2 import IPRoute, NetlinkError import pprint +from pyroute2 import IPRoute, NetlinkError + +from .tc_ops import TcOpsBase + LOG = logging.getLogger('pipelined.qos.tc_pyroute2') QUEUE_PREFIX = '1:' diff --git a/lte/gateway/python/magma/pipelined/qos/utils.py b/lte/gateway/python/magma/pipelined/qos/utils.py index 8feb001e56fc..00b70ba68532 100644 --- a/lte/gateway/python/magma/pipelined/qos/utils.py +++ b/lte/gateway/python/magma/pipelined/qos/utils.py @@ -13,10 +13,13 @@ import logging from collections import deque + from magma.common.redis.client import get_default_client from magma.common.redis.containers import RedisHashDict -from magma.common.redis.serializers import get_json_serializer, \ - get_json_deserializer +from magma.common.redis.serializers import ( + get_json_deserializer, + get_json_serializer, +) LOG = logging.getLogger('pipelined.qos.id_manager') diff --git a/lte/gateway/python/magma/pipelined/redirect.py b/lte/gateway/python/magma/pipelined/redirect.py index 59cc7f2eda7a..21958f9f6b2b 100644 --- a/lte/gateway/python/magma/pipelined/redirect.py +++ b/lte/gateway/python/magma/pipelined/redirect.py @@ -11,23 +11,29 @@ limitations under the License. """ -import netifaces -import aiodns import asyncio import ipaddress from collections import namedtuple -from redis import RedisError from urllib.parse import urlsplit -from memoize import Memoizer +import aiodns +import netifaces from magma.configuration.service_configs import get_service_config_value from magma.pipelined.imsi import encode_imsi from magma.pipelined.openflow import flows from magma.pipelined.openflow.magma_match import MagmaMatch -from magma.pipelined.openflow.registers import IMSI_REG, DIRECTION_REG, \ - Direction, SCRATCH_REGS, REG_ZERO_VAL, RULE_VERSION_REG, RULE_NUM_REG +from magma.pipelined.openflow.registers import ( + DIRECTION_REG, + IMSI_REG, + REG_ZERO_VAL, + RULE_NUM_REG, + RULE_VERSION_REG, + SCRATCH_REGS, + Direction, +) from magma.redirectd.redirect_store import RedirectDict - +from memoize import Memoizer +from redis import RedisError from ryu.lib.packet import ether_types from ryu.ofproto.inet import IPPROTO_TCP, IPPROTO_UDP from ryu.ofproto.ofproto_v1_4 import OFPP_LOCAL diff --git a/lte/gateway/python/magma/pipelined/rpc_servicer.py b/lte/gateway/python/magma/pipelined/rpc_servicer.py index fc014a2074f3..1674abd4e55b 100644 --- a/lte/gateway/python/magma/pipelined/rpc_servicer.py +++ b/lte/gateway/python/magma/pipelined/rpc_servicer.py @@ -10,57 +10,63 @@ See the License for the specific language governing permissions and limitations under the License. """ -import os -import logging import concurrent.futures +import logging +import os import queue +from collections import OrderedDict from concurrent.futures import Future from typing import List, Tuple -from collections import OrderedDict import grpc from lte.protos import pipelined_pb2_grpc +from lte.protos.mobilityd_pb2 import IPAddress from lte.protos.pipelined_pb2 import ( - SetupFlowsResult, - RequestOriginType, + ActivateFlowsRequest, ActivateFlowsResult, - DeactivateFlowsResult, + AllTableAssignments, + CauseIE, DeactivateFlowsRequest, + DeactivateFlowsResult, FlowResponse, + OffendingIE, + PdrState, + RequestOriginType, RuleModResult, - SetupUEMacRequest, + SessionSet, + SetupFlowsResult, SetupPolicyRequest, SetupQuotaRequest, - ActivateFlowsRequest, - AllTableAssignments, + SetupUEMacRequest, TableAssignment, - SessionSet, - PdrState, UPFSessionContextState, - OffendingIE, VersionedPolicy, - CauseIE) -from lte.protos.mobilityd_pb2 import IPAddress -from lte.protos.subscriberdb_pb2 import AggregatedMaximumBitrate +) from lte.protos.session_manager_pb2 import RuleRecordTable +from lte.protos.subscriberdb_pb2 import AggregatedMaximumBitrate +from magma.pipelined.app.check_quota import CheckQuotaController from magma.pipelined.app.dpi import DPIController from magma.pipelined.app.enforcement import EnforcementController from magma.pipelined.app.enforcement_stats import EnforcementStatsController -from magma.pipelined.app.ue_mac import UEMacAddressController from magma.pipelined.app.ipfix import IPFIXController -from magma.pipelined.app.check_quota import CheckQuotaController -from magma.pipelined.app.vlan_learn import VlanLearnController +from magma.pipelined.app.ng_services import NGServiceController from magma.pipelined.app.tunnel_learn import TunnelLearnController -from magma.pipelined.policy_converters import convert_ipv4_str_to_ip_proto, \ - convert_ipv6_bytes_to_ip_proto -from magma.pipelined.ipv6_prefix_store import get_ipv6_interface_id, get_ipv6_prefix +from magma.pipelined.app.ue_mac import UEMacAddressController +from magma.pipelined.app.vlan_learn import VlanLearnController +from magma.pipelined.imsi import encode_imsi +from magma.pipelined.ipv6_prefix_store import ( + get_ipv6_interface_id, + get_ipv6_prefix, +) from magma.pipelined.metrics import ( - ENFORCEMENT_STATS_RULE_INSTALL_FAIL, ENFORCEMENT_RULE_INSTALL_FAIL, + ENFORCEMENT_STATS_RULE_INSTALL_FAIL, ) -from magma.pipelined.imsi import encode_imsi from magma.pipelined.ng_manager.session_state_manager_util import PDRRuleEntry -from magma.pipelined.app.ng_services import NGServiceController +from magma.pipelined.policy_converters import ( + convert_ipv4_str_to_ip_proto, + convert_ipv6_bytes_to_ip_proto, +) grpc_msg_queue = queue.Queue() DEFAULT_CALL_TIMEOUT = 15 diff --git a/lte/gateway/python/magma/pipelined/rule_mappers.py b/lte/gateway/python/magma/pipelined/rule_mappers.py index 038a65ddd6de..aab336d63f43 100644 --- a/lte/gateway/python/magma/pipelined/rule_mappers.py +++ b/lte/gateway/python/magma/pipelined/rule_mappers.py @@ -15,13 +15,14 @@ from collections import namedtuple from lte.protos.mobilityd_pb2 import IPAddress -from magma.pipelined.imsi import encode_imsi from magma.common.redis.client import get_default_client from magma.common.redis.containers import RedisFlatDict, RedisHashDict -from magma.common.redis.serializers import get_json_deserializer, \ - get_json_serializer -from magma.common.redis.serializers import RedisSerde - +from magma.common.redis.serializers import ( + RedisSerde, + get_json_deserializer, + get_json_serializer, +) +from magma.pipelined.imsi import encode_imsi SubscriberRuleKey = namedtuple('SubscriberRuleKey', 'key_type imsi ip_addr rule_id') diff --git a/lte/gateway/python/magma/pipelined/service_manager.py b/lte/gateway/python/magma/pipelined/service_manager.py index 2823fb6a6d66..ef937761b832 100644 --- a/lte/gateway/python/magma/pipelined/service_manager.py +++ b/lte/gateway/python/magma/pipelined/service_manager.py @@ -32,11 +32,11 @@ # pylint does not play well with aioeventlet, as it uses asyncio.async which # produces a parse error -import time import asyncio import logging +import time +from collections import OrderedDict, namedtuple from concurrent.futures import Future -from collections import namedtuple, OrderedDict from typing import List import aioeventlet @@ -44,44 +44,48 @@ from lte.protos.mobilityd_pb2_grpc import MobilityServiceStub from lte.protos.session_manager_pb2_grpc import ( LocalSessionManagerStub, - SetInterfaceForUserPlaneStub) -from magma.pipelined.app.base import ControllerType + SetInterfaceForUserPlaneStub, +) +from magma.common.service import MagmaService +from magma.common.service_registry import ServiceRegistry +from magma.configuration import environment from magma.pipelined.app import of_rest_server from magma.pipelined.app.access_control import AccessControlController -from magma.pipelined.app.conntrack import ConntrackController -from magma.pipelined.app.tunnel_learn import TunnelLearnController -from magma.pipelined.app.vlan_learn import VlanLearnController from magma.pipelined.app.arp import ArpController -from magma.pipelined.app.ipv6_solicitation import \ - IPV6SolicitationController +from magma.pipelined.app.base import ControllerType +from magma.pipelined.app.check_quota import CheckQuotaController +from magma.pipelined.app.classifier import Classifier +from magma.pipelined.app.conntrack import ConntrackController from magma.pipelined.app.dpi import DPIController -from magma.pipelined.app.gy import GYController from magma.pipelined.app.enforcement import EnforcementController +from magma.pipelined.app.enforcement_stats import EnforcementStatsController +from magma.pipelined.app.gy import GYController +from magma.pipelined.app.he import HeaderEnrichmentController +from magma.pipelined.app.inout import ( + EGRESS, + INGRESS, + PHYSICAL_TO_LOGICAL, + InOutController, +) from magma.pipelined.app.ipfix import IPFIXController +from magma.pipelined.app.ipv6_solicitation import IPV6SolicitationController from magma.pipelined.app.li_mirror import LIMirrorController -from magma.pipelined.app.enforcement_stats import EnforcementStatsController -from magma.pipelined.app.inout import EGRESS, INGRESS, PHYSICAL_TO_LOGICAL, \ - InOutController -from magma.pipelined.app.ue_mac import UEMacAddressController -from magma.pipelined.app.xwf_passthru import XWFPassthruController +from magma.pipelined.app.ng_services import NGServiceController from magma.pipelined.app.startup_flows import StartupFlows -from magma.pipelined.app.check_quota import CheckQuotaController +from magma.pipelined.app.tunnel_learn import TunnelLearnController +from magma.pipelined.app.ue_mac import UEMacAddressController from magma.pipelined.app.uplink_bridge import UplinkBridgeController -from magma.pipelined.app.ng_services import NGServiceController - -from magma.pipelined.rule_mappers import RuleIDToNumMapper, \ - SessionRuleToVersionMapper +from magma.pipelined.app.vlan_learn import VlanLearnController +from magma.pipelined.app.xwf_passthru import XWFPassthruController +from magma.pipelined.internal_ip_allocator import InternalIPAllocator from magma.pipelined.ipv6_prefix_store import InterfaceIDToPrefixMapper +from magma.pipelined.rule_mappers import ( + RuleIDToNumMapper, + SessionRuleToVersionMapper, +) from magma.pipelined.tunnel_id_store import TunnelToTunnelMapper -from magma.pipelined.internal_ip_allocator import InternalIPAllocator from ryu.base.app_manager import AppManager -from magma.common.service import MagmaService -from magma.common.service_registry import ServiceRegistry -from magma.configuration import environment -from magma.pipelined.app.classifier import Classifier -from magma.pipelined.app.he import HeaderEnrichmentController, PROXY_TABLE - # Type is either Physical or Logical, highest order_priority is at zero App = namedtuple('App', ['name', 'module', 'type', 'order_priority']) diff --git a/lte/gateway/python/magma/pipelined/set_interface_client.py b/lte/gateway/python/magma/pipelined/set_interface_client.py index 18271bd55108..26893d598368 100644 --- a/lte/gateway/python/magma/pipelined/set_interface_client.py +++ b/lte/gateway/python/magma/pipelined/set_interface_client.py @@ -11,12 +11,10 @@ limitations under the License. """ -import grpc import logging -from lte.protos.session_manager_pb2 import ( - UPFNodeState, - UPFSessionConfigState) +import grpc +from lte.protos.session_manager_pb2 import UPFNodeState, UPFSessionConfigState from lte.protos.session_manager_pb2_grpc import SetInterfaceForUserPlaneStub DEFAULT_GRPC_TIMEOUT = 5 diff --git a/lte/gateway/python/magma/pipelined/tests/app/flow_query.py b/lte/gateway/python/magma/pipelined/tests/app/flow_query.py index 25905d5f85f0..b80a1e4e5133 100644 --- a/lte/gateway/python/magma/pipelined/tests/app/flow_query.py +++ b/lte/gateway/python/magma/pipelined/tests/app/flow_query.py @@ -17,7 +17,6 @@ from integ_tests.s1aptests.ovs import LOCALHOST from integ_tests.s1aptests.ovs.rest_api import get_datapath, get_flows - from ryu.lib import hub FlowStats = namedtuple('FlowData', ['packets', 'bytes', 'duration_sec', diff --git a/lte/gateway/python/magma/pipelined/tests/app/ng_set_session_msg.py b/lte/gateway/python/magma/pipelined/tests/app/ng_set_session_msg.py index 78f3d691e45c..29e612e12696 100644 --- a/lte/gateway/python/magma/pipelined/tests/app/ng_set_session_msg.py +++ b/lte/gateway/python/magma/pipelined/tests/app/ng_set_session_msg.py @@ -13,27 +13,27 @@ from collections import namedtuple -from magma.subscriberdb.sid import SIDUtils -from magma.pipelined.policy_converters import convert_ipv4_str_to_ip_proto -from lte.protos.policydb_pb2 import FlowMatch, FlowDescription, PolicyRule -from lte.protos.session_manager_pb2 import NodeID from lte.protos.pipelined_pb2 import ( - SessionSet, - SetGroupFAR, - FwdParam, - Action, - OuterHeaderCreation, - SetGroupPDR, PDI, - Fsm_state, - PdrState, + Action, ActivateFlowsRequest, DeactivateFlowsRequest, - RuleModResult, + Fsm_state, + FwdParam, + OuterHeaderCreation, + PdrState, RequestOriginType, + RuleModResult, + SessionSet, + SetGroupFAR, + SetGroupPDR, ) - +from lte.protos.policydb_pb2 import FlowDescription, FlowMatch, PolicyRule +from lte.protos.session_manager_pb2 import NodeID from magma.pipelined.ng_manager.session_state_manager_util import FARRuleEntry +from magma.pipelined.policy_converters import convert_ipv4_str_to_ip_proto +from magma.subscriberdb.sid import SIDUtils + QoSEnforceRuleEntry = namedtuple( 'QoSEnforceRuleEntry', ['imsi', 'rule_id', 'ipv4_dst', 'allow', 'priority', 'hard_timeout', 'direction']) diff --git a/lte/gateway/python/magma/pipelined/tests/app/packet_builder.py b/lte/gateway/python/magma/pipelined/tests/app/packet_builder.py index 4adfaa18a19c..eebe0296980a 100644 --- a/lte/gateway/python/magma/pipelined/tests/app/packet_builder.py +++ b/lte/gateway/python/magma/pipelined/tests/app/packet_builder.py @@ -13,10 +13,21 @@ import abc import logging -logging.getLogger("scapy.runtime").setLevel(logging.ERROR) -from scapy.all import Ether, IP, IPv6, ARP, TCP, UDP, ICMP, DHCP, BOOTP, \ - wrpcap, rdpcap +logging.getLogger("scapy.runtime").setLevel(logging.ERROR) +from scapy.all import ( + ARP, + BOOTP, + DHCP, + ICMP, + IP, + TCP, + UDP, + Ether, + IPv6, + rdpcap, + wrpcap, +) ''' diff --git a/lte/gateway/python/magma/pipelined/tests/app/packet_injector.py b/lte/gateway/python/magma/pipelined/tests/app/packet_injector.py index 77786b76c732..0795843986da 100644 --- a/lte/gateway/python/magma/pipelined/tests/app/packet_injector.py +++ b/lte/gateway/python/magma/pipelined/tests/app/packet_injector.py @@ -13,8 +13,9 @@ import abc import logging + logging.getLogger("scapy.runtime").setLevel(logging.ERROR) -from scapy.all import sendp, srp, wrpcap, rdpcap +from scapy.all import rdpcap, sendp, srp, wrpcap class PacketInjector(metaclass=abc.ABCMeta): diff --git a/lte/gateway/python/magma/pipelined/tests/app/start_pipelined.py b/lte/gateway/python/magma/pipelined/tests/app/start_pipelined.py index abb8211a2434..567866f018e8 100644 --- a/lte/gateway/python/magma/pipelined/tests/app/start_pipelined.py +++ b/lte/gateway/python/magma/pipelined/tests/app/start_pipelined.py @@ -12,18 +12,19 @@ """ import logging -import threading import subprocess -from enum import Enum +import threading from collections import namedtuple from concurrent.futures import Future +from enum import Enum -from magma.pipelined.rule_mappers import RuleIDToNumMapper +from magma.pipelined.app.base import ControllerType, MagmaController from magma.pipelined.internal_ip_allocator import InternalIPAllocator -from magma.pipelined.app.base import MagmaController, ControllerType -from magma.pipelined.tests.app.exceptions import ServiceRunningError,\ - BadConfigError - +from magma.pipelined.rule_mappers import RuleIDToNumMapper +from magma.pipelined.tests.app.exceptions import ( + BadConfigError, + ServiceRunningError, +) from ryu.base.app_manager import AppManager from ryu.lib import hub diff --git a/lte/gateway/python/magma/pipelined/tests/app/subscriber.py b/lte/gateway/python/magma/pipelined/tests/app/subscriber.py index 79e40f5c533a..b58fffb34e95 100644 --- a/lte/gateway/python/magma/pipelined/tests/app/subscriber.py +++ b/lte/gateway/python/magma/pipelined/tests/app/subscriber.py @@ -11,18 +11,19 @@ limitations under the License. """ +import abc import logging import time from collections import namedtuple -import abc import grpc -from lte.protos.pipelined_pb2 import ActivateFlowsRequest, \ - DeactivateFlowsRequest -from ryu.lib import hub - -from magma.subscriberdb.sid import SIDUtils +from lte.protos.pipelined_pb2 import ( + ActivateFlowsRequest, + DeactivateFlowsRequest, +) from magma.pipelined.policy_converters import convert_ip_str_to_ip_proto +from magma.subscriberdb.sid import SIDUtils +from ryu.lib import hub SubContextConfig = namedtuple('ContextConfig', ['imsi', 'ip', 'ambr', 'table_id']) diff --git a/lte/gateway/python/magma/pipelined/tests/app/table_isolation.py b/lte/gateway/python/magma/pipelined/tests/app/table_isolation.py index 5d789abae85b..e098459967c1 100644 --- a/lte/gateway/python/magma/pipelined/tests/app/table_isolation.py +++ b/lte/gateway/python/magma/pipelined/tests/app/table_isolation.py @@ -14,18 +14,23 @@ import abc import copy +from integ_tests.s1aptests.ovs import LOCALHOST +from integ_tests.s1aptests.ovs.rest_api import ( + add_flowentry, + delete_flowentry, + get_datapath, +) from lte.protos.mobilityd_pb2 import IPAddress from magma.pipelined.imsi import encode_imsi -from magma.pipelined.policy_converters import convert_ip_str_to_ip_proto from magma.pipelined.openflow.magma_match import MagmaMatch -from magma.pipelined.openflow.registers import DIRECTION_REG, Direction, \ - IMSI_REG -from integ_tests.s1aptests.ovs import LOCALHOST -from integ_tests.s1aptests.ovs.rest_api import get_datapath,\ - delete_flowentry, add_flowentry - -from ryu.lib.packet import ether_types +from magma.pipelined.openflow.registers import ( + DIRECTION_REG, + IMSI_REG, + Direction, +) +from magma.pipelined.policy_converters import convert_ip_str_to_ip_proto from ryu.lib import hub +from ryu.lib.packet import ether_types class TableIsolator(abc.ABC): diff --git a/lte/gateway/python/magma/pipelined/tests/ng_node_rpc_servicer.py b/lte/gateway/python/magma/pipelined/tests/ng_node_rpc_servicer.py index a7fd7adcdd49..aa66f634ae04 100644 --- a/lte/gateway/python/magma/pipelined/tests/ng_node_rpc_servicer.py +++ b/lte/gateway/python/magma/pipelined/tests/ng_node_rpc_servicer.py @@ -11,36 +11,38 @@ limitations under the License. """ -import warnings -from typing import List import subprocess +import threading import unittest -from unittest import TestCase import unittest.mock -from unittest.mock import MagicMock -import grpc +import warnings from concurrent import futures +from typing import List +from unittest import TestCase +from unittest.mock import MagicMock +import grpc +from lte.protos import session_manager_pb2_grpc +from lte.protos.session_manager_pb2 import UPFNodeState +from lte.protos.session_manager_pb2_grpc import SetInterfaceForUserPlaneStub from magma.pipelined.bridge_util import BridgeTools +from magma.pipelined.ng_manager.node_state_manager import NodeStateManager +from magma.pipelined.set_interface_client import ( + send_node_state_association_request, +) from magma.pipelined.tests.app.start_pipelined import ( + PipelinedController, TestSetup, - PipelinedController) - +) from magma.pipelined.tests.pipelined_test_util import ( + create_service_manager, start_ryu_app_thread, stop_ryu_app_thread, - create_service_manager, - wait_after_send) - -import threading - -from lte.protos import session_manager_pb2_grpc -from lte.protos.session_manager_pb2_grpc import SetInterfaceForUserPlaneStub -from lte.protos.session_manager_pb2 import UPFNodeState -from magma.pipelined.ng_manager.node_state_manager import NodeStateManager -from magma.pipelined.set_interface_client import send_node_state_association_request -from ryu.lib import hub + wait_after_send, +) from orc8r.protos.common_pb2 import Void +from ryu.lib import hub + class SMFAssociationServerTest(session_manager_pb2_grpc.SetInterfaceForUserPlaneServicer): diff --git a/lte/gateway/python/magma/pipelined/tests/old_tests/test_controller.py b/lte/gateway/python/magma/pipelined/tests/old_tests/test_controller.py index da2552f6c944..58bc171e415f 100644 --- a/lte/gateway/python/magma/pipelined/tests/old_tests/test_controller.py +++ b/lte/gateway/python/magma/pipelined/tests/old_tests/test_controller.py @@ -11,22 +11,19 @@ limitations under the License. """ -from netaddr import IPNetwork +import threading +import time import unittest from unittest.mock import MagicMock -import time -import threading +from magma.pipelined.app.base import ControllerType, MagmaController +from magma.pipelined.openflow.exceptions import MagmaOFError +from magma.pkt_tester.tests.test_topology_builder import check_env +from netaddr import IPNetwork from nose.plugins.skip import SkipTest from ryu.app.ofctl.exception import InvalidDatapath from ryu.base.app_manager import AppManager -from magma.pipelined.openflow.exceptions import MagmaOFError -from magma.pkt_tester.tests.test_topology_builder import check_env -from magma.pipelined.app.base import MagmaController, ControllerType - - - """ Writing tests for pipelined ============================= @@ -190,8 +187,8 @@ def setUp(self): def _generate_topology(self): # import here, after we've checked the environment - from ovstest import util from magma.pkt_tester.topology_builder import TopologyBuilder + from ovstest import util self._topo_builder = TopologyBuilder() @@ -209,8 +206,8 @@ def _generate_topology(self): def test_delete_all_flows(self): # import here, after we've checked the environment - from ovstest import util from magma.pkt_tester.topology_builder import OvsException + from ovstest import util # basic setup self._generate_topology() @@ -242,8 +239,8 @@ def test_delete_all_flows(self): def test_delete_table_flows(self): # import here, after we've checked the environment - from ovstest import util from magma.pkt_tester.topology_builder import OvsException + from ovstest import util # basic setup self._generate_topology() diff --git a/lte/gateway/python/magma/pipelined/tests/old_tests/test_inout.py b/lte/gateway/python/magma/pipelined/tests/old_tests/test_inout.py index 8afb41fe91d6..d9ab8a0d0926 100644 --- a/lte/gateway/python/magma/pipelined/tests/old_tests/test_inout.py +++ b/lte/gateway/python/magma/pipelined/tests/old_tests/test_inout.py @@ -10,9 +10,10 @@ See the License for the specific language governing permissions and limitations under the License. """ -from test_controller import BaseMagmaTest import unittest +from test_controller import BaseMagmaTest + @unittest.skip("temporarily disabled") class InoutTest(BaseMagmaTest.MagmaControllerTest): @@ -22,8 +23,8 @@ def setUp(self): def _generate_topology(self): # import here, after we've checked the environment - from ovstest import util from magma.pkt_tester.topology_builder import TopologyBuilder + from ovstest import util self._topo_builder = TopologyBuilder() @@ -40,8 +41,8 @@ def _generate_topology(self): self.assertFalse(self._topo_builder.invalid_devices()) def test_add_inout_flows(self): - from ovstest import util from magma.pkt_tester.topology_builder import OvsException + from ovstest import util self._generate_topology() self.controller_thread.start() diff --git a/lte/gateway/python/magma/pipelined/tests/pipelined_test_util.py b/lte/gateway/python/magma/pipelined/tests/pipelined_test_util.py index 942b2a517890..dba901f333d6 100644 --- a/lte/gateway/python/magma/pipelined/tests/pipelined_test_util.py +++ b/lte/gateway/python/magma/pipelined/tests/pipelined_test_util.py @@ -15,30 +15,33 @@ import os import re import subprocess -import netifaces -import fakeredis - from collections import namedtuple from concurrent.futures import Future from difflib import unified_diff from typing import Dict, List, Optional - -from unittest import TestCase -from unittest import mock +from unittest import TestCase, mock from unittest.mock import MagicMock -from ryu.lib import hub +import fakeredis +import netifaces from lte.protos.mconfig.mconfigs_pb2 import PipelineD -from lte.protos.pipelined_pb2 import SetupFlowsResult, SetupPolicyRequest, \ - UpdateSubscriberQuotaStateRequest, SetupUEMacRequest +from lte.protos.pipelined_pb2 import ( + SetupFlowsResult, + SetupPolicyRequest, + SetupUEMacRequest, + UpdateSubscriberQuotaStateRequest, +) +from magma.pipelined.app.base import global_epoch from magma.pipelined.bridge_util import BridgeTools +from magma.pipelined.openflow import flows from magma.pipelined.service_manager import ServiceManager -from magma.pipelined.tests.app.exceptions import BadConfigError, \ - ServiceRunningError +from magma.pipelined.tests.app.exceptions import ( + BadConfigError, + ServiceRunningError, +) from magma.pipelined.tests.app.flow_query import RyuDirectFlowQuery from magma.pipelined.tests.app.start_pipelined import StartThread -from magma.pipelined.app.base import global_epoch -from magma.pipelined.openflow import flows +from ryu.lib import hub """ Pipelined test util functions can be used for testing pipelined, the usage of diff --git a/lte/gateway/python/magma/pipelined/tests/test_access_control.py b/lte/gateway/python/magma/pipelined/tests/test_access_control.py index 9608389e9df8..19d7257687e6 100644 --- a/lte/gateway/python/magma/pipelined/tests/test_access_control.py +++ b/lte/gateway/python/magma/pipelined/tests/test_access_control.py @@ -16,23 +16,34 @@ from concurrent.futures import Future from lte.protos.mconfig.mconfigs_pb2 import PipelineD -from magma.pipelined.app.access_control import \ - AccessControlController +from magma.pipelined.app.access_control import AccessControlController +from magma.pipelined.bridge_util import BridgeTools from magma.pipelined.openflow.magma_match import MagmaMatch from magma.pipelined.openflow.registers import Direction +from magma.pipelined.tests.app.flow_query import RyuDirectFlowQuery as FlowQuery from magma.pipelined.tests.app.packet_builder import IPPacketBuilder -from magma.pipelined.tests.app.start_pipelined import TestSetup, \ - PipelinedController -from magma.pipelined.tests.app.subscriber import SubContextConfig, default_ambr_config -from magma.pipelined.tests.app.table_isolation import RyuDirectTableIsolator, \ - RyuForwardFlowArgsBuilder from magma.pipelined.tests.app.packet_injector import ScapyPacketInjector -from magma.pipelined.tests.app.flow_query import RyuDirectFlowQuery \ - as FlowQuery -from magma.pipelined.bridge_util import BridgeTools -from magma.pipelined.tests.pipelined_test_util import start_ryu_app_thread, \ - stop_ryu_app_thread, FlowTest, wait_after_send, FlowVerifier, \ - create_service_manager, assert_bridge_snapshot_match +from magma.pipelined.tests.app.start_pipelined import ( + PipelinedController, + TestSetup, +) +from magma.pipelined.tests.app.subscriber import ( + SubContextConfig, + default_ambr_config, +) +from magma.pipelined.tests.app.table_isolation import ( + RyuDirectTableIsolator, + RyuForwardFlowArgsBuilder, +) +from magma.pipelined.tests.pipelined_test_util import ( + FlowTest, + FlowVerifier, + assert_bridge_snapshot_match, + create_service_manager, + start_ryu_app_thread, + stop_ryu_app_thread, + wait_after_send, +) from ryu.lib.packet import ether_types diff --git a/lte/gateway/python/magma/pipelined/tests/test_arp.py b/lte/gateway/python/magma/pipelined/tests/test_arp.py index bdf9e24bcf97..f8818acb8d87 100644 --- a/lte/gateway/python/magma/pipelined/tests/test_arp.py +++ b/lte/gateway/python/magma/pipelined/tests/test_arp.py @@ -11,29 +11,37 @@ limitations under the License. """ -import unittest import time +import unittest import warnings from concurrent.futures import Future from lte.protos.mconfig.mconfigs_pb2 import PipelineD -from magma.pipelined.openflow.registers import DIRECTION_REG -from magma.pipelined.tests.app.flow_query import RyuRestFlowQuery from magma.pipelined.app.arp import ArpController -from magma.pipelined.tests.app.table_isolation import RyuRestTableIsolator,\ - RyuForwardFlowArgsBuilder -from magma.pipelined.tests.app.packet_injector import ScapyPacketInjector from magma.pipelined.bridge_util import BridgeTools -from magma.pipelined.tests.app.packet_builder import IPPacketBuilder,\ - ARPPacketBuilder -from magma.pipelined.tests.app.start_pipelined import TestSetup, \ - PipelinedController from magma.pipelined.openflow.registers import DIRECTION_REG, Direction -from magma.pipelined.tests.app.table_isolation import RyuDirectTableIsolator, \ - RyuForwardFlowArgsBuilder -from magma.pipelined.tests.pipelined_test_util import start_ryu_app_thread, \ - stop_ryu_app_thread, create_service_manager, wait_after_send, \ - SnapshotVerifier +from magma.pipelined.tests.app.flow_query import RyuRestFlowQuery +from magma.pipelined.tests.app.packet_builder import ( + ARPPacketBuilder, + IPPacketBuilder, +) +from magma.pipelined.tests.app.packet_injector import ScapyPacketInjector +from magma.pipelined.tests.app.start_pipelined import ( + PipelinedController, + TestSetup, +) +from magma.pipelined.tests.app.table_isolation import ( + RyuDirectTableIsolator, + RyuForwardFlowArgsBuilder, + RyuRestTableIsolator, +) +from magma.pipelined.tests.pipelined_test_util import ( + SnapshotVerifier, + create_service_manager, + start_ryu_app_thread, + stop_ryu_app_thread, + wait_after_send, +) def _pkt_total(stats): diff --git a/lte/gateway/python/magma/pipelined/tests/test_arp_non_nat.py b/lte/gateway/python/magma/pipelined/tests/test_arp_non_nat.py index ba21d4715f9d..58fa3d376b51 100644 --- a/lte/gateway/python/magma/pipelined/tests/test_arp_non_nat.py +++ b/lte/gateway/python/magma/pipelined/tests/test_arp_non_nat.py @@ -10,28 +10,43 @@ See the License for the specific language governing permissions and limitations under the License. """ +import ipaddress import time import unittest import warnings -import ipaddress from concurrent.futures import Future from lte.protos.mconfig.mconfigs_pb2 import PipelineD +from lte.protos.mobilityd_pb2 import ( + IPAddress, + IPBlock, + ListAddedIPBlocksResponse, +) +from magma.pipelined.app import arp from magma.pipelined.app.arp import ArpController -from magma.pipelined.tests.app.packet_injector import ScapyPacketInjector from magma.pipelined.bridge_util import BridgeTools +from magma.pipelined.openflow.registers import ( + DIRECTION_REG, + IMSI_REG, + Direction, +) from magma.pipelined.tests.app.packet_builder import ARPPacketBuilder -from magma.pipelined.tests.app.start_pipelined import TestSetup, \ - PipelinedController -from magma.pipelined.openflow.registers import IMSI_REG, DIRECTION_REG,\ - Direction -from magma.pipelined.tests.app.table_isolation import RyuDirectTableIsolator, \ - RyuForwardFlowArgsBuilder -from magma.pipelined.tests.pipelined_test_util import start_ryu_app_thread, \ - stop_ryu_app_thread, create_service_manager, wait_after_send, \ - SnapshotVerifier -from magma.pipelined.app import arp -from lte.protos.mobilityd_pb2 import ListAddedIPBlocksResponse, IPAddress, IPBlock +from magma.pipelined.tests.app.packet_injector import ScapyPacketInjector +from magma.pipelined.tests.app.start_pipelined import ( + PipelinedController, + TestSetup, +) +from magma.pipelined.tests.app.table_isolation import ( + RyuDirectTableIsolator, + RyuForwardFlowArgsBuilder, +) +from magma.pipelined.tests.pipelined_test_util import ( + SnapshotVerifier, + create_service_manager, + start_ryu_app_thread, + stop_ryu_app_thread, + wait_after_send, +) def _pkt_total(stats): diff --git a/lte/gateway/python/magma/pipelined/tests/test_check_quota.py b/lte/gateway/python/magma/pipelined/tests/test_check_quota.py index 561aa18cfb1c..d11381bbcde5 100644 --- a/lte/gateway/python/magma/pipelined/tests/test_check_quota.py +++ b/lte/gateway/python/magma/pipelined/tests/test_check_quota.py @@ -18,17 +18,17 @@ from lte.protos.mconfig.mconfigs_pb2 import PipelineD from lte.protos.pipelined_pb2 import SubscriberQuotaUpdate from lte.protos.subscriberdb_pb2 import SubscriberID +from magma.pipelined.bridge_util import BridgeTools from magma.pipelined.tests.app.start_pipelined import ( - TestSetup, PipelinedController, + TestSetup, ) -from magma.pipelined.bridge_util import BridgeTools from magma.pipelined.tests.pipelined_test_util import ( SnapshotVerifier, + create_service_manager, start_ryu_app_thread, stop_ryu_app_thread, - create_service_manager, - wait_after_send + wait_after_send, ) diff --git a/lte/gateway/python/magma/pipelined/tests/test_classifier.py b/lte/gateway/python/magma/pipelined/tests/test_classifier.py index 969e748f83d9..78e287aa17ae 100644 --- a/lte/gateway/python/magma/pipelined/tests/test_classifier.py +++ b/lte/gateway/python/magma/pipelined/tests/test_classifier.py @@ -11,28 +11,29 @@ limitations under the License. """ -import unittest -import os -import warnings import ipaddress +import os import socket +import unittest +import warnings from concurrent.futures import Future + +from lte.protos.mobilityd_pb2 import IPAddress +from magma.pipelined.app.classifier import Classifier +from magma.pipelined.bridge_util import BridgeTools from magma.pipelined.tests.app.start_pipelined import ( - TestSetup, PipelinedController, + TestSetup, ) -from magma.pipelined.bridge_util import BridgeTools from magma.pipelined.tests.pipelined_test_util import ( + SnapshotVerifier, + assert_bridge_snapshot_match, + create_service_manager, start_ryu_app_thread, stop_ryu_app_thread, - create_service_manager, - assert_bridge_snapshot_match, + wait_after_send, ) -from magma.pipelined.tests.pipelined_test_util import start_ryu_app_thread, \ - stop_ryu_app_thread, create_service_manager, wait_after_send, \ - SnapshotVerifier -from magma.pipelined.app.classifier import Classifier -from lte.protos.mobilityd_pb2 import IPAddress + class ClassifierTest(unittest.TestCase): BRIDGE = 'testing_br' diff --git a/lte/gateway/python/magma/pipelined/tests/test_classifier_traffic.py b/lte/gateway/python/magma/pipelined/tests/test_classifier_traffic.py index d0cff3ae1d2d..287ae9986fb3 100644 --- a/lte/gateway/python/magma/pipelined/tests/test_classifier_traffic.py +++ b/lte/gateway/python/magma/pipelined/tests/test_classifier_traffic.py @@ -15,31 +15,31 @@ from concurrent.futures import Future from lte.protos.mconfig.mconfigs_pb2 import PipelineD +from lte.protos.mobilityd_pb2 import IPAddress +from magma.pipelined.app.classifier import Classifier from magma.pipelined.app.inout import INGRESS +from magma.pipelined.bridge_util import BridgeTools +from magma.pipelined.openflow.magma_match import MagmaMatch +from magma.pipelined.tests.app.flow_query import RyuDirectFlowQuery as FlowQuery from magma.pipelined.tests.app.packet_injector import ScapyPacketInjector from magma.pipelined.tests.app.start_pipelined import ( - TestSetup, PipelinedController, + TestSetup, ) -from magma.pipelined.openflow.magma_match import MagmaMatch -from magma.pipelined.tests.app.flow_query import RyuDirectFlowQuery \ - as FlowQuery -from magma.pipelined.bridge_util import BridgeTools from magma.pipelined.tests.pipelined_test_util import ( + FlowTest, + FlowVerifier, + SnapshotVerifier, + create_service_manager, start_ryu_app_thread, stop_ryu_app_thread, - create_service_manager, wait_after_send, - FlowVerifier, - FlowTest, - SnapshotVerifier, ) from ryu.lib import hub -from scapy.contrib.gtp import GTP_U_Header from scapy.all import * -from magma.pipelined.app.classifier import Classifier -from scapy.all import Ether, IP, UDP, ARP -from lte.protos.mobilityd_pb2 import IPAddress +from scapy.all import ARP, IP, UDP, Ether +from scapy.contrib.gtp import GTP_U_Header + class GTPTrafficTest(unittest.TestCase): BRIDGE = 'testing_br' diff --git a/lte/gateway/python/magma/pipelined/tests/test_conntrack.py b/lte/gateway/python/magma/pipelined/tests/test_conntrack.py index da0cbfff6ab8..3c1bf670d02c 100644 --- a/lte/gateway/python/magma/pipelined/tests/test_conntrack.py +++ b/lte/gateway/python/magma/pipelined/tests/test_conntrack.py @@ -11,23 +11,34 @@ limitations under the License. """ +import pathlib import unittest import warnings from concurrent.futures import Future -import pathlib from lte.protos.mconfig.mconfigs_pb2 import PipelineD from magma.pipelined.app.conntrack import ConntrackController -from magma.pipelined.tests.app.start_pipelined import TestSetup, \ - PipelinedController -from magma.pipelined.tests.app.subscriber import SubContextConfig, default_ambr_config -from magma.pipelined.tests.app.table_isolation import RyuDirectTableIsolator, \ - RyuForwardFlowArgsBuilder -from magma.pipelined.tests.app.packet_injector import ScapyPacketInjector from magma.pipelined.bridge_util import BridgeTools -from magma.pipelined.tests.pipelined_test_util import start_ryu_app_thread, \ - stop_ryu_app_thread, create_service_manager, assert_bridge_snapshot_match, \ - SnapshotVerifier +from magma.pipelined.tests.app.packet_injector import ScapyPacketInjector +from magma.pipelined.tests.app.start_pipelined import ( + PipelinedController, + TestSetup, +) +from magma.pipelined.tests.app.subscriber import ( + SubContextConfig, + default_ambr_config, +) +from magma.pipelined.tests.app.table_isolation import ( + RyuDirectTableIsolator, + RyuForwardFlowArgsBuilder, +) +from magma.pipelined.tests.pipelined_test_util import ( + SnapshotVerifier, + assert_bridge_snapshot_match, + create_service_manager, + start_ryu_app_thread, + stop_ryu_app_thread, +) class ConntrackTest(unittest.TestCase): diff --git a/lte/gateway/python/magma/pipelined/tests/test_cwf_restart_resilience.py b/lte/gateway/python/magma/pipelined/tests/test_cwf_restart_resilience.py index e0002d9aa13f..77f983ad1fec 100644 --- a/lte/gateway/python/magma/pipelined/tests/test_cwf_restart_resilience.py +++ b/lte/gateway/python/magma/pipelined/tests/test_cwf_restart_resilience.py @@ -12,22 +12,32 @@ """ import unittest +import warnings from concurrent.futures import Future from unittest.mock import MagicMock -import warnings from lte.protos.mconfig.mconfigs_pb2 import PipelineD from lte.protos.pipelined_pb2 import SetupUEMacRequest, UEMacFlowRequest -from orc8r.protos.directoryd_pb2 import DirectoryRecord -from magma.subscriberdb.sid import SIDUtils -from magma.pipelined.bridge_util import BridgeTools from magma.pipelined.app.base import global_epoch -from magma.pipelined.tests.app.start_pipelined import PipelinedController, \ - TestSetup -from magma.pipelined.tests.pipelined_test_util import FlowTest, FlowVerifier, \ - create_service_manager, start_ryu_app_thread, stop_ryu_app_thread, \ - wait_after_send, SnapshotVerifier, get_enforcement_stats, \ - wait_for_enforcement_stats, fake_cwf_setup +from magma.pipelined.bridge_util import BridgeTools +from magma.pipelined.tests.app.start_pipelined import ( + PipelinedController, + TestSetup, +) +from magma.pipelined.tests.pipelined_test_util import ( + FlowTest, + FlowVerifier, + SnapshotVerifier, + create_service_manager, + fake_cwf_setup, + get_enforcement_stats, + start_ryu_app_thread, + stop_ryu_app_thread, + wait_after_send, + wait_for_enforcement_stats, +) +from magma.subscriberdb.sid import SIDUtils +from orc8r.protos.directoryd_pb2 import DirectoryRecord class CWFRestartResilienceTest(unittest.TestCase): diff --git a/lte/gateway/python/magma/pipelined/tests/test_dpi.py b/lte/gateway/python/magma/pipelined/tests/test_dpi.py index be83f16f10fb..dfc49846669d 100644 --- a/lte/gateway/python/magma/pipelined/tests/test_dpi.py +++ b/lte/gateway/python/magma/pipelined/tests/test_dpi.py @@ -16,22 +16,20 @@ from concurrent.futures import Future from lte.protos.mconfig.mconfigs_pb2 import PipelineD -from lte.protos.policydb_pb2 import FlowMatch from lte.protos.pipelined_pb2 import FlowRequest - - +from lte.protos.policydb_pb2 import FlowMatch +from magma.pipelined.bridge_util import BridgeTools +from magma.pipelined.policy_converters import convert_ipv4_str_to_ip_proto from magma.pipelined.tests.app.start_pipelined import ( - TestSetup, PipelinedController, + TestSetup, ) -from magma.pipelined.bridge_util import BridgeTools from magma.pipelined.tests.pipelined_test_util import ( + SnapshotVerifier, + create_service_manager, start_ryu_app_thread, stop_ryu_app_thread, - create_service_manager, - SnapshotVerifier, ) -from magma.pipelined.policy_converters import convert_ipv4_str_to_ip_proto class DPITest(unittest.TestCase): diff --git a/lte/gateway/python/magma/pipelined/tests/test_encoding.py b/lte/gateway/python/magma/pipelined/tests/test_encoding.py index c7e79eba99e6..9055ab3cc498 100644 --- a/lte/gateway/python/magma/pipelined/tests/test_encoding.py +++ b/lte/gateway/python/magma/pipelined/tests/test_encoding.py @@ -13,9 +13,13 @@ import unittest -from magma.pipelined.encoding import encrypt_str, get_hash, encode_str, \ - decrypt_str from lte.protos.mconfig.mconfigs_pb2 import PipelineD +from magma.pipelined.encoding import ( + decrypt_str, + encode_str, + encrypt_str, + get_hash, +) class EncodingTest(unittest.TestCase): diff --git a/lte/gateway/python/magma/pipelined/tests/test_enforcement.py b/lte/gateway/python/magma/pipelined/tests/test_enforcement.py index 2539ff8066c0..44c66be5b328 100644 --- a/lte/gateway/python/magma/pipelined/tests/test_enforcement.py +++ b/lte/gateway/python/magma/pipelined/tests/test_enforcement.py @@ -12,37 +12,55 @@ """ import unittest -from concurrent.futures import Future - import warnings +from concurrent.futures import Future from typing import List from lte.protos.mconfig.mconfigs_pb2 import PipelineD -from lte.protos.policydb_pb2 import FlowDescription, FlowMatch, PolicyRule,\ - HeaderEnrichment +from lte.protos.mobilityd_pb2 import IPAddress from lte.protos.pipelined_pb2 import VersionedPolicy +from lte.protos.policydb_pb2 import ( + FlowDescription, + FlowMatch, + HeaderEnrichment, + PolicyRule, +) +from magma.pipelined.app import he from magma.pipelined.app.enforcement import EnforcementController -from lte.protos.mobilityd_pb2 import IPAddress from magma.pipelined.bridge_util import BridgeTools -from magma.pipelined.policy_converters import flow_match_to_magma_match -from magma.pipelined.tests.app.flow_query import RyuDirectFlowQuery \ - as FlowQuery -from magma.pipelined.tests.app.packet_builder import IPPacketBuilder, \ - TCPPacketBuilder, IPv6PacketBuilder +from magma.pipelined.policy_converters import ( + convert_ipv4_str_to_ip_proto, + convert_ipv6_bytes_to_ip_proto, + flow_match_to_magma_match, +) +from magma.pipelined.tests.app.flow_query import RyuDirectFlowQuery as FlowQuery +from magma.pipelined.tests.app.packet_builder import ( + IPPacketBuilder, + IPv6PacketBuilder, + TCPPacketBuilder, +) from magma.pipelined.tests.app.packet_injector import ScapyPacketInjector -from magma.pipelined.tests.app.start_pipelined import PipelinedController, \ - TestSetup +from magma.pipelined.tests.app.start_pipelined import ( + PipelinedController, + TestSetup, +) from magma.pipelined.tests.app.subscriber import RyuDirectSubscriberContext -from magma.pipelined.tests.app.table_isolation import RyuDirectTableIsolator, \ - RyuForwardFlowArgsBuilder -from magma.pipelined.policy_converters import convert_ipv4_str_to_ip_proto, \ - convert_ipv6_bytes_to_ip_proto -from magma.pipelined.tests.pipelined_test_util import FlowTest, FlowVerifier, \ - PktsToSend, SubTest, create_service_manager, start_ryu_app_thread, \ - stop_ryu_app_thread, wait_after_send, SnapshotVerifier, \ - fake_controller_setup - -from magma.pipelined.app import he +from magma.pipelined.tests.app.table_isolation import ( + RyuDirectTableIsolator, + RyuForwardFlowArgsBuilder, +) +from magma.pipelined.tests.pipelined_test_util import ( + FlowTest, + FlowVerifier, + PktsToSend, + SnapshotVerifier, + SubTest, + create_service_manager, + fake_controller_setup, + start_ryu_app_thread, + stop_ryu_app_thread, + wait_after_send, +) def mocked_activate_he_urls_for_ue(ip: IPAddress, rule_id, urls: List[str], imsi: str, msisdn: str): diff --git a/lte/gateway/python/magma/pipelined/tests/test_enforcement_stats.py b/lte/gateway/python/magma/pipelined/tests/test_enforcement_stats.py index eb97fc608b70..f67ec78a576f 100644 --- a/lte/gateway/python/magma/pipelined/tests/test_enforcement_stats.py +++ b/lte/gateway/python/magma/pipelined/tests/test_enforcement_stats.py @@ -12,30 +12,49 @@ """ import unittest +import warnings from concurrent.futures import Future from unittest.mock import MagicMock -import warnings -from lte.protos.pipelined_pb2 import VersionedPolicy from lte.protos.mconfig.mconfigs_pb2 import PipelineD -from lte.protos.policydb_pb2 import FlowDescription, FlowMatch, PolicyRule, \ - RedirectInformation +from lte.protos.pipelined_pb2 import VersionedPolicy +from lte.protos.policydb_pb2 import ( + FlowDescription, + FlowMatch, + PolicyRule, + RedirectInformation, +) from magma.pipelined.app.enforcement import EnforcementController from magma.pipelined.bridge_util import BridgeTools -from magma.pipelined.policy_converters import convert_ipv4_str_to_ip_proto, \ - convert_ipv6_bytes_to_ip_proto -from magma.pipelined.tests.app.packet_builder import IPPacketBuilder, \ - TCPPacketBuilder +from magma.pipelined.policy_converters import ( + convert_ipv4_str_to_ip_proto, + convert_ipv6_bytes_to_ip_proto, +) +from magma.pipelined.tests.app.packet_builder import ( + IPPacketBuilder, + TCPPacketBuilder, +) from magma.pipelined.tests.app.packet_injector import ScapyPacketInjector -from magma.pipelined.tests.app.start_pipelined import PipelinedController, \ - TestSetup +from magma.pipelined.tests.app.start_pipelined import ( + PipelinedController, + TestSetup, +) from magma.pipelined.tests.app.subscriber import RyuDirectSubscriberContext -from magma.pipelined.tests.app.table_isolation import RyuDirectTableIsolator, \ - RyuForwardFlowArgsBuilder -from magma.pipelined.tests.pipelined_test_util import create_service_manager, \ - get_enforcement_stats, start_ryu_app_thread, stop_ryu_app_thread, \ - wait_after_send, wait_for_enforcement_stats, FlowTest, SnapshotVerifier, \ - fake_controller_setup +from magma.pipelined.tests.app.table_isolation import ( + RyuDirectTableIsolator, + RyuForwardFlowArgsBuilder, +) +from magma.pipelined.tests.pipelined_test_util import ( + FlowTest, + SnapshotVerifier, + create_service_manager, + fake_controller_setup, + get_enforcement_stats, + start_ryu_app_thread, + stop_ryu_app_thread, + wait_after_send, + wait_for_enforcement_stats, +) from scapy.all import IP diff --git a/lte/gateway/python/magma/pipelined/tests/test_gy.py b/lte/gateway/python/magma/pipelined/tests/test_gy.py index 14ee785f3cb5..76587c7f034a 100644 --- a/lte/gateway/python/magma/pipelined/tests/test_gy.py +++ b/lte/gateway/python/magma/pipelined/tests/test_gy.py @@ -11,33 +11,53 @@ limitations under the License. """ -import warnings import unittest +import warnings from concurrent.futures import Future from unittest.mock import MagicMock from lte.protos.mconfig.mconfigs_pb2 import PipelineD from lte.protos.pipelined_pb2 import VersionedPolicy -from lte.protos.policydb_pb2 import FlowDescription, FlowMatch, PolicyRule, \ - RedirectInformation +from lte.protos.policydb_pb2 import ( + FlowDescription, + FlowMatch, + PolicyRule, + RedirectInformation, +) from magma.pipelined.app.gy import GYController from magma.pipelined.bridge_util import BridgeTools -from magma.pipelined.policy_converters import flow_match_to_magma_match, \ - convert_ipv4_str_to_ip_proto -from magma.pipelined.tests.app.packet_builder import TCPPacketBuilder, \ - IPPacketBuilder +from magma.pipelined.policy_converters import ( + convert_ipv4_str_to_ip_proto, + flow_match_to_magma_match, +) +from magma.pipelined.tests.app.flow_query import RyuDirectFlowQuery as FlowQuery +from magma.pipelined.tests.app.packet_builder import ( + IPPacketBuilder, + TCPPacketBuilder, +) from magma.pipelined.tests.app.packet_injector import ScapyPacketInjector -from magma.pipelined.tests.app.start_pipelined import PipelinedController, \ - TestSetup +from magma.pipelined.tests.app.start_pipelined import ( + PipelinedController, + TestSetup, +) from magma.pipelined.tests.app.subscriber import RyuDirectSubscriberContext -from magma.pipelined.tests.app.table_isolation import RyuDirectTableIsolator, \ - RyuForwardFlowArgsBuilder -from magma.pipelined.tests.app.flow_query import RyuDirectFlowQuery \ - as FlowQuery -from magma.pipelined.tests.pipelined_test_util import SnapshotVerifier, \ - create_service_manager, start_ryu_app_thread, fake_controller_setup, \ - stop_ryu_app_thread, wait_after_send, FlowTest, SnapshotVerifier, \ - SubTest, FlowVerifier, PktsToSend +from magma.pipelined.tests.app.table_isolation import ( + RyuDirectTableIsolator, + RyuForwardFlowArgsBuilder, +) +from magma.pipelined.tests.pipelined_test_util import ( + FlowTest, + FlowVerifier, + PktsToSend, + SnapshotVerifier, + SubTest, + create_service_manager, + fake_controller_setup, + start_ryu_app_thread, + stop_ryu_app_thread, + wait_after_send, +) + class GYTableTest(unittest.TestCase): BRIDGE = 'testing_br' diff --git a/lte/gateway/python/magma/pipelined/tests/test_he.py b/lte/gateway/python/magma/pipelined/tests/test_he.py index 14a36b8cf7d2..fff36d4ef769 100644 --- a/lte/gateway/python/magma/pipelined/tests/test_he.py +++ b/lte/gateway/python/magma/pipelined/tests/test_he.py @@ -18,33 +18,40 @@ from lte.protos.mconfig.mconfigs_pb2 import PipelineD from lte.protos.mobilityd_pb2 import IPAddress - -from magma.pipelined.app.he import HeaderEnrichmentController -from magma.pipelined.app.enforcement import EnforcementController -from lte.protos.policydb_pb2 import FlowDescription, FlowMatch, PolicyRule, \ - HeaderEnrichment from lte.protos.pipelined_pb2 import VersionedPolicy - -from magma.pipelined.bridge_util import BridgeTools -from magma.pipelined.tests.app.subscriber import RyuDirectSubscriberContext - -from magma.pipelined.tests.app.start_pipelined import TestSetup, \ - PipelinedController -from magma.pipelined.tests.pipelined_test_util import start_ryu_app_thread, \ - stop_ryu_app_thread, create_service_manager, wait_after_send, \ - SnapshotVerifier -from magma.pipelined.policy_converters import convert_ip_str_to_ip_proto - -from magma.pipelined.tests.app.table_isolation import RyuDirectTableIsolator, \ - RyuForwardFlowArgsBuilder - -from magma.pipelined.openflow.messages import MessageHub -from magma.pipelined.openflow.messages import MsgChannel +from lte.protos.policydb_pb2 import ( + FlowDescription, + FlowMatch, + HeaderEnrichment, + PolicyRule, +) from magma.pipelined.app import he +from magma.pipelined.app.enforcement import EnforcementController +from magma.pipelined.app.he import HeaderEnrichmentController +from magma.pipelined.bridge_util import BridgeTools +from magma.pipelined.openflow.messages import MessageHub, MsgChannel from magma.pipelined.openflow.registers import Direction -from magma.pipelined.policy_converters import convert_ipv4_str_to_ip_proto - -from magma.pipelined.tests.pipelined_test_util import fake_controller_setup +from magma.pipelined.policy_converters import ( + convert_ip_str_to_ip_proto, + convert_ipv4_str_to_ip_proto, +) +from magma.pipelined.tests.app.start_pipelined import ( + PipelinedController, + TestSetup, +) +from magma.pipelined.tests.app.subscriber import RyuDirectSubscriberContext +from magma.pipelined.tests.app.table_isolation import ( + RyuDirectTableIsolator, + RyuForwardFlowArgsBuilder, +) +from magma.pipelined.tests.pipelined_test_util import ( + SnapshotVerifier, + create_service_manager, + fake_controller_setup, + start_ryu_app_thread, + stop_ryu_app_thread, + wait_after_send, +) def mocked_activate_he_urls_for_ue(ip: IPAddress, rule_id: str, urls: List[str], imsi: str, msisdn: str): diff --git a/lte/gateway/python/magma/pipelined/tests/test_imsi_encoding.py b/lte/gateway/python/magma/pipelined/tests/test_imsi_encoding.py index efa91b7be45c..d8eb0aafa3bf 100644 --- a/lte/gateway/python/magma/pipelined/tests/test_imsi_encoding.py +++ b/lte/gateway/python/magma/pipelined/tests/test_imsi_encoding.py @@ -13,7 +13,7 @@ import unittest -from magma.pipelined.imsi import encode_imsi, decode_imsi +from magma.pipelined.imsi import decode_imsi, encode_imsi class IMSIEncodingTest(unittest.TestCase): diff --git a/lte/gateway/python/magma/pipelined/tests/test_inout.py b/lte/gateway/python/magma/pipelined/tests/test_inout.py index e6e15e9b0c9a..9b5bce321b4c 100644 --- a/lte/gateway/python/magma/pipelined/tests/test_inout.py +++ b/lte/gateway/python/magma/pipelined/tests/test_inout.py @@ -15,17 +15,17 @@ import warnings from concurrent.futures import Future +from magma.pipelined.bridge_util import BridgeTools from magma.pipelined.tests.app.start_pipelined import ( - TestSetup, PipelinedController, + TestSetup, ) -from magma.pipelined.bridge_util import BridgeTools from magma.pipelined.tests.pipelined_test_util import ( - start_ryu_app_thread, - stop_ryu_app_thread, - create_service_manager, assert_bridge_snapshot_match, + create_service_manager, fake_inout_setup, + start_ryu_app_thread, + stop_ryu_app_thread, ) from ryu.ofproto.ofproto_v1_4 import OFPP_LOCAL diff --git a/lte/gateway/python/magma/pipelined/tests/test_inout_non_nat.py b/lte/gateway/python/magma/pipelined/tests/test_inout_non_nat.py index f0ef8af8641f..00aac8d00cce 100644 --- a/lte/gateway/python/magma/pipelined/tests/test_inout_non_nat.py +++ b/lte/gateway/python/magma/pipelined/tests/test_inout_non_nat.py @@ -11,34 +11,32 @@ limitations under the License. """ import ipaddress +import logging import subprocess -import time import threading +import time import unittest import warnings from concurrent.futures import Future -import logging from typing import List -from ryu.lib import hub - -from lte.protos.mobilityd_pb2 import IPAddress, GWInfo, IPBlock +from lte.protos.mobilityd_pb2 import GWInfo, IPAddress, IPBlock +from magma.pipelined.app import inout +from magma.pipelined.bridge_util import BridgeTools from magma.pipelined.tests.app.start_pipelined import ( - TestSetup, PipelinedController, + TestSetup, ) -from magma.pipelined.bridge_util import BridgeTools from magma.pipelined.tests.pipelined_test_util import ( + SnapshotVerifier, + create_service_manager, + fake_inout_setup, start_ryu_app_thread, stop_ryu_app_thread, - create_service_manager, - SnapshotVerifier, - fake_inout_setup ) +from ryu.lib import hub from ryu.ofproto.ofproto_v1_4 import OFPP_LOCAL -from magma.pipelined.app import inout - gw_info_map = {} gw_info_lock = threading.RLock() # re-entrant locks diff --git a/lte/gateway/python/magma/pipelined/tests/test_internal_pkt_ipfix_export.py b/lte/gateway/python/magma/pipelined/tests/test_internal_pkt_ipfix_export.py index 41b71383a873..9ffa8676d85f 100644 --- a/lte/gateway/python/magma/pipelined/tests/test_internal_pkt_ipfix_export.py +++ b/lte/gateway/python/magma/pipelined/tests/test_internal_pkt_ipfix_export.py @@ -12,20 +12,25 @@ """ import unittest -from concurrent.futures import Future - import warnings +from concurrent.futures import Future from lte.protos.mconfig.mconfigs_pb2 import PipelineD -from lte.protos.policydb_pb2 import FlowMatch from lte.protos.pipelined_pb2 import FlowRequest +from lte.protos.policydb_pb2 import FlowMatch from magma.pipelined.app.dpi import DPIController from magma.pipelined.bridge_util import BridgeTools from magma.pipelined.policy_converters import convert_ipv4_str_to_ip_proto -from magma.pipelined.tests.app.start_pipelined import PipelinedController, \ - TestSetup -from magma.pipelined.tests.pipelined_test_util import create_service_manager, \ - start_ryu_app_thread, stop_ryu_app_thread, SnapshotVerifier +from magma.pipelined.tests.app.start_pipelined import ( + PipelinedController, + TestSetup, +) +from magma.pipelined.tests.pipelined_test_util import ( + SnapshotVerifier, + create_service_manager, + start_ryu_app_thread, + stop_ryu_app_thread, +) class InternalPktIpfixExportTest(unittest.TestCase): diff --git a/lte/gateway/python/magma/pipelined/tests/test_ipv6_prefix_mapper.py b/lte/gateway/python/magma/pipelined/tests/test_ipv6_prefix_mapper.py index cd6d98307583..d33410b7b423 100644 --- a/lte/gateway/python/magma/pipelined/tests/test_ipv6_prefix_mapper.py +++ b/lte/gateway/python/magma/pipelined/tests/test_ipv6_prefix_mapper.py @@ -13,8 +13,11 @@ import unittest -from magma.pipelined.ipv6_prefix_store import InterfaceIDToPrefixMapper, \ - get_ipv6_interface_id, get_ipv6_prefix +from magma.pipelined.ipv6_prefix_store import ( + InterfaceIDToPrefixMapper, + get_ipv6_interface_id, + get_ipv6_prefix, +) class InterfaceMappersTest(unittest.TestCase): diff --git a/lte/gateway/python/magma/pipelined/tests/test_ipv6_solicitation.py b/lte/gateway/python/magma/pipelined/tests/test_ipv6_solicitation.py index 2230835a1bc3..468e4fef3f21 100644 --- a/lte/gateway/python/magma/pipelined/tests/test_ipv6_solicitation.py +++ b/lte/gateway/python/magma/pipelined/tests/test_ipv6_solicitation.py @@ -16,25 +16,38 @@ from concurrent.futures import Future from lte.protos.mconfig.mconfigs_pb2 import PipelineD -from magma.pipelined.ipv6_prefix_store import get_ipv6_interface_id,\ - get_ipv6_prefix -from magma.pipelined.app.ipv6_solicitation import \ - IPV6SolicitationController -from magma.pipelined.tests.app.packet_injector import ScapyPacketInjector +from magma.pipelined.app.ipv6_solicitation import IPV6SolicitationController from magma.pipelined.bridge_util import BridgeTools -from magma.pipelined.tests.app.start_pipelined import TestSetup, \ - PipelinedController +from magma.pipelined.ipv6_prefix_store import ( + get_ipv6_interface_id, + get_ipv6_prefix, +) from magma.pipelined.openflow.registers import DIRECTION_REG, Direction -from magma.pipelined.tests.app.table_isolation import RyuDirectTableIsolator, \ - RyuForwardFlowArgsBuilder -from magma.pipelined.tests.pipelined_test_util import start_ryu_app_thread, \ - stop_ryu_app_thread, create_service_manager, wait_after_send, \ - SnapshotVerifier - -from scapy.arch import get_if_hwaddr, get_if_addr -from scapy.layers.l2 import ARP, Ether, Dot1Q -from scapy.layers.inet6 import IPv6, ICMPv6ND_RS, ICMPv6NDOptSrcLLAddr, \ - ICMPv6NDOptPrefixInfo, ICMPv6ND_NS +from magma.pipelined.tests.app.packet_injector import ScapyPacketInjector +from magma.pipelined.tests.app.start_pipelined import ( + PipelinedController, + TestSetup, +) +from magma.pipelined.tests.app.table_isolation import ( + RyuDirectTableIsolator, + RyuForwardFlowArgsBuilder, +) +from magma.pipelined.tests.pipelined_test_util import ( + SnapshotVerifier, + create_service_manager, + start_ryu_app_thread, + stop_ryu_app_thread, + wait_after_send, +) +from scapy.arch import get_if_addr, get_if_hwaddr +from scapy.layers.inet6 import ( + ICMPv6ND_NS, + ICMPv6ND_RS, + ICMPv6NDOptPrefixInfo, + ICMPv6NDOptSrcLLAddr, + IPv6, +) +from scapy.layers.l2 import ARP, Dot1Q, Ether class IPV6RouterSolicitationTableTest(unittest.TestCase): diff --git a/lte/gateway/python/magma/pipelined/tests/test_li_mirror.py b/lte/gateway/python/magma/pipelined/tests/test_li_mirror.py index a28809403d95..667beb5b9572 100644 --- a/lte/gateway/python/magma/pipelined/tests/test_li_mirror.py +++ b/lte/gateway/python/magma/pipelined/tests/test_li_mirror.py @@ -15,20 +15,19 @@ import warnings from concurrent.futures import Future -from ryu.lib import hub - +from magma.pipelined.bridge_util import BridgeTools from magma.pipelined.tests.app.start_pipelined import ( - TestSetup, PipelinedController, + TestSetup, ) -from magma.pipelined.bridge_util import BridgeTools from magma.pipelined.tests.pipelined_test_util import ( - start_ryu_app_thread, - stop_ryu_app_thread, - create_service_manager, assert_bridge_snapshot_match, + create_service_manager, fake_inout_setup, + start_ryu_app_thread, + stop_ryu_app_thread, ) +from ryu.lib import hub from ryu.ofproto.ofproto_v1_4 import OFPP_LOCAL diff --git a/lte/gateway/python/magma/pipelined/tests/test_ng_servicer_node.py b/lte/gateway/python/magma/pipelined/tests/test_ng_servicer_node.py index 69b218c1958e..c1b4c11268f8 100644 --- a/lte/gateway/python/magma/pipelined/tests/test_ng_servicer_node.py +++ b/lte/gateway/python/magma/pipelined/tests/test_ng_servicer_node.py @@ -12,36 +12,35 @@ """ import logging -import warnings -from typing import List import subprocess import unittest -from unittest import TestCase import unittest.mock +import warnings from collections import OrderedDict from concurrent.futures import Future -from unittest.mock import MagicMock +from typing import List +from unittest import TestCase +from unittest.mock import MagicMock, Mock +from lte.protos import ( + pipelined_pb2, + pipelined_pb2_grpc, + session_manager_pb2_grpc, +) +from lte.protos.session_manager_pb2 import UPFAssociationState +from magma.pipelined.app.ng_services import NGServiceController from magma.pipelined.bridge_util import BridgeTools from magma.pipelined.tests.app.start_pipelined import ( - TestSetup, - PipelinedController, - ) - + PipelinedController, + TestSetup, +) from magma.pipelined.tests.pipelined_test_util import ( + create_service_manager, start_ryu_app_thread, stop_ryu_app_thread, - create_service_manager, - wait_after_send + wait_after_send, ) -from magma.pipelined.app.ng_services import NGServiceController -from lte.protos import pipelined_pb2_grpc -from lte.protos import pipelined_pb2 -from lte.protos import session_manager_pb2_grpc -from lte.protos.session_manager_pb2 import ( - UPFAssociationState) -from unittest.mock import Mock, MagicMock def mocked_send_node_state_message_success (node_message): return True diff --git a/lte/gateway/python/magma/pipelined/tests/test_ng_servicer_session.py b/lte/gateway/python/magma/pipelined/tests/test_ng_servicer_session.py index 5ee774dbd1f1..d82806f5762a 100644 --- a/lte/gateway/python/magma/pipelined/tests/test_ng_servicer_session.py +++ b/lte/gateway/python/magma/pipelined/tests/test_ng_servicer_session.py @@ -11,28 +11,26 @@ limitations under the License. """ -import warnings import unittest -from unittest import TestCase import unittest.mock +import warnings from collections import OrderedDict from concurrent.futures import Future +from unittest import TestCase from unittest.mock import MagicMock +from lte.protos.pipelined_pb2 import CauseIE from magma.pipelined.bridge_util import BridgeTools +from magma.pipelined.tests.app.ng_set_session_msg import CreateSessionUtil from magma.pipelined.tests.app.start_pipelined import ( + PipelinedController, TestSetup, - PipelinedController) - +) from magma.pipelined.tests.pipelined_test_util import ( + create_service_manager, start_ryu_app_thread, stop_ryu_app_thread, - create_service_manager) - -from lte.protos.pipelined_pb2 import CauseIE - -from magma.pipelined.tests.app.ng_set_session_msg import ( - CreateSessionUtil) +) FAULTY_PDR_SESSION = 1 FAULTY_FAR_SESSION = 2 diff --git a/lte/gateway/python/magma/pipelined/tests/test_qos.py b/lte/gateway/python/magma/pipelined/tests/test_qos.py index 310f79989f2e..d113091216fc 100644 --- a/lte/gateway/python/magma/pipelined/tests/test_qos.py +++ b/lte/gateway/python/magma/pipelined/tests/test_qos.py @@ -11,20 +11,28 @@ limitations under the License. """ import asyncio +import logging import subprocess import unittest from collections import namedtuple from unittest.mock import MagicMock, call, patch -from magma.pipelined.bridge_util import BridgeTools +from lte.protos.policydb_pb2 import FlowMatch +from magma.pipelined.bridge_util import BridgeTools from magma.pipelined.qos.common import QosImplType, QosManager, SubscriberState from magma.pipelined.qos.qos_meter_impl import MeterManager from magma.pipelined.qos.qos_tc_impl import TrafficClass, argSplit, run_cmd -from magma.pipelined.qos.types import QosInfo, get_key_json, get_key, get_subscriber_key, \ - get_subscriber_data, get_data, get_data_json +from magma.pipelined.qos.types import ( + QosInfo, + get_data, + get_data_json, + get_key, + get_key_json, + get_subscriber_data, + get_subscriber_key, +) from magma.pipelined.qos.utils import IdManager -from lte.protos.policydb_pb2 import FlowMatch -import logging + class TestQosCommon(unittest.TestCase): def testIdManager(self): diff --git a/lte/gateway/python/magma/pipelined/tests/test_qos_pyroute2.py b/lte/gateway/python/magma/pipelined/tests/test_qos_pyroute2.py index 366ebb76773d..69f8f49fad7a 100755 --- a/lte/gateway/python/magma/pipelined/tests/test_qos_pyroute2.py +++ b/lte/gateway/python/magma/pipelined/tests/test_qos_pyroute2.py @@ -1,18 +1,16 @@ -from pyroute2 import IPRoute -from pyroute2 import NetlinkError -from pyroute2 import protocols - -import unittest -import socket import logging -import traceback -import time import pprint +import socket import subprocess +import time +import traceback +import unittest + from magma.pipelined.bridge_util import BridgeTools from magma.pipelined.qos.qos_tc_impl import TrafficClass -from magma.pipelined.qos.tc_ops_pyroute2 import TcOpsPyRoute2 from magma.pipelined.qos.tc_ops_cmd import TcOpsCmd +from magma.pipelined.qos.tc_ops_pyroute2 import TcOpsPyRoute2 +from pyroute2 import IPRoute, NetlinkError, protocols LOG = logging.getLogger('pipelined.qos.tc_rtnl') diff --git a/lte/gateway/python/magma/pipelined/tests/test_redirect.py b/lte/gateway/python/magma/pipelined/tests/test_redirect.py index 786b7325eb49..30966497589f 100644 --- a/lte/gateway/python/magma/pipelined/tests/test_redirect.py +++ b/lte/gateway/python/magma/pipelined/tests/test_redirect.py @@ -12,30 +12,46 @@ """ import unittest +import warnings from concurrent.futures import Future from unittest.mock import MagicMock -import warnings -from lte.protos.pipelined_pb2 import VersionedPolicy from lte.protos.mconfig.mconfigs_pb2 import PipelineD -from lte.protos.policydb_pb2 import FlowDescription, FlowMatch, PolicyRule, \ - RedirectInformation +from lte.protos.pipelined_pb2 import VersionedPolicy +from lte.protos.policydb_pb2 import ( + FlowDescription, + FlowMatch, + PolicyRule, + RedirectInformation, +) from magma.pipelined.app.enforcement import EnforcementController from magma.pipelined.bridge_util import BridgeTools -from magma.pipelined.policy_converters import flow_match_to_magma_match, \ - convert_ipv4_str_to_ip_proto -from magma.pipelined.tests.app.flow_query import RyuDirectFlowQuery \ - as FlowQuery +from magma.pipelined.policy_converters import ( + convert_ipv4_str_to_ip_proto, + flow_match_to_magma_match, +) +from magma.pipelined.tests.app.flow_query import RyuDirectFlowQuery as FlowQuery from magma.pipelined.tests.app.packet_builder import TCPPacketBuilder from magma.pipelined.tests.app.packet_injector import ScapyPacketInjector -from magma.pipelined.tests.app.start_pipelined import PipelinedController, \ - TestSetup +from magma.pipelined.tests.app.start_pipelined import ( + PipelinedController, + TestSetup, +) from magma.pipelined.tests.app.subscriber import RyuDirectSubscriberContext -from magma.pipelined.tests.app.table_isolation import RyuDirectTableIsolator, \ - RyuForwardFlowArgsBuilder -from magma.pipelined.tests.pipelined_test_util import FlowTest, FlowVerifier, \ - create_service_manager, start_ryu_app_thread, stop_ryu_app_thread, \ - wait_after_send, assert_bridge_snapshot_match, fake_controller_setup +from magma.pipelined.tests.app.table_isolation import ( + RyuDirectTableIsolator, + RyuForwardFlowArgsBuilder, +) +from magma.pipelined.tests.pipelined_test_util import ( + FlowTest, + FlowVerifier, + assert_bridge_snapshot_match, + create_service_manager, + fake_controller_setup, + start_ryu_app_thread, + stop_ryu_app_thread, + wait_after_send, +) class RedirectTest(unittest.TestCase): diff --git a/lte/gateway/python/magma/pipelined/tests/test_restart_resilience.py b/lte/gateway/python/magma/pipelined/tests/test_restart_resilience.py index 1b8f7cd06ada..eb445c676d13 100644 --- a/lte/gateway/python/magma/pipelined/tests/test_restart_resilience.py +++ b/lte/gateway/python/magma/pipelined/tests/test_restart_resilience.py @@ -12,34 +12,58 @@ """ import unittest +import warnings from concurrent.futures import Future from unittest.mock import MagicMock -import warnings from lte.protos.mconfig.mconfigs_pb2 import PipelineD -from lte.protos.policydb_pb2 import FlowDescription, FlowMatch, PolicyRule, \ - RedirectInformation -from lte.protos.pipelined_pb2 import ActivateFlowsRequest, SetupFlowsRequest, \ - VersionedPolicy -from magma.subscriberdb.sid import SIDUtils +from lte.protos.pipelined_pb2 import ( + ActivateFlowsRequest, + SetupFlowsRequest, + VersionedPolicy, +) +from lte.protos.policydb_pb2 import ( + FlowDescription, + FlowMatch, + PolicyRule, + RedirectInformation, +) +from magma.pipelined.app.base import global_epoch from magma.pipelined.app.enforcement import EnforcementController from magma.pipelined.app.enforcement_stats import EnforcementStatsController from magma.pipelined.bridge_util import BridgeTools -from magma.pipelined.app.base import global_epoch -from magma.pipelined.policy_converters import flow_match_to_magma_match, \ - convert_ipv4_str_to_ip_proto -from magma.pipelined.tests.app.packet_builder import IPPacketBuilder, \ - TCPPacketBuilder +from magma.pipelined.policy_converters import ( + convert_ipv4_str_to_ip_proto, + flow_match_to_magma_match, +) +from magma.pipelined.tests.app.packet_builder import ( + IPPacketBuilder, + TCPPacketBuilder, +) from magma.pipelined.tests.app.packet_injector import ScapyPacketInjector -from magma.pipelined.tests.app.start_pipelined import PipelinedController, \ - TestSetup -from magma.pipelined.tests.app.subscriber import RyuDirectSubscriberContext, default_ambr_config -from magma.pipelined.tests.app.table_isolation import RyuDirectTableIsolator, \ - RyuForwardFlowArgsBuilder -from magma.pipelined.tests.pipelined_test_util import fake_controller_setup, \ - create_service_manager, start_ryu_app_thread, stop_ryu_app_thread, \ - wait_after_send, SnapshotVerifier, get_enforcement_stats, \ - wait_for_enforcement_stats +from magma.pipelined.tests.app.start_pipelined import ( + PipelinedController, + TestSetup, +) +from magma.pipelined.tests.app.subscriber import ( + RyuDirectSubscriberContext, + default_ambr_config, +) +from magma.pipelined.tests.app.table_isolation import ( + RyuDirectTableIsolator, + RyuForwardFlowArgsBuilder, +) +from magma.pipelined.tests.pipelined_test_util import ( + SnapshotVerifier, + create_service_manager, + fake_controller_setup, + get_enforcement_stats, + start_ryu_app_thread, + stop_ryu_app_thread, + wait_after_send, + wait_for_enforcement_stats, +) +from magma.subscriberdb.sid import SIDUtils from scapy.all import IP diff --git a/lte/gateway/python/magma/pipelined/tests/test_rule_mappers.py b/lte/gateway/python/magma/pipelined/tests/test_rule_mappers.py index 3f0f1ea32add..3618e50eedd2 100644 --- a/lte/gateway/python/magma/pipelined/tests/test_rule_mappers.py +++ b/lte/gateway/python/magma/pipelined/tests/test_rule_mappers.py @@ -12,12 +12,12 @@ """ import unittest -import fakeredis -from unittest.mock import MagicMock from unittest import mock +from unittest.mock import MagicMock -from magma.pipelined.rule_mappers import SessionRuleToVersionMapper +import fakeredis from magma.pipelined.policy_converters import convert_ipv4_str_to_ip_proto +from magma.pipelined.rule_mappers import SessionRuleToVersionMapper class RuleMappersTest(unittest.TestCase): diff --git a/lte/gateway/python/magma/pipelined/tests/test_service_manager.py b/lte/gateway/python/magma/pipelined/tests/test_service_manager.py index e11ea092eab4..3a72b05cdee8 100644 --- a/lte/gateway/python/magma/pipelined/tests/test_service_manager.py +++ b/lte/gateway/python/magma/pipelined/tests/test_service_manager.py @@ -13,21 +13,21 @@ import unittest from collections import OrderedDict -import fakeredis -from unittest.mock import MagicMock from unittest import mock +from unittest.mock import MagicMock -from magma.pipelined.app.base import ControllerType +import fakeredis +from lte.protos.mconfig.mconfigs_pb2 import PipelineD from magma.pipelined.app.access_control import AccessControlController from magma.pipelined.app.arp import ArpController -from lte.protos.mconfig.mconfigs_pb2 import PipelineD +from magma.pipelined.app.base import ControllerType from magma.pipelined.app.dpi import DPIController -from magma.pipelined.app.gy import GYController from magma.pipelined.app.enforcement import EnforcementController from magma.pipelined.app.enforcement_stats import EnforcementStatsController -from magma.pipelined.app.inout import INGRESS, EGRESS, PHYSICAL_TO_LOGICAL -from magma.pipelined.app.ipfix import IPFIXController +from magma.pipelined.app.gy import GYController from magma.pipelined.app.he import HeaderEnrichmentController +from magma.pipelined.app.inout import EGRESS, INGRESS, PHYSICAL_TO_LOGICAL +from magma.pipelined.app.ipfix import IPFIXController from magma.pipelined.service_manager import ( ServiceManager, TableNumException, diff --git a/lte/gateway/python/magma/pipelined/tests/test_tunnel_learn.py b/lte/gateway/python/magma/pipelined/tests/test_tunnel_learn.py index b7904f0fbcfc..d82eb2dd7d32 100644 --- a/lte/gateway/python/magma/pipelined/tests/test_tunnel_learn.py +++ b/lte/gateway/python/magma/pipelined/tests/test_tunnel_learn.py @@ -17,16 +17,27 @@ from lte.protos.mconfig.mconfigs_pb2 import PipelineD from magma.pipelined.app.tunnel_learn import TunnelLearnController -from magma.pipelined.tests.app.start_pipelined import TestSetup, \ - PipelinedController +from magma.pipelined.bridge_util import BridgeTools from magma.pipelined.tests.app.packet_builder import IPPacketBuilder -from magma.pipelined.tests.app.subscriber import SubContextConfig, default_ambr_config -from magma.pipelined.tests.app.table_isolation import RyuDirectTableIsolator, \ - RyuForwardFlowArgsBuilder from magma.pipelined.tests.app.packet_injector import ScapyPacketInjector -from magma.pipelined.bridge_util import BridgeTools -from magma.pipelined.tests.pipelined_test_util import start_ryu_app_thread, \ - stop_ryu_app_thread, create_service_manager, assert_bridge_snapshot_match +from magma.pipelined.tests.app.start_pipelined import ( + PipelinedController, + TestSetup, +) +from magma.pipelined.tests.app.subscriber import ( + SubContextConfig, + default_ambr_config, +) +from magma.pipelined.tests.app.table_isolation import ( + RyuDirectTableIsolator, + RyuForwardFlowArgsBuilder, +) +from magma.pipelined.tests.pipelined_test_util import ( + assert_bridge_snapshot_match, + create_service_manager, + start_ryu_app_thread, + stop_ryu_app_thread, +) class TunnelLearnTest(unittest.TestCase): diff --git a/lte/gateway/python/magma/pipelined/tests/test_ue_mac.py b/lte/gateway/python/magma/pipelined/tests/test_ue_mac.py index c8511815acee..fae42dfa092f 100644 --- a/lte/gateway/python/magma/pipelined/tests/test_ue_mac.py +++ b/lte/gateway/python/magma/pipelined/tests/test_ue_mac.py @@ -16,24 +16,23 @@ from concurrent.futures import Future from magma.pipelined.app.ue_mac import UEMacAddressController +from magma.pipelined.bridge_util import BridgeTools +from magma.pipelined.openflow.magma_match import MagmaMatch +from magma.pipelined.tests.app.flow_query import RyuDirectFlowQuery as FlowQuery from magma.pipelined.tests.app.packet_builder import EtherPacketBuilder from magma.pipelined.tests.app.packet_injector import ScapyPacketInjector from magma.pipelined.tests.app.start_pipelined import ( - TestSetup, PipelinedController, + TestSetup, ) -from magma.pipelined.openflow.magma_match import MagmaMatch -from magma.pipelined.tests.app.flow_query import RyuDirectFlowQuery \ - as FlowQuery -from magma.pipelined.bridge_util import BridgeTools from magma.pipelined.tests.pipelined_test_util import ( + FlowTest, + FlowVerifier, + SnapshotVerifier, + create_service_manager, start_ryu_app_thread, stop_ryu_app_thread, - create_service_manager, wait_after_send, - FlowVerifier, - FlowTest, - SnapshotVerifier, ) diff --git a/lte/gateway/python/magma/pipelined/tests/test_ue_passthrough.py b/lte/gateway/python/magma/pipelined/tests/test_ue_passthrough.py index 3fe8ad1b4340..69b255fa5940 100644 --- a/lte/gateway/python/magma/pipelined/tests/test_ue_passthrough.py +++ b/lte/gateway/python/magma/pipelined/tests/test_ue_passthrough.py @@ -15,32 +15,34 @@ from concurrent.futures import Future from lte.protos.mconfig.mconfigs_pb2 import PipelineD +from magma.pipelined.app.inout import EGRESS, INGRESS from magma.pipelined.app.ue_mac import UEMacAddressController -from magma.pipelined.app.inout import INGRESS, EGRESS -from magma.pipelined.app.ue_mac import UEMacAddressController -from magma.pipelined.tests.app.packet_builder import EtherPacketBuilder, \ - UDPPacketBuilder, ARPPacketBuilder, DHCPPacketBuilder +from magma.pipelined.bridge_util import BridgeTools +from magma.pipelined.openflow.magma_match import MagmaMatch +from magma.pipelined.tests.app.flow_query import RyuDirectFlowQuery as FlowQuery +from magma.pipelined.tests.app.packet_builder import ( + ARPPacketBuilder, + DHCPPacketBuilder, + EtherPacketBuilder, + UDPPacketBuilder, +) from magma.pipelined.tests.app.packet_injector import ScapyPacketInjector from magma.pipelined.tests.app.start_pipelined import ( - TestSetup, PipelinedController, + TestSetup, ) -from magma.pipelined.openflow.magma_match import MagmaMatch -from magma.pipelined.tests.app.flow_query import RyuDirectFlowQuery \ - as FlowQuery -from ryu.ofproto.ofproto_v1_4 import OFPP_LOCAL -from magma.pipelined.bridge_util import BridgeTools from magma.pipelined.tests.pipelined_test_util import ( - start_ryu_app_thread, - stop_ryu_app_thread, - create_service_manager, - wait_after_send, - FlowVerifier, FlowTest, + FlowVerifier, SnapshotVerifier, + create_service_manager, fake_inout_setup, + start_ryu_app_thread, + stop_ryu_app_thread, + wait_after_send, ) from ryu.lib import hub +from ryu.ofproto.ofproto_v1_4 import OFPP_LOCAL class UEMacAddressTest(unittest.TestCase): diff --git a/lte/gateway/python/magma/pipelined/tests/test_uplink_bridge.py b/lte/gateway/python/magma/pipelined/tests/test_uplink_bridge.py index 3234d3c77efb..b9a930708994 100644 --- a/lte/gateway/python/magma/pipelined/tests/test_uplink_bridge.py +++ b/lte/gateway/python/magma/pipelined/tests/test_uplink_bridge.py @@ -10,27 +10,27 @@ See the License for the specific language governing permissions and limitations under the License. """ +import logging import subprocess import unittest import warnings from concurrent.futures import Future -import logging -from ryu.lib import hub +from magma.pipelined.bridge_util import BridgeTools from magma.pipelined.tests.app.start_pipelined import ( - TestSetup, PipelinedController, + TestSetup, ) -from magma.pipelined.bridge_util import BridgeTools from magma.pipelined.tests.pipelined_test_util import ( - start_ryu_app_thread, - stop_ryu_app_thread, - create_service_manager, assert_bridge_snapshot_match, - get_ovsdb_port_tag, - get_iface_ipv4, + create_service_manager, get_iface_gw_ipv4, + get_iface_ipv4, + get_ovsdb_port_tag, + start_ryu_app_thread, + stop_ryu_app_thread, ) +from ryu.lib import hub class UplinkBridgeTest(unittest.TestCase): diff --git a/lte/gateway/python/magma/pipelined/tests/test_vlan_learn.py b/lte/gateway/python/magma/pipelined/tests/test_vlan_learn.py index d95e2c0cce50..8f108bd0fae6 100644 --- a/lte/gateway/python/magma/pipelined/tests/test_vlan_learn.py +++ b/lte/gateway/python/magma/pipelined/tests/test_vlan_learn.py @@ -15,19 +15,18 @@ import warnings from concurrent.futures import Future -from ryu.lib import hub - +from magma.pipelined.bridge_util import BridgeTools from magma.pipelined.tests.app.start_pipelined import ( - TestSetup, PipelinedController, + TestSetup, ) -from magma.pipelined.bridge_util import BridgeTools from magma.pipelined.tests.pipelined_test_util import ( + assert_bridge_snapshot_match, + create_service_manager, start_ryu_app_thread, stop_ryu_app_thread, - create_service_manager, - assert_bridge_snapshot_match, ) +from ryu.lib import hub class VlanLearnTest(unittest.TestCase): diff --git a/lte/gateway/python/magma/pipelined/tunnel_id_store.py b/lte/gateway/python/magma/pipelined/tunnel_id_store.py index 1635e8e34e6d..82ae6d06e34a 100644 --- a/lte/gateway/python/magma/pipelined/tunnel_id_store.py +++ b/lte/gateway/python/magma/pipelined/tunnel_id_store.py @@ -14,8 +14,10 @@ from magma.common.redis.client import get_default_client from magma.common.redis.containers import RedisHashDict -from magma.common.redis.serializers import get_json_deserializer, \ - get_json_serializer +from magma.common.redis.serializers import ( + get_json_deserializer, + get_json_serializer, +) class TunnelToTunnelMapper: diff --git a/lte/gateway/python/magma/pipelined/utils.py b/lte/gateway/python/magma/pipelined/utils.py index 8a0fc093309b..7f08baa0edfa 100644 --- a/lte/gateway/python/magma/pipelined/utils.py +++ b/lte/gateway/python/magma/pipelined/utils.py @@ -11,10 +11,12 @@ limitations under the License. """ import logging + from magma.pipelined.openflow import flows from ryu import cfg from ryu.lib.ovs import bridge + class Utils: DROP_PRIORITY = flows.MINIMUM_PRIORITY + 1 # For allowing unlcassified flows for app/service type rules. diff --git a/setup.cfg b/setup.cfg index 411ec724dce0..8e943c985d7b 100644 --- a/setup.cfg +++ b/setup.cfg @@ -3,3 +3,7 @@ ignore = WPS115, # Found upper-case constant in a class WPS318, # Found extra indentation WPS319 # Found bracket in wrong position + +[isort] +profile=wemake +src_paths=isort,test \ No newline at end of file From 3286b3c4ad6f7b5d69e218a73990a6cdc8315da4 Mon Sep 17 00:00:00 2001 From: Karthik Subraveti Date: Thu, 8 Apr 2021 16:57:43 -0700 Subject: [PATCH 71/91] Was missing upgrade_precheck tags for reading service configs (#6050) Signed-off-by: Karthik Subraveti --- .../playbooks/roles/services/tasks/main.yml | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/orc8r/cloud/deploy/orc8r_deployer/docker/root/scripts/playbooks/roles/services/tasks/main.yml b/orc8r/cloud/deploy/orc8r_deployer/docker/root/scripts/playbooks/roles/services/tasks/main.yml index aebaa60812fe..166a68035602 100644 --- a/orc8r/cloud/deploy/orc8r_deployer/docker/root/scripts/playbooks/roles/services/tasks/main.yml +++ b/orc8r/cloud/deploy/orc8r_deployer/docker/root/scripts/playbooks/roles/services/tasks/main.yml @@ -2,23 +2,31 @@ ansible.builtin.command: "orcl configure check -c service" register: result failed_when: result.rc != 0 - tags: install_precheck + tags: + - install_precheck + - upgrade_precheck - name: Open configuration file shell: "cat {{ config_dir }}/service.tfvars.json" register: result - tags: install_precheck + tags: + - install_precheck + - upgrade_precheck - name: Set Service Configs set_fact: service_configs: "{{ result.stdout | from_json }}" - tags: install_precheck + tags: + - install_precheck + - upgrade_precheck - name: Validate Orc8r deployment type assert: that: service_configs.orc8r_deployment_type in ['fwa', 'ffwa', 'all'] fail_msg: 'orc8r_deployment_type can only be one of these options [fwa, ffwa, all]' - tags: install_precheck + tags: + - install_precheck + - upgrade_precheck - name: Image tasks import_tasks: docker.yml From a6f7321a998860614146673076ed274a909132e4 Mon Sep 17 00:00:00 2001 From: Oriol Batalla Date: Thu, 8 Apr 2021 19:37:59 -0700 Subject: [PATCH 72/91] [feg] S8_proxy add QOS to CreateSessionResponse (#5978) Signed-off-by: Oriol Batalla --- cwf/gateway/go.mod | 2 +- cwf/gateway/go.sum | 6 + feg/cloud/go/protos/s8_proxy.pb.go | 191 +++++++++--------- feg/gateway/go.mod | 6 +- feg/gateway/go.sum | 33 +-- .../s8_proxy/servicers/mock_pgw/cs.go | 46 ++++- .../s8_proxy/servicers/mock_pgw/mock_pgw.go | 12 +- .../s8_proxy/servicers/proto_conversions.go | 114 +++++++++-- .../services/s8_proxy/servicers/s8_proxy.go | 3 +- .../s8_proxy/servicers/s8_proxy_test.go | 74 +++++-- .../services/s8_proxy/servicers/senders.go | 4 + feg/protos/s8_proxy.proto | 6 +- 12 files changed, 330 insertions(+), 167 deletions(-) diff --git a/cwf/gateway/go.mod b/cwf/gateway/go.mod index 2dca54b2982f..8d8647330c0c 100644 --- a/cwf/gateway/go.mod +++ b/cwf/gateway/go.mod @@ -40,7 +40,7 @@ require ( github.com/go-openapi/swag v0.19.5 github.com/go-redis/redis v6.15.5+incompatible github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b - github.com/golang/protobuf v1.4.3 + github.com/golang/protobuf v1.5.2 github.com/onsi/ginkgo v1.14.0 // indirect github.com/pkg/errors v0.9.1 github.com/prometheus/client_golang v1.5.1 diff --git a/cwf/gateway/go.sum b/cwf/gateway/go.sum index da3fd0ec118a..0f805d84c488 100644 --- a/cwf/gateway/go.sum +++ b/cwf/gateway/go.sum @@ -250,6 +250,8 @@ github.com/golang/protobuf v1.4.2 h1:+Z5KGCizgyZCbGh1KZqA0fcLLkwbsjIzS4aV2v7wJX0 github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= github.com/golang/protobuf v1.4.3 h1:JjCZWpVbqXDqFVmTfYWEVTMIYrL/NPdPSCHPJ0T/raM= github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= +github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= +github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= github.com/golang/snappy v0.0.0-20180518054509-2e65f85255db/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= github.com/golang/snappy v0.0.1/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= github.com/gomodule/redigo v2.0.0+incompatible/go.mod h1:B4C85qUVwatsJoIUNIfCRsp7qO0iAmpGFZ4EELWSbC4= @@ -577,6 +579,8 @@ github.com/wadey/gocovmerge v0.0.0-20160331181800-b5bfa59ec0ad/go.mod h1:Hy8o65+ github.com/warthog618/sms v0.3.0/go.mod h1:+bYZGeBxu003sxD5xhzsrIPBAjPBzTABsRTwSpd7ld4= github.com/wmnsk/go-gtp v0.7.15/go.mod h1:v1psjZ7skpPSDegH23Amg9rNufs0BoXNM+GBtW5t58I= github.com/wmnsk/go-gtp v0.7.17/go.mod h1:IQ5tTgk1EDaKLLAum2vzYheKX9JeRngM1fm9ohdXAac= +github.com/wmnsk/go-gtp v0.7.19/go.mod h1:qdjTIBWIWjsNJdGs1EpmbcLjakUDPP7nRcEfKr2g1GQ= +github.com/wmnsk/go-gtp v0.7.20/go.mod h1:qdjTIBWIWjsNJdGs1EpmbcLjakUDPP7nRcEfKr2g1GQ= github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU= github.com/xlab/treeprint v0.0.0-20180616005107-d6fb6747feb6/go.mod h1:ce1O1j6UtZfjr22oyGxGLbauSBp2YVXpARAosm7dHBg= github.com/xordataexchange/crypt v0.0.3-0.20170626215501-b2862e3d0a77/go.mod h1:aYKd//L2LvnjZzWKhF00oedf4jCCReLcmhLdhm1A27Q= @@ -768,6 +772,8 @@ google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2 google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= google.golang.org/protobuf v1.25.0 h1:Ejskq+SyPohKW+1uil0JJMtmHCgJPJ/qWTxr8qp+R4c= google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c= +google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= +google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= gopkg.in/DATA-DOG/go-sqlmock.v1 v1.3.0 h1:FVCohIoYO7IJoDDVpV2pdq7SgrMH6wHnuTyrdrxJNoY= gopkg.in/DATA-DOG/go-sqlmock.v1 v1.3.0/go.mod h1:OdE7CF6DbADk7lN8LIKRzRJTTZXIjtWgA5THM5lhBAw= gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw= diff --git a/feg/cloud/go/protos/s8_proxy.pb.go b/feg/cloud/go/protos/s8_proxy.pb.go index 906cfae96067..b07fb0be41c6 100644 --- a/feg/cloud/go/protos/s8_proxy.pb.go +++ b/feg/cloud/go/protos/s8_proxy.pb.go @@ -452,8 +452,9 @@ func (m *ServingNetwork) GetMnc() string { type BearerContext struct { Id uint32 `protobuf:"varint,1,opt,name=id,proto3" json:"id,omitempty"` UserPlaneFteid *Fteid `protobuf:"bytes,2,opt,name=user_plane_fteid,json=userPlaneFteid,proto3" json:"user_plane_fteid,omitempty"` - Qos *QosInformation `protobuf:"bytes,3,opt,name=qos,proto3" json:"qos,omitempty"` - ChargingId uint32 `protobuf:"varint,4,opt,name=charging_id,json=chargingId,proto3" json:"charging_id,omitempty"` + ValidQos bool `protobuf:"varint,3,opt,name=valid_qos,json=validQos,proto3" json:"valid_qos,omitempty"` + Qos *QosInformation `protobuf:"bytes,4,opt,name=qos,proto3" json:"qos,omitempty"` + ChargingId uint32 `protobuf:"varint,5,opt,name=charging_id,json=chargingId,proto3" json:"charging_id,omitempty"` XXX_NoUnkeyedLiteral struct{} `json:"-"` XXX_unrecognized []byte `json:"-"` XXX_sizecache int32 `json:"-"` @@ -498,6 +499,13 @@ func (m *BearerContext) GetUserPlaneFteid() *Fteid { return nil } +func (m *BearerContext) GetValidQos() bool { + if m != nil { + return m.ValidQos + } + return false +} + func (m *BearerContext) GetQos() *QosInformation { if m != nil { return m.Qos @@ -1149,95 +1157,96 @@ func init() { func init() { proto.RegisterFile("feg/protos/s8_proxy.proto", fileDescriptor_8a775e17ac280154) } var fileDescriptor_8a775e17ac280154 = []byte{ - // 1402 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xac, 0x57, 0xdd, 0x6e, 0xdb, 0x36, - 0x14, 0x8e, 0xfc, 0x13, 0xdb, 0x27, 0x91, 0xa3, 0xb2, 0x59, 0xa3, 0xfe, 0x00, 0x4d, 0xd5, 0x75, - 0xcd, 0x3a, 0x2c, 0xc9, 0xd2, 0x20, 0xe8, 0x06, 0x14, 0x9b, 0x93, 0xb8, 0x5d, 0x80, 0xd4, 0xf3, - 0x68, 0x27, 0x03, 0x7a, 0x23, 0xd0, 0x14, 0xa3, 0x12, 0x93, 0x25, 0x85, 0x94, 0x93, 0x66, 0xc0, - 0x80, 0xdd, 0x0e, 0xd8, 0xcd, 0x1e, 0x63, 0x97, 0xbb, 0xd9, 0x33, 0xed, 0x6e, 0xaf, 0x30, 0x90, - 0xa2, 0x1d, 0x39, 0x99, 0xb1, 0x16, 0xe8, 0x95, 0x0f, 0xbf, 0xf3, 0x91, 0x3a, 0x3c, 0xfc, 0x0e, - 0x0f, 0x0d, 0xb7, 0x4f, 0x58, 0xb8, 0x91, 0x8a, 0x24, 0x4b, 0xe4, 0x86, 0x7c, 0xe6, 0xa7, 0x22, - 0x79, 0x7b, 0xb1, 0xae, 0xc7, 0xa8, 0x31, 0x24, 0xe1, 0x90, 0xac, 0x9f, 0xb0, 0xd0, 0xfb, 0xa7, - 0x0a, 0x2b, 0x7b, 0x82, 0x91, 0x8c, 0xf5, 0x98, 0x94, 0x3c, 0x89, 0x31, 0x3b, 0x1d, 0x31, 0x99, - 0x75, 0xc3, 0x73, 0x74, 0x07, 0xea, 0x69, 0x78, 0xde, 0x0a, 0x02, 0x21, 0x5d, 0x6b, 0xd5, 0x5a, - 0x6b, 0xe0, 0xc9, 0x18, 0x21, 0xa8, 0xf0, 0xa1, 0xe4, 0x6e, 0x49, 0xe3, 0xda, 0x46, 0xb7, 0x60, - 0x7e, 0x28, 0xb9, 0x0c, 0x62, 0xb7, 0xac, 0x51, 0x33, 0x42, 0x0e, 0x94, 0x87, 0x8c, 0xbb, 0x15, - 0x0d, 0x2a, 0x13, 0xed, 0xc2, 0x92, 0x64, 0xe2, 0x8c, 0xc7, 0xa1, 0x1f, 0xb3, 0xec, 0x3c, 0x11, - 0x3f, 0xba, 0xd5, 0x55, 0x6b, 0x6d, 0x61, 0xeb, 0xf6, 0xfa, 0x24, 0xb4, 0xf5, 0x5e, 0xce, 0xe8, - 0xe4, 0x04, 0xdc, 0x94, 0x53, 0x63, 0xb4, 0x0d, 0xe5, 0x51, 0xc4, 0xdd, 0x79, 0x3d, 0xcf, 0x2b, - 0xcc, 0x3b, 0x92, 0x4c, 0x1c, 0x26, 0x94, 0x64, 0x3c, 0x89, 0x0f, 0xe2, 0x93, 0x44, 0x0c, 0xb5, - 0x89, 0x15, 0x1d, 0x7d, 0x0e, 0x75, 0x41, 0x32, 0x3f, 0xbb, 0x48, 0x99, 0x5b, 0x5b, 0xb5, 0xd6, - 0x9a, 0x5b, 0xa8, 0x30, 0x15, 0xb7, 0xfa, 0xfd, 0x8b, 0x94, 0xe1, 0x9a, 0x20, 0x99, 0x32, 0x14, - 0x3d, 0x0d, 0xe2, 0x9c, 0x5e, 0xbf, 0x46, 0xef, 0xee, 0x77, 0x72, 0x7a, 0x1a, 0xc4, 0x9a, 0xfe, - 0x05, 0x94, 0x53, 0x42, 0xdc, 0x86, 0x8e, 0xe9, 0x7e, 0x91, 0x19, 0xc4, 0x2a, 0x6f, 0x4c, 0xca, - 0x56, 0x14, 0x99, 0xd8, 0xb0, 0xe2, 0xaa, 0xe4, 0x90, 0x34, 0x76, 0x21, 0x4f, 0x0e, 0x49, 0x63, - 0xf4, 0x10, 0x2a, 0x64, 0x38, 0x10, 0xee, 0x82, 0x5e, 0x65, 0xa9, 0xb0, 0x4a, 0x6b, 0x38, 0x10, - 0x58, 0x3b, 0xd1, 0x1e, 0x34, 0x25, 0x8b, 0x18, 0x55, 0x0b, 0xf9, 0xc3, 0x24, 0x60, 0xee, 0xa2, - 0x0e, 0xef, 0xde, 0x54, 0x02, 0x0d, 0xe1, 0x55, 0x12, 0x30, 0x1d, 0xa8, 0x2d, 0x8b, 0x10, 0xfa, - 0x1a, 0x9a, 0x03, 0x46, 0x04, 0x13, 0x3e, 0x4d, 0xe2, 0x8c, 0xbd, 0xcd, 0x5c, 0x5b, 0x7f, 0xd3, - 0x2d, 0x2c, 0xb2, 0xab, 0x09, 0x7b, 0xb9, 0x1f, 0xdb, 0x83, 0xe2, 0x10, 0xdd, 0x03, 0xa0, 0x3e, - 0x09, 0xcf, 0xfd, 0x8c, 0xf1, 0xc0, 0x6d, 0xae, 0x5a, 0x6b, 0x36, 0xae, 0xd3, 0x56, 0x78, 0xde, - 0x67, 0x3c, 0x40, 0x8f, 0x61, 0x89, 0xc7, 0x01, 0xcf, 0x77, 0xeb, 0x9f, 0x44, 0x24, 0x74, 0x97, - 0x56, 0xad, 0xb5, 0x45, 0xdc, 0xbc, 0x84, 0x5f, 0x44, 0x24, 0x44, 0x5f, 0x82, 0x4b, 0xdf, 0x10, - 0x11, 0x2a, 0x3d, 0x28, 0x83, 0xd0, 0x8c, 0x09, 0x2e, 0x33, 0x4e, 0xa5, 0xeb, 0xe8, 0xc4, 0xac, - 0x8c, 0xfd, 0x7b, 0xd3, 0x6e, 0xb4, 0x09, 0x8d, 0x8c, 0x0f, 0x99, 0xff, 0x53, 0x12, 0x33, 0xf7, - 0x86, 0x8e, 0xfe, 0x66, 0x21, 0xfa, 0x3e, 0x1f, 0xb2, 0xd7, 0x49, 0xcc, 0x70, 0x3d, 0x33, 0x96, - 0xf7, 0xa7, 0x05, 0x2b, 0x33, 0x24, 0xa2, 0x0e, 0x23, 0x22, 0x54, 0x8b, 0xdd, 0xc6, 0xca, 0x44, - 0x4d, 0x28, 0xd1, 0x5c, 0xe5, 0x36, 0x2e, 0x51, 0xae, 0x18, 0x92, 0x50, 0x2d, 0x70, 0x1b, 0x2b, - 0x53, 0x21, 0x82, 0x50, 0xad, 0x6e, 0x1b, 0x2b, 0x53, 0x21, 0x19, 0xa1, 0x5a, 0xd1, 0x36, 0x56, - 0xa6, 0x42, 0x18, 0xcd, 0xb5, 0x6a, 0x63, 0x65, 0xa2, 0x65, 0xa8, 0xbe, 0x62, 0x9d, 0x01, 0xd7, - 0x22, 0xb4, 0x71, 0x3e, 0x50, 0x15, 0xd4, 0xce, 0xe1, 0xba, 0x86, 0xcd, 0xc8, 0xdb, 0x86, 0xe6, - 0x74, 0x35, 0xe8, 0x9a, 0xa2, 0xd4, 0x94, 0xa5, 0x32, 0x35, 0x12, 0x53, 0x53, 0x90, 0xca, 0xf4, - 0xfe, 0xb0, 0xc0, 0x9e, 0x3a, 0x3e, 0xb5, 0x1b, 0x1e, 0x98, 0xed, 0x95, 0x78, 0x80, 0xbe, 0x02, - 0x67, 0x24, 0x99, 0xf0, 0xd3, 0x88, 0xc4, 0xcc, 0x3f, 0xd1, 0xa7, 0x58, 0xd2, 0x49, 0x74, 0x0a, - 0x49, 0x7c, 0xa1, 0x70, 0xdc, 0x54, 0xcc, 0xae, 0x22, 0xea, 0x31, 0xfa, 0x0c, 0xca, 0xa7, 0x89, - 0xd4, 0x99, 0x98, 0xae, 0xdb, 0xef, 0x13, 0x39, 0x55, 0x76, 0xa7, 0x89, 0x44, 0xf7, 0x61, 0x61, - 0x72, 0xc2, 0x3c, 0x30, 0xc9, 0x82, 0x31, 0x74, 0x10, 0x78, 0xbf, 0x97, 0xa0, 0x39, 0x3d, 0x51, - 0x6d, 0x28, 0xa5, 0x7c, 0x7c, 0x18, 0x29, 0xe5, 0xe8, 0x11, 0x34, 0x53, 0xc1, 0x13, 0xc1, 0xb3, - 0x0b, 0x3f, 0x62, 0x67, 0x2c, 0x32, 0x07, 0x63, 0x8f, 0xd1, 0x43, 0x05, 0xa2, 0xa7, 0xf0, 0x51, - 0x2a, 0x18, 0x1b, 0xa6, 0x5a, 0x77, 0x94, 0xa4, 0x64, 0xc0, 0x23, 0x9e, 0x5d, 0x98, 0x53, 0x5b, - 0xbe, 0x74, 0xee, 0x4d, 0x7c, 0x4a, 0x83, 0x85, 0x49, 0x67, 0xa3, 0x28, 0x66, 0x62, 0x3c, 0x2f, - 0x0f, 0x77, 0xe5, 0xd2, 0x7f, 0x5c, 0x74, 0xab, 0x40, 0x4f, 0x29, 0x1f, 0x9f, 0xf7, 0x29, 0xe5, - 0xe8, 0x01, 0x94, 0xc3, 0x81, 0x30, 0x77, 0xd3, 0xb5, 0x0a, 0x56, 0x3e, 0x45, 0x51, 0x45, 0x5e, - 0x9b, 0x41, 0x19, 0x0e, 0x84, 0xb7, 0x09, 0x15, 0x35, 0x40, 0x37, 0xa1, 0x3a, 0x10, 0xfe, 0x28, - 0xd2, 0xa9, 0xa8, 0xe0, 0xca, 0x40, 0x1c, 0x45, 0x06, 0x0c, 0xf2, 0x14, 0x68, 0x70, 0x3f, 0xf2, - 0x7e, 0x86, 0xe5, 0xff, 0xba, 0x69, 0xd0, 0x03, 0x58, 0xe4, 0xe9, 0xd9, 0xb6, 0x4f, 0x72, 0x8f, - 0x91, 0xcd, 0x82, 0xc2, 0x0c, 0xd9, 0x50, 0x76, 0x26, 0x94, 0xd2, 0x84, 0xb2, 0x33, 0xa6, 0xdc, - 0x07, 0x3d, 0xf4, 0x53, 0xc1, 0x4e, 0xf8, 0x5b, 0x93, 0x4d, 0x50, 0x50, 0x57, 0x23, 0x1e, 0x81, - 0xfa, 0xb8, 0xe0, 0xd0, 0x43, 0xb0, 0x03, 0x16, 0x65, 0xc4, 0x97, 0x8c, 0x26, 0x71, 0x90, 0x7f, - 0xb3, 0x8a, 0x17, 0x35, 0xd8, 0xcb, 0x31, 0xb4, 0x09, 0xcb, 0x01, 0xb9, 0x88, 0x78, 0xf8, 0x26, - 0xf3, 0x25, 0xd1, 0xfd, 0x40, 0xd5, 0xa9, 0x39, 0x56, 0x34, 0xf6, 0xf5, 0xb4, 0x4b, 0x2d, 0xed, - 0x11, 0xa8, 0xe6, 0xf2, 0xfb, 0x30, 0x5b, 0x42, 0x50, 0xd1, 0xa2, 0xcf, 0xf7, 0xa2, 0x6d, 0xef, - 0xef, 0x12, 0xb8, 0x57, 0x5a, 0xa2, 0x4c, 0x93, 0x58, 0x32, 0xd5, 0x13, 0x8b, 0x0d, 0xc1, 0x7a, - 0xe7, 0x86, 0x50, 0x7a, 0x8f, 0x86, 0xf0, 0x18, 0x96, 0x48, 0x1a, 0xfb, 0x82, 0xc9, 0x4c, 0x70, - 0x7d, 0x57, 0x9b, 0xe8, 0x9a, 0x24, 0x55, 0xa1, 0x8c, 0xd1, 0x2b, 0x97, 0x6f, 0xe5, 0xca, 0xe5, - 0xbb, 0x09, 0x0b, 0xd4, 0x4f, 0xc3, 0x73, 0x53, 0xd5, 0xd5, 0x19, 0x55, 0xdd, 0xa0, 0xdd, 0xf0, - 0x3c, 0xcf, 0xe8, 0xf5, 0x6e, 0x30, 0xff, 0x7e, 0xdd, 0x60, 0x13, 0x1a, 0x61, 0x96, 0xfa, 0x4c, - 0x88, 0x64, 0x2c, 0xec, 0xe2, 0x5d, 0xfc, 0x32, 0x4b, 0xdb, 0xca, 0x85, 0xeb, 0xa1, 0xb1, 0xbc, - 0xbf, 0x2c, 0x58, 0xd9, 0x67, 0x11, 0xfb, 0x10, 0xaf, 0x8f, 0xbb, 0xd0, 0x30, 0xe1, 0x4f, 0xce, - 0xb3, 0x9e, 0x03, 0x07, 0xc1, 0x87, 0xce, 0x95, 0x77, 0x08, 0xee, 0x95, 0xb8, 0x2f, 0x25, 0x32, - 0x95, 0x06, 0xeb, 0x5d, 0xd2, 0xf0, 0x1c, 0x16, 0xda, 0xf4, 0x4d, 0x62, 0x36, 0xff, 0xbe, 0x3b, - 0xf7, 0x9a, 0xb0, 0x98, 0x4f, 0xcf, 0x63, 0xf0, 0xb6, 0xa0, 0x3e, 0xfe, 0x88, 0xea, 0x33, 0x94, - 0x8c, 0x24, 0x33, 0xd7, 0x68, 0x3e, 0xd0, 0xbd, 0x42, 0x86, 0x93, 0x5e, 0x21, 0xc3, 0x27, 0xdf, - 0x40, 0xcd, 0x88, 0x17, 0x01, 0xcc, 0x1f, 0x75, 0x8e, 0x7a, 0xed, 0x7d, 0x67, 0x0e, 0xd5, 0xa1, - 0x72, 0xd0, 0x3d, 0xde, 0x76, 0x2c, 0x63, 0xed, 0x38, 0x25, 0xe5, 0x57, 0xd8, 0xf1, 0x8e, 0x53, - 0x46, 0x0d, 0xa8, 0x76, 0x92, 0xf8, 0xa0, 0xeb, 0x54, 0x9f, 0xfc, 0x6a, 0x41, 0xcd, 0xbc, 0x9f, - 0xd0, 0x22, 0xd4, 0x71, 0xbb, 0xd7, 0xc6, 0xc7, 0x7a, 0x91, 0x06, 0x54, 0x8f, 0xfa, 0xb8, 0xd5, - 0x71, 0x2c, 0x65, 0xbe, 0x6c, 0x2b, 0xb3, 0xa4, 0x16, 0xfc, 0xe1, 0xb0, 0xd5, 0x71, 0xca, 0xa8, - 0x06, 0xe5, 0x97, 0xad, 0x8e, 0x53, 0x51, 0xd0, 0xb7, 0xbd, 0x6e, 0xcb, 0xa9, 0xaa, 0x6f, 0xb4, - 0xf3, 0x39, 0xf3, 0x68, 0x01, 0x6a, 0xc7, 0x07, 0xb8, 0x7f, 0xd4, 0x3a, 0x74, 0x6a, 0xe8, 0x06, - 0xd8, 0xb9, 0xc3, 0xef, 0xec, 0xfa, 0x07, 0xdf, 0xf5, 0x9d, 0xba, 0x5a, 0xf3, 0xb0, 0xdf, 0xf6, - 0x5f, 0x39, 0x0d, 0x34, 0x0f, 0xa5, 0x0e, 0x76, 0xe0, 0xc9, 0x6f, 0x16, 0xdc, 0xb8, 0xf6, 0xfa, - 0x41, 0x9f, 0x80, 0xd7, 0xea, 0x76, 0xd4, 0x4b, 0xf8, 0x8c, 0x07, 0x2c, 0xf0, 0xe5, 0x68, 0x20, - 0xa9, 0xe0, 0xe6, 0xc6, 0x67, 0x82, 0x9f, 0x70, 0x16, 0x38, 0x73, 0xe8, 0x63, 0x58, 0x1d, 0x4a, - 0x5f, 0x51, 0xa7, 0x18, 0x71, 0x92, 0x5d, 0xb2, 0x2c, 0xf4, 0x29, 0x3c, 0x32, 0x6f, 0xd7, 0xff, - 0xa1, 0x96, 0xb6, 0x7e, 0x29, 0x41, 0xad, 0xf7, 0xac, 0xab, 0x5e, 0xe0, 0xe8, 0x35, 0xd8, 0x53, - 0x97, 0x0b, 0x2a, 0x3e, 0x5d, 0x67, 0xbc, 0xc4, 0xef, 0x3c, 0x9c, 0xcd, 0x99, 0xe8, 0xce, 0x9b, - 0x53, 0x6b, 0x4f, 0xa9, 0x72, 0x6a, 0xed, 0x19, 0x75, 0x36, 0xb5, 0xf6, 0x2c, 0x4d, 0x7b, 0x73, - 0xe8, 0x39, 0xd4, 0x7b, 0x2c, 0x0e, 0x94, 0xd0, 0xd0, 0xad, 0xc2, 0x94, 0x82, 0x70, 0xef, 0xac, - 0x5c, 0xc3, 0x8d, 0x22, 0xe7, 0x76, 0xef, 0xbe, 0xbe, 0xad, 0x7d, 0x1b, 0xea, 0x5f, 0x09, 0x8d, - 0x92, 0x51, 0xb0, 0x11, 0x26, 0xe6, 0xef, 0xc9, 0x60, 0x5e, 0xff, 0x3e, 0xfd, 0x37, 0x00, 0x00, - 0xff, 0xff, 0x6c, 0x25, 0x0b, 0xae, 0xb3, 0x0c, 0x00, 0x00, + // 1423 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xac, 0x57, 0xdd, 0x6e, 0x1b, 0x37, + 0x16, 0xf6, 0xe8, 0xc7, 0x1a, 0x1d, 0x7b, 0xe4, 0x09, 0xe3, 0x8d, 0x27, 0x3f, 0x40, 0x9c, 0xc9, + 0x66, 0xe3, 0xcd, 0x62, 0x6d, 0xaf, 0x63, 0x18, 0xd9, 0x05, 0x82, 0xad, 0x6c, 0x2b, 0xa9, 0x01, + 0x47, 0x55, 0x28, 0xd9, 0x05, 0x72, 0x33, 0xa0, 0x38, 0xf4, 0x84, 0xe8, 0x68, 0x66, 0x4c, 0x8e, + 0xec, 0xb8, 0x40, 0x81, 0xde, 0x16, 0xe8, 0x4d, 0x1f, 0xa5, 0x37, 0x7d, 0x84, 0x3e, 0x4b, 0xef, + 0xfa, 0x0a, 0x05, 0x39, 0x94, 0x3c, 0xb2, 0x6b, 0x34, 0x01, 0x72, 0xa5, 0xc3, 0xef, 0x7c, 0xe4, + 0x1c, 0x1e, 0x7e, 0x87, 0x87, 0x82, 0xbb, 0x27, 0x2c, 0xda, 0xc8, 0x44, 0x9a, 0xa7, 0x72, 0x43, + 0xbe, 0x08, 0x32, 0x91, 0x7e, 0xb8, 0x58, 0xd7, 0x63, 0xd4, 0x1c, 0x91, 0x68, 0x44, 0xd6, 0x4f, + 0x58, 0xe4, 0xff, 0x5e, 0x87, 0x95, 0x3d, 0xc1, 0x48, 0xce, 0xfa, 0x4c, 0x4a, 0x9e, 0x26, 0x98, + 0x9d, 0x8e, 0x99, 0xcc, 0x7b, 0xd1, 0x39, 0xba, 0x07, 0x76, 0x16, 0x9d, 0xb7, 0xc3, 0x50, 0x48, + 0xcf, 0x5a, 0xb5, 0xd6, 0x9a, 0x78, 0x3a, 0x46, 0x08, 0x6a, 0x7c, 0x24, 0xb9, 0x57, 0xd1, 0xb8, + 0xb6, 0xd1, 0x1d, 0x98, 0x1f, 0x49, 0x2e, 0xc3, 0xc4, 0xab, 0x6a, 0xd4, 0x8c, 0x90, 0x0b, 0xd5, + 0x11, 0xe3, 0x5e, 0x4d, 0x83, 0xca, 0x44, 0xbb, 0xb0, 0x24, 0x99, 0x38, 0xe3, 0x49, 0x14, 0x24, + 0x2c, 0x3f, 0x4f, 0xc5, 0x37, 0x5e, 0x7d, 0xd5, 0x5a, 0x5b, 0xd8, 0xba, 0xbb, 0x3e, 0x0d, 0x6d, + 0xbd, 0x5f, 0x30, 0xba, 0x05, 0x01, 0xb7, 0xe4, 0xcc, 0x18, 0x6d, 0x43, 0x75, 0x1c, 0x73, 0x6f, + 0x5e, 0xcf, 0xf3, 0x4b, 0xf3, 0x8e, 0x24, 0x13, 0x87, 0x29, 0x25, 0x39, 0x4f, 0x93, 0x83, 0xe4, + 0x24, 0x15, 0x23, 0x6d, 0x62, 0x45, 0x47, 0xff, 0x06, 0x5b, 0x90, 0x3c, 0xc8, 0x2f, 0x32, 0xe6, + 0x35, 0x56, 0xad, 0xb5, 0xd6, 0x16, 0x2a, 0x4d, 0xc5, 0xed, 0xc1, 0xe0, 0x22, 0x63, 0xb8, 0x21, + 0x48, 0xae, 0x0c, 0x45, 0xcf, 0xc2, 0xa4, 0xa0, 0xdb, 0xd7, 0xe8, 0xbd, 0xfd, 0x6e, 0x41, 0xcf, + 0xc2, 0x44, 0xd3, 0xff, 0x03, 0xd5, 0x8c, 0x10, 0xaf, 0xa9, 0x63, 0x7a, 0x58, 0x66, 0x86, 0x89, + 0xca, 0x1b, 0x93, 0xb2, 0x1d, 0xc7, 0x26, 0x36, 0xac, 0xb8, 0x2a, 0x39, 0x24, 0x4b, 0x3c, 0x28, + 0x92, 0x43, 0xb2, 0x04, 0x3d, 0x86, 0x1a, 0x19, 0x0d, 0x85, 0xb7, 0xa0, 0x57, 0x59, 0x2a, 0xad, + 0xd2, 0x1e, 0x0d, 0x05, 0xd6, 0x4e, 0xb4, 0x07, 0x2d, 0xc9, 0x62, 0x46, 0xd5, 0x42, 0xc1, 0x28, + 0x0d, 0x99, 0xb7, 0xa8, 0xc3, 0x7b, 0x30, 0x93, 0x40, 0x43, 0x78, 0x93, 0x86, 0x4c, 0x07, 0xea, + 0xc8, 0x32, 0x84, 0xfe, 0x0f, 0xad, 0x21, 0x23, 0x82, 0x89, 0x80, 0xa6, 0x49, 0xce, 0x3e, 0xe4, + 0x9e, 0xa3, 0xbf, 0xe9, 0x95, 0x16, 0xd9, 0xd5, 0x84, 0xbd, 0xc2, 0x8f, 0x9d, 0x61, 0x79, 0x88, + 0x1e, 0x00, 0xd0, 0x80, 0x44, 0xe7, 0x41, 0xce, 0x78, 0xe8, 0xb5, 0x56, 0xad, 0x35, 0x07, 0xdb, + 0xb4, 0x1d, 0x9d, 0x0f, 0x18, 0x0f, 0xd1, 0x53, 0x58, 0xe2, 0x49, 0xc8, 0x8b, 0xdd, 0x06, 0x27, + 0x31, 0x89, 0xbc, 0xa5, 0x55, 0x6b, 0x6d, 0x11, 0xb7, 0x2e, 0xe1, 0x57, 0x31, 0x89, 0xd0, 0x7f, + 0xc1, 0xa3, 0xef, 0x89, 0x88, 0x94, 0x1e, 0x94, 0x41, 0x68, 0xce, 0x04, 0x97, 0x39, 0xa7, 0xd2, + 0x73, 0x75, 0x62, 0x56, 0x26, 0xfe, 0xbd, 0x59, 0x37, 0xda, 0x84, 0x66, 0xce, 0x47, 0x2c, 0xf8, + 0x36, 0x4d, 0x98, 0x77, 0x4b, 0x47, 0x7f, 0xbb, 0x14, 0xfd, 0x80, 0x8f, 0xd8, 0xbb, 0x34, 0x61, + 0xd8, 0xce, 0x8d, 0xe5, 0xff, 0x6c, 0xc1, 0xca, 0x0d, 0x12, 0x51, 0x87, 0x11, 0x13, 0xaa, 0xc5, + 0xee, 0x60, 0x65, 0xa2, 0x16, 0x54, 0x68, 0xa1, 0x72, 0x07, 0x57, 0x28, 0x57, 0x0c, 0x49, 0xa8, + 0x16, 0xb8, 0x83, 0x95, 0xa9, 0x10, 0x41, 0xa8, 0x56, 0xb7, 0x83, 0x95, 0xa9, 0x90, 0x9c, 0x50, + 0xad, 0x68, 0x07, 0x2b, 0x53, 0x21, 0x8c, 0x16, 0x5a, 0x75, 0xb0, 0x32, 0xd1, 0x32, 0xd4, 0xdf, + 0xb0, 0xee, 0x90, 0x6b, 0x11, 0x3a, 0xb8, 0x18, 0xa8, 0x0a, 0xea, 0x14, 0xb0, 0xad, 0x61, 0x33, + 0xf2, 0xb7, 0xa1, 0x35, 0x5b, 0x0d, 0xba, 0xa6, 0x28, 0x35, 0x65, 0xa9, 0x4c, 0x8d, 0x24, 0xd4, + 0x14, 0xa4, 0x32, 0xfd, 0x5f, 0x2d, 0x70, 0x66, 0x8e, 0x4f, 0xed, 0x86, 0x87, 0x66, 0x7b, 0x15, + 0x1e, 0xa2, 0xff, 0x81, 0x3b, 0x96, 0x4c, 0x04, 0x59, 0x4c, 0x12, 0x16, 0x9c, 0xe8, 0x53, 0xac, + 0xe8, 0x24, 0xba, 0xa5, 0x24, 0xbe, 0x52, 0x38, 0x6e, 0x29, 0x66, 0x4f, 0x11, 0xf5, 0x18, 0xdd, + 0x87, 0xe6, 0x19, 0x89, 0x79, 0x18, 0x9c, 0xa6, 0x52, 0xe7, 0xc3, 0xc6, 0xb6, 0x06, 0xde, 0xa6, + 0x12, 0xfd, 0x0b, 0xaa, 0x0a, 0xae, 0x5d, 0x2b, 0xea, 0xb7, 0xa9, 0x9c, 0xa9, 0xc9, 0xd3, 0x54, + 0xa2, 0x87, 0xb0, 0x30, 0x3d, 0x7e, 0x1e, 0x9a, 0xbc, 0xc1, 0x04, 0x3a, 0x08, 0xfd, 0x9f, 0x2a, + 0xd0, 0x9a, 0x9d, 0xa8, 0x76, 0x9b, 0x51, 0x3e, 0x39, 0xa9, 0x8c, 0x72, 0xf4, 0x04, 0x5a, 0x99, + 0xe0, 0xa9, 0xe0, 0xf9, 0x45, 0x10, 0xb3, 0x33, 0x16, 0x9b, 0x53, 0x73, 0x26, 0xe8, 0xa1, 0x02, + 0xd1, 0x73, 0xf8, 0x5b, 0x26, 0x18, 0x1b, 0x65, 0x5a, 0x94, 0x94, 0x64, 0x64, 0xc8, 0x63, 0x9e, + 0x5f, 0x98, 0x23, 0x5d, 0xbe, 0x74, 0xee, 0x4d, 0x7d, 0x4a, 0xa0, 0xa5, 0x49, 0x67, 0xe3, 0x38, + 0x61, 0x62, 0x32, 0xaf, 0x38, 0xf8, 0x95, 0x4b, 0xff, 0x71, 0xd9, 0xad, 0x02, 0x3d, 0xa5, 0x7c, + 0x22, 0x86, 0x53, 0xca, 0xd1, 0x23, 0xa8, 0x46, 0x43, 0x61, 0x2e, 0xae, 0x6b, 0xe5, 0xad, 0x7c, + 0x8a, 0xa2, 0x6e, 0x80, 0xc6, 0x0d, 0x94, 0xd1, 0x50, 0xf8, 0x9b, 0x50, 0x53, 0x03, 0x74, 0x1b, + 0xea, 0x43, 0x11, 0x8c, 0x63, 0x9d, 0x8a, 0x1a, 0xae, 0x0d, 0xc5, 0x51, 0x6c, 0xc0, 0xb0, 0x48, + 0x81, 0x06, 0xf7, 0x63, 0xff, 0x3b, 0x58, 0xfe, 0xb3, 0x6b, 0x08, 0x3d, 0x82, 0x45, 0x9e, 0x9d, + 0x6d, 0x07, 0xa4, 0xf0, 0x18, 0x4d, 0x2d, 0x28, 0xcc, 0x90, 0x0d, 0x65, 0x67, 0x4a, 0xa9, 0x4c, + 0x29, 0x3b, 0x13, 0xca, 0x43, 0xd0, 0xc3, 0x20, 0x13, 0xec, 0x84, 0x7f, 0x30, 0xd9, 0x04, 0x05, + 0xf5, 0x34, 0xe2, 0x13, 0xb0, 0x27, 0xd5, 0x88, 0x1e, 0x83, 0x13, 0xb2, 0x38, 0x27, 0x81, 0x64, + 0x34, 0x4d, 0xc2, 0xe2, 0x9b, 0x75, 0xbc, 0xa8, 0xc1, 0x7e, 0x81, 0xa1, 0x4d, 0x58, 0x0e, 0xc9, + 0x45, 0xcc, 0xa3, 0xf7, 0x79, 0x20, 0x89, 0x6e, 0x16, 0xaa, 0x88, 0xcd, 0xb1, 0xa2, 0x89, 0xaf, + 0xaf, 0x5d, 0x6a, 0x69, 0x9f, 0x40, 0xbd, 0xd0, 0xe6, 0xe7, 0xd9, 0x12, 0x82, 0x9a, 0xae, 0x88, + 0x62, 0x2f, 0xda, 0xf6, 0x7f, 0xab, 0x80, 0x77, 0xa5, 0x5f, 0xca, 0x2c, 0x4d, 0x24, 0x53, 0x0d, + 0xb3, 0xdc, 0x2d, 0xac, 0x8f, 0xee, 0x16, 0x95, 0x4f, 0xe8, 0x16, 0x4f, 0x61, 0x89, 0x64, 0x49, + 0x20, 0x98, 0xcc, 0x05, 0xd7, 0x17, 0xb9, 0x89, 0xae, 0x45, 0x32, 0x15, 0xca, 0x04, 0xbd, 0x72, + 0x33, 0xd7, 0xae, 0xdc, 0xcc, 0x9b, 0xb0, 0x40, 0x83, 0x2c, 0x3a, 0x37, 0x25, 0x5f, 0xbf, 0xa1, + 0xe4, 0x9b, 0xb4, 0x17, 0x9d, 0x17, 0x19, 0xbd, 0xde, 0x2a, 0xe6, 0x3f, 0xad, 0x55, 0x6c, 0x42, + 0x33, 0xca, 0xb3, 0x80, 0x09, 0x91, 0x4e, 0x84, 0x5d, 0xbe, 0xa8, 0x5f, 0xe7, 0x59, 0x47, 0xb9, + 0xb0, 0x1d, 0x19, 0xcb, 0xff, 0xc5, 0x82, 0x95, 0x7d, 0x16, 0xb3, 0xcf, 0xf1, 0x34, 0xb9, 0x0f, + 0x4d, 0x13, 0xfe, 0xf4, 0x3c, 0xed, 0x02, 0x38, 0x08, 0x3f, 0x77, 0xae, 0xfc, 0x43, 0xf0, 0xae, + 0xc4, 0x7d, 0x29, 0x91, 0x99, 0x34, 0x58, 0x1f, 0x93, 0x86, 0x97, 0xb0, 0xd0, 0xa1, 0xef, 0x53, + 0xb3, 0xf9, 0x4f, 0xdd, 0xb9, 0xdf, 0x82, 0xc5, 0x62, 0x7a, 0x11, 0x83, 0xbf, 0x05, 0xf6, 0xe4, + 0x23, 0xaa, 0x09, 0x51, 0x32, 0x96, 0xcc, 0x5c, 0xa3, 0xc5, 0x40, 0x37, 0x12, 0x19, 0x4d, 0x1b, + 0x89, 0x8c, 0x9e, 0x7d, 0x01, 0x0d, 0x23, 0x5e, 0x04, 0x30, 0x7f, 0xd4, 0x3d, 0xea, 0x77, 0xf6, + 0xdd, 0x39, 0x64, 0x43, 0xed, 0xa0, 0x77, 0xbc, 0xed, 0x5a, 0xc6, 0xda, 0x71, 0x2b, 0xca, 0xaf, + 0xb0, 0xe3, 0x1d, 0xb7, 0x8a, 0x9a, 0x50, 0xef, 0xa6, 0xc9, 0x41, 0xcf, 0xad, 0x3f, 0xfb, 0xc1, + 0x82, 0x86, 0x79, 0x5c, 0xa1, 0x45, 0xb0, 0x71, 0xa7, 0xdf, 0xc1, 0xc7, 0x7a, 0x91, 0x26, 0xd4, + 0x8f, 0x06, 0xb8, 0xdd, 0x75, 0x2d, 0x65, 0xbe, 0xee, 0x28, 0xb3, 0xa2, 0x16, 0xfc, 0xfa, 0xb0, + 0xdd, 0x75, 0xab, 0xa8, 0x01, 0xd5, 0xd7, 0xed, 0xae, 0x5b, 0x53, 0xd0, 0x97, 0xfd, 0x5e, 0xdb, + 0xad, 0xab, 0x6f, 0x74, 0x8a, 0x39, 0xf3, 0x68, 0x01, 0x1a, 0xc7, 0x07, 0x78, 0x70, 0xd4, 0x3e, + 0x74, 0x1b, 0xe8, 0x16, 0x38, 0x85, 0x23, 0xe8, 0xee, 0x06, 0x07, 0x5f, 0x0d, 0x5c, 0x5b, 0xad, + 0x79, 0x38, 0xe8, 0x04, 0x6f, 0xdc, 0x26, 0x9a, 0x87, 0x4a, 0x17, 0xbb, 0xf0, 0xec, 0x47, 0x0b, + 0x6e, 0x5d, 0x7b, 0x1a, 0xa1, 0x7f, 0x80, 0xdf, 0xee, 0x75, 0xd5, 0x33, 0xf9, 0x8c, 0x87, 0x2c, + 0x0c, 0xe4, 0x78, 0x28, 0xa9, 0xe0, 0xe6, 0xc6, 0x67, 0x82, 0x9f, 0x70, 0x16, 0xba, 0x73, 0xe8, + 0xef, 0xb0, 0x3a, 0x92, 0x81, 0xa2, 0xce, 0x30, 0x92, 0x34, 0xbf, 0x64, 0x59, 0xe8, 0x9f, 0xf0, + 0xc4, 0x3c, 0x6c, 0xff, 0x82, 0x5a, 0xd9, 0xfa, 0xbe, 0x02, 0x8d, 0xfe, 0x8b, 0x9e, 0x7a, 0x9e, + 0xa3, 0x77, 0xe0, 0xcc, 0x5c, 0x2e, 0xa8, 0xfc, 0xae, 0xbd, 0xe1, 0x99, 0x7e, 0xef, 0xf1, 0xcd, + 0x9c, 0xa9, 0xee, 0xfc, 0x39, 0xb5, 0xf6, 0x8c, 0x2a, 0x67, 0xd6, 0xbe, 0xa1, 0xce, 0x66, 0xd6, + 0xbe, 0x49, 0xd3, 0xfe, 0x1c, 0x7a, 0x09, 0x76, 0x9f, 0x25, 0xa1, 0x12, 0x1a, 0xba, 0x53, 0x9a, + 0x52, 0x12, 0xee, 0xbd, 0x95, 0x6b, 0xb8, 0x51, 0xe4, 0xdc, 0xee, 0xfd, 0x77, 0x77, 0xb5, 0x6f, + 0x43, 0xfd, 0x65, 0xa1, 0x71, 0x3a, 0x0e, 0x37, 0xa2, 0xd4, 0xfc, 0x77, 0x19, 0xce, 0xeb, 0xdf, + 0xe7, 0x7f, 0x04, 0x00, 0x00, 0xff, 0xff, 0xee, 0xb9, 0x0b, 0x7d, 0xd0, 0x0c, 0x00, 0x00, } // Reference imports to suppress errors if they are not otherwise used. diff --git a/feg/gateway/go.mod b/feg/gateway/go.mod index cc932fcd75df..390776a9b222 100644 --- a/feg/gateway/go.mod +++ b/feg/gateway/go.mod @@ -29,7 +29,7 @@ require ( github.com/go-openapi/swag v0.19.5 github.com/go-redis/redis v6.14.1+incompatible github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b - github.com/golang/protobuf v1.4.3 + github.com/golang/protobuf v1.5.2 github.com/ishidawataru/sctp v0.0.0-20191218070446-00ab2ac2db07 github.com/mitchellh/mapstructure v1.3.3 // indirect github.com/pkg/errors v0.9.1 @@ -40,13 +40,13 @@ require ( github.com/stretchr/objx v0.3.0 // indirect github.com/stretchr/testify v1.6.1 github.com/thoas/go-funk v0.7.0 - github.com/wmnsk/go-gtp v0.7.17 + github.com/wmnsk/go-gtp v0.7.20 golang.org/x/crypto v0.0.0-20201016220609-9e8e0b390897 // indirect golang.org/x/net v0.0.0-20201110031124-69a78807bb2b golang.org/x/sys v0.0.0-20201018230417-eeed37f84f13 // indirect golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 // indirect google.golang.org/grpc v1.33.2 - google.golang.org/protobuf v1.25.0 + google.golang.org/protobuf v1.26.0 gopkg.in/yaml.v3 v3.0.0-20200615113413-eeeca48fe776 // indirect layeh.com/radius v0.0.0-20200615152116-663b41c3bf86 magma/feg/cloud/go v0.0.0 diff --git a/feg/gateway/go.sum b/feg/gateway/go.sum index 06c6aa78a965..e96a6a7dca01 100644 --- a/feg/gateway/go.sum +++ b/feg/gateway/go.sum @@ -76,12 +76,10 @@ github.com/corbym/gocrest v1.0.3/go.mod h1:maVFL5lbdS2PgfOQgGRWDYTeunSWQeiEgoNdT github.com/coreos/bbolt v1.3.2/go.mod h1:iRUV2dpdMOn7Bo10OQBFzIJO9kkE559Wcmn+qkEiiKk= github.com/coreos/etcd v3.3.10+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE= github.com/coreos/etcd v3.3.12+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE= -github.com/coreos/go-etcd v2.0.0+incompatible/go.mod h1:Jez6KQU2B/sWsbdaef3ED8NzMklzPG4d5KIOhIy30Tk= github.com/coreos/go-oidc v2.1.0+incompatible/go.mod h1:CgnwVTmzoESiwO9qyAFEMiHoZ1nMCKZlZ9V6mm3/LKc= github.com/coreos/go-semver v0.2.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk= github.com/coreos/go-systemd v0.0.0-20190321100706-95778dfbb74e/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4= github.com/coreos/pkg v0.0.0-20180928190104-399ea9e2e55f/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA= -github.com/cpuguy83/go-md2man v1.0.10/go.mod h1:SmD6nW6nTyfqj6ABTjUi3V3JVMnlJmwcJI5acqYI6dE= github.com/cpuguy83/go-md2man/v2 v2.0.0/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU= github.com/davecgh/go-spew v0.0.0-20151105211317-5215b55f46b2/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= @@ -260,8 +258,9 @@ github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrU github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w= github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0= github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8= -github.com/golang/protobuf v1.4.3 h1:JjCZWpVbqXDqFVmTfYWEVTMIYrL/NPdPSCHPJ0T/raM= -github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= +github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= +github.com/golang/protobuf v1.5.2 h1:ROPKBNFfQgOUMifHyP+KYbvpjbdoFNs+aK7DXlji0Tw= +github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= github.com/golang/snappy v0.0.0-20180518054509-2e65f85255db/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= github.com/golang/snappy v0.0.1/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= github.com/gomodule/redigo v2.0.0+incompatible/go.mod h1:B4C85qUVwatsJoIUNIfCRsp7qO0iAmpGFZ4EELWSbC4= @@ -273,8 +272,7 @@ github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMyw github.com/google/go-cmp v0.4.0 h1:xsAVV57WRhGj6kEIi8ReJzQlHHqcBYCElAvkovg3B/4= github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.5.4 h1:L8R9j+yAqZuZjsqh/z+F1NCffTKKLShY6zXTItVIZ8M= -github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.5 h1:Khx7svrCpmxxtHBq5j2mp/xVjsi8hQMfNLvJFAlrGgU= github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/gofuzz v0.0.0-20161122191042-44d81051d367/go.mod h1:HP5RmnzzSNb993RKQDq4+1A4ia9nllfqcQFTQJedwGI= github.com/google/gofuzz v0.0.0-20170612174753-24818f796faf/go.mod h1:HP5RmnzzSNb993RKQDq4+1A4ia9nllfqcQFTQJedwGI= @@ -539,7 +537,6 @@ github.com/robfig/cron/v3 v3.0.1/go.mod h1:eQICP3HwyT7UooqI/z+Ov+PtYAWygg1TEWWzG github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6SoW27p1b0cqNHllgS5HIMJraePCO15w5zCzIWYg= github.com/rs/cors v1.6.0/go.mod h1:gFx+x8UowdsKA9AchylcLynDq+nNFfI8FkUZdN/jGCU= github.com/rubyist/circuitbreaker v2.2.1+incompatible/go.mod h1:Ycs3JgJADPuzJDwffe12k6BZT8hxVi6lFK+gWYJLN4A= -github.com/russross/blackfriday v1.5.2/go.mod h1:JO/DiYxRf+HjHt06OyowR9PTA263kcR/rfWxYHBV53g= github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= github.com/ryanuber/columnize v0.0.0-20160712163229-9b3edd62028f/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts= github.com/ryanuber/columnize v2.1.0+incompatible/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts= @@ -573,7 +570,6 @@ github.com/spf13/cast v1.3.0/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkU github.com/spf13/cast v1.3.1 h1:nFm6S0SMdyzrzcmThSipiEubIDy8WEXKNZ0UOgiRpng= github.com/spf13/cast v1.3.1/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE= github.com/spf13/cobra v0.0.3/go.mod h1:1l0Ry5zgKvJasoi3XT1TypsSe7PqH0Sj9dhYf7v3XqQ= -github.com/spf13/cobra v0.0.5/go.mod h1:3K3wKZymM7VvHMDS9+Akkh4K60UwM26emMESw8tLCHU= github.com/spf13/cobra v1.0.0 h1:6m/oheQuQ13N9ks4hubMG6BnvwOeaJrqSPLahSnczz8= github.com/spf13/cobra v1.0.0/go.mod h1:/6GTrnGXV9HjY+aR4k0oJ5tcvakLuG6EuKReYlHNrgE= github.com/spf13/jwalterweatherman v1.0.0/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo= @@ -583,7 +579,6 @@ github.com/spf13/pflag v0.0.0-20170130214245-9ff6c6923cff/go.mod h1:DYY7MBk1bdzu github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA= github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= -github.com/spf13/viper v1.3.2/go.mod h1:ZiWeW+zYFKm7srdB9IoDzzZXaJaI5eL9QjNiN/DMA2s= github.com/spf13/viper v1.4.0/go.mod h1:PTJ7Z/lr49W6bUbkmS1V3by4uWynFiR9p7+dSq/yZzE= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= @@ -611,7 +606,6 @@ github.com/tidwall/pretty v1.0.0/go.mod h1:XNkn88O1ChpSDQmQeStsy+sBenx6DDtFZJxhV github.com/tmc/grpc-websocket-proxy v0.0.0-20190109142713-0ad062ec5ee5/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U= github.com/toqueteos/webbrowser v1.2.0/go.mod h1:XWoZq4cyp9WeUeak7w7LXRUQf1F1ATJMir8RTqb4ayM= github.com/ugorji/go v1.1.4/go.mod h1:uQMGLiO92mf5W77hV/PUCpI3pbzQx3CRekS0kk+RGrc= -github.com/ugorji/go/codec v0.0.0-20181204163529-d75b2dcb6bc8/go.mod h1:VFNgLljTbGfSG7qAOspJ7OScBnGdDN/yBr0sguwnwf0= github.com/valyala/bytebufferpool v1.0.0 h1:GqA5TC/0021Y/b9FG4Oi9Mr3q7XYx6KllzawFIhcdPw= github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc= github.com/valyala/fasttemplate v0.0.0-20170224212429-dcecefd839c4 h1:gKMu1Bf6QINDnvyZuTaACm9ofY+PRh+5vFz4oxBZeF8= @@ -627,10 +621,8 @@ github.com/vishvananda/netns v0.0.0-20191106174202-0a2b9b5464df/go.mod h1:JP3t17 github.com/wadey/gocovmerge v0.0.0-20160331181800-b5bfa59ec0ad h1:W0LEBv82YCGEtcmPA3uNZBI33/qF//HAAs3MawDjRa0= github.com/wadey/gocovmerge v0.0.0-20160331181800-b5bfa59ec0ad/go.mod h1:Hy8o65+MXnS6EwGElrSRjUzQDLXreJlzYLlWiHtt8hM= github.com/warthog618/sms v0.3.0/go.mod h1:+bYZGeBxu003sxD5xhzsrIPBAjPBzTABsRTwSpd7ld4= -github.com/wmnsk/go-gtp v0.7.15 h1:D0L2ISdfsVwzHvN/l7uyTvdqewUl+p0BDFElN0p7Uy0= -github.com/wmnsk/go-gtp v0.7.15/go.mod h1:v1psjZ7skpPSDegH23Amg9rNufs0BoXNM+GBtW5t58I= -github.com/wmnsk/go-gtp v0.7.17 h1:Q/zZopp/6mJPgqz5kb/yseOYTQ7v/E4Hp99Y4DzwnyY= -github.com/wmnsk/go-gtp v0.7.17/go.mod h1:IQ5tTgk1EDaKLLAum2vzYheKX9JeRngM1fm9ohdXAac= +github.com/wmnsk/go-gtp v0.7.20 h1:N8aobzvxWz5l/lC1qPewuPyN6jL7O2Sdoe92JiGn/MM= +github.com/wmnsk/go-gtp v0.7.20/go.mod h1:qdjTIBWIWjsNJdGs1EpmbcLjakUDPP7nRcEfKr2g1GQ= github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU= github.com/xlab/treeprint v0.0.0-20180616005107-d6fb6747feb6/go.mod h1:ce1O1j6UtZfjr22oyGxGLbauSBp2YVXpARAosm7dHBg= github.com/xordataexchange/crypt v0.0.3-0.20170626215501-b2862e3d0a77/go.mod h1:aYKd//L2LvnjZzWKhF00oedf4jCCReLcmhLdhm1A27Q= @@ -649,7 +641,6 @@ go.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q= golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20181029021203-45a5f77698d3/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20181106171534-e4dc69e5b2fd/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= -golang.org/x/crypto v0.0.0-20181203042331-505ab145d0a9/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20190211182817-74369b46fc67/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2 h1:VklqNMn3ovrHsnt90PveolxSbWFaJdECFbxSq0Mqo2M= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= @@ -661,8 +652,6 @@ golang.org/x/crypto v0.0.0-20190611184440-5c40567a22f8/go.mod h1:yigFU9vqHzYiE8U golang.org/x/crypto v0.0.0-20190617133340-57b3e21c3d56/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20190907121410-71b5226ff739 h1:Gc7JIyxvWgD6m+QmVryY0MstDORNYididDGxgZ6Tnpk= golang.org/x/crypto v0.0.0-20190907121410-71b5226ff739/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= -golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550 h1:ObdrDkeb4kJdCP557AjRjq69pTHfNouLtWZG7j9rPN8= -golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20200510223506-06a226fb4e37/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20201016220609-9e8e0b390897 h1:pLI5jrR7OSLijeIDcmRxNmw2api+jEfxLoykJVice/E= @@ -702,8 +691,6 @@ golang.org/x/net v0.0.0-20200202094626-16171245cfb2 h1:CCH4IOTTfewWjGOlSp+zGcjut golang.org/x/net v0.0.0-20200202094626-16171245cfb2/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20200324143707-d3edc9973b7e h1:3G+cUijn7XD+S4eJFddp53Pv7+slrESplyjG25HgL+k= golang.org/x/net v0.0.0-20200324143707-d3edc9973b7e/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= -golang.org/x/net v0.0.0-20200822124328-c89045814202 h1:VvcQYSHwXgi7W+TpUR6A9g6Up98WAHf3f/ulnJ62IyA= -golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= golang.org/x/net v0.0.0-20201110031124-69a78807bb2b h1:uwuIcX0g4Yl1NC5XAz37xsr2lTtcqevgzYNVt49waME= golang.org/x/net v0.0.0-20201110031124-69a78807bb2b/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= @@ -725,7 +712,6 @@ golang.org/x/sys v0.0.0-20181026203630-95b1ffbd15a5/go.mod h1:STP8DvDyc/dI5b8T5h golang.org/x/sys v0.0.0-20181107165924-66b7b1311ac8/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20181122145206-62eef0e2fa9b/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20181205085412-a5c9d58dba9a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190204203706-41f3e6584952/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190209173611-3b5209105503/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= @@ -800,8 +786,6 @@ google.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRn google.golang.org/genproto v0.0.0-20190425155659-357c62f0e4bb/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55 h1:gSJIx1SDwno+2ElGhA4+qG2zF97qiUzTM+rQ0klBOcE= google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= -google.golang.org/genproto v0.0.0-20191108220845-16a3f7862a1a h1:Ob5/580gVHBJZgXnff1cZDbG+xLtMVE5mDRTe+nIsX4= -google.golang.org/genproto v0.0.0-20191108220845-16a3f7862a1a/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013 h1:+kGHl1aib/qcwaRi1CbqBZ1rk19r85MNUf8HaBghugY= google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo= google.golang.org/grpc v1.17.0/go.mod h1:6QZJwpn2B+Zp71q/5VxRsJ6NXXVCE5NRUHRo+f3cWCs= @@ -817,7 +801,6 @@ google.golang.org/grpc v1.27.1 h1:zvIju4sqAGvwKspUQOhwnpcqSbzi7/H6QomNNjTL4sk= google.golang.org/grpc v1.27.1/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= google.golang.org/grpc v1.31.0 h1:T7P4R73V3SSDPhH7WW7ATbfViLtmamH0DKrP3f9AuDI= google.golang.org/grpc v1.31.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= -google.golang.org/grpc v1.33.0/go.mod h1:fr5YgcSWrqhRRxogOsw7RzIpsmvOZ6IcH4kBYTpR3n0= google.golang.org/grpc v1.33.2 h1:EQyQC3sa8M+p6Ulc8yy9SWSS2GVwyRc83gAbG8lrl4o= google.golang.org/grpc v1.33.2/go.mod h1:JMHMWHQWaTccqQQlmk3MJZS+GWXOdAesneDmEnv2fbc= google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= @@ -826,10 +809,12 @@ google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQ google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE= google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo= google.golang.org/protobuf v1.22.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= -google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= google.golang.org/protobuf v1.25.0 h1:Ejskq+SyPohKW+1uil0JJMtmHCgJPJ/qWTxr8qp+R4c= google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c= +google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= +google.golang.org/protobuf v1.26.0 h1:bxAC2xTBsZGibn2RTntX0oH50xLsqy1OxA9tTL3p/lk= +google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= gopkg.in/DATA-DOG/go-sqlmock.v1 v1.3.0 h1:FVCohIoYO7IJoDDVpV2pdq7SgrMH6wHnuTyrdrxJNoY= gopkg.in/DATA-DOG/go-sqlmock.v1 v1.3.0/go.mod h1:OdE7CF6DbADk7lN8LIKRzRJTTZXIjtWgA5THM5lhBAw= gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw= diff --git a/feg/gateway/services/s8_proxy/servicers/mock_pgw/cs.go b/feg/gateway/services/s8_proxy/servicers/mock_pgw/cs.go index 985a9cc7d45f..219c8791aa14 100644 --- a/feg/gateway/services/s8_proxy/servicers/mock_pgw/cs.go +++ b/feg/gateway/services/s8_proxy/servicers/mock_pgw/cs.go @@ -186,6 +186,15 @@ func (mPgw *MockPgw) getHandleCreateSessionRequest() gtpv2.HandlerFunc { } } + qosPCI := uint8(0) + if bearer.QoSProfile.PCI { + qosPCI = 1 + } + qosPVI := uint8(0) + if bearer.QoSProfile.PVI { + qosPVI = 1 + } + // send csRspFromPGW := message.NewCreateSessionResponse( sgwTEIDc, msg.Sequence(), @@ -198,6 +207,9 @@ func (mPgw *MockPgw) getHandleCreateSessionRequest() gtpv2.HandlerFunc { ie.NewEPSBearerID(bearer.EBI), pgwFTEIDu, ie.NewChargingID(bearer.ChargingID), + ie.NewBearerQoS(qosPCI, bearer.QoSProfile.PL, qosPVI, + bearer.QoSProfile.QCI, bearer.QoSProfile.MBRUL, bearer.QoSProfile.MBRDL, + bearer.QoSProfile.GBRUL, bearer.QoSProfile.GBRDL), )) session.AddTEID(gtpv2.IFTypeS5S8PGWGTPC, pgwFTEIDc.MustTEID()) @@ -333,7 +345,8 @@ func createQosIE(qp *gtpv2.QoSProfile) *ie.IE { } -func (mPgw *MockPgw) getHandleCreateSessionRequestWithDeniedService(errorCause uint8) gtpv2.HandlerFunc { +// getHandleCreateSessionRequestWithErrorCause Responds with an arbitrary error cause +func (mPgw *MockPgw) getHandleCreateSessionRequestWithErrorCause(errorCause uint8) gtpv2.HandlerFunc { return func(c *gtpv2.Conn, sgwAddr net.Addr, msg message.Message) error { fmt.Println("mock PGW received a CreateSessionRequest, but returning ERROR") csReqFromSGW := msg.(*message.CreateSessionRequest) @@ -361,7 +374,8 @@ func (mPgw *MockPgw) getHandleCreateSessionRequestWithDeniedService(errorCause u } } -func (mPgw *MockPgw) getHandleCreateSessionRequestWithMissingIE() gtpv2.HandlerFunc { +// getHandleCreateSessionResponseWithMissingIE responds with a CreateSessionResponse that has a missing field +func (mPgw *MockPgw) getHandleCreateSessionResponseWithMissingIE() gtpv2.HandlerFunc { return func(c *gtpv2.Conn, sgwAddr net.Addr, msg message.Message) error { fmt.Println("mock PGW received a CreateSessionRequest, but returning ERROR") csReqFromSGW := msg.(*message.CreateSessionRequest) @@ -393,3 +407,31 @@ func (mPgw *MockPgw) getHandleCreateSessionRequestWithMissingIE() gtpv2.HandlerF return nil } } + +// getHandleCreateSessionRequestWithMissingIE responds with a CauseMandatoryIEMissing +func (mPgw *MockPgw) getHandleCreateSessionRequestWithMissingIE(missingIE *ie.IE) gtpv2.HandlerFunc { + return func(c *gtpv2.Conn, sgwAddr net.Addr, msg message.Message) error { + fmt.Println("mock PGW received a CreateSessionRequest, but returning ERROR") + csReqFromSGW := msg.(*message.CreateSessionRequest) + sgwTEID := csReqFromSGW.SenderFTEIDC + if sgwTEID != nil { + _, err := sgwTEID.TEID() + if err != nil { + return err + } + } else { + return >pv2.RequiredIEMissingError{Type: ie.FullyQualifiedTEID} + } + + // Mising pgwFTEID and bearer pgwFTEIDu + csRspFromPGW := message.NewCreateSessionResponse( + sgwTEID.MustTEID(), msg.Sequence(), + ie.NewCause(gtpv2.CauseMandatoryIEMissing, 0, 0, 0, missingIE), + ) + + if err := c.RespondTo(sgwAddr, csReqFromSGW, csRspFromPGW); err != nil { + return err + } + return nil + } +} diff --git a/feg/gateway/services/s8_proxy/servicers/mock_pgw/mock_pgw.go b/feg/gateway/services/s8_proxy/servicers/mock_pgw/mock_pgw.go index 62f34a6e3f15..e3009d040501 100644 --- a/feg/gateway/services/s8_proxy/servicers/mock_pgw/mock_pgw.go +++ b/feg/gateway/services/s8_proxy/servicers/mock_pgw/mock_pgw.go @@ -89,13 +89,19 @@ func (mPgw *MockPgw) Start(ctx context.Context, pgwAddrsStr string) error { func (mPgw *MockPgw) SetCreateSessionWithErrorCause(errorCause uint8) { mPgw.AddHandler( message.MsgTypeCreateSessionRequest, - mPgw.getHandleCreateSessionRequestWithDeniedService(errorCause)) + mPgw.getHandleCreateSessionRequestWithErrorCause(errorCause)) } -func (mPgw *MockPgw) SetCreateSessionWithMissingIE() { +func (mPgw *MockPgw) SetCreateSessionResponseWithMissingIE() { mPgw.AddHandler( message.MsgTypeCreateSessionRequest, - mPgw.getHandleCreateSessionRequestWithMissingIE()) + mPgw.getHandleCreateSessionResponseWithMissingIE()) +} + +func (mPgw *MockPgw) SetCreateSessionRequestWithMissingIE(missingIE *ie.IE) { + mPgw.AddHandler( + message.MsgTypeCreateSessionRequest, + mPgw.getHandleCreateSessionRequestWithMissingIE(missingIE)) } // ONLY FOR DEBUGGING PURPOSES diff --git a/feg/gateway/services/s8_proxy/servicers/proto_conversions.go b/feg/gateway/services/s8_proxy/servicers/proto_conversions.go index e1ca481a7d72..7783889f3437 100644 --- a/feg/gateway/services/s8_proxy/servicers/proto_conversions.go +++ b/feg/gateway/services/s8_proxy/servicers/proto_conversions.go @@ -26,22 +26,14 @@ import ( // the message is proper it also returns the session. In case it there is an error it returns // the cause of error func parseCreateSessionResponse(msg message.Message) (csRes *protos.CreateSessionResponsePgw, err error) { + //glog.V(4).Infof("Received Create Session Response (gtp):\n%s", message.Prettify(msg)) csResGtp := msg.(*message.CreateSessionResponse) glog.V(2).Infof("Received Create Session Response (gtp):\n%s", csResGtp.String()) - csRes = &protos.CreateSessionResponsePgw{} // check Cause value first. if causeIE := csResGtp.Cause; causeIE != nil { - cause, err2 := causeIE.Cause() - if err2 != nil { - err = fmt.Errorf("Couldn't check cause of csRes: %s", err2) - return - } - if cause != gtpv2.CauseRequestAccepted { - csRes.GtpError = &protos.GtpError{ - Cause: uint32(cause), - Msg: fmt.Sprintf("Message with sequence # %d not accepted", msg.Sequence()), - } + csRes.GtpError, err = handleCause(causeIE, msg) + if err != nil || csRes.GtpError != nil { return } // If we get here, the message will be processed @@ -117,6 +109,14 @@ func parseCreateSessionResponse(msg message.Message) (csRes *protos.CreateSessio if err != nil { return } + + case ie.BearerQoS: + // save for testing purposes + bearerCtx.Qos, err = handleQOStoProto(childIE) + if err != nil { + return + } + bearerCtx.ValidQos = true } } csRes.BearerContext = bearerCtx @@ -132,24 +132,19 @@ func parseCreateSessionResponse(msg message.Message) (csRes *protos.CreateSessio // the cause of error func parseDelteSessionResponse(msg message.Message) ( dsRes *protos.DeleteSessionResponsePgw, err error) { + //glog.V(4).Infof("Received Delete Session Response (gtp):\n%s", (msg)) cdResGtp := msg.(*message.DeleteSessionResponse) glog.V(2).Infof("Received Delete Session Response (gtp):\n%s", cdResGtp.String()) dsRes = &protos.DeleteSessionResponsePgw{} // check Cause value first. if causeIE := cdResGtp.Cause; causeIE != nil { - cause, err2 := causeIE.Cause() - if err2 != nil { - err = fmt.Errorf("Couldn't check cause of delete session response: %s", err2) - return - } - if cause != gtpv2.CauseRequestAccepted { - dsRes.GtpError = &protos.GtpError{ - Cause: uint32(cause), - Msg: fmt.Sprintf("DeleteSessionRequest with sequence # %d not accepted", msg.Sequence()), - } + + dsRes.GtpError, err = handleCause(causeIE, msg) + if err != nil || dsRes.GtpError != nil { return } + // If we get here, the message will be processed } else { dsRes.GtpError = errorIeMissing(ie.Cause) return @@ -165,6 +160,29 @@ func errorIeMissing(missingIE uint8) *protos.GtpError { } } +func handleCause(causeIE *ie.IE, msg message.Message) (*protos.GtpError, error) { + cause, err := causeIE.Cause() + if err != nil { + return nil, fmt.Errorf("Couldn't check cause of %s: %s", msg.MessageTypeName(), err) + } + + switch cause { + case gtpv2.CauseRequestAccepted: + return nil, nil + default: + gtpErrorString := fmt.Sprintf("%s with sequence # %d not accepted. Cause: %d", msg.MessageTypeName(), msg.Sequence(), cause) + offendingIE, _ := causeIE.OffendingIE() + if offendingIE != nil { + gtpErrorString = fmt.Sprintf("%s %s: %d %s", gtpErrorString, " With Offending IE", offendingIE.Type, offendingIE) + } + glog.Warning(gtpErrorString) + return &protos.GtpError{ + Cause: uint32(cause), + Msg: gtpErrorString, + }, nil + } +} + // handleFTEID converts FTEID IE format into Proto format returning also the type of interface func handleFTEID(fteidIE *ie.IE) (*protos.Fteid, uint8, error) { interfaceType, err := fteidIE.InterfaceType() @@ -221,3 +239,57 @@ func handlePDNAddressAllocation(paaIE *ie.IE) (*protos.PdnAddressAllocation, pro } return &paa, pdnType, nil } + +func handleQOStoProto(qosIE *ie.IE) (*protos.QosInformation, error) { + qos := &protos.QosInformation{} + + // priority level + pl, err := qosIE.PriorityLevel() + if err != nil { + return nil, err + } + qos.PriorityLevel = uint32(pl) + + // qci label + qci, err := qosIE.QCILabel() + if err != nil { + return nil, err + } + qos.Qci = uint32(qci) + + // Preemption Capability + if qosIE.PreemptionCapability() { + qos.PreemptionCapability = 1 + } + + // Preemption Vulnerability + if qosIE.PreemptionVulnerability() { + qos.PreemptionVulnerability = 1 + } + + // maximum bitrate + mAmbr := &protos.Ambr{} + mAmbr.BrUl, err = qosIE.MBRForUplink() + if err != nil { + return nil, err + } + mAmbr.BrDl, err = qosIE.MBRForDownlink() + if err != nil { + return nil, err + } + qos.Mbr = mAmbr + + // granted bitrate + gAmbr := &protos.Ambr{} + gAmbr.BrUl, err = qosIE.GBRForUplink() + if err != nil { + return nil, err + } + gAmbr.BrDl, err = qosIE.GBRForDownlink() + if err != nil { + return nil, err + } + qos.Gbr = gAmbr + + return qos, nil +} diff --git a/feg/gateway/services/s8_proxy/servicers/s8_proxy.go b/feg/gateway/services/s8_proxy/servicers/s8_proxy.go index d988c3863527..f1d705119d66 100644 --- a/feg/gateway/services/s8_proxy/servicers/s8_proxy.go +++ b/feg/gateway/services/s8_proxy/servicers/s8_proxy.go @@ -159,7 +159,8 @@ func (s *S8Proxy) configOrRequestedPgwAddress(pgwAddrsFromRequest string) (*net. } func validateCreateSessionRequest(csr *protos.CreateSessionRequestPgw) error { - if csr.BearerContext == nil || csr.BearerContext.UserPlaneFteid == nil || csr.BearerContext.Id == 0 { + if csr.BearerContext == nil || csr.BearerContext.UserPlaneFteid == nil || csr.BearerContext.Id == 0 || + csr.BearerContext.Qos == nil { return fmt.Errorf("CreateSessionRequest missing fields %+v", csr) } return nil diff --git a/feg/gateway/services/s8_proxy/servicers/s8_proxy_test.go b/feg/gateway/services/s8_proxy/servicers/s8_proxy_test.go index 9cbcd8832b22..1d03072d59ef 100644 --- a/feg/gateway/services/s8_proxy/servicers/s8_proxy_test.go +++ b/feg/gateway/services/s8_proxy/servicers/s8_proxy_test.go @@ -14,7 +14,6 @@ package servicers import ( "context" "fmt" - "regexp" "strconv" "strings" "sync" @@ -80,14 +79,26 @@ func TestS8proxyCreateAndDeleteSession(t *testing.T) { assert.Equal(t, PAA, csRes.Paa.Ipv4Address) assert.Equal(t, "", csRes.Paa.Ipv6Address) - // check received QOS + // check QOS received at PGW sentQos := csReq.BearerContext.Qos - receivedQos := mockPgw.LastQos - assert.Equal(t, sentQos.Gbr.BrDl, receivedQos.Gbr.BrDl) - assert.Equal(t, sentQos.Gbr.BrUl, receivedQos.Gbr.BrUl) - assert.Equal(t, sentQos.Mbr.BrDl, receivedQos.Mbr.BrDl) - assert.Equal(t, sentQos.Mbr.BrUl, receivedQos.Mbr.BrUl) - assert.Equal(t, sentQos.Qci, receivedQos.Qci) + receivedAtPGWQos := mockPgw.LastQos + assert.Equal(t, sentQos.Gbr.BrDl, receivedAtPGWQos.Gbr.BrDl) + assert.Equal(t, sentQos.Gbr.BrUl, receivedAtPGWQos.Gbr.BrUl) + assert.Equal(t, sentQos.Mbr.BrDl, receivedAtPGWQos.Mbr.BrDl) + assert.Equal(t, sentQos.Mbr.BrUl, receivedAtPGWQos.Mbr.BrUl) + assert.Equal(t, sentQos.Qci, receivedAtPGWQos.Qci) + + // check QOS received at Response (should be the same as the sent) + + assert.NotEmpty(t, csRes.BearerContext.Qos) + receivedQOS := csRes.BearerContext.Qos + + assert.True(t, csRes.BearerContext.ValidQos) + assert.Equal(t, sentQos.Gbr.BrDl, receivedQOS.Gbr.BrDl) + assert.Equal(t, sentQos.Gbr.BrUl, receivedQOS.Gbr.BrUl) + assert.Equal(t, sentQos.Mbr.BrDl, receivedQOS.Mbr.BrDl) + assert.Equal(t, sentQos.Mbr.BrUl, receivedQOS.Mbr.BrUl) + assert.Equal(t, sentQos.Qci, receivedQOS.Qci) // ------------------------ // ---- Delete Session ---- @@ -248,7 +259,7 @@ func TestS8ProxyDeleteWithMissingParamaters(t *testing.T) { assert.Error(t, err) } -func TestS8proxyCreateSessionWithErrors(t *testing.T) { +func TestS8proxyCreateSessionWithServiceDenial(t *testing.T) { // set up client ans server s8p, mockPgw := startSgwAndPgw(t, GtpTimeoutForTest) defer mockPgw.Close() @@ -257,10 +268,6 @@ func TestS8proxyCreateSessionWithErrors(t *testing.T) { // ---- Create Session ---- csReq := getDefaultCreateSessionRequest(mockPgw.LocalAddr().String()) - // ------------------------ - // ---- Create Session ---- - csReq = getDefaultCreateSessionRequest(mockPgw.LocalAddr().String()) - // PGW denies service mockPgw.SetCreateSessionWithErrorCause(gtpv2.CauseServiceDenied) csRes, err := s8p.CreateSession(context.Background(), csReq) @@ -268,18 +275,49 @@ func TestS8proxyCreateSessionWithErrors(t *testing.T) { assert.NotEmpty(t, csRes) assert.NotEmpty(t, csRes.GtpError) assert.Equal(t, gtpv2.CauseServiceDenied, uint8(csRes.GtpError.Cause)) +} + +func TestS8proxyCreateSessionWithMissingIEonResponse(t *testing.T) { + // set up client ans server + s8p, mockPgw := startSgwAndPgw(t, GtpTimeoutForTest) + defer mockPgw.Close() + + // ------------------------ + // ---- Create Session ---- + csReq := getDefaultCreateSessionRequest(mockPgw.LocalAddr().String()) // s8_proxy forces a missing IE - mockPgw.SetCreateSessionWithMissingIE() - csRes, err = s8p.CreateSession(context.Background(), csReq) + mockPgw.SetCreateSessionResponseWithMissingIE() + csRes, err := s8p.CreateSession(context.Background(), csReq) assert.NoError(t, err) assert.NotEmpty(t, csRes) assert.NotEmpty(t, csRes.GtpError) assert.Equal(t, gtpv2.CauseMandatoryIEMissing, uint8(csRes.GtpError.Cause)) // check the error code is FullyQualifiedTEID - re := regexp.MustCompile("[0-9]+") - msg := re.FindString(csRes.GtpError.Msg) - assert.Equal(t, strconv.FormatUint(uint64(ie.FullyQualifiedTEID), 10), msg) + + assert.Contains(t, csRes.GtpError.Msg, strconv.FormatUint(uint64(ie.FullyQualifiedTEID), 10)) +} + +func TestS8proxyCreateSessionWithMissingIEMessage(t *testing.T) { + // set up client ans server + s8p, mockPgw := startSgwAndPgw(t, GtpTimeoutForTest) + defer mockPgw.Close() + + // ------------------------ + // ---- Create Session ---- + csReq := getDefaultCreateSessionRequest(mockPgw.LocalAddr().String()) + + // s8_proxy forces a missing IE + missingIe := ie.New(ie.BearerContext, 0, nil) + mockPgw.SetCreateSessionRequestWithMissingIE(missingIe) + csRes, err := s8p.CreateSession(context.Background(), csReq) + assert.NoError(t, err) + assert.NotEmpty(t, csRes) + assert.NotEmpty(t, csRes.GtpError) + assert.Equal(t, gtpv2.CauseMandatoryIEMissing, uint8(csRes.GtpError.Cause)) + + // check log meesage contains the name of the missing ie + assert.Contains(t, csRes.GtpError.Msg, missingIe.Name()) } func TestS8proxyValidateCreateSession(t *testing.T) { diff --git a/feg/gateway/services/s8_proxy/servicers/senders.go b/feg/gateway/services/s8_proxy/servicers/senders.go index 3f645cc7ee3e..a88996812237 100644 --- a/feg/gateway/services/s8_proxy/servicers/senders.go +++ b/feg/gateway/services/s8_proxy/servicers/senders.go @@ -38,6 +38,8 @@ func (s *S8Proxy) sendAndReceiveCreateSession( cPgwUDPAddr.String(), csReq.String()) glog.V(2).Infof("Send Create Session Request (gtp) to %s:\n%s", cPgwUDPAddr.String(), csReqMsg.(*message.CreateSessionRequest).String()) + //glog.V(4).Infof("Send Create Session Request (gtp) to %s:\n%s", + // cPgwUDPAddr.String(), message.Prettify(csReqMsg)) grpcMessage, err := s.gtpClient.SendMessageAndExtractGrpc(csReq.Imsi, csReq.CAgwTeid, cPgwUDPAddr, csReqMsg) if err != nil { @@ -63,6 +65,8 @@ func (s *S8Proxy) sendAndReceiveDeleteSession(req *protos.DeleteSessionRequestPg dsReqMsg) glog.V(2).Infof("Send Delete Session Request (gtp) to %s:\n%s", cPgwUDPAddr.String(), dsReqMsg.(*message.DeleteSessionRequest).String()) + //glog.V(4).Infof("Send Delete Session Request (gtp) to %s:\n%s", + // cPgwUDPAddr.String(), message.Prettify(dsReqMsg)) grpcMessage, err := s.gtpClient.SendMessageAndExtractGrpc(req.Imsi, req.CAgwTeid, cPgwUDPAddr, dsReqMsg) if err != nil { return nil, fmt.Errorf("no response message to DeleteSessionRequest: %s", err) diff --git a/feg/protos/s8_proxy.proto b/feg/protos/s8_proxy.proto index b20533c4e4f2..e3326e947cee 100644 --- a/feg/protos/s8_proxy.proto +++ b/feg/protos/s8_proxy.proto @@ -96,8 +96,9 @@ message ServingNetwork { message BearerContext { uint32 id = 1; Fteid user_plane_fteid = 2; - QosInformation qos = 3; - uint32 charging_id = 4; + bool valid_qos = 3; + QosInformation qos = 4; + uint32 charging_id = 5; } message QosInformation { @@ -166,5 +167,4 @@ message EchoResponse{ message GtpError { uint32 cause = 1; string msg= 2; - } From 0855e6b3eaa95fd0bde1229c469a045c00aae2a2 Mon Sep 17 00:00:00 2001 From: Pravin Shelar Date: Fri, 9 Apr 2021 09:32:02 +0530 Subject: [PATCH 73/91] AGW: separate out lockfile for debian and ubuntu (#6028) This would allow us to set ref lock file for each distro. Signed-off-by: Pravin B Shelar --- lte/gateway/fabfile.py | 2 +- lte/gateway/release/Makefile | 2 +- lte/gateway/release/build-magma.sh | 8 +- .../{magma.lockfile => magma.lockfile.debian} | 0 lte/gateway/release/magma.lockfile.ubuntu | 1292 +++++++++++++++++ lte/gateway/release/pydep | 4 + 6 files changed, 1302 insertions(+), 6 deletions(-) rename lte/gateway/release/{magma.lockfile => magma.lockfile.debian} (100%) create mode 100644 lte/gateway/release/magma.lockfile.ubuntu diff --git a/lte/gateway/fabfile.py b/lte/gateway/fabfile.py index 4ea4994d6a2a..cf781b2ccdfe 100644 --- a/lte/gateway/fabfile.py +++ b/lte/gateway/fabfile.py @@ -92,7 +92,7 @@ def package(vcs='hg', all_deps="False", run('mkdir -p ~/magma-deps') print("Generating lte/setup.py and orc8r/setup.py magma dependency packages") run('./release/pydep finddep --install-from-repo -b --build-output ~/magma-deps' - + (' -l ./release/magma.lockfile') + + (' -l ./release/magma.lockfile.%s' % os) + ' python/setup.py' + (' %s/setup.py' % ORC8R_AGW_PYTHON_ROOT)) diff --git a/lte/gateway/release/Makefile b/lte/gateway/release/Makefile index bf5e3917ba9c..06f6a6c64dfc 100644 --- a/lte/gateway/release/Makefile +++ b/lte/gateway/release/Makefile @@ -15,4 +15,4 @@ PY_LTE = $(MAGMA_ROOT)/lte/gateway/python PY_ORC8R = $(MAGMA_ROOT)/orc8r/gateway/python magma.lockfile: $(PY_LTE)/setup.py $(PY_ORC8R)/setup.py - ./pydep finddep --install-from-official -l ./magma.lockfile $(PY_ORC8R)/setup.py $(PY_LTE)/setup.py + ./pydep finddep --install-from-repo -l ./magma.lockfile.$(os_release) $(PY_ORC8R)/setup.py $(PY_LTE)/setup.py diff --git a/lte/gateway/release/build-magma.sh b/lte/gateway/release/build-magma.sh index f0d4a09d431f..4ae5ec53834a 100755 --- a/lte/gateway/release/build-magma.sh +++ b/lte/gateway/release/build-magma.sh @@ -287,7 +287,7 @@ FULL_VERSION=${VERSION}-$(date +%s)-${COMMIT_HASH} # adjust mtime of a setup.py to force update # (e.g. `touch ${PY_LTE}/setup.py`) pushd "${RELEASE_DIR}" || exit 1 -make -e magma.lockfile +make os_release=$OS -e magma.lockfile popd cd ${PY_ORC8R} @@ -295,15 +295,15 @@ make protos PKG_VERSION=${FULL_VERSION} ${PY_VERSION} setup.py install --root ${PY_TMP_BUILD} --install-layout deb \ --no-compile --single-version-externally-managed -ORC8R_PY_DEPS=`${RELEASE_DIR}/pydep lockfile ${RELEASE_DIR}/magma.lockfile` +ORC8R_PY_DEPS=`${RELEASE_DIR}/pydep lockfile ${RELEASE_DIR}/magma.lockfile.$OS` cd ${PY_LTE} make protos make swagger PKG_VERSION=${FULL_VERSION} ${PY_VERSION} setup.py install --root ${PY_TMP_BUILD} --install-layout deb \ --no-compile --single-version-externally-managed -${RELEASE_DIR}/pydep finddep -l ${RELEASE_DIR}/magma.lockfile setup.py -LTE_PY_DEPS=`${RELEASE_DIR}/pydep lockfile ${RELEASE_DIR}/magma.lockfile` +${RELEASE_DIR}/pydep finddep -l ${RELEASE_DIR}/magma.lockfile.$OS setup.py +LTE_PY_DEPS=`${RELEASE_DIR}/pydep lockfile ${RELEASE_DIR}/magma.lockfile.$OS` # now the binaries are built, we can package up everything else and build the # magma package. diff --git a/lte/gateway/release/magma.lockfile b/lte/gateway/release/magma.lockfile.debian similarity index 100% rename from lte/gateway/release/magma.lockfile rename to lte/gateway/release/magma.lockfile.debian diff --git a/lte/gateway/release/magma.lockfile.ubuntu b/lte/gateway/release/magma.lockfile.ubuntu new file mode 100644 index 000000000000..bc4fd20de5bf --- /dev/null +++ b/lte/gateway/release/magma.lockfile.ubuntu @@ -0,0 +1,1292 @@ +{ + "dependencies": { + "aiodns": { + "root": true, + "source": "apt", + "sysdep": "python3-aiodns", + "version": "1.1.1" + }, + "aioeventlet": { + "root": true, + "source": "apt", + "sysdep": "python3-aioeventlet", + "version": "0.5.1" + }, + "aioh2": { + "root": true, + "source": "pypi", + "sysdep": "python3-aioh2", + "version": "0.2.2" + }, + "aiohttp": { + "root": true, + "source": "apt", + "sysdep": "python3-aiohttp", + "version": "1.2.0" + }, + "argparse": { + "root": false, + "source": "pypi", + "sysdep": "python3-argparse", + "version": "1.4.0" + }, + "astroid": { + "root": false, + "source": "pypi", + "sysdep": "python3-astroid", + "version": "2.4.2" + }, + "attrs": { + "root": false, + "source": "pypi", + "sysdep": "python3-attrs", + "version": "20.3.0" + }, + "bravado-core": { + "root": true, + "source": "pypi", + "sysdep": "python3-bravado-core", + "version": "5.16.1" + }, + "certifi": { + "root": true, + "source": "pypi", + "sysdep": "python3-certifi", + "version": "2019.6.16" + }, + "cffi": { + "root": false, + "source": "pypi", + "sysdep": "python3-cffi", + "version": "1.14.5" + }, + "chardet": { + "root": true, + "source": "pypi", + "sysdep": "python3-chardet", + "version": "3.0.4" + }, + "click": { + "root": false, + "source": "pypi", + "sysdep": "python3-click", + "version": "7.1.2" + }, + "cryptography": { + "root": true, + "source": "pypi", + "sysdep": "python3-cryptography", + "version": "3.2.1" + }, + "cython": { + "root": true, + "source": "pypi", + "sysdep": "python3-cython", + "version": "0.29.1" + }, + "dnspython": { + "root": false, + "source": "pypi", + "sysdep": "python3-dnspython", + "version": "1.16.0" + }, + "docker": { + "root": true, + "source": "pypi", + "sysdep": "python3-docker", + "version": "4.0.2" + }, + "envoy": { + "root": true, + "source": "pypi", + "sysdep": "python3-envoy", + "version": "0.0.3" + }, + "eventlet": { + "root": true, + "source": "pypi", + "sysdep": "python3-eventlet", + "version": "0.26.1" + }, + "fire": { + "root": true, + "source": "pypi", + "sysdep": "python3-fire", + "version": "0.2.0" + }, + "flask": { + "root": true, + "source": "pypi", + "sysdep": "python3-flask", + "version": "1.0.2" + }, + "freezegun": { + "root": true, + "source": "pypi", + "sysdep": "python3-freezegun", + "version": "0.3.15" + }, + "glob2": { + "root": true, + "source": "pypi", + "sysdep": "python3-glob2", + "version": "0.7" + }, + "greenlet": { + "root": false, + "source": "pypi", + "sysdep": "python3-greenlet", + "version": "0.4.16" + }, + "grpcio": { + "root": true, + "source": "pypi", + "sysdep": "python3-grpcio", + "version": "1.36.1" + }, + "h2": { + "root": true, + "source": "pypi", + "sysdep": "python3-h2", + "version": "3.2.0" + }, + "hpack": { + "root": true, + "source": "pypi", + "sysdep": "python3-hpack", + "version": "3.0.0" + }, + "hyperframe": { + "root": false, + "source": "pypi", + "sysdep": "python3-hyperframe", + "version": "5.2.0" + }, + "idna": { + "root": true, + "source": "pypi", + "sysdep": "python3-idna", + "version": "2.8" + }, + "importlib-metadata": { + "root": false, + "source": "pypi", + "sysdep": "python3-importlib-metadata", + "version": "2.1.1" + }, + "importlib-resources": { + "root": false, + "source": "pypi", + "sysdep": "python3-importlib-resources", + "version": "3.0.0" + }, + "isort": { + "root": false, + "source": "pypi", + "sysdep": "python3-isort", + "version": "4.3.21" + }, + "itsdangerous": { + "root": false, + "source": "pypi", + "sysdep": "python3-itsdangerous", + "version": "1.1.0" + }, + "jinja2": { + "root": false, + "source": "pypi", + "sysdep": "python3-jinja2", + "version": "2.10" + }, + "js-regex": { + "root": false, + "source": "pypi", + "sysdep": "python3-js-regex", + "version": "1.0.1" + }, + "jsonpickle": { + "root": true, + "source": "apt", + "sysdep": "python3-jsonpickle", + "version": "0.9.3" + }, + "jsonref": { + "root": false, + "source": "pypi", + "sysdep": "python3-jsonref", + "version": "0.2" + }, + "jsonschema": { + "root": true, + "source": "pypi", + "sysdep": "python3-jsonschema", + "version": "3.1.0" + }, + "lazy-object-proxy": { + "root": false, + "source": "pypi", + "sysdep": "python3-lazy-object-proxy", + "version": "1.4.3" + }, + "lxml": { + "root": true, + "source": "pypi", + "sysdep": "python3-lxml", + "version": "4.6.2" + }, + "markupsafe": { + "root": false, + "source": "pypi", + "sysdep": "python3-markupsafe", + "version": "1.1.1" + }, + "mccabe": { + "root": false, + "source": "pypi", + "sysdep": "python3-mccabe", + "version": "0.6.1" + }, + "monotonic": { + "root": false, + "source": "pypi", + "sysdep": "python3-monotonic", + "version": "1.5" + }, + "msgpack": { + "root": false, + "source": "pypi", + "sysdep": "python3-msgpack", + "version": "1.0.0" + }, + "netaddr": { + "root": false, + "source": "pypi", + "sysdep": "python3-netaddr", + "version": "0.8.0" + }, + "netifaces": { + "root": true, + "source": "pypi", + "sysdep": "python3-netifaces", + "version": "0.10.9" + }, + "oslo.config": { + "root": false, + "source": "pypi", + "sysdep": "python3-oslo.config", + "version": "2.5.0" + }, + "ovs": { + "root": false, + "source": "pypi", + "sysdep": "python3-ovs", + "version": "2.6.0" + }, + "pbr": { + "root": false, + "source": "pypi", + "sysdep": "python3-pbr", + "version": "5.4.5" + }, + "priority": { + "root": false, + "source": "pypi", + "sysdep": "python3-priority", + "version": "1.3.0" + }, + "protobuf": { + "root": true, + "source": "pypi", + "sysdep": "python3-protobuf", + "version": "3.14.0" + }, + "psutil": { + "root": true, + "source": "pypi", + "sysdep": "python3-psutil", + "version": "5.6.6" + }, + "pycares": { + "root": true, + "source": "pypi", + "sysdep": "python3-pycares", + "version": "3.1.1" + }, + "pycparser": { + "root": false, + "source": "pypi", + "sysdep": "python3-pycparser", + "version": "2.20" + }, + "pycryptodome": { + "root": true, + "source": "pypi", + "sysdep": "python3-pycryptodome", + "version": "3.9.9" + }, + "pylint": { + "root": true, + "source": "pypi", + "sysdep": "python3-pylint", + "version": "2.6.2" + }, + "pymemoize": { + "root": true, + "source": "pypi", + "sysdep": "python3-pymemoize", + "version": "1.0.2" + }, + "pyroute2": { + "root": true, + "source": "pypi", + "sysdep": "python3-pyroute2", + "version": "0.5.14" + }, + "pyrsistent": { + "root": false, + "source": "pypi", + "sysdep": "python3-pyrsistent", + "version": "0.17.3" + }, + "pystemd": { + "root": true, + "source": "pypi", + "sysdep": "python3-pystemd", + "version": "0.8.0" + }, + "python-dateutil": { + "root": true, + "source": "pypi", + "sysdep": "python3-python-dateutil", + "version": "2.8.1" + }, + "python-redis-lock": { + "root": true, + "source": "pypi", + "sysdep": "python3-python-redis-lock", + "version": "3.7.0" + }, + "pytz": { + "root": false, + "source": "pypi", + "sysdep": "python3-pytz", + "version": "2020.1" + }, + "pyyaml": { + "root": true, + "source": "apt", + "sysdep": "python3-yaml", + "version": "3.12" + }, + "redis": { + "root": true, + "source": "pypi", + "sysdep": "python3-redis", + "version": "3.5.3" + }, + "redis-collections": { + "root": true, + "source": "pypi", + "sysdep": "python3-redis-collections", + "version": "0.9.0" + }, + "repoze.lru": { + "root": false, + "source": "pypi", + "sysdep": "python3-repoze.lru", + "version": "0.7" + }, + "requests": { + "root": true, + "source": "pypi", + "sysdep": "python3-requests", + "version": "2.22.0" + }, + "rfc3987": { + "root": true, + "source": "pypi", + "sysdep": "python3-rfc3987", + "version": "1.3.8" + }, + "routes": { + "root": false, + "source": "pypi", + "sysdep": "python3-routes", + "version": "2.4.1" + }, + "ryu": { + "root": true, + "source": "pypi", + "sysdep": "python3-ryu", + "version": "4.30" + }, + "scapy": { + "root": true, + "source": "pypi", + "sysdep": "python3-scapy", + "version": "2.4.4" + }, + "sentry-sdk": { + "root": true, + "source": "pypi", + "sysdep": "python3-sentry-sdk", + "version": "1.0.0" + }, + "simplejson": { + "root": false, + "source": "apt", + "sysdep": "python3-simplejson", + "version": "3.10.0" + }, + "six": { + "root": true, + "source": "pypi", + "sysdep": "python3-six", + "version": "1.12.0" + }, + "snowflake": { + "root": true, + "source": "pypi", + "sysdep": "python3-snowflake", + "version": "0.0.3" + }, + "spyne": { + "root": true, + "source": "pypi", + "sysdep": "python3-spyne", + "version": "2.12.16" + }, + "stevedore": { + "root": false, + "source": "pypi", + "sysdep": "python3-stevedore", + "version": "1.32.0" + }, + "strict-rfc3339": { + "root": true, + "source": "pypi", + "sysdep": "python3-strict-rfc3339", + "version": "0.7" + }, + "swagger-spec-validator": { + "root": false, + "source": "pypi", + "sysdep": "python3-swagger-spec-validator", + "version": "2.7.3" + }, + "termcolor": { + "root": false, + "source": "apt", + "sysdep": "python3-termcolor", + "version": "1.1.0" + }, + "tinyrpc": { + "root": false, + "source": "pypi", + "sysdep": "python3-tinyrpc", + "version": "1.0.4" + }, + "toml": { + "root": false, + "source": "pypi", + "sysdep": "python3-toml", + "version": "0.10.2" + }, + "typed-ast": { + "root": false, + "source": "pypi", + "sysdep": "python3-typed-ast", + "version": "1.4.2" + }, + "urllib3": { + "root": true, + "source": "pypi", + "sysdep": "python3-urllib3", + "version": "1.25.3" + }, + "webcolors": { + "root": true, + "source": "pypi", + "sysdep": "python3-webcolors", + "version": "1.11.1" + }, + "webob": { + "root": false, + "source": "pypi", + "sysdep": "python3-webob", + "version": "1.2" + }, + "websocket-client": { + "root": true, + "source": "pypi", + "sysdep": "python3-websocket-client", + "version": "0.56.0" + }, + "werkzeug": { + "root": false, + "source": "pypi", + "sysdep": "python3-werkzeug", + "version": "0.14" + }, + "wrapt": { + "root": false, + "source": "pypi", + "sysdep": "python3-wrapt", + "version": "1.12.1" + }, + "wsgiserver": { + "root": true, + "source": "pypi", + "sysdep": "python3-wsgiserver", + "version": "1.3" + }, + "zipp": { + "root": false, + "source": "pypi", + "sysdep": "python3-zipp", + "version": "1.2.0" + } + }, + "override": { + "focal": { + "dependencies": { + "aiodns": { + "root": true, + "source": "apt", + "sysdep": "python3-aiodns", + "version": "2.0.0" + }, + "aioh2": { + "root": true, + "source": "apt", + "sysdep": "python3-aioh2", + "version": "0.2.2" + }, + "aiohttp": { + "root": true, + "source": "pypi", + "sysdep": "python3-aiohttp", + "version": "3.6.2" + }, + "astroid": { + "root": false, + "source": "apt", + "sysdep": "python3-astroid", + "version": "2.4.2" + }, + "async-timeout": { + "root": false, + "source": "apt", + "sysdep": "python3-async-timeout", + "version": "3.0.1" + }, + "attrs": { + "root": false, + "source": "pypi", + "sysdep": "python3-attrs", + "version": "19.3.0" + }, + "bravado-core": { + "root": true, + "source": "apt", + "sysdep": "python3-bravado-core", + "version": "5.16.1" + }, + "certifi": { + "root": true, + "source": "apt", + "sysdep": "python3-certifi", + "version": "2019.11.28" + }, + "click": { + "root": true, + "source": "apt", + "sysdep": "python3-click", + "version": "7.1.2" + }, + "cryptography": { + "root": true, + "source": "pypi", + "sysdep": "python3-cryptography", + "version": "2.8" + }, + "cython": { + "root": true, + "source": "apt", + "sysdep": "python3-cython", + "version": "0.29.1" + }, + "docker": { + "root": true, + "source": "apt", + "sysdep": "python3-docker", + "version": "4.0.2" + }, + "envoy": { + "root": true, + "source": "apt", + "sysdep": "python3-envoy", + "version": "0.0.3" + }, + "eventlet": { + "root": true, + "source": "apt", + "sysdep": "python3-eventlet", + "version": "0.26.1" + }, + "fire": { + "root": true, + "source": "apt", + "sysdep": "python3-fire", + "version": "0.2.1" + }, + "flask": { + "root": true, + "source": "apt", + "sysdep": "python3-flask", + "version": "1.1.1" + }, + "freezegun": { + "root": true, + "source": "apt", + "sysdep": "python3-freezegun", + "version": "0.3.15" + }, + "glob2": { + "root": true, + "source": "apt", + "sysdep": "python3-glob2", + "version": "0.7" + }, + "greenlet": { + "root": false, + "source": "apt", + "sysdep": "python3-greenlet", + "version": "0.4.16" + }, + "grpcio": { + "root": true, + "source": "apt", + "sysdep": "python3-grpcio", + "version": "1.34.0" + }, + "h2": { + "root": true, + "source": "apt", + "sysdep": "python3-h2", + "version": "3.2.0" + }, + "hpack": { + "root": true, + "source": "apt", + "sysdep": "python3-hpack", + "version": "3.0.0" + }, + "hyperframe": { + "root": false, + "source": "apt", + "sysdep": "python3-hyperframe", + "version": "5.2.0" + }, + "idna": { + "root": true, + "source": "apt", + "sysdep": "python3-idna", + "version": "2.8" + }, + "importlib-metadata": { + "root": false, + "source": "apt", + "sysdep": "python3-importlib-metadata", + "version": "1.5.0" + }, + "isort": { + "root": false, + "source": "apt", + "sysdep": "python3-isort", + "version": "5.7.0" + }, + "itsdangerous": { + "root": true, + "source": "apt", + "sysdep": "python3-itsdangerous", + "version": "1.1.0" + }, + "jinja2": { + "root": true, + "source": "apt", + "sysdep": "python3-jinja2", + "version": "2.10.1" + }, + "js-regex": { + "root": false, + "source": "apt", + "sysdep": "python3-js-regex", + "version": "1.0.1" + }, + "json-pointer": { + "root": true, + "source": "pypi", + "sysdep": "python3-json-pointer", + "version": "0.1.2" + }, + "jsonpickle": { + "root": true, + "source": "apt", + "sysdep": "python3-jsonpickle", + "version": "1.2" + }, + "jsonref": { + "root": false, + "source": "apt", + "sysdep": "python3-jsonref", + "version": "0.2" + }, + "jsonschema": { + "root": true, + "source": "pypi", + "sysdep": "python3-jsonschema", + "version": "3.1.0" + }, + "lazy-object-proxy": { + "root": false, + "source": "pypi", + "sysdep": "python3-lazy-object-proxy", + "version": "1.4.3" + }, + "lxml": { + "root": true, + "source": "apt", + "sysdep": "python3-lxml", + "version": "4.6.2" + }, + "mccabe": { + "root": false, + "source": "apt", + "sysdep": "python3-mccabe", + "version": "0.6.1" + }, + "msgpack": { + "root": false, + "source": "apt", + "sysdep": "python3-msgpack", + "version": "0.6.2" + }, + "multidict": { + "root": false, + "source": "apt", + "sysdep": "python3-multidict", + "version": "4.7.6" + }, + "netifaces": { + "root": true, + "source": "pypi", + "sysdep": "python3-netifaces", + "version": "0.10.4" + }, + "ovs": { + "root": true, + "source": "apt", + "sysdep": "python3-ovs", + "version": "2.13.3" + }, + "priority": { + "root": false, + "source": "apt", + "sysdep": "python3-priority", + "version": "1.3.0" + }, + "prometheus-client": { + "root": true, + "source": "pypi", + "sysdep": "python3-prometheus-client", + "version": "0.3.1" + }, + "protobuf": { + "root": true, + "source": "pypi", + "sysdep": "python3-protobuf", + "version": "3.14.0" + }, + "psutil": { + "root": true, + "source": "apt", + "sysdep": "python3-psutil", + "version": "5.6.6" + }, + "pycares": { + "root": true, + "source": "pypi", + "sysdep": "python3-pycares", + "version": "3.1.1" + }, + "pycryptodome": { + "root": true, + "source": "apt", + "sysdep": "python3-pycryptodome", + "version": "3.9.9" + }, + "pylint": { + "root": true, + "source": "apt", + "sysdep": "python3-pylint", + "version": "2.6.0" + }, + "pymemoize": { + "root": true, + "source": "apt", + "sysdep": "python3-pymemoize", + "version": "1.0.2" + }, + "pyrsistent": { + "root": false, + "source": "pypi", + "sysdep": "python3-pyrsistent", + "version": "0.15.5" + }, + "pystemd": { + "root": true, + "source": "apt", + "sysdep": "python3-pystemd", + "version": "0.8.0" + }, + "python-dateutil": { + "root": true, + "source": "apt", + "sysdep": "python3-python-dateutil", + "version": "2.8.1" + }, + "python-redis-lock": { + "root": true, + "source": "apt", + "sysdep": "python3-python-redis-lock", + "version": "3.7.0" + }, + "python3-systemd": { + "root": true, + "source": "apt", + "sysdep": "python3-systemd", + "version": "234" + }, + "pytz": { + "root": true, + "source": "apt", + "sysdep": "python3-pytz", + "version": "2020.1" + }, + "pyyaml": { + "root": true, + "source": "apt", + "sysdep": "python3-yaml", + "version": "5.3.1" + }, + "redis": { + "root": true, + "source": "apt", + "sysdep": "python3-redis", + "version": "3.3.11" + }, + "redis-collections": { + "root": true, + "source": "apt", + "sysdep": "python3-redis-collections", + "version": "0.8.1" + }, + "rfc3987": { + "root": true, + "source": "apt", + "sysdep": "python3-rfc3987", + "version": "1.3.8" + }, + "ryu": { + "root": true, + "source": "apt", + "sysdep": "python3-ryu", + "version": "4.30" + }, + "scapy": { + "root": true, + "source": "apt", + "sysdep": "python3-scapy", + "version": "2.4.4" + }, + "sentry-sdk": { + "root": true, + "source": "apt", + "sysdep": "python3-sentry-sdk", + "version": "1.0.0" + }, + "simplejson": { + "root": false, + "source": "pypi", + "sysdep": "python3-simplejson", + "version": "3.16.0" + }, + "six": { + "root": true, + "source": "apt", + "sysdep": "python3-six", + "version": "1.14.0" + }, + "snowflake": { + "root": true, + "source": "apt", + "sysdep": "python3-snowflake", + "version": "0.0.3" + }, + "sortedcontainers": { + "root": false, + "source": "apt", + "sysdep": "python3-sortedcontainers", + "version": "2.3.0" + }, + "spyne": { + "root": true, + "source": "pypi", + "sysdep": "python3-spyne", + "version": "2.13.16" + }, + "strict-rfc3339": { + "root": true, + "source": "apt", + "sysdep": "python3-strict-rfc3339", + "version": "0.7" + }, + "swagger-spec-validator": { + "root": false, + "source": "apt", + "sysdep": "python3-swagger-spec-validator", + "version": "2.7.3" + }, + "toml": { + "root": false, + "source": "apt", + "sysdep": "python3-toml", + "version": "0.10.2" + }, + "typing-extensions": { + "root": false, + "source": "apt", + "sysdep": "python3-typing-extensions", + "version": "3.7.4.3" + }, + "webcolors": { + "root": true, + "source": "pypi", + "sysdep": "python3-webcolors", + "version": "1.11.1" + }, + "websocket-client": { + "root": true, + "source": "apt", + "sysdep": "python3-websocket-client", + "version": "0.56.0" + }, + "werkzeug": { + "root": false, + "source": "apt", + "sysdep": "python3-werkzeug", + "version": "0.14" + }, + "wrapt": { + "root": false, + "source": "pypi", + "sysdep": "python3-wrapt", + "version": "1.11.2" + }, + "wsgiserver": { + "root": true, + "source": "apt", + "sysdep": "python3-wsgiserver", + "version": "1.3" + }, + "yarl": { + "root": false, + "source": "apt", + "sysdep": "python3-yarl", + "version": "1.6.3" + } + }, + "root_packages": { + "aiodns": { + "version": "2.0.0" + }, + "aioh2": { + "version": "0.2.2" + }, + "aiohttp": { + "version": "3.6.2" + }, + "bravado-core": { + "version": "5.16.1" + }, + "certifi": { + "version": "2019.11.28" + }, + "click": { + "version": "7.1.2" + }, + "cryptography": { + "version": "2.8" + }, + "fire": { + "version": "0.2.1" + }, + "flask": { + "version": "1.1.1" + }, + "grpcio": { + "version": "1.34.0" + }, + "itsdangerous": { + "version": "1.1.0" + }, + "jinja2": { + "version": "2.10.1" + }, + "json-pointer": { + "version": "0.1.2" + }, + "jsonpickle": { + "version": "1.2" + }, + "jsonschema": { + "version": "3.1.0" + }, + "netifaces": { + "version": "0.10.4" + }, + "ovs": { + "version": "2.13.3" + }, + "prometheus-client": { + "version": "0.3.1" + }, + "protobuf": { + "version": "3.14.0" + }, + "psutil": { + "version": "5.6.6" + }, + "pycares": { + "version": "3.1.1" + }, + "pylint": { + "version": "2.6.0" + }, + "pystemd": { + "version": "0.8.0" + }, + "pytz": { + "version": "2020.1" + }, + "pyyaml": { + "version": "5.3.1" + }, + "redis": { + "version": "3.3.11" + }, + "redis-collections": { + "version": "0.8.1" + }, + "rfc3987": { + "version": "1.3.8" + }, + "sentry-sdk": { + "version": "1.0.0" + }, + "setuptools": { + "version": "49.6.0" + }, + "six": { + "version": "1.14.0" + }, + "snowflake": { + "version": "0.0.3" + }, + "spyne": { + "version": "2.13.16" + }, + "strict-rfc3339": { + "version": "0.7" + }, + "systemd": { + "version": "234" + }, + "systemd-python": { + "version": "234" + }, + "webcolors": { + "version": "1.11.1" + } + } + } + }, + "root_packages": { + "aiodns": { + "version": "1.1.1" + }, + "aioeventlet": { + "version": "0.5.1" + }, + "aioh2": { + "version": "0.2.2" + }, + "aiohttp": { + "version": "1.2.0" + }, + "bravado-core": { + "version": "5.16.1" + }, + "certifi": { + "version": "2019.6.16" + }, + "chardet": { + "version": "3.0.4" + }, + "click": { + "version": "7.1.2" + }, + "cryptography": { + "version": "3.2.1" + }, + "cython": { + "version": "0.29.1" + }, + "docker": { + "version": "4.0.2" + }, + "envoy": { + "version": "0.0.3" + }, + "eventlet": { + "version": "0.26.1" + }, + "fire": { + "version": "0.2.0" + }, + "flask": { + "version": "1.0.2" + }, + "freezegun": { + "version": "0.3.15" + }, + "glob2": { + "version": "0.7" + }, + "grpcio": { + "version": "1.36.1" + }, + "h2": { + "version": "3.2.0" + }, + "hpack": { + "version": "3.0.0" + }, + "idna": { + "version": "2.8" + }, + "itsdangerous": { + "version": "1.1.0" + }, + "jinja2": { + "version": "2.10" + }, + "jsonpickle": { + "version": "0.9.3" + }, + "jsonschema": { + "version": "3.1.0" + }, + "lxml": { + "version": "4.6.2" + }, + "netifaces": { + "version": "0.10.9" + }, + "protobuf": { + "version": "3.14.0" + }, + "psutil": { + "version": "5.6.6" + }, + "pycares": { + "version": "3.1.1" + }, + "pycryptodome": { + "version": "3.9.9" + }, + "pylint": { + "version": "2.6.2" + }, + "pymemoize": { + "version": "1.0.2" + }, + "pyroute2": { + "version": "0.5.14" + }, + "pystemd": { + "version": "0.8.0" + }, + "python-dateutil": { + "version": "2.8.1" + }, + "python-redis-lock": { + "version": "3.7.0" + }, + "pytz": { + "version": "2020.1" + }, + "pyyaml": { + "version": "3.12" + }, + "redis": { + "version": "3.5.3" + }, + "redis-collections": { + "version": "0.9.0" + }, + "requests": { + "version": "2.22.0" + }, + "rfc3987": { + "version": "1.3.8" + }, + "ryu": { + "version": "4.30" + }, + "scapy": { + "version": "2.4.4" + }, + "sentry-sdk": { + "version": "1.0.0" + }, + "setuptools": { + "version": "49.6.0" + }, + "six": { + "version": "1.12.0" + }, + "snowflake": { + "version": "0.0.3" + }, + "spyne": { + "version": "2.12.16" + }, + "strict-rfc3339": { + "version": "0.7" + }, + "urllib3": { + "version": "1.25.3" + }, + "webcolors": { + "version": "1.11.1" + }, + "websocket-client": { + "version": "0.56.0" + }, + "wsgiserver": { + "version": "1.3" + } + } +} diff --git a/lte/gateway/release/pydep b/lte/gateway/release/pydep index df619b8ab511..534af31d66c1 100755 --- a/lte/gateway/release/pydep +++ b/lte/gateway/release/pydep @@ -463,6 +463,9 @@ class Lockfile(object): log.warning('dependencies may not be generated correctly') self._release = release_info.get('VERSION_CODENAME', self._default_release) + if reference_lockfile is None: + reference_lockfile = "./release/magma.lockfile.{}".format(self._release) + def lower_keys(d: Dict[str, Any]): return {k.lower(): v for k, v in d.items()} @@ -1010,6 +1013,7 @@ def main(args): with open(args.lockfile, 'r') as r: lf = Lockfile(data=r.read()) except FileNotFoundError: + log.error("file not found: {}".format(args.lockfile)); pass else: lf = Lockfile() From a1da8aabe7f608680d4cf50510d57daa3d496a8a Mon Sep 17 00:00:00 2001 From: Pravin Shelar Date: Fri, 9 Apr 2021 09:33:56 +0530 Subject: [PATCH 74/91] AGW: reset OVS bridges on sctpd restart. (#6026) Perform complete OVS reset on sctpd restart. Signed-off-by: Pravin B Shelar --- lte/gateway/python/scripts/config_stateless_agw.py | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/lte/gateway/python/scripts/config_stateless_agw.py b/lte/gateway/python/scripts/config_stateless_agw.py index d8303ea3d9f0..6df1ef63da92 100755 --- a/lte/gateway/python/scripts/config_stateless_agw.py +++ b/lte/gateway/python/scripts/config_stateless_agw.py @@ -155,6 +155,14 @@ def disable_stateless_agw(): sys.exit(check_stateless_services().value) +def ovs_reset_bridges(): + subprocess.call("ifdown uplink_br0".split()) + subprocess.call("ifdown gtp_br0".split()) + subprocess.call("service openvswitch-switch restart".split()) + subprocess.call("ifup uplink_br0".split()) + subprocess.call("ifup gtp_br0".split()) + + def sctpd_pre_start(): if check_stateless_services() == return_codes.STATEFUL: # switching from stateless to stateful @@ -163,8 +171,7 @@ def sctpd_pre_start(): # Clean up all mobilityd, MME, pipelined and sessiond Redis keys clear_redis_state() # Clean up OVS flows - subprocess.call("service openvswitch-switch restart".split()) - + ovs_reset_bridges() sys.exit(0) From 31cde1de70de44b029b6fe84610bc93c932e56cd Mon Sep 17 00:00:00 2001 From: rashmi Date: Fri, 9 Apr 2021 16:28:34 +0530 Subject: [PATCH 75/91] Modified code to update qos from create session response only valid_qos flag is set Signed-off-by: rashmi --- .../c/oai/lib/s8_proxy/s8_client_api.cpp | 24 +++++++++++++------ 1 file changed, 17 insertions(+), 7 deletions(-) diff --git a/lte/gateway/c/oai/lib/s8_proxy/s8_client_api.cpp b/lte/gateway/c/oai/lib/s8_proxy/s8_client_api.cpp index 5e6579fc056b..47a30b50b57e 100644 --- a/lte/gateway/c/oai/lib/s8_proxy/s8_client_api.cpp +++ b/lte/gateway/c/oai/lib/s8_proxy/s8_client_api.cpp @@ -28,7 +28,7 @@ extern task_zmq_ctx_t grpc_service_task_zmq_ctx; static void convert_proto_msg_to_itti_csr( magma::feg::CreateSessionResponsePgw& response, - s8_create_session_response_t* s5_response); + s8_create_session_response_t* s5_response, bearer_qos_t dflt_bearer_qos); static void get_qos_from_proto_msg( const magma::feg::QosInformation& proto_qos, bearer_qos_t* bearer_qos) { @@ -154,7 +154,8 @@ static void recv_s8_delete_session_response( } static void recv_s8_create_session_response( - imsi64_t imsi64, teid_t context_teid, const grpc::Status& status, + imsi64_t imsi64, teid_t context_teid, bearer_qos_t dflt_bearer_qos, + const grpc::Status& status, magma::feg::CreateSessionResponsePgw& response) { OAILOG_FUNC_IN(LOG_SGW_S8); s8_create_session_response_t* s5_response = NULL; @@ -172,7 +173,7 @@ static void recv_s8_create_session_response( message_p->ittiMsgHeader.imsi = imsi64; s5_response->context_teid = context_teid; if (status.ok()) { - convert_proto_msg_to_itti_csr(response, s5_response); + convert_proto_msg_to_itti_csr(response, s5_response, dflt_bearer_qos); } else { OAILOG_ERROR( LOG_SGW_S8, @@ -405,6 +406,7 @@ void send_s8_create_session_request( imsi64_t imsi64) { OAILOG_FUNC_IN(LOG_SGW_S8); magma::feg::CreateSessionRequestPgw csr_req; + bearer_qos_t dflt_bearer_qos = {0}; // teid shall remain same for both sgw's s11 interface and s8 interface as // teid is allocated per PDN @@ -414,18 +416,21 @@ void send_s8_create_session_request( sgw_s11_teid); fill_s8_create_session_req(msg, &csr_req, sgw_s11_teid); + dflt_bearer_qos = + msg->bearer_contexts_to_be_created.bearer_contexts[0].bearer_level_qos; magma::S8Client::s8_create_session_request( csr_req, - [imsi64, sgw_s11_teid]( + [imsi64, sgw_s11_teid, dflt_bearer_qos]( grpc::Status status, magma::feg::CreateSessionResponsePgw response) { - recv_s8_create_session_response(imsi64, sgw_s11_teid, status, response); + recv_s8_create_session_response( + imsi64, sgw_s11_teid, dflt_bearer_qos, status, response); }); } static void convert_proto_msg_to_itti_csr( magma::feg::CreateSessionResponsePgw& response, - s8_create_session_response_t* s5_response) { + s8_create_session_response_t* s5_response, bearer_qos_t dflt_bearer_qos) { OAILOG_FUNC_IN(LOG_SGW_S8); s5_response->apn_restriction_value = response.apn_restriction(); get_fteid_from_proto_msg( @@ -437,7 +442,12 @@ static void convert_proto_msg_to_itti_csr( s8_bc->eps_bearer_id = response.bearer_context().id(); s5_response->eps_bearer_id = s8_bc->eps_bearer_id; s8_bc->charging_id = response.bearer_context().charging_id(); - get_qos_from_proto_msg(response.bearer_context().qos(), &s8_bc->qos); + if (response.bearer_context().valid_qos()) { + get_qos_from_proto_msg(response.bearer_context().qos(), &s8_bc->qos); + } else { + // If qos is not received from PGW, set the qos that was sent in CS Req + s8_bc->qos = dflt_bearer_qos; + } get_fteid_from_proto_msg( response.bearer_context().user_plane_fteid(), &s8_bc->pgw_s8_up); if (response.has_gtp_error()) { From 6ec27cad429e3e440b19e0191045caad27cc1fc6 Mon Sep 17 00:00:00 2001 From: Andrei Lee Date: Fri, 9 Apr 2021 04:07:54 -0700 Subject: [PATCH 76/91] [fuji] NMS DB data migration uses sequelize-models ^0.1.6 (#6058) Signed-off-by: Andrei Lee --- nms/app/packages/magmalte/scripts/fuji-upgrade/runs-on-nms.sh | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/nms/app/packages/magmalte/scripts/fuji-upgrade/runs-on-nms.sh b/nms/app/packages/magmalte/scripts/fuji-upgrade/runs-on-nms.sh index 741054b3586d..7035b9d02a00 100644 --- a/nms/app/packages/magmalte/scripts/fuji-upgrade/runs-on-nms.sh +++ b/nms/app/packages/magmalte/scripts/fuji-upgrade/runs-on-nms.sh @@ -44,11 +44,11 @@ done cat << EOF -------------------------------------------------------------------------------- - Upgrading @fbcnms/sequelize-models to ^0.1.5 + Upgrading @fbcnms/sequelize-models to ^0.1.6 -------------------------------------------------------------------------------- EOF pushd /usr/src/packages/magmalte -yarn upgrade @fbcnms/sequelize-models@^0.1.5 +yarn upgrade @fbcnms/sequelize-models@^0.1.6 yarn popd cat << EOF From 8ffa3832f9712559d827bc4fddb1cb25f8abed09 Mon Sep 17 00:00:00 2001 From: Pravin Shelar Date: Fri, 9 Apr 2021 21:24:34 +0530 Subject: [PATCH 77/91] AGW: MME: GTP: add S8 TEID args (#6046) * AGW: MME: GTP: add S8 TEID args Add missing TEIDs to program flow from eNB to PGW and vice-versa. now the API takes in in and out TEID for S1 (eNB) tunnel as well as S8 (PGW) tunnel. Signed-off-by: Pravin B Shelar --- .../openflow/controller/ControllerEvents.cpp | 24 +++++++- .../openflow/controller/ControllerEvents.h | 13 ++++- .../openflow/controller/ControllerMain.cpp | 14 ++--- .../lib/openflow/controller/ControllerMain.h | 6 +- .../openflow/controller/GTPApplication.cpp | 57 ++++++++++++------- .../lib/openflow/controller/GTPApplication.h | 10 ++-- .../c/oai/tasks/gtpv1-u/gtp_tunnel_openflow.c | 10 ++-- lte/gateway/c/oai/tasks/gtpv1-u/gtpv1u.h | 10 ++-- lte/gateway/c/oai/tasks/gtpv1-u/gtpv1u_task.c | 9 +-- .../c/oai/tasks/sgw_s8/sgw_s8_handlers.c | 3 +- .../c/oai/test/openflow/test_gtp_app.cpp | 21 ++++--- 11 files changed, 115 insertions(+), 62 deletions(-) diff --git a/lte/gateway/c/oai/lib/openflow/controller/ControllerEvents.cpp b/lte/gateway/c/oai/lib/openflow/controller/ControllerEvents.cpp index 50e12df9f74c..759b2584b247 100644 --- a/lte/gateway/c/oai/lib/openflow/controller/ControllerEvents.cpp +++ b/lte/gateway/c/oai/lib/openflow/controller/ControllerEvents.cpp @@ -144,6 +144,8 @@ AddGTPTunnelEvent::AddGTPTunnelEvent( pgw_ip_(INADDR_ZERO), in_tei_(in_tei), out_tei_(out_tei), + pgw_in_tei_(0), + pgw_out_tei_(0), imsi_(imsi), dl_flow_valid_(false), dl_flow_(), @@ -162,6 +164,8 @@ AddGTPTunnelEvent::AddGTPTunnelEvent( pgw_ip_(INADDR_ZERO), in_tei_(in_tei), out_tei_(out_tei), + pgw_in_tei_(0), + pgw_out_tei_(0), imsi_(imsi), dl_flow_valid_(true), dl_flow_(*dl_flow), @@ -173,7 +177,8 @@ AddGTPTunnelEvent::AddGTPTunnelEvent( AddGTPTunnelEvent::AddGTPTunnelEvent( const struct in_addr ue_ip, struct in6_addr* ue_ipv6, int vlan, const struct in_addr enb_ip, const struct in_addr pgw_ip, - const uint32_t in_tei, const uint32_t out_tei, const char* imsi, + const uint32_t in_tei, const uint32_t out_tei, const uint32_t pgw_in_tei, + const uint32_t pgw_out_tei, const char* imsi, const struct ip_flow_dl* dl_flow, const uint32_t dl_flow_precedence, uint32_t enb_gtp_port, uint32_t pgw_gtp_port) : ue_info_(ue_ip, ue_ipv6, vlan), @@ -181,6 +186,8 @@ AddGTPTunnelEvent::AddGTPTunnelEvent( pgw_ip_(pgw_ip), in_tei_(in_tei), out_tei_(out_tei), + pgw_in_tei_(pgw_in_tei), + pgw_out_tei_(pgw_out_tei), imsi_(imsi), dl_flow_valid_(false), dl_flow_(), @@ -192,13 +199,16 @@ AddGTPTunnelEvent::AddGTPTunnelEvent( AddGTPTunnelEvent::AddGTPTunnelEvent( const struct in_addr ue_ip, struct in6_addr* ue_ipv6, int vlan, const struct in_addr enb_ip, const struct in_addr pgw_ip, - const uint32_t in_tei, const uint32_t out_tei, const char* imsi, - uint32_t enb_gtp_port, uint32_t pgw_gtp_port) + const uint32_t in_tei, const uint32_t out_tei, const uint32_t pgw_in_tei, + const uint32_t pgw_out_tei, const char* imsi, uint32_t enb_gtp_port, + uint32_t pgw_gtp_port) : ue_info_(ue_ip, vlan), enb_ip_(enb_ip), pgw_ip_(pgw_ip), in_tei_(in_tei), out_tei_(out_tei), + pgw_in_tei_(pgw_in_tei), + pgw_out_tei_(pgw_out_tei), imsi_(imsi), dl_flow_valid_(false), dl_flow_(), @@ -231,6 +241,14 @@ const uint32_t AddGTPTunnelEvent::get_out_tei() const { return out_tei_; } +const uint32_t AddGTPTunnelEvent::get_pgw_in_tei() const { + return pgw_in_tei_; +} + +const uint32_t AddGTPTunnelEvent::get_pgw_out_tei() const { + return pgw_out_tei_; +} + const std::string& AddGTPTunnelEvent::get_imsi() const { return imsi_; } diff --git a/lte/gateway/c/oai/lib/openflow/controller/ControllerEvents.h b/lte/gateway/c/oai/lib/openflow/controller/ControllerEvents.h index 8bee0b9b49da..23909cf41615 100644 --- a/lte/gateway/c/oai/lib/openflow/controller/ControllerEvents.h +++ b/lte/gateway/c/oai/lib/openflow/controller/ControllerEvents.h @@ -187,15 +187,17 @@ class AddGTPTunnelEvent : public ExternalEvent { AddGTPTunnelEvent( const struct in_addr ue_ip, struct in6_addr* ue_ipv6, int vlan, const struct in_addr enb_ip, const struct in_addr pgw_ip, - const uint32_t in_tei, const uint32_t out_tei, const char* imsi, + const uint32_t in_tei, const uint32_t out_tei, const uint32_t pgw_in_tei, + const uint32_t pgw_out_tei, const char* imsi, const struct ip_flow_dl* dl_flow, const uint32_t dl_flow_precedence, uint32_t enb_gtp_port, uint32_t pgw_gtp_port); AddGTPTunnelEvent( const struct in_addr ue_ip, struct in6_addr* ue_ipv6, int vlan, const struct in_addr enb_ip, const struct in_addr pgw_ip, - const uint32_t in_tei, const uint32_t out_tei, const char* imsi, - uint32_t enb_gtp_port, uint32_t pgw_gtp_port); + const uint32_t in_tei, const uint32_t out_tei, const uint32_t pgw_in_tei, + const uint32_t pgw_out_tei, const char* imsi, uint32_t enb_gtp_port, + uint32_t pgw_gtp_port); const struct UeNetworkInfo& get_ue_info() const; const struct in_addr& get_ue_ip() const; @@ -206,6 +208,9 @@ class AddGTPTunnelEvent : public ExternalEvent { const uint32_t get_in_tei() const; const uint32_t get_out_tei() const; + const uint32_t get_pgw_in_tei() const; + const uint32_t get_pgw_out_tei() const; + const std::string& get_imsi() const; const bool is_dl_flow_valid() const; const struct ip_flow_dl& get_dl_flow() const; @@ -219,6 +224,8 @@ class AddGTPTunnelEvent : public ExternalEvent { const struct in_addr pgw_ip_; const uint32_t in_tei_; const uint32_t out_tei_; + const uint32_t pgw_in_tei_; + const uint32_t pgw_out_tei_; const std::string imsi_; const struct ip_flow_dl dl_flow_; const bool dl_flow_valid_; diff --git a/lte/gateway/c/oai/lib/openflow/controller/ControllerMain.cpp b/lte/gateway/c/oai/lib/openflow/controller/ControllerMain.cpp index bf501ef8d4fd..81204a87fddb 100644 --- a/lte/gateway/c/oai/lib/openflow/controller/ControllerMain.cpp +++ b/lte/gateway/c/oai/lib/openflow/controller/ControllerMain.cpp @@ -126,18 +126,18 @@ int openflow_controller_del_gtp_tunnel( int openflow_controller_add_gtp_s8_tunnel( struct in_addr ue, struct in6_addr* ue_ipv6, int vlan, struct in_addr enb, - struct in_addr pgw, uint32_t i_tei, uint32_t o_tei, const char* imsi, - struct ip_flow_dl* flow_dl, uint32_t flow_precedence_dl, - uint32_t enb_gtp_port, uint32_t pgw_gtp_port) { + struct in_addr pgw, uint32_t i_tei, uint32_t o_tei, uint32_t pgw_i_tei, + uint32_t pgw_o_tei, const char* imsi, struct ip_flow_dl* flow_dl, + uint32_t flow_precedence_dl, uint32_t enb_gtp_port, uint32_t pgw_gtp_port) { if (flow_dl) { auto add_tunnel = std::make_shared( - ue, ue_ipv6, vlan, enb, pgw, i_tei, o_tei, imsi, flow_dl, - flow_precedence_dl, enb_gtp_port, pgw_gtp_port); + ue, ue_ipv6, vlan, enb, pgw, i_tei, o_tei, pgw_i_tei, pgw_o_tei, imsi, + flow_dl, flow_precedence_dl, enb_gtp_port, pgw_gtp_port); ctrl.inject_external_event(add_tunnel, external_event_callback); } else { auto add_tunnel = std::make_shared( - ue, ue_ipv6, vlan, enb, pgw, i_tei, o_tei, imsi, enb_gtp_port, - pgw_gtp_port); + ue, ue_ipv6, vlan, enb, pgw, i_tei, o_tei, pgw_i_tei, pgw_o_tei, imsi, + enb_gtp_port, pgw_gtp_port); ctrl.inject_external_event(add_tunnel, external_event_callback); } OAILOG_FUNC_RETURN(LOG_GTPV1U, RETURNok); diff --git a/lte/gateway/c/oai/lib/openflow/controller/ControllerMain.h b/lte/gateway/c/oai/lib/openflow/controller/ControllerMain.h index c859f31d558d..9d30252d87e3 100644 --- a/lte/gateway/c/oai/lib/openflow/controller/ControllerMain.h +++ b/lte/gateway/c/oai/lib/openflow/controller/ControllerMain.h @@ -56,9 +56,9 @@ int openflow_controller_delete_paging_rule(struct in_addr ue_ip); int openflow_controller_add_gtp_s8_tunnel( struct in_addr ue, struct in6_addr* ue_ipv6, int vlan, struct in_addr enb, - struct in_addr pgw, uint32_t i_tei, uint32_t o_tei, const char* imsi, - struct ip_flow_dl* flow_dl, uint32_t flow_precedence_dl, - uint32_t enb_gtp_port, uint32_t pgw_gtp_port); + struct in_addr pgw, uint32_t i_tei, uint32_t o_tei, uint32_t pgw_i_tei, + uint32_t pgw_o_tei, const char* imsi, struct ip_flow_dl* flow_dl, + uint32_t flow_precedence_dl, uint32_t enb_gtp_port, uint32_t pgw_gtp_port); int openflow_controller_del_gtp_s8_tunnel( struct in_addr ue, struct in6_addr* ue_ipv6, uint32_t i_tei, diff --git a/lte/gateway/c/oai/lib/openflow/controller/GTPApplication.cpp b/lte/gateway/c/oai/lib/openflow/controller/GTPApplication.cpp index 494d72d67a0f..4b1c64243600 100644 --- a/lte/gateway/c/oai/lib/openflow/controller/GTPApplication.cpp +++ b/lte/gateway/c/oai/lib/openflow/controller/GTPApplication.cpp @@ -49,12 +49,12 @@ GTPApplication::GTPApplication( void GTPApplication::event_callback( const ControllerEvent& ev, const OpenflowMessenger& messenger) { if (ev.get_type() == EVENT_ADD_GTP_TUNNEL) { - printf("pbs: reg add tun"); auto add_tunnel_event = static_cast(ev); add_uplink_tunnel_flow(add_tunnel_event, messenger); add_downlink_tunnel_flow( - add_tunnel_event, messenger, uplink_port_num_, false); - add_downlink_tunnel_flow(add_tunnel_event, messenger, mtr_port_num_, false); + add_tunnel_event, messenger, uplink_port_num_, false, false); + add_downlink_tunnel_flow( + add_tunnel_event, messenger, mtr_port_num_, false, false); add_downlink_arp_flow(add_tunnel_event, messenger, uplink_port_num_); add_downlink_arp_flow(add_tunnel_event, messenger, mtr_port_num_); } else if (ev.get_type() == EVENT_DELETE_GTP_TUNNEL) { @@ -65,16 +65,22 @@ void GTPApplication::event_callback( delete_downlink_arp_flow(del_tunnel_event, messenger, uplink_port_num_); delete_downlink_arp_flow(del_tunnel_event, messenger, mtr_port_num_); } else if (ev.get_type() == EVENT_ADD_GTP_S8_TUNNEL) { - printf("pbs: S8 add tun"); auto add_tunnel_event = static_cast(ev); + auto imsi = IMSIEncoder::compact_imsi(add_tunnel_event.get_imsi()); + + OAILOG_DEBUG_UE(LOG_GTPV1U, imsi, "s8: add: TEID: s1-in %u s1-out %u s8-in %u s8-out %u\n", + add_tunnel_event.get_in_tei(), add_tunnel_event.get_out_tei(), + add_tunnel_event.get_pgw_in_tei(), add_tunnel_event.get_pgw_out_tei()); + add_uplink_s8_tunnel_flow(add_tunnel_event, messenger); int pgw_port = add_tunnel_event.get_pgw_gtp_portno(); if (pgw_port == 0) { pgw_port = GTPApplication::gtp0_port_num_; } - add_downlink_tunnel_flow(add_tunnel_event, messenger, pgw_port, true); - add_downlink_tunnel_flow(add_tunnel_event, messenger, mtr_port_num_, true); + add_downlink_tunnel_flow(add_tunnel_event, messenger, pgw_port, true, true); + add_downlink_tunnel_flow( + add_tunnel_event, messenger, mtr_port_num_, true, true); add_downlink_arp_flow(add_tunnel_event, messenger, mtr_port_num_); } else if (ev.get_type() == EVENT_DELETE_GTP_S8_TUNNEL) { auto del_tunnel_event = static_cast(ev); @@ -207,7 +213,7 @@ void GTPApplication::add_uplink_s8_tunnel_flow( add_tunnel_match(uplink_fm, ev.get_enb_gtp_portno(), ev.get_in_tei()); add_tunnel_flow_action( - ev.get_out_tei(), ev.get_in_tei(), ev.get_imsi(), ev.get_pgw_ip(), + ev.get_pgw_out_tei(), ev.get_in_tei(), ev.get_imsi(), ev.get_pgw_ip(), ev.get_pgw_gtp_portno(), ev.get_connection(), messenger, uplink_fm, "S8 Uplink", true); } @@ -412,28 +418,36 @@ void GTPApplication::add_tunnel_flow_action( void GTPApplication::add_downlink_tunnel_flow_action( const AddGTPTunnelEvent& ev, const OpenflowMessenger& messenger, - of13::FlowMod downlink_fm, bool passthrough) { + of13::FlowMod downlink_fm, bool passthrough, bool from_pgw) { + uint32_t in_teid; + if (from_pgw) { + in_teid = ev.get_pgw_in_tei(); + } else { + in_teid = ev.get_in_tei(); + } + add_tunnel_flow_action( - ev.get_out_tei(), ev.get_in_tei(), ev.get_imsi(), ev.get_enb_ip(), + ev.get_out_tei(), in_teid, ev.get_imsi(), ev.get_enb_ip(), ev.get_enb_gtp_portno(), ev.get_connection(), messenger, downlink_fm, "S1 Downlink", passthrough); } void GTPApplication::add_downlink_tunnel_flow_ipv4( const AddGTPTunnelEvent& ev, const OpenflowMessenger& messenger, - uint32_t ingress_port, bool passthrough) { + uint32_t ingress_port, bool passthrough, bool from_pgw) { uint32_t flow_priority = convert_precedence_to_priority(ev.get_dl_flow_precedence()); of13::FlowMod downlink_fm = messenger.create_default_flow_mod(0, of13::OFPFC_ADD, flow_priority); add_downlink_match(downlink_fm, ev.get_ue_ip(), ingress_port); - add_downlink_tunnel_flow_action(ev, messenger, downlink_fm, passthrough); + add_downlink_tunnel_flow_action( + ev, messenger, downlink_fm, passthrough, from_pgw); } void GTPApplication::add_downlink_tunnel_flow_ipv6( const AddGTPTunnelEvent& ev, const OpenflowMessenger& messenger, - uint32_t ingress_port, bool passthrough) { + uint32_t ingress_port, bool passthrough, bool from_pgw) { uint32_t flow_priority = convert_precedence_to_priority(ev.get_dl_flow_precedence()); of13::FlowMod downlink_fm = @@ -441,35 +455,40 @@ void GTPApplication::add_downlink_tunnel_flow_ipv6( add_downlink_match_ipv6( downlink_fm, ev.get_ue_info().get_ipv6(), ingress_port); - add_downlink_tunnel_flow_action(ev, messenger, downlink_fm, passthrough); + add_downlink_tunnel_flow_action( + ev, messenger, downlink_fm, passthrough, from_pgw); } void GTPApplication::add_downlink_tunnel_flow_ded_brr( const AddGTPTunnelEvent& ev, const OpenflowMessenger& messenger, - uint32_t ingress_port, bool passthrough) { + uint32_t ingress_port, bool passthrough, bool from_pgw) { uint32_t flow_priority = convert_precedence_to_priority(ev.get_dl_flow_precedence()); of13::FlowMod downlink_fm = messenger.create_default_flow_mod(0, of13::OFPFC_ADD, flow_priority); add_ded_brr_dl_match(downlink_fm, ev.get_dl_flow(), ingress_port); - add_downlink_tunnel_flow_action(ev, messenger, downlink_fm, passthrough); + add_downlink_tunnel_flow_action( + ev, messenger, downlink_fm, passthrough, from_pgw); } void GTPApplication::add_downlink_tunnel_flow( const AddGTPTunnelEvent& ev, const OpenflowMessenger& messenger, - uint32_t ingress_port, bool passthrough) { + uint32_t ingress_port, bool passthrough, bool from_pgw) { if (ev.is_dl_flow_valid()) { - add_downlink_tunnel_flow_ded_brr(ev, messenger, ingress_port, passthrough); + add_downlink_tunnel_flow_ded_brr( + ev, messenger, ingress_port, passthrough, from_pgw); return; } UeNetworkInfo ue_info = ev.get_ue_info(); if (ue_info.is_ue_ipv4_addr_valid()) { - add_downlink_tunnel_flow_ipv4(ev, messenger, ingress_port, passthrough); + add_downlink_tunnel_flow_ipv4( + ev, messenger, ingress_port, passthrough, from_pgw); } if (ue_info.is_ue_ipv6_addr_valid()) { - add_downlink_tunnel_flow_ipv6(ev, messenger, ingress_port, passthrough); + add_downlink_tunnel_flow_ipv6( + ev, messenger, ingress_port, passthrough, from_pgw); } } diff --git a/lte/gateway/c/oai/lib/openflow/controller/GTPApplication.h b/lte/gateway/c/oai/lib/openflow/controller/GTPApplication.h index 69d7592605bb..a8e82a569573 100644 --- a/lte/gateway/c/oai/lib/openflow/controller/GTPApplication.h +++ b/lte/gateway/c/oai/lib/openflow/controller/GTPApplication.h @@ -63,7 +63,7 @@ class GTPApplication : public Application { */ void add_downlink_tunnel_flow( const AddGTPTunnelEvent& ev, const OpenflowMessenger& messenger, - uint32_t port_number, bool passthrough); + uint32_t port_number, bool passthrough, bool from_pgw); /* * Add downlink tunnel flow for S8 @@ -181,17 +181,17 @@ class GTPApplication : public Application { void add_downlink_tunnel_flow_action( const AddGTPTunnelEvent& ev, const OpenflowMessenger& messenger, - of13::FlowMod downlink_fm, bool passthrough); + of13::FlowMod downlink_fm, bool passthrough, bool from_pgw); void add_downlink_tunnel_flow_ipv4( const AddGTPTunnelEvent& ev, const OpenflowMessenger& messenger, - uint32_t port_number, bool passthrough); + uint32_t port_number, bool passthrough, bool from_pgw); void add_downlink_tunnel_flow_ipv6( const AddGTPTunnelEvent& ev, const OpenflowMessenger& messenger, - uint32_t port_number, bool passthrough); + uint32_t port_number, bool passthrough, bool from_pgw); void add_downlink_tunnel_flow_ded_brr( const AddGTPTunnelEvent& ev, const OpenflowMessenger& messenger, - uint32_t port_number, bool passthrough); + uint32_t port_number, bool passthrough, bool from_pgw); void delete_downlink_tunnel_flow_ipv4( const DeleteGTPTunnelEvent& ev, const OpenflowMessenger& messenger, diff --git a/lte/gateway/c/oai/tasks/gtpv1-u/gtp_tunnel_openflow.c b/lte/gateway/c/oai/tasks/gtpv1-u/gtp_tunnel_openflow.c index 0f6d6e9f27d5..118914a02a75 100644 --- a/lte/gateway/c/oai/tasks/gtpv1-u/gtp_tunnel_openflow.c +++ b/lte/gateway/c/oai/tasks/gtpv1-u/gtp_tunnel_openflow.c @@ -270,14 +270,16 @@ int openflow_del_tunnel( /* S8 tunnel related APIs */ int openflow_add_s8_tunnel( struct in_addr ue, struct in6_addr* ue_ipv6, int vlan, struct in_addr enb, - struct in_addr pgw, uint32_t i_tei, uint32_t o_tei, Imsi_t imsi, - struct ip_flow_dl* flow_dl, uint32_t flow_precedence_dl) { + struct in_addr pgw, uint32_t i_tei, uint32_t o_tei, uint32_t pgw_i_tei, + uint32_t pgw_o_tei, Imsi_t imsi, struct ip_flow_dl* flow_dl, + uint32_t flow_precedence_dl) { uint32_t enb_portno = find_gtp_port_no(enb); uint32_t pgw_portno = find_gtp_port_no(pgw); return openflow_controller_add_gtp_s8_tunnel( - ue, ue_ipv6, vlan, enb, pgw, i_tei, o_tei, (const char*) imsi.digit, - flow_dl, flow_precedence_dl, enb_portno, pgw_portno); + ue, ue_ipv6, vlan, enb, pgw, i_tei, o_tei, pgw_i_tei, pgw_o_tei, + (const char*) imsi.digit, flow_dl, flow_precedence_dl, enb_portno, + pgw_portno); } int openflow_del_s8_tunnel( diff --git a/lte/gateway/c/oai/tasks/gtpv1-u/gtpv1u.h b/lte/gateway/c/oai/tasks/gtpv1-u/gtpv1u.h index ca01b53b0d6d..459265fdee23 100644 --- a/lte/gateway/c/oai/tasks/gtpv1-u/gtpv1u.h +++ b/lte/gateway/c/oai/tasks/gtpv1-u/gtpv1u.h @@ -145,8 +145,9 @@ struct gtp_tunnel_ops { uint32_t i_tei, uint32_t o_tei, struct ip_flow_dl* flow_dl); int (*add_s8_tunnel)( struct in_addr ue, struct in6_addr* ue_ipv6, int vlan, struct in_addr enb, - struct in_addr pgw, uint32_t i_tei, uint32_t o_tei, Imsi_t imsi, - struct ip_flow_dl* flow_dl, uint32_t flow_precedence_dl); + struct in_addr pgw, uint32_t i_tei, uint32_t o_tei, uint32_t pgw_i_tei, + uint32_t pgw_o_tei, Imsi_t imsi, struct ip_flow_dl* flow_dl, + uint32_t flow_precedence_dl); int (*del_s8_tunnel)( struct in_addr enb, struct in_addr pgw, struct in_addr ue, struct in6_addr* ue_ipv6, uint32_t i_tei, uint32_t o_tei, @@ -176,6 +177,7 @@ int gtpv1u_add_tunnel( int gtpv1u_add_s8_tunnel( struct in_addr ue, struct in6_addr* ue_ipv6, int vlan, struct in_addr enb, - struct in_addr pgw, uint32_t i_tei, uint32_t o_tei, Imsi_t imsi, - struct ip_flow_dl* flow_dl, uint32_t flow_precedence_dl); + struct in_addr pgw, uint32_t i_tei, uint32_t o_tei, uint32_t pgw_i_tei, + uint32_t pgw_o_tei, Imsi_t imsi, struct ip_flow_dl* flow_dl, + uint32_t flow_precedence_dl); #endif /* FILE_GTPV1_U_SEEN */ diff --git a/lte/gateway/c/oai/tasks/gtpv1-u/gtpv1u_task.c b/lte/gateway/c/oai/tasks/gtpv1-u/gtpv1u_task.c index f492af7be8cf..dd3a99e284eb 100644 --- a/lte/gateway/c/oai/tasks/gtpv1-u/gtpv1u_task.c +++ b/lte/gateway/c/oai/tasks/gtpv1-u/gtpv1u_task.c @@ -202,13 +202,14 @@ int gtpv1u_add_tunnel( int gtpv1u_add_s8_tunnel( struct in_addr ue, struct in6_addr* ue_ipv6, int vlan, struct in_addr enb, - struct in_addr pgw, uint32_t i_tei, uint32_t o_tei, Imsi_t imsi, - struct ip_flow_dl* flow_dl, uint32_t flow_precedence_dl) { + struct in_addr pgw, uint32_t i_tei, uint32_t o_tei, uint32_t pgw_i_tei, + uint32_t pgw_o_tei, Imsi_t imsi, struct ip_flow_dl* flow_dl, + uint32_t flow_precedence_dl) { OAILOG_DEBUG(LOG_GTPV1U, "Add S8 tunnel ue %s", inet_ntoa(ue)); if (gtp_tunnel_ops->add_s8_tunnel) { return gtp_tunnel_ops->add_s8_tunnel( - ue, ue_ipv6, vlan, enb, pgw, i_tei, o_tei, imsi, flow_dl, - flow_precedence_dl); + ue, ue_ipv6, vlan, enb, pgw, i_tei, o_tei, pgw_i_tei, pgw_o_tei, imsi, + flow_dl, flow_precedence_dl); } else { return -EINVAL; } diff --git a/lte/gateway/c/oai/tasks/sgw_s8/sgw_s8_handlers.c b/lte/gateway/c/oai/tasks/sgw_s8/sgw_s8_handlers.c index 765a6d9a58ce..ff0642e03bd2 100644 --- a/lte/gateway/c/oai/tasks/sgw_s8/sgw_s8_handlers.c +++ b/lte/gateway/c/oai/tasks/sgw_s8/sgw_s8_handlers.c @@ -551,7 +551,8 @@ static int sgw_s8_add_gtp_tunnel( rv = gtpv1u_add_s8_tunnel( ue_ipv4, ue_ipv6, vlan, enb, pgw, eps_bearer_ctxt_p->s_gw_teid_S1u_S12_S4_up, - eps_bearer_ctxt_p->enb_teid_S1u, imsi, NULL, DEFAULT_PRECEDENCE); + eps_bearer_ctxt_p->enb_teid_S1u, 0, 0, imsi, NULL, + DEFAULT_PRECEDENCE); if (rv < 0) { OAILOG_ERROR_UE( LOG_SGW_S8, sgw_context_p->imsi64, diff --git a/lte/gateway/c/oai/test/openflow/test_gtp_app.cpp b/lte/gateway/c/oai/test/openflow/test_gtp_app.cpp index 80657d5ae2ba..977c4bd66c9d 100644 --- a/lte/gateway/c/oai/test/openflow/test_gtp_app.cpp +++ b/lte/gateway/c/oai/test/openflow/test_gtp_app.cpp @@ -911,17 +911,20 @@ TEST_F(GTPApplicationTest, TestAddTunnelS8) { struct in_addr enb_ip; enb_ip.s_addr = inet_addr("0.0.0.2"); struct in_addr pgw_ip; - enb_ip.s_addr = inet_addr("0.0.0.22"); - uint32_t in_tei = 1; - uint32_t out_tei = 2; - char imsi[] = "001010000000013"; - int vlan = 0; - int enb_port = 100; - int pgw_port = 200; + enb_ip.s_addr = inet_addr("0.0.0.22"); + uint32_t in_tei = 1; + uint32_t out_tei = 2; + uint32_t pgw_in_tei = 3; + uint32_t pgw_out_tei = 4; + + char imsi[] = "001010000000013"; + int vlan = 0; + int enb_port = 100; + int pgw_port = 200; AddGTPTunnelEvent add_tunnel( - ue_ip, NULL, vlan, enb_ip, pgw_ip, in_tei, out_tei, imsi, enb_port, - pgw_port); + ue_ip, NULL, vlan, enb_ip, pgw_ip, in_tei, out_tei, pgw_in_tei, + pgw_out_tei, imsi, enb_port, pgw_port); // Uplink EXPECT_CALL( *messenger, From b7b743d453ad3cd270a873116666c31321211b30 Mon Sep 17 00:00:00 2001 From: Pravin Shelar Date: Fri, 9 Apr 2021 22:15:25 +0530 Subject: [PATCH 78/91] AGW: build: Fix magma lockfile ref. (#6071) Signed-off-by: Pravin B Shelar --- circleci/fabfile.py | 6 +++--- orc8r/tools/fab/pkg.py | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/circleci/fabfile.py b/circleci/fabfile.py index 7b4d37829022..b222ccb15b1d 100644 --- a/circleci/fabfile.py +++ b/circleci/fabfile.py @@ -324,15 +324,15 @@ def _deploy_lte_packages(repo: str, magma_root: str): get('/tmp/packages.tar.gz', 'packages.tar.gz') get('/tmp/packages.txt', 'packages.txt') get('/tmp/magma_version', 'magma_version') - get(f'{repo_name}/{magma_root}/lte/gateway/release/magma.lockfile', - 'magma.lockfile') + get(f'{repo_name}/{magma_root}/lte/gateway/release/magma.lockfile.debian', + 'magma.lockfile.debian') with open('magma_version') as f: magma_version = f.readlines()[0].strip() s3_path = f's3://magma-images/gateway/{magma_version}' local(f'aws s3 cp packages.txt {s3_path}.deplist ' f'--acl bucket-owner-full-control') - local(f'aws s3 cp magma.lockfile {s3_path}.lockfile ' + local(f'aws s3 cp magma.lockfile.debian {s3_path}.lockfile.debian ' f'--acl bucket-owner-full-control') local(f'aws s3 cp packages.tar.gz {s3_path}.deps.tar.gz ' f'--acl bucket-owner-full-control') diff --git a/orc8r/tools/fab/pkg.py b/orc8r/tools/fab/pkg.py index 2ec5ee6c306a..37465b6e74be 100644 --- a/orc8r/tools/fab/pkg.py +++ b/orc8r/tools/fab/pkg.py @@ -120,7 +120,7 @@ def upload_pkgs_to_aws(): # Upload to AWS s3_path = 's3://magma-images/gateway/' + magma_version local('aws s3 cp /tmp/packages.txt ' + s3_path + '.deplist') - local('aws s3 cp release/magma.lockfile ' + s3_path + '.lockfile') + local('aws s3 cp release/magma.lockfile.debian ' + s3_path + '.lockfile.debian') local('aws s3 cp /tmp/packages.tar.gz ' + s3_path + '.deps.tar.gz') # Clean up From d1ff61933fc456b5bd1aa60ddc3292ed50ef8bf1 Mon Sep 17 00:00:00 2001 From: Alex Rodriguez Date: Fri, 9 Apr 2021 11:13:04 -0600 Subject: [PATCH 79/91] Adding ghz role in ansible to install and build (#5929) Signed-off-by: Alex Rodriguez --- docs/docusaurus/i18n/en.json | 3 +++ lte/gateway/deploy/roles/dev_common/tasks/main.yml | 7 +++++++ 2 files changed, 10 insertions(+) diff --git a/docs/docusaurus/i18n/en.json b/docs/docusaurus/i18n/en.json index af3b3381ecd9..975f6b78940b 100644 --- a/docs/docusaurus/i18n/en.json +++ b/docs/docusaurus/i18n/en.json @@ -346,6 +346,9 @@ "proposals/p012_resource-tagging": { "title": "proposals/p012_resource-tagging" }, + "proposals/p013_Ubuntu_upgrade": { + "title": "AGW Ubuntu upgrade" + }, "proposals/qos_enforcement": { "title": "proposals/qos_enforcement" }, diff --git a/lte/gateway/deploy/roles/dev_common/tasks/main.yml b/lte/gateway/deploy/roles/dev_common/tasks/main.yml index 9f695a98eb23..3cfbbed8d1a6 100644 --- a/lte/gateway/deploy/roles/dev_common/tasks/main.yml +++ b/lte/gateway/deploy/roles/dev_common/tasks/main.yml @@ -354,6 +354,13 @@ CODEGEN_ROOT: "{{ codegen_root }}" when: preburn +- name: Download gHZ binary from artifactory + get_url: + url: https://artifactory.magmacore.org:443/artifactory/blob-prod/ghz + dest: /usr/local/bin/ghz + mode: 0755 + when: full_provision + - name: Fix kernel commandline for interface naming. shell: | sed -i 's/enp0s3/eth0/g' /etc/netplan/50-cloud-init.yaml From 36765ba5c1bd09933602c7ec402efedef2dee499 Mon Sep 17 00:00:00 2001 From: pruthvihebbani <38202471+pruthvihebbani@users.noreply.github.com> Date: Fri, 9 Apr 2021 23:24:31 +0530 Subject: [PATCH 80/91] [agw][new feature] IMEI restriction support (#5331) * Add blocked imei configuration support Signed-off-by: Pruthvi Hebbani --- lte/gateway/c/oai/common/common_ies.h | 3 +- lte/gateway/c/oai/common/common_types.h | 2 + lte/gateway/c/oai/common/conversions.h | 39 +++ lte/gateway/c/oai/include/mme_config.h | 16 + lte/gateway/c/oai/tasks/mme_app/mme_config.c | 64 ++++ lte/gateway/c/oai/tasks/nas/emm/Attach.c | 36 ++ .../c/oai/tasks/nas/emm/Identification.c | 17 +- .../c/oai/tasks/nas/emm/SecurityModeControl.c | 70 +++- lte/gateway/c/oai/tasks/nas/emm/emm_data.h | 1 + lte/gateway/c/oai/tasks/nas/emm/msg/emm_msg.c | 7 +- .../c/oai/tasks/nas/emm/sap/emm_send.c | 2 + .../configs/templates/mme.conf.template | 11 + .../files/update_mme_config_for_sanity.sh | 22 ++ .../integ_tests/s1aptests/s1ap_utils.py | 22 +- .../test_imei_restriction_no_imeisv_in_smc.py | 313 ++++++++++++++++++ .../s1aptests/test_imei_restriction_smc.py | 177 ++++++++++ .../test_imei_restriction_wildcard_snr.py | 148 +++++++++ 17 files changed, 936 insertions(+), 14 deletions(-) create mode 100644 lte/gateway/python/integ_tests/s1aptests/test_imei_restriction_no_imeisv_in_smc.py create mode 100644 lte/gateway/python/integ_tests/s1aptests/test_imei_restriction_smc.py create mode 100644 lte/gateway/python/integ_tests/s1aptests/test_imei_restriction_wildcard_snr.py diff --git a/lte/gateway/c/oai/common/common_ies.h b/lte/gateway/c/oai/common/common_ies.h index 66e828db1e50..79f2f812ee9e 100644 --- a/lte/gateway/c/oai/common/common_ies.h +++ b/lte/gateway/c/oai/common/common_ies.h @@ -36,7 +36,8 @@ #include "3gpp_23.003.h" #define TMSI_SIZE 4 -#define MAX_IMEISV_SIZE 15 +#define MAX_IMEISV_SIZE 16 +#define MAX_IMEI_SIZE 15 #define MAX_MME_NAME_LENGTH 255 #define MAX_VLR_NAME_LENGTH 255 #define SGS_ASSOC_ACTIVE 1 diff --git a/lte/gateway/c/oai/common/common_types.h b/lte/gateway/c/oai/common/common_types.h index 721bb413e46d..31ab40e6c0ed 100644 --- a/lte/gateway/c/oai/common/common_types.h +++ b/lte/gateway/c/oai/common/common_types.h @@ -97,7 +97,9 @@ typedef teid_t s1u_teid_t; // IMSI typedef uint64_t imsi64_t; +typedef uint64_t imei64_t; #define IMSI_64_FMT "%" SCNu64 +#define IMEI_64_FMT "%" SCNu64 #define IMSI_64_FMT_DYN_LEN "%.*lu" #define INVALID_IMSI64 (imsi64_t) 0 diff --git a/lte/gateway/c/oai/common/conversions.h b/lte/gateway/c/oai/common/conversions.h index 446db74c2d5b..212763a19e6d 100644 --- a/lte/gateway/c/oai/common/conversions.h +++ b/lte/gateway/c/oai/common/conversions.h @@ -42,6 +42,7 @@ #include #include #include +#include #include "common_types.h" #include "3gpp_23.003.h" @@ -399,6 +400,10 @@ #define IMSI_STRING_TO_IMSI64(sTRING, iMSI64_pTr) \ sscanf(sTRING, IMSI_64_FMT, iMSI64_pTr) +/* Convert the IMEI contained by a char string NULL terminated to uint64_t */ +#define IMEI_STRING_TO_IMEI64(sTRING, iMEI64_pTr) \ + sscanf(sTRING, IMEI_64_FMT, iMEI64_pTr) + #define IMSI64_TO_CSFBIMSI(iMsI64_t, cSfBiMsI_t) \ { \ if ((iMsI64_t / 100000000000000) != 0) { \ @@ -518,6 +523,40 @@ imsi64_t imsi_to_imsi64(const imsi_t* const imsi); } \ } +#define IMEI_MOBID_TO_IMEI64(iMeI_t_PtR, iMEI64) \ + { \ + /*len = TAC((8 digits + SNR (6 digits) - 1)*/ \ + int len = 13; \ + (*iMEI64) += (uint64_t)((iMeI_t_PtR)->u.num.tac1 * (pow(10, (len--)))); \ + (*iMEI64) += (uint64_t)((iMeI_t_PtR)->u.num.tac2 * (pow(10, (len--)))); \ + (*iMEI64) += (uint64_t)((iMeI_t_PtR)->u.num.tac3 * (pow(10, (len--)))); \ + (*iMEI64) += (uint64_t)((iMeI_t_PtR)->u.num.tac4 * (pow(10, (len--)))); \ + (*iMEI64) += (uint64_t)((iMeI_t_PtR)->u.num.tac5 * (pow(10, (len--)))); \ + (*iMEI64) += (uint64_t)((iMeI_t_PtR)->u.num.tac6 * (pow(10, (len--)))); \ + (*iMEI64) += (uint64_t)((iMeI_t_PtR)->u.num.tac7 * (pow(10, (len--)))); \ + (*iMEI64) += (uint64_t)((iMeI_t_PtR)->u.num.tac8 * (pow(10, (len--)))); \ + (*iMEI64) += (uint64_t)((iMeI_t_PtR)->u.num.snr1 * (pow(10, (len--)))); \ + (*iMEI64) += (uint64_t)((iMeI_t_PtR)->u.num.snr2 * (pow(10, (len--)))); \ + (*iMEI64) += (uint64_t)((iMeI_t_PtR)->u.num.snr3 * (pow(10, (len--)))); \ + (*iMEI64) += (uint64_t)((iMeI_t_PtR)->u.num.snr4 * (pow(10, (len--)))); \ + (*iMEI64) += (uint64_t)((iMeI_t_PtR)->u.num.snr5 * (pow(10, (len--)))); \ + (*iMEI64) += (uint64_t)((iMeI_t_PtR)->u.num.snr6 * (pow(10, (len--)))); \ + } + +#define IMEI_MOBID_TO_IMEI_TAC64(iMeI_t_PtR, tAc_PtR) \ + { \ + /* len = TAC length(8 digits) - 1*/ \ + int len = 7; \ + (*tAc_PtR) += (uint64_t)((iMeI_t_PtR)->u.num.tac1 * (pow(10, (len--)))); \ + (*tAc_PtR) += (uint64_t)((iMeI_t_PtR)->u.num.tac2 * (pow(10, (len--)))); \ + (*tAc_PtR) += (uint64_t)((iMeI_t_PtR)->u.num.tac3 * (pow(10, (len--)))); \ + (*tAc_PtR) += (uint64_t)((iMeI_t_PtR)->u.num.tac4 * (pow(10, (len--)))); \ + (*tAc_PtR) += (uint64_t)((iMeI_t_PtR)->u.num.tac5 * (pow(10, (len--)))); \ + (*tAc_PtR) += (uint64_t)((iMeI_t_PtR)->u.num.tac6 * (pow(10, (len--)))); \ + (*tAc_PtR) += (uint64_t)((iMeI_t_PtR)->u.num.tac7 * (pow(10, (len--)))); \ + (*tAc_PtR) += (uint64_t)((iMeI_t_PtR)->u.num.tac8 * (pow(10, (len)))); \ + } + #define IMSI_TO_OCTET_STRING(iMsI_sTr, iMsI_len, aSN) \ do { \ int len = 0; \ diff --git a/lte/gateway/c/oai/include/mme_config.h b/lte/gateway/c/oai/include/mme_config.h index a4b87aba2c0e..dee94a8bd46a 100644 --- a/lte/gateway/c/oai/include/mme_config.h +++ b/lte/gateway/c/oai/include/mme_config.h @@ -47,6 +47,7 @@ #include "3gpp_24.008.h" #include "log.h" #include "service303.h" +#include "hashtable.h" /* Currently supporting max 5 GUMMEI's in the mme configuration */ #define MIN_GUMMEI 1 @@ -58,8 +59,12 @@ #define CIDR_SPLIT_LIST_COUNT 2 #define MAX_APN_CORRECTION_MAP_LIST 10 #define MAX_RESTRICTED_PLMN 10 +#define MAX_LEN_TAC 8 +#define MAX_LEN_SNR 6 +#define MAX_LEN_IMEI 15 #define MAX_FED_MODE_MAP_CONFIG 10 #define MAX_IMSI_LENGTH 15 +#define MAX_IMEI_HTBL_SZ 32 #define MME_CONFIG_STRING_MME_CONFIG "MME" #define MME_CONFIG_STRING_PID_DIRECTORY "PID_DIRECTORY" @@ -118,6 +123,9 @@ #define MME_CONFIG_STRING_TAC "TAC" #define MME_CONFIG_STRING_RESTRICTED_PLMN_LIST "RESTRICTED_PLMN_LIST" +#define MME_CONFIG_STRING_BLOCKED_IMEI_LIST "BLOCKED_IMEI_LIST" +#define MME_CONFIG_STRING_IMEI_TAC "IMEI_TAC" +#define MME_CONFIG_STRING_SNR "SNR" #define MME_CONFIG_STRING_NETWORK_INTERFACES_CONFIG "NETWORK_INTERFACES" #define MME_CONFIG_STRING_INTERFACE_NAME_FOR_S1_MME \ @@ -310,6 +318,12 @@ typedef struct restricted_plmn_s { plmn_t plmn[MAX_RESTRICTED_PLMN]; } restricted_plmn_config_t; +typedef struct blocked_imei_list_s { + int num; + // data is NULL + hash_table_uint64_ts_t* imei_htbl; +} blocked_imei_list_t; + typedef struct fed_mode_map_s { uint8_t mode; plmn_t plmn; @@ -355,6 +369,8 @@ typedef struct mme_config_s { restricted_plmn_config_t restricted_plmn; + blocked_imei_list_t blocked_imei; + served_tai_t served_tai; service303_data_t service303_config; diff --git a/lte/gateway/c/oai/tasks/mme_app/mme_config.c b/lte/gateway/c/oai/tasks/mme_app/mme_config.c index 3db96810e0f9..cd13672d12a3 100644 --- a/lte/gateway/c/oai/tasks/mme_app/mme_config.c +++ b/lte/gateway/c/oai/tasks/mme_app/mme_config.c @@ -63,6 +63,7 @@ #include "bstrlib.h" #include "mme_default_values.h" #include "service303.h" +#include "conversions.h" #if EMBEDDED_SGW #include "sgw_config.h" #endif @@ -277,6 +278,10 @@ void mme_config_exit(void) { for (int i = 0; i < mme_config.e_dns_emulation.nb_sgw_entries; i++) { bdestroy_wrapper(&mme_config.e_dns_emulation.sgw_id[i]); } + + if (mme_config.blocked_imei.imei_htbl) { + hashtable_uint64_ts_destroy(mme_config.blocked_imei.imei_htbl); + } } //------------------------------------------------------------------------------ @@ -311,6 +316,8 @@ int mme_config_parse_file(mme_config_t* config_pP) { const char* csfb_mcc = NULL; const char* csfb_mnc = NULL; const char* lac = NULL; + const char* tac_str = NULL; + const char* snr_str = NULL; config_init(&cfg); @@ -1043,6 +1050,63 @@ int mme_config_parse_file(mme_config_t* config_pP) { } } + // BLOCKED IMEI LIST SETTING + setting = config_setting_get_member( + setting_mme, MME_CONFIG_STRING_BLOCKED_IMEI_LIST); + char imei_str[MAX_LEN_IMEI + 1] = {0}; + imei64_t imei64 = 0; + config_pP->blocked_imei.num = 0; + OAILOG_INFO(LOG_MME_APP, "MME_CONFIG_STRING_BLOCKED_IMEI_LIST \n"); + if (setting != NULL) { + num = config_setting_length(setting); + OAILOG_INFO(LOG_MME_APP, "Number of blocked IMEIs configured =%d\n", num); + if (num > 0) { + // Create IMEI hashtable + hashtable_rc_t h_rc = HASH_TABLE_OK; + bstring b = bfromcstr("mme_app_config_imei_htbl"); + config_pP->blocked_imei.imei_htbl = + hashtable_uint64_ts_create(MAX_IMEI_HTBL_SZ, NULL, b); + bdestroy_wrapper(&b); + AssertFatal( + config_pP->blocked_imei.imei_htbl != NULL, + "Error creating IMEI hashtable\n"); + + for (i = 0; i < num; i++) { + memset(imei_str, 0, (MAX_LEN_IMEI + 1)); + sub2setting = config_setting_get_elem(setting, i); + if (sub2setting != NULL) { + if ((config_setting_lookup_string( + sub2setting, MME_CONFIG_STRING_IMEI_TAC, &tac_str))) { + AssertFatal( + strlen(tac_str) == MAX_LEN_TAC, + "Bad TAC length (%ld), it must be %u digits\n", + strlen(tac_str), MAX_LEN_TAC); + memcpy(imei_str, tac_str, strlen(tac_str)); + } + if ((config_setting_lookup_string( + sub2setting, MME_CONFIG_STRING_SNR, &snr_str))) { + if (strlen(snr_str)) { + AssertFatal( + strlen(snr_str) == MAX_LEN_SNR, + "Bad SNR length (%ld), it must be %u digits\n", + strlen(snr_str), MAX_LEN_SNR); + memcpy(&imei_str[strlen(tac_str)], snr_str, strlen(snr_str)); + } + } + // Store IMEI into hashlist + imei64 = 0; + IMEI_STRING_TO_IMEI64(imei_str, &imei64); + h_rc = hashtable_uint64_ts_insert( + config_pP->blocked_imei.imei_htbl, (const hash_key_t) imei64, + 0); + AssertFatal(h_rc == HASH_TABLE_OK, "Hashtable insertion failed\n"); + + config_pP->blocked_imei.num += 1; + } + } + } + } + // NETWORK INTERFACE SETTING setting = config_setting_get_member( setting_mme, MME_CONFIG_STRING_NETWORK_INTERFACES_CONFIG); diff --git a/lte/gateway/c/oai/tasks/nas/emm/Attach.c b/lte/gateway/c/oai/tasks/nas/emm/Attach.c index 12551442711c..8943477d2453 100644 --- a/lte/gateway/c/oai/tasks/nas/emm/Attach.c +++ b/lte/gateway/c/oai/tasks/nas/emm/Attach.c @@ -138,6 +138,8 @@ static int emm_attach_success_authentication_cb(emm_context_t* emm_context); static int emm_attach_failure_authentication_cb(emm_context_t* emm_context); static int emm_attach_success_security_cb(emm_context_t* emm_context); static int emm_attach_failure_security_cb(emm_context_t* emm_context); +static int emm_attach_identification_after_smc_success_cb( + emm_context_t* emm_context); /* Abnormal case attach procedures @@ -1301,6 +1303,40 @@ static int emm_attach_success_security_cb(emm_context_t* emm_context) { OAILOG_INFO(LOG_NAS_EMM, "ATTACH - Security procedure success!\n"); nas_emm_attach_proc_t* attach_proc = get_nas_specific_procedure_attach(emm_context); + if (!attach_proc) { + OAILOG_INFO_UE( + LOG_NAS_EMM, emm_context->_imsi64, + "EMM-PROC - attach_proc is NULL \n"); + OAILOG_FUNC_RETURN(LOG_NAS_EMM, RETURNerror); + } + + if (emm_context->initiate_identity_after_smc) { + emm_context->initiate_identity_after_smc = false; + OAILOG_INFO_UE( + LOG_NAS_EMM, emm_context->_imsi64, "Trigger identity procedure\n"); + rc = emm_proc_identification( + emm_context, (nas_emm_proc_t*) attach_proc, IDENTITY_TYPE_2_IMEISV, + emm_attach_identification_after_smc_success_cb, + emm_attach_failure_identification_cb); + + OAILOG_FUNC_RETURN(LOG_NAS_EMM, rc); + } + + rc = emm_attach(emm_context); + OAILOG_FUNC_RETURN(LOG_NAS_EMM, rc); +} + +//------------------------------------------------------------------------------ +static int emm_attach_identification_after_smc_success_cb( + emm_context_t* emm_context) { + OAILOG_FUNC_IN(LOG_NAS_EMM); + int rc = RETURNerror; + + OAILOG_INFO( + LOG_NAS_EMM, + "ATTACH - Identity procedure after smc procedure success!\n"); + nas_emm_attach_proc_t* attach_proc = + get_nas_specific_procedure_attach(emm_context); if (attach_proc) { rc = emm_attach(emm_context); diff --git a/lte/gateway/c/oai/tasks/nas/emm/Identification.c b/lte/gateway/c/oai/tasks/nas/emm/Identification.c index e9a25be6ffb5..fa2fcac232d8 100644 --- a/lte/gateway/c/oai/tasks/nas/emm/Identification.c +++ b/lte/gateway/c/oai/tasks/nas/emm/Identification.c @@ -46,7 +46,7 @@ /**************** E X T E R N A L D E F I N I T I O N S ****************/ /****************************************************************************/ extern int check_plmn_restriction(imsi_t imsi); - +extern int validate_imei(imeisv_t* imeisv); /****************************************************************************/ /******************* L O C A L D E F I N I T I O N S *******************/ /****************************************************************************/ @@ -244,9 +244,18 @@ int emm_proc_identification_complete( */ emm_ctx_set_valid_imei(emm_ctx, imei); } else if (imeisv) { - /* - * Update the IMEISV - */ + // Validate IMEI + int emm_cause = validate_imei(imeisv); + if (emm_cause != EMM_CAUSE_SUCCESS) { + OAILOG_ERROR( + LOG_NAS_EMM, + "EMMAS-SAP - Sending Attach Reject for ue_id =" MME_UE_S1AP_ID_FMT + " , emm_cause =(%d)\n", + ue_id, emm_cause); + rc = emm_proc_attach_reject(ue_id, emm_cause); + OAILOG_FUNC_RETURN(LOG_NAS_EMM, rc); + } + // Update the IMEISV emm_ctx_set_valid_imeisv(emm_ctx, imeisv); } else if (tmsi) { /* diff --git a/lte/gateway/c/oai/tasks/nas/emm/SecurityModeControl.c b/lte/gateway/c/oai/tasks/nas/emm/SecurityModeControl.c index c3498111bb94..88ae1b298111 100644 --- a/lte/gateway/c/oai/tasks/nas/emm/SecurityModeControl.c +++ b/lte/gateway/c/oai/tasks/nas/emm/SecurityModeControl.c @@ -66,6 +66,7 @@ #include "3gpp_36.401.h" #include "NasSecurityAlgorithms.h" #include "emm_asDef.h" +#include "emm_cause.h" #include "emm_cnDef.h" #include "emm_fsm.h" #include "emm_regDef.h" @@ -75,6 +76,7 @@ #include "nas/securityDef.h" #include "security_types.h" #include "mme_app_defs.h" +#include "conversions.h" /****************************************************************************/ /**************** E X T E R N A L D E F I N I T I O N S ****************/ @@ -375,6 +377,46 @@ int emm_proc_security_mode_control( OAILOG_FUNC_RETURN(LOG_NAS_EMM, rc); } +/**************************************************************************** + ** ** + ** Name: validate_imei() ** + ** ** + ** Description: Check if the received imei matches with the ** + ** blocked imei list ** + ** ** + ** Inputs: imei/imeisv string : imei received in security mode ** + ** complete/attach req ** + ** Outputs: ** + ** Return: EMM cause ** + ** Others: None ** + ** ** + ***************************************************************************/ +int validate_imei(imeisv_t* imeisv) { + OAILOG_FUNC_IN(LOG_NAS_EMM); + /* First convert only TAC to uint64_t. If TAC is not found in the hashlist, + * convert IMEI(TAC + SNR) into uint64_t and check if the key is found + * the hashlist + */ + imei64_t tac64 = 0; + IMEI_MOBID_TO_IMEI_TAC64(imeisv, &tac64); + hashtable_rc_t h_rc = hashtable_uint64_ts_is_key_exists( + mme_config.blocked_imei.imei_htbl, (const hash_key_t) tac64); + + if (HASH_TABLE_OK == h_rc) { + OAILOG_FUNC_RETURN(LOG_NAS_EMM, EMM_CAUSE_IMEI_NOT_ACCEPTED); + } else { + // Convert to imei to uint64_t + imei64_t imei64 = 0; + IMEI_MOBID_TO_IMEI64(imeisv, &imei64); + hashtable_rc_t h_rc = hashtable_uint64_ts_is_key_exists( + mme_config.blocked_imei.imei_htbl, (const hash_key_t) imei64); + if (HASH_TABLE_OK == h_rc) { + OAILOG_FUNC_RETURN(LOG_NAS_EMM, EMM_CAUSE_IMEI_NOT_ACCEPTED); + } + } + OAILOG_FUNC_RETURN(LOG_NAS_EMM, EMM_CAUSE_SUCCESS); +} + /**************************************************************************** ** ** ** Name: emm_proc_security_mode_complete() ** @@ -414,6 +456,14 @@ int emm_proc_security_mode_complete( ue_mm_context = mme_ue_context_exists_mme_ue_s1ap_id(ue_id); if (ue_mm_context) { emm_ctx = &ue_mm_context->emm_context; + if (!emm_ctx) { + OAILOG_ERROR( + LOG_NAS_EMM, + "EMM-PROC - emm context is NULL for (ue_id=" MME_UE_S1AP_ID_FMT + ")\n", + ue_id); + OAILOG_FUNC_RETURN(LOG_NAS_EMM, RETURNerror); + } } else { OAILOG_FUNC_RETURN(LOG_NAS_EMM, RETURNerror); } @@ -429,7 +479,14 @@ int emm_proc_security_mode_complete( void* timer_callback_arg = NULL; nas_stop_T3460(ue_id, &smc_proc->T3460, timer_callback_arg); - if (imeisvmob) { + /* If MME requested for imeisv in security mode cmd + * and UE did not include the same in security mode complete, + * set initiate_identity_after_smc flag to send identity request + * with identity type set to imeisv + */ + if (smc_proc->imeisv_request && !imeisvmob) { + emm_ctx->initiate_identity_after_smc = true; + } else if (smc_proc->imeisv_request && imeisvmob) { imeisv_t imeisv = {0}; imeisv.u.num.tac1 = imeisvmob->tac1; imeisv.u.num.tac2 = imeisvmob->tac2; @@ -448,6 +505,17 @@ int emm_proc_security_mode_complete( imeisv.u.num.svn1 = imeisvmob->svn1; imeisv.u.num.svn2 = imeisvmob->svn2; imeisv.u.num.parity = imeisvmob->oddeven; + + int emm_cause = validate_imei(&imeisv); + if (emm_cause != EMM_CAUSE_SUCCESS) { + OAILOG_ERROR( + LOG_NAS_EMM, + "EMMAS-SAP - Sending Attach Reject for ue_id =" MME_UE_S1AP_ID_FMT + " , emm_cause =(%d)\n", + ue_id, emm_cause); + rc = emm_proc_attach_reject(ue_id, emm_cause); + OAILOG_FUNC_RETURN(LOG_NAS_EMM, rc); + } emm_ctx_set_valid_imeisv(emm_ctx, &imeisv); } diff --git a/lte/gateway/c/oai/tasks/nas/emm/emm_data.h b/lte/gateway/c/oai/tasks/nas/emm/emm_data.h index a62d0b105e8f..e35522a369c0 100644 --- a/lte/gateway/c/oai/tasks/nas/emm/emm_data.h +++ b/lte/gateway/c/oai/tasks/nas/emm/emm_data.h @@ -371,6 +371,7 @@ typedef struct emm_context_s { */ bool nw_init_bearer_deactv; new_attach_info_t* new_attach_info; + bool initiate_identity_after_smc; } emm_context_t; /* diff --git a/lte/gateway/c/oai/tasks/nas/emm/msg/emm_msg.c b/lte/gateway/c/oai/tasks/nas/emm/msg/emm_msg.c index c32f214b3832..1d560020fa95 100644 --- a/lte/gateway/c/oai/tasks/nas/emm/msg/emm_msg.c +++ b/lte/gateway/c/oai/tasks/nas/emm/msg/emm_msg.c @@ -174,12 +174,11 @@ int emm_msg_decode(EMM_msg* msg, uint8_t* buffer, uint32_t len) { case SECURITY_MODE_COMPLETE: decode_result = decode_security_mode_complete( &msg->security_mode_complete, buffer, len); - // MAX_IMEISV_SIZE is defined as 15, but IMEISV is - // actually 16 digits. Extra char for null termination. - char imeisv[MAX_IMEISV_SIZE + 2]; + // IMEISV is 16 digits. Extra char for null termination. + char imeisv[MAX_IMEISV_SIZE + 1]; IMEISV_MOBID_TO_STRING( &msg->security_mode_complete.imeisv.imeisv, imeisv, - MAX_IMEISV_SIZE + 2); + MAX_IMEISV_SIZE + 1); OAILOG_INFO(LOG_NAS_EMM, "EMM-MSG - IMEISV: %s", imeisv); break; diff --git a/lte/gateway/c/oai/tasks/nas/emm/sap/emm_send.c b/lte/gateway/c/oai/tasks/nas/emm/sap/emm_send.c index fe4a5d22dc74..b198bad8657d 100644 --- a/lte/gateway/c/oai/tasks/nas/emm/sap/emm_send.c +++ b/lte/gateway/c/oai/tasks/nas/emm/sap/emm_send.c @@ -1308,6 +1308,8 @@ int emm_send_identity_request( emm_msg->identitytype = IDENTITY_TYPE_2_TMSI; } else if (msg->ident_type == IDENTITY_TYPE_2_IMEI) { emm_msg->identitytype = IDENTITY_TYPE_2_IMEI; + } else if (msg->ident_type == IDENTITY_TYPE_2_IMEISV) { + emm_msg->identitytype = IDENTITY_TYPE_2_IMEISV; } else { /* * All other values are interpreted as "IMSI" diff --git a/lte/gateway/configs/templates/mme.conf.template b/lte/gateway/configs/templates/mme.conf.template index 503236cf8c20..46ebf5265d93 100644 --- a/lte/gateway/configs/templates/mme.conf.template +++ b/lte/gateway/configs/templates/mme.conf.template @@ -106,6 +106,17 @@ MME : {% endfor %} ); + # List of blocked IMEIs + # By default this list is empty + # Max number of blocked IMEI is 10 + # Length of IMEI=15 digits, length of IMEISV=16 digits + BLOCKED_IMEI_LIST = ( + # Sample IMEI: TAC(8 digits) + SNR (6 digits) + #{ TAC="99000482"; SNR="351037"} + # Sample IMEI without SNR: TAC(8 digits) + #{ TAC="99000482";} + ); + CSFB : { NON_EPS_SERVICE_CONTROL = "{{ non_eps_service_control }}"; diff --git a/lte/gateway/deploy/roles/magma/files/update_mme_config_for_sanity.sh b/lte/gateway/deploy/roles/magma/files/update_mme_config_for_sanity.sh index e9795ce185a0..5378548d3a3c 100755 --- a/lte/gateway/deploy/roles/magma/files/update_mme_config_for_sanity.sh +++ b/lte/gateway/deploy/roles/magma/files/update_mme_config_for_sanity.sh @@ -87,6 +87,27 @@ function configure_restricted_plmn { "$mme_config_file" } +function configure_blocked_imei { + # Remove default blocked imei(s) from MME configuration file + sed -i -e '/BLOCKED_IMEI_LIST/{n;N;N;N;d}' \ + "$mme_config_file" + + # Configure blocked imei(s) in MME configuration file + blocked_imei_config=( + '{ IMEI_TAC="99000482"; SNR="351037" }' + '{ IMEI_TAC="99333821"; }' + ) + blocked_imei_cmd_str="" + for config in "${blocked_imei_config[@]}" + do + blocked_imei_cmd_str="$blocked_imei_cmd_str\ \ \ \ \ \ \ \ $config,\n" + done + blocked_imei_cmd_str=${blocked_imei_cmd_str::-3} + + sed -i -e "/BLOCKED_IMEI_LIST/a $blocked_imei_cmd_str" \ + "$mme_config_file" +} + function restore_mme_config { # Restore the MME configuration from the backup configuration file and # delete the backup configuration file, so that MME will use latest @@ -108,6 +129,7 @@ if [[ $1 == "modify" ]]; then configure_multiple_plmn_tac reduce_mobile_reachability_timer_value configure_restricted_plmn + configure_blocked_imei elif [[ $1 == "restore" ]]; then # Restore the MME configuration file from the backup config file restore_mme_config diff --git a/lte/gateway/python/integ_tests/s1aptests/s1ap_utils.py b/lte/gateway/python/integ_tests/s1aptests/s1ap_utils.py index 1c2c1c7b3896..3637989a0dc6 100644 --- a/lte/gateway/python/integ_tests/s1aptests/s1ap_utils.py +++ b/lte/gateway/python/integ_tests/s1aptests/s1ap_utils.py @@ -552,6 +552,7 @@ class SubscriberUtil(object): SID_PREFIX = "IMSI00101" IMSI_LEN = 15 + MAX_IMEI_LEN = 16 def __init__(self, subscriber_client): """ @@ -563,6 +564,8 @@ def __init__(self, subscriber_client): """ self._sid_idx = 1 self._ue_id = 1 + self._imei_idx = 1 + self._imei_default = 3805468432113170 # Maintain references to UE configs to prevent GC self._ue_cfgs = [] @@ -580,11 +583,21 @@ def _gen_next_sid(self): print("Using subscriber IMSI %s" % sid) return sid - def _get_s1ap_sub(self, sid): + def _generate_imei(self, num_ues=1): + """ Generates 16 digit IMEI which includes SVN """ + imei = str(self._imei_default + self._imei_idx) + assert(len(imei) <= self.MAX_IMEI_LEN), "Invalid IMEI length" + self._imei_idx += 1 + print("Using IMEI %s" % imei) + return imei + + + def _get_s1ap_sub(self, sid, imei): """ Get the subscriber data in s1aptester format. Args: The string representation of the subscriber id + and imei """ ue_cfg = s1ap_types.ueConfig_t() ue_cfg.ue_id = self._ue_id @@ -593,8 +606,8 @@ def _get_s1ap_sub(self, sid): # cast into a uint8. for i in range(0, 15): ue_cfg.imsi[i] = ctypes.c_ubyte(int(sid[4 + i])) - ue_cfg.imei[i] = ctypes.c_ubyte(int("1")) - ue_cfg.imei[15] = ctypes.c_ubyte(int("1")) + for i in range(0, len(imei)): + ue_cfg.imei[i] = ctypes.c_ubyte(int(imei[i])) ue_cfg.imsiLen = self.IMSI_LEN self._ue_cfgs.append(ue_cfg) self._ue_id += 1 @@ -607,7 +620,8 @@ def add_sub(self, num_ues=1): for _ in range(num_ues): sid = self._gen_next_sid() self._subscriber_client.add_subscriber(sid) - subscribers.append(self._get_s1ap_sub(sid)) + imei = self._generate_imei() + subscribers.append(self._get_s1ap_sub(sid, imei)) self._subscriber_client.wait_for_changes() return subscribers diff --git a/lte/gateway/python/integ_tests/s1aptests/test_imei_restriction_no_imeisv_in_smc.py b/lte/gateway/python/integ_tests/s1aptests/test_imei_restriction_no_imeisv_in_smc.py new file mode 100644 index 000000000000..a48119cd5faa --- /dev/null +++ b/lte/gateway/python/integ_tests/s1aptests/test_imei_restriction_no_imeisv_in_smc.py @@ -0,0 +1,313 @@ +""" +Copyright 2020 The Magma Authors. + +This source code is licensed under the BSD-style license found in the +LICENSE file in the root directory of this source tree. + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +""" + + +import unittest + +import s1ap_types +import s1ap_wrapper +import time +import ctypes + + +class TestImeiRestrictionNoImeisvInSmc(unittest.TestCase): + def setUp(self): + self._s1ap_wrapper = s1ap_wrapper.TestWrapper() + + def tearDown(self): + self._s1ap_wrapper.cleanup() + + def test_imei_restriction_no_imeisv_in_smc(self): + """ + This TC does the following: + UE 1: + 1. Send security mode complete message without imeisv + 2. Receive identity request with id type imeisv + 3. Send identity rsp with blocked imeisv + 4. Verify attach reject is received with cause(5) + IMEI_NOT_ACCEPTED + UE 2: + 1. Send security mode complete message without imeisv + 2. Receive identity request with id type imeisv + 3. Send identity rsp with an allowed imeisv + 4. Detach the UE + + If this TC is executed individually run + test_modify_mme_config_for_sanity.py to add + { IMEI="9900048235103723" } + under the BLOCKED_IMEI_LIST in mme.conf.template. + + After execution of this TC run test_restore_mme_config_after_sanity.py + to restore the old mme.conf.template. + """ + num_ues = 2 + ue_ids = [] + self._s1ap_wrapper.configUEDevice(num_ues) + for _ in range(num_ues): + req = self._s1ap_wrapper.ue_req + ue_ids.append(req.ue_id) + # Send Attach Request + attach_req = s1ap_types.ueAttachRequest_t() + sec_ctxt = s1ap_types.TFW_CREATE_NEW_SECURITY_CONTEXT + id_type = s1ap_types.TFW_MID_TYPE_IMSI + eps_type = s1ap_types.TFW_EPS_ATTACH_TYPE_EPS_ATTACH + pdn_type = s1ap_types.pdn_Type() + pdn_type.pres = True + pdn_type.pdn_type = 1 + attach_req.ue_Id = ue_ids[0] + attach_req.mIdType = id_type + attach_req.epsAttachType = eps_type + attach_req.useOldSecCtxt = sec_ctxt + attach_req.pdnType_pr = pdn_type + + self._s1ap_wrapper._s1_util.issue_cmd( + s1ap_types.tfwCmd.UE_ATTACH_REQUEST, attach_req + ) + print( + "********************** Sent attach req for UE id ", ue_ids[0], + ) + + response = self._s1ap_wrapper.s1_util.get_response() + self.assertEqual( + response.msg_type, s1ap_types.tfwCmd.UE_AUTH_REQ_IND.value + ) + auth_req = response.cast(s1ap_types.ueAuthReqInd_t) + print( + "********************** Received auth req for UE id", + auth_req.ue_Id, + ) + + # Send Authentication Response + auth_res = s1ap_types.ueAuthResp_t() + auth_res.ue_Id = ue_ids[0] + sqnRecvd = s1ap_types.ueSqnRcvd_t() + sqnRecvd.pres = 0 + auth_res.sqnRcvd = sqnRecvd + self._s1ap_wrapper._s1_util.issue_cmd( + s1ap_types.tfwCmd.UE_AUTH_RESP, auth_res + ) + print("********************** Sent auth rsp for UE id", ue_ids[0]) + + response = self._s1ap_wrapper.s1_util.get_response() + self.assertEqual( + response.msg_type, s1ap_types.tfwCmd.UE_SEC_MOD_CMD_IND.value + ) + sec_mod_cmd = response.cast(s1ap_types.ueSecModeCmdInd_t) + print( + "********************** Received security mode cmd for UE id", + sec_mod_cmd.ue_Id, + ) + + # Send Security Mode Complete + sec_mode_complete = s1ap_types.ueSecModeComplete_t() + sec_mode_complete.ue_Id = ue_ids[0] + # Do not include imeisv + sec_mode_complete.noImeisv = True + self._s1ap_wrapper._s1_util.issue_cmd( + s1ap_types.tfwCmd.UE_SEC_MOD_COMPLETE, sec_mode_complete + ) + print( + "********************** Sent security mode complete for UE id", + ue_ids[0], + ) + + response = self._s1ap_wrapper.s1_util.get_response() + self.assertEqual( + response.msg_type, s1ap_types.tfwCmd.UE_IDENTITY_REQ_IND.value + ) + id_req = response.cast(s1ap_types.ueIdentityReqInd_t) + print( + "********************** Received identity req for UE id", + id_req.ue_Id, + ) + + # Mobile Identity types + # IMSI=1, IMEI=2, IMEISV=3, TMSI=4, TMGI=5, GUTI=6 + identity_resp = s1ap_types.ueIdentityResp_t() + identity_resp.ue_Id = ue_ids[0] + identity_resp.idType = 3 + identity_resp.idValPres = True + imeisv = "9900048235103723" + # Check if the len of imeisv exceeds 16 + self.assertLessEqual(len(imeisv), 16) + for i in range(0, len(imeisv)): + identity_resp.idVal[i] = ctypes.c_ubyte(int(imeisv[i])) + self._s1ap_wrapper._s1_util.issue_cmd( + s1ap_types.tfwCmd.UE_IDENTITY_RESP, identity_resp + ) + print("********************** Sent identity rsp for UE id", ue_ids[0]) + + # Receive Attach Reject + response = self._s1ap_wrapper.s1_util.get_response() + self.assertEqual( + response.msg_type, s1ap_types.tfwCmd.UE_ATTACH_REJECT_IND.value + ) + attach_rej = response.cast(s1ap_types.ueAttachRejInd_t) + print( + "********************** Received attach reject for UE id %d" + " with emm cause %d" % (ue_ids[0], attach_rej.cause) + ) + + # Verify cause + self.assertEqual( + attach_rej.cause, s1ap_types.TFW_EMM_CAUSE_IMEI_NOT_ACCEPTED + ) + + # UE Context release + response = self._s1ap_wrapper.s1_util.get_response() + self.assertEqual( + response.msg_type, s1ap_types.tfwCmd.UE_CTX_REL_IND.value + ) + ue_context_rel = response.cast(s1ap_types.ueCntxtRelReq_t) + print( + "********************** Received UE_CTX_REL_IND for UE id ", + ue_context_rel.ue_Id, + ) + + # Attach the 2nd UE + attach_req = s1ap_types.ueAttachRequest_t() + sec_ctxt = s1ap_types.TFW_CREATE_NEW_SECURITY_CONTEXT + id_type = s1ap_types.TFW_MID_TYPE_IMSI + eps_type = s1ap_types.TFW_EPS_ATTACH_TYPE_EPS_ATTACH + pdn_type = s1ap_types.pdn_Type() + pdn_type.pres = True + pdn_type.pdn_type = 1 + attach_req.ue_Id = ue_ids[1] + attach_req.mIdType = id_type + attach_req.epsAttachType = eps_type + attach_req.useOldSecCtxt = sec_ctxt + attach_req.pdnType_pr = pdn_type + + self._s1ap_wrapper._s1_util.issue_cmd( + s1ap_types.tfwCmd.UE_ATTACH_REQUEST, attach_req + ) + print( + "********************** Sent attach req for UE id ", ue_ids[1], + ) + + response = self._s1ap_wrapper.s1_util.get_response() + self.assertEqual( + response.msg_type, s1ap_types.tfwCmd.UE_AUTH_REQ_IND.value + ) + auth_req = response.cast(s1ap_types.ueAuthReqInd_t) + print( + "********************** Received auth req for UE id", + auth_req.ue_Id, + ) + # Send Authentication Response + auth_res = s1ap_types.ueAuthResp_t() + auth_res.ue_Id = ue_ids[1] + sqnRecvd = s1ap_types.ueSqnRcvd_t() + sqnRecvd.pres = 0 + auth_res.sqnRcvd = sqnRecvd + self._s1ap_wrapper._s1_util.issue_cmd( + s1ap_types.tfwCmd.UE_AUTH_RESP, auth_res + ) + print("********************** Sent auth rsp for UE id", ue_ids[1]) + + response = self._s1ap_wrapper.s1_util.get_response() + self.assertEqual( + response.msg_type, s1ap_types.tfwCmd.UE_SEC_MOD_CMD_IND.value + ) + sec_mod_cmd = response.cast(s1ap_types.ueSecModeCmdInd_t) + print( + "********************** Received security mode cmd for UE id", + sec_mod_cmd.ue_Id, + ) + + # Send Security Mode Complete + sec_mode_complete = s1ap_types.ueSecModeComplete_t() + sec_mode_complete.ue_Id = ue_ids[1] + # Do not include imeisv + sec_mode_complete.noImeisv = True + self._s1ap_wrapper._s1_util.issue_cmd( + s1ap_types.tfwCmd.UE_SEC_MOD_COMPLETE, sec_mode_complete + ) + print( + "********************** Sent security mode complete for UE id", + ue_ids[1], + ) + + response = self._s1ap_wrapper.s1_util.get_response() + self.assertEqual( + response.msg_type, s1ap_types.tfwCmd.UE_IDENTITY_REQ_IND.value + ) + id_req = response.cast(s1ap_types.ueIdentityReqInd_t) + print( + "********************** Received identity req for UE id", + id_req.ue_Id, + ) + # Send Identity Request + id_req = response.cast(s1ap_types.ueIdentityReqInd_t) + identity_resp = s1ap_types.ueIdentityResp_t() + identity_resp.ue_Id = id_req.ue_Id + identity_resp.idType = id_req.idType + self._s1ap_wrapper._s1_util.issue_cmd( + s1ap_types.tfwCmd.UE_IDENTITY_RESP, identity_resp + ) + print("********************** Sent identity rsp for UE id", ue_ids[1]) + + response = self._s1ap_wrapper.s1_util.get_response() + self.assertEqual( + response.msg_type, s1ap_types.tfwCmd.INT_CTX_SETUP_IND.value + ) + + response = self._s1ap_wrapper.s1_util.get_response() + self.assertEqual( + response.msg_type, s1ap_types.tfwCmd.UE_ATTACH_ACCEPT_IND.value + ) + attach_acc = response.cast(s1ap_types.ueAttachAccept_t) + print( + "********************** Received attach accept for UE id", + attach_acc.ue_Id, + ) + # Send Attach Complete + attach_complete = s1ap_types.ueAttachComplete_t() + attach_complete.ue_Id = ue_ids[1] + self._s1ap_wrapper._s1_util.issue_cmd( + s1ap_types.tfwCmd.UE_ATTACH_COMPLETE, attach_complete + ) + print( + "********************** Sent attach complete for UE id", ue_ids[1], + ) + response = self._s1ap_wrapper.s1_util.get_response() + self.assertEqual( + response.msg_type, s1ap_types.tfwCmd.UE_EMM_INFORMATION.value + ) + + print("********************** Sleeping for 0.5 seconds ") + time.sleep(0.5) + # Now detach the UE + print("********************** Running UE detach for UE id ", ue_ids[1]) + detach_req = s1ap_types.uedetachReq_t() + detach_req.ue_Id = ue_ids[1] + detach_req.ueDetType = ( + s1ap_types.ueDetachType_t.UE_SWITCHOFF_DETACH.value + ) + self._s1ap_wrapper._s1_util.issue_cmd( + s1ap_types.tfwCmd.UE_DETACH_REQUEST, detach_req + ) + # Wait for UE context release command + response = self._s1ap_wrapper.s1_util.get_response() + self.assertEqual( + response.msg_type, s1ap_types.tfwCmd.UE_CTX_REL_IND.value + ) + ue_context_rel = response.cast(s1ap_types.ueCntxtRelReq_t) + print( + "********************** Received UE_CTX_REL_IND for UE id ", + ue_context_rel.ue_Id, + ) + + +if __name__ == "__main__": + unittest.main() diff --git a/lte/gateway/python/integ_tests/s1aptests/test_imei_restriction_smc.py b/lte/gateway/python/integ_tests/s1aptests/test_imei_restriction_smc.py new file mode 100644 index 000000000000..11cbdc5cff33 --- /dev/null +++ b/lte/gateway/python/integ_tests/s1aptests/test_imei_restriction_smc.py @@ -0,0 +1,177 @@ +""" +Copyright 2020 The Magma Authors. + +This source code is licensed under the BSD-style license found in the +LICENSE file in the root directory of this source tree. + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +""" + + +import unittest + +import s1ap_types +import s1ap_wrapper +import time +import ctypes + + +class TestImeiRestrictionSmc(unittest.TestCase): + def setUp(self): + self._s1ap_wrapper = s1ap_wrapper.TestWrapper() + + def tearDown(self): + self._s1ap_wrapper.cleanup() + + def test_imei_restriction_smc(self): + """ + This TC does the following: + 1. Send security mode complete message with a blocked imeisv + which is same as the one configured in mme.conf.template + 2. Verify that attach reject is received with cause(5) + IMEI_NOT_ACCEPTED + 3. Attach a 2nd UE with an allowed IMEISV by invoking attach utility + function + 4. Detach the UE + + If this TC is executed individually run + test_modify_mme_config_for_sanity.py to add + { IMEI="9900048235103723"} + under the BLOCKED_IMEI_LIST in mme.conf.template. + + After execution of this TC run test_restore_mme_config_after_sanity.py + to restore the old mme.conf.template. + """ + num_ues = 2 + ue_ids = [] + self._s1ap_wrapper.configUEDevice(num_ues) + for _ in range(num_ues): + req = self._s1ap_wrapper.ue_req + ue_ids.append(req.ue_id) + # Send Attach Request + attach_req = s1ap_types.ueAttachRequest_t() + sec_ctxt = s1ap_types.TFW_CREATE_NEW_SECURITY_CONTEXT + id_type = s1ap_types.TFW_MID_TYPE_IMSI + eps_type = s1ap_types.TFW_EPS_ATTACH_TYPE_EPS_ATTACH + pdn_type = s1ap_types.pdn_Type() + pdn_type.pres = True + pdn_type.pdn_type = 1 + attach_req.ue_Id = ue_ids[0] + attach_req.mIdType = id_type + attach_req.epsAttachType = eps_type + attach_req.useOldSecCtxt = sec_ctxt + attach_req.pdnType_pr = pdn_type + + self._s1ap_wrapper._s1_util.issue_cmd( + s1ap_types.tfwCmd.UE_ATTACH_REQUEST, attach_req + ) + print( + "********************** Sent attach req for UE id ", ue_ids[0], + ) + + response = self._s1ap_wrapper.s1_util.get_response() + self.assertEqual( + response.msg_type, s1ap_types.tfwCmd.UE_AUTH_REQ_IND.value + ) + auth_req = response.cast(s1ap_types.ueAuthReqInd_t) + print( + "********************** Received auth req for UE id", + auth_req.ue_Id, + ) + + # Send Authentication Response + auth_res = s1ap_types.ueAuthResp_t() + auth_res.ue_Id = ue_ids[0] + sqnRecvd = s1ap_types.ueSqnRcvd_t() + sqnRecvd.pres = 0 + auth_res.sqnRcvd = sqnRecvd + self._s1ap_wrapper._s1_util.issue_cmd( + s1ap_types.tfwCmd.UE_AUTH_RESP, auth_res + ) + print("********************** Sent auth rsp for UE id", ue_ids[0]) + + response = self._s1ap_wrapper.s1_util.get_response() + self.assertEqual( + response.msg_type, s1ap_types.tfwCmd.UE_SEC_MOD_CMD_IND.value + ) + sec_mod_cmd = response.cast(s1ap_types.ueSecModeCmdInd_t) + print( + "********************** Received security mode cmd for UE id", + sec_mod_cmd.ue_Id, + ) + + # Send Security Mode Complete + sec_mode_complete = s1ap_types.ueSecModeComplete_t() + sec_mode_complete.ue_Id = ue_ids[0] + sec_mode_complete.imeisv_pres = True + imeisv = "9900048235103723" + # Check if the len of imeisv exceeds 16 + self.assertLessEqual(len(imeisv), 16) + for i in range(0, len(imeisv)): + sec_mode_complete.imeisv[i] = ctypes.c_ubyte(int(imeisv[i])) + self._s1ap_wrapper._s1_util.issue_cmd( + s1ap_types.tfwCmd.UE_SEC_MOD_COMPLETE, sec_mode_complete + ) + print( + "********************** Sent security mode complete for UE id", + ue_ids[0], + ) + + # Receive Attach Reject + response = self._s1ap_wrapper.s1_util.get_response() + self.assertEqual( + response.msg_type, s1ap_types.tfwCmd.UE_ATTACH_REJECT_IND.value + ) + attach_rej = response.cast(s1ap_types.ueAttachRejInd_t) + print( + "********************** Received attach reject for UE id %d" + " with emm cause %d" % (attach_rej.ue_Id, attach_rej.cause) + ) + + # Verify cause + self.assertEqual( + attach_rej.cause, s1ap_types.TFW_EMM_CAUSE_IMEI_NOT_ACCEPTED + ) + + # UE Context release + response = self._s1ap_wrapper.s1_util.get_response() + self.assertEqual( + response.msg_type, s1ap_types.tfwCmd.UE_CTX_REL_IND.value + ) + + ue_context_rel = response.cast(s1ap_types.ueCntxtRelReq_t) + print( + "********************** Received UE_CTX_REL_IND for UE id ", + ue_context_rel.ue_Id, + ) + + # Attach the 2nd UE + print( + "************ Running end-end attach for UE id ************", + ue_ids[1], + ) + self._s1ap_wrapper._s1_util.attach( + ue_ids[1], + s1ap_types.tfwCmd.UE_END_TO_END_ATTACH_REQUEST, + s1ap_types.tfwCmd.UE_ATTACH_ACCEPT_IND, + s1ap_types.ueAttachAccept_t, + ) + + # Wait on EMM Information from MME + self._s1ap_wrapper._s1_util.receive_emm_info() + + print("********************** Sleeping for 2 seconds") + time.sleep(2) + print("********************** Running UE detach for UE id ", ue_ids[1]) + # Now detach the UE + self._s1ap_wrapper.s1_util.detach( + ue_ids[1], s1ap_types.ueDetachType_t.UE_NORMAL_DETACH.value + ) + + +if __name__ == "__main__": + unittest.main() diff --git a/lte/gateway/python/integ_tests/s1aptests/test_imei_restriction_wildcard_snr.py b/lte/gateway/python/integ_tests/s1aptests/test_imei_restriction_wildcard_snr.py new file mode 100644 index 000000000000..13acf4a82af5 --- /dev/null +++ b/lte/gateway/python/integ_tests/s1aptests/test_imei_restriction_wildcard_snr.py @@ -0,0 +1,148 @@ +""" +Copyright 2020 The Magma Authors. + +This source code is licensed under the BSD-style license found in the +LICENSE file in the root directory of this source tree. + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +""" + + +import unittest + +import s1ap_types +import s1ap_wrapper +import ctypes + + +class TestImeiRestrictionWildcardSnr(unittest.TestCase): + def setUp(self): + self._s1ap_wrapper = s1ap_wrapper.TestWrapper() + + def tearDown(self): + self._s1ap_wrapper.cleanup() + + def test_imei_restriction_wildcard_snr(self): + """ + This TC validates imei restriction scenario where only tac + part of imei is configured i.e snr is wildcarded: + 1. Send security mode complete message with a blocked imeisv + where tac is same as the one configured in mme.conf.template + 2. Verify that attach reject is received with cause(5) + IMEI_NOT_ACCEPTED + + If this TC is executed individually run + test_modify_mme_config_for_sanity.py to add + { IMEI_TAC="99333821"} + under the BLOCKED_IMEI_LIST in mme.conf.template. + + After execution of this TC run test_restore_mme_config_after_sanity.py + to restore the old mme.conf.template. + """ + num_ues = 1 + self._s1ap_wrapper.configUEDevice(num_ues) + req = self._s1ap_wrapper.ue_req + # Send Attach Request + attach_req = s1ap_types.ueAttachRequest_t() + sec_ctxt = s1ap_types.TFW_CREATE_NEW_SECURITY_CONTEXT + id_type = s1ap_types.TFW_MID_TYPE_IMSI + eps_type = s1ap_types.TFW_EPS_ATTACH_TYPE_EPS_ATTACH + pdn_type = s1ap_types.pdn_Type() + pdn_type.pres = True + pdn_type.pdn_type = 1 + attach_req.ue_Id = req.ue_id + attach_req.mIdType = id_type + attach_req.epsAttachType = eps_type + attach_req.useOldSecCtxt = sec_ctxt + attach_req.pdnType_pr = pdn_type + + self._s1ap_wrapper._s1_util.issue_cmd( + s1ap_types.tfwCmd.UE_ATTACH_REQUEST, attach_req + ) + print( + "********************** Sent attach req for UE id ", req.ue_id, + ) + + response = self._s1ap_wrapper.s1_util.get_response() + self.assertEqual( + response.msg_type, s1ap_types.tfwCmd.UE_AUTH_REQ_IND.value + ) + auth_req = response.cast(s1ap_types.ueAuthReqInd_t) + print( + "********************** Received auth req for UE id", + auth_req.ue_Id, + ) + + # Send Authentication Response + auth_res = s1ap_types.ueAuthResp_t() + auth_res.ue_Id = req.ue_id + sqnRecvd = s1ap_types.ueSqnRcvd_t() + sqnRecvd.pres = 0 + auth_res.sqnRcvd = sqnRecvd + self._s1ap_wrapper._s1_util.issue_cmd( + s1ap_types.tfwCmd.UE_AUTH_RESP, auth_res + ) + print("********************** Sent auth rsp for UE id", req.ue_id) + + response = self._s1ap_wrapper.s1_util.get_response() + self.assertEqual( + response.msg_type, s1ap_types.tfwCmd.UE_SEC_MOD_CMD_IND.value + ) + sec_mod_cmd = response.cast(s1ap_types.ueSecModeCmdInd_t) + print( + "********************** Received security mode cmd for UE id", + sec_mod_cmd.ue_Id, + ) + + # Send Security Mode Complete + sec_mode_complete = s1ap_types.ueSecModeComplete_t() + sec_mode_complete.ue_Id = req.ue_id + sec_mode_complete.imeisv_pres = True + imeisv = "9933382135103723" + # Check if the len of imeisv exceeds 16 + self.assertLessEqual(len(imeisv), 16) + for i in range(0, len(imeisv)): + sec_mode_complete.imeisv[i] = ctypes.c_ubyte(int(imeisv[i])) + self._s1ap_wrapper._s1_util.issue_cmd( + s1ap_types.tfwCmd.UE_SEC_MOD_COMPLETE, sec_mode_complete + ) + print( + "********************** Sent security mode complete for UE id", + req.ue_id, + ) + + # Receive Attach Reject + response = self._s1ap_wrapper.s1_util.get_response() + self.assertEqual( + response.msg_type, s1ap_types.tfwCmd.UE_ATTACH_REJECT_IND.value + ) + attach_rej = response.cast(s1ap_types.ueAttachRejInd_t) + print( + "********************** Received attach reject for UE id %d" + " with emm cause %d" % (attach_rej.ue_Id, attach_rej.cause) + ) + + # Verify cause + self.assertEqual( + attach_rej.cause, s1ap_types.TFW_EMM_CAUSE_IMEI_NOT_ACCEPTED + ) + + # UE Context release + response = self._s1ap_wrapper.s1_util.get_response() + self.assertEqual( + response.msg_type, s1ap_types.tfwCmd.UE_CTX_REL_IND.value + ) + + ue_context_rel = response.cast(s1ap_types.ueCntxtRelReq_t) + print( + "********************** Received UE_CTX_REL_IND for UE id ", + ue_context_rel.ue_Id, + ) + + +if __name__ == "__main__": + unittest.main() From c0790ee1e3822baaa933acc10f649ec35940f53f Mon Sep 17 00:00:00 2001 From: Pravin B Shelar Date: Thu, 8 Apr 2021 23:15:20 +0530 Subject: [PATCH 81/91] AGW: MME: GTP: add S8 TEID args Add missing TEIDs to program flow from eNB to PGW and vice-versa. now the API takes in in and out TEID for S1 (eNB) tunnel as well as S8 (PGW) tunnel. Signed-off-by: Pravin B Shelar --- .../openflow/controller/ControllerEvents.cpp | 24 +++++++- .../openflow/controller/ControllerEvents.h | 13 ++++- .../openflow/controller/ControllerMain.cpp | 14 ++--- .../lib/openflow/controller/ControllerMain.h | 6 +- .../openflow/controller/GTPApplication.cpp | 57 ++++++++++++------- .../lib/openflow/controller/GTPApplication.h | 10 ++-- .../c/oai/tasks/gtpv1-u/gtp_tunnel_openflow.c | 10 ++-- lte/gateway/c/oai/tasks/gtpv1-u/gtpv1u.h | 16 ++---- lte/gateway/c/oai/tasks/gtpv1-u/gtpv1u_task.c | 9 +-- .../c/oai/tasks/sgw_s8/sgw_s8_handlers.c | 3 +- .../c/oai/test/openflow/test_gtp_app.cpp | 21 ++++--- 11 files changed, 115 insertions(+), 68 deletions(-) diff --git a/lte/gateway/c/oai/lib/openflow/controller/ControllerEvents.cpp b/lte/gateway/c/oai/lib/openflow/controller/ControllerEvents.cpp index 50e12df9f74c..759b2584b247 100644 --- a/lte/gateway/c/oai/lib/openflow/controller/ControllerEvents.cpp +++ b/lte/gateway/c/oai/lib/openflow/controller/ControllerEvents.cpp @@ -144,6 +144,8 @@ AddGTPTunnelEvent::AddGTPTunnelEvent( pgw_ip_(INADDR_ZERO), in_tei_(in_tei), out_tei_(out_tei), + pgw_in_tei_(0), + pgw_out_tei_(0), imsi_(imsi), dl_flow_valid_(false), dl_flow_(), @@ -162,6 +164,8 @@ AddGTPTunnelEvent::AddGTPTunnelEvent( pgw_ip_(INADDR_ZERO), in_tei_(in_tei), out_tei_(out_tei), + pgw_in_tei_(0), + pgw_out_tei_(0), imsi_(imsi), dl_flow_valid_(true), dl_flow_(*dl_flow), @@ -173,7 +177,8 @@ AddGTPTunnelEvent::AddGTPTunnelEvent( AddGTPTunnelEvent::AddGTPTunnelEvent( const struct in_addr ue_ip, struct in6_addr* ue_ipv6, int vlan, const struct in_addr enb_ip, const struct in_addr pgw_ip, - const uint32_t in_tei, const uint32_t out_tei, const char* imsi, + const uint32_t in_tei, const uint32_t out_tei, const uint32_t pgw_in_tei, + const uint32_t pgw_out_tei, const char* imsi, const struct ip_flow_dl* dl_flow, const uint32_t dl_flow_precedence, uint32_t enb_gtp_port, uint32_t pgw_gtp_port) : ue_info_(ue_ip, ue_ipv6, vlan), @@ -181,6 +186,8 @@ AddGTPTunnelEvent::AddGTPTunnelEvent( pgw_ip_(pgw_ip), in_tei_(in_tei), out_tei_(out_tei), + pgw_in_tei_(pgw_in_tei), + pgw_out_tei_(pgw_out_tei), imsi_(imsi), dl_flow_valid_(false), dl_flow_(), @@ -192,13 +199,16 @@ AddGTPTunnelEvent::AddGTPTunnelEvent( AddGTPTunnelEvent::AddGTPTunnelEvent( const struct in_addr ue_ip, struct in6_addr* ue_ipv6, int vlan, const struct in_addr enb_ip, const struct in_addr pgw_ip, - const uint32_t in_tei, const uint32_t out_tei, const char* imsi, - uint32_t enb_gtp_port, uint32_t pgw_gtp_port) + const uint32_t in_tei, const uint32_t out_tei, const uint32_t pgw_in_tei, + const uint32_t pgw_out_tei, const char* imsi, uint32_t enb_gtp_port, + uint32_t pgw_gtp_port) : ue_info_(ue_ip, vlan), enb_ip_(enb_ip), pgw_ip_(pgw_ip), in_tei_(in_tei), out_tei_(out_tei), + pgw_in_tei_(pgw_in_tei), + pgw_out_tei_(pgw_out_tei), imsi_(imsi), dl_flow_valid_(false), dl_flow_(), @@ -231,6 +241,14 @@ const uint32_t AddGTPTunnelEvent::get_out_tei() const { return out_tei_; } +const uint32_t AddGTPTunnelEvent::get_pgw_in_tei() const { + return pgw_in_tei_; +} + +const uint32_t AddGTPTunnelEvent::get_pgw_out_tei() const { + return pgw_out_tei_; +} + const std::string& AddGTPTunnelEvent::get_imsi() const { return imsi_; } diff --git a/lte/gateway/c/oai/lib/openflow/controller/ControllerEvents.h b/lte/gateway/c/oai/lib/openflow/controller/ControllerEvents.h index 8bee0b9b49da..23909cf41615 100644 --- a/lte/gateway/c/oai/lib/openflow/controller/ControllerEvents.h +++ b/lte/gateway/c/oai/lib/openflow/controller/ControllerEvents.h @@ -187,15 +187,17 @@ class AddGTPTunnelEvent : public ExternalEvent { AddGTPTunnelEvent( const struct in_addr ue_ip, struct in6_addr* ue_ipv6, int vlan, const struct in_addr enb_ip, const struct in_addr pgw_ip, - const uint32_t in_tei, const uint32_t out_tei, const char* imsi, + const uint32_t in_tei, const uint32_t out_tei, const uint32_t pgw_in_tei, + const uint32_t pgw_out_tei, const char* imsi, const struct ip_flow_dl* dl_flow, const uint32_t dl_flow_precedence, uint32_t enb_gtp_port, uint32_t pgw_gtp_port); AddGTPTunnelEvent( const struct in_addr ue_ip, struct in6_addr* ue_ipv6, int vlan, const struct in_addr enb_ip, const struct in_addr pgw_ip, - const uint32_t in_tei, const uint32_t out_tei, const char* imsi, - uint32_t enb_gtp_port, uint32_t pgw_gtp_port); + const uint32_t in_tei, const uint32_t out_tei, const uint32_t pgw_in_tei, + const uint32_t pgw_out_tei, const char* imsi, uint32_t enb_gtp_port, + uint32_t pgw_gtp_port); const struct UeNetworkInfo& get_ue_info() const; const struct in_addr& get_ue_ip() const; @@ -206,6 +208,9 @@ class AddGTPTunnelEvent : public ExternalEvent { const uint32_t get_in_tei() const; const uint32_t get_out_tei() const; + const uint32_t get_pgw_in_tei() const; + const uint32_t get_pgw_out_tei() const; + const std::string& get_imsi() const; const bool is_dl_flow_valid() const; const struct ip_flow_dl& get_dl_flow() const; @@ -219,6 +224,8 @@ class AddGTPTunnelEvent : public ExternalEvent { const struct in_addr pgw_ip_; const uint32_t in_tei_; const uint32_t out_tei_; + const uint32_t pgw_in_tei_; + const uint32_t pgw_out_tei_; const std::string imsi_; const struct ip_flow_dl dl_flow_; const bool dl_flow_valid_; diff --git a/lte/gateway/c/oai/lib/openflow/controller/ControllerMain.cpp b/lte/gateway/c/oai/lib/openflow/controller/ControllerMain.cpp index bf501ef8d4fd..81204a87fddb 100644 --- a/lte/gateway/c/oai/lib/openflow/controller/ControllerMain.cpp +++ b/lte/gateway/c/oai/lib/openflow/controller/ControllerMain.cpp @@ -126,18 +126,18 @@ int openflow_controller_del_gtp_tunnel( int openflow_controller_add_gtp_s8_tunnel( struct in_addr ue, struct in6_addr* ue_ipv6, int vlan, struct in_addr enb, - struct in_addr pgw, uint32_t i_tei, uint32_t o_tei, const char* imsi, - struct ip_flow_dl* flow_dl, uint32_t flow_precedence_dl, - uint32_t enb_gtp_port, uint32_t pgw_gtp_port) { + struct in_addr pgw, uint32_t i_tei, uint32_t o_tei, uint32_t pgw_i_tei, + uint32_t pgw_o_tei, const char* imsi, struct ip_flow_dl* flow_dl, + uint32_t flow_precedence_dl, uint32_t enb_gtp_port, uint32_t pgw_gtp_port) { if (flow_dl) { auto add_tunnel = std::make_shared( - ue, ue_ipv6, vlan, enb, pgw, i_tei, o_tei, imsi, flow_dl, - flow_precedence_dl, enb_gtp_port, pgw_gtp_port); + ue, ue_ipv6, vlan, enb, pgw, i_tei, o_tei, pgw_i_tei, pgw_o_tei, imsi, + flow_dl, flow_precedence_dl, enb_gtp_port, pgw_gtp_port); ctrl.inject_external_event(add_tunnel, external_event_callback); } else { auto add_tunnel = std::make_shared( - ue, ue_ipv6, vlan, enb, pgw, i_tei, o_tei, imsi, enb_gtp_port, - pgw_gtp_port); + ue, ue_ipv6, vlan, enb, pgw, i_tei, o_tei, pgw_i_tei, pgw_o_tei, imsi, + enb_gtp_port, pgw_gtp_port); ctrl.inject_external_event(add_tunnel, external_event_callback); } OAILOG_FUNC_RETURN(LOG_GTPV1U, RETURNok); diff --git a/lte/gateway/c/oai/lib/openflow/controller/ControllerMain.h b/lte/gateway/c/oai/lib/openflow/controller/ControllerMain.h index c859f31d558d..9d30252d87e3 100644 --- a/lte/gateway/c/oai/lib/openflow/controller/ControllerMain.h +++ b/lte/gateway/c/oai/lib/openflow/controller/ControllerMain.h @@ -56,9 +56,9 @@ int openflow_controller_delete_paging_rule(struct in_addr ue_ip); int openflow_controller_add_gtp_s8_tunnel( struct in_addr ue, struct in6_addr* ue_ipv6, int vlan, struct in_addr enb, - struct in_addr pgw, uint32_t i_tei, uint32_t o_tei, const char* imsi, - struct ip_flow_dl* flow_dl, uint32_t flow_precedence_dl, - uint32_t enb_gtp_port, uint32_t pgw_gtp_port); + struct in_addr pgw, uint32_t i_tei, uint32_t o_tei, uint32_t pgw_i_tei, + uint32_t pgw_o_tei, const char* imsi, struct ip_flow_dl* flow_dl, + uint32_t flow_precedence_dl, uint32_t enb_gtp_port, uint32_t pgw_gtp_port); int openflow_controller_del_gtp_s8_tunnel( struct in_addr ue, struct in6_addr* ue_ipv6, uint32_t i_tei, diff --git a/lte/gateway/c/oai/lib/openflow/controller/GTPApplication.cpp b/lte/gateway/c/oai/lib/openflow/controller/GTPApplication.cpp index 494d72d67a0f..ceda943a36e5 100644 --- a/lte/gateway/c/oai/lib/openflow/controller/GTPApplication.cpp +++ b/lte/gateway/c/oai/lib/openflow/controller/GTPApplication.cpp @@ -49,12 +49,12 @@ GTPApplication::GTPApplication( void GTPApplication::event_callback( const ControllerEvent& ev, const OpenflowMessenger& messenger) { if (ev.get_type() == EVENT_ADD_GTP_TUNNEL) { - printf("pbs: reg add tun"); auto add_tunnel_event = static_cast(ev); add_uplink_tunnel_flow(add_tunnel_event, messenger); add_downlink_tunnel_flow( - add_tunnel_event, messenger, uplink_port_num_, false); - add_downlink_tunnel_flow(add_tunnel_event, messenger, mtr_port_num_, false); + add_tunnel_event, messenger, uplink_port_num_, false, false); + add_downlink_tunnel_flow( + add_tunnel_event, messenger, mtr_port_num_, false, false); add_downlink_arp_flow(add_tunnel_event, messenger, uplink_port_num_); add_downlink_arp_flow(add_tunnel_event, messenger, mtr_port_num_); } else if (ev.get_type() == EVENT_DELETE_GTP_TUNNEL) { @@ -65,16 +65,22 @@ void GTPApplication::event_callback( delete_downlink_arp_flow(del_tunnel_event, messenger, uplink_port_num_); delete_downlink_arp_flow(del_tunnel_event, messenger, mtr_port_num_); } else if (ev.get_type() == EVENT_ADD_GTP_S8_TUNNEL) { - printf("pbs: S8 add tun"); auto add_tunnel_event = static_cast(ev); + auto imsi = IMSIEncoder::compact_imsi(add_tunnel_event.get_imsi()); + + OAILOG_DEBUG_UE(LOG_GTPV1U, imsi, "s8: add: TEID: s1-in %u s1-out %u s8-in %u s8-out %u\n", + add_tunnel_event.get_in_tei(), add_tunnel_event.get_out_tei(), + add_tunnel_event.get_pgw_in_tei(), add_tunnel_event.get_pgw_out_tei()); + add_uplink_s8_tunnel_flow(add_tunnel_event, messenger); int pgw_port = add_tunnel_event.get_pgw_gtp_portno(); if (pgw_port == 0) { pgw_port = GTPApplication::gtp0_port_num_; } - add_downlink_tunnel_flow(add_tunnel_event, messenger, pgw_port, true); - add_downlink_tunnel_flow(add_tunnel_event, messenger, mtr_port_num_, true); + add_downlink_tunnel_flow(add_tunnel_event, messenger, pgw_port, true, true); + add_downlink_tunnel_flow( + add_tunnel_event, messenger, mtr_port_num_, true, true); add_downlink_arp_flow(add_tunnel_event, messenger, mtr_port_num_); } else if (ev.get_type() == EVENT_DELETE_GTP_S8_TUNNEL) { auto del_tunnel_event = static_cast(ev); @@ -207,7 +213,7 @@ void GTPApplication::add_uplink_s8_tunnel_flow( add_tunnel_match(uplink_fm, ev.get_enb_gtp_portno(), ev.get_in_tei()); add_tunnel_flow_action( - ev.get_out_tei(), ev.get_in_tei(), ev.get_imsi(), ev.get_pgw_ip(), + ev.get_pgw_out_tei(), ev.get_in_tei(), ev.get_imsi(), ev.get_pgw_ip(), ev.get_pgw_gtp_portno(), ev.get_connection(), messenger, uplink_fm, "S8 Uplink", true); } @@ -412,28 +418,36 @@ void GTPApplication::add_tunnel_flow_action( void GTPApplication::add_downlink_tunnel_flow_action( const AddGTPTunnelEvent& ev, const OpenflowMessenger& messenger, - of13::FlowMod downlink_fm, bool passthrough) { + of13::FlowMod downlink_fm, bool passthrough, bool from_pgw) { + uint32_t in_teid; + if (from_pgw) { + in_teid = ev.get_in_tei(); + } else { + in_teid = ev.get_in_tei(); + } + add_tunnel_flow_action( - ev.get_out_tei(), ev.get_in_tei(), ev.get_imsi(), ev.get_enb_ip(), + ev.get_out_tei(), in_teid, ev.get_imsi(), ev.get_enb_ip(), ev.get_enb_gtp_portno(), ev.get_connection(), messenger, downlink_fm, "S1 Downlink", passthrough); } void GTPApplication::add_downlink_tunnel_flow_ipv4( const AddGTPTunnelEvent& ev, const OpenflowMessenger& messenger, - uint32_t ingress_port, bool passthrough) { + uint32_t ingress_port, bool passthrough, bool from_pgw) { uint32_t flow_priority = convert_precedence_to_priority(ev.get_dl_flow_precedence()); of13::FlowMod downlink_fm = messenger.create_default_flow_mod(0, of13::OFPFC_ADD, flow_priority); add_downlink_match(downlink_fm, ev.get_ue_ip(), ingress_port); - add_downlink_tunnel_flow_action(ev, messenger, downlink_fm, passthrough); + add_downlink_tunnel_flow_action( + ev, messenger, downlink_fm, passthrough, from_pgw); } void GTPApplication::add_downlink_tunnel_flow_ipv6( const AddGTPTunnelEvent& ev, const OpenflowMessenger& messenger, - uint32_t ingress_port, bool passthrough) { + uint32_t ingress_port, bool passthrough, bool from_pgw) { uint32_t flow_priority = convert_precedence_to_priority(ev.get_dl_flow_precedence()); of13::FlowMod downlink_fm = @@ -441,35 +455,40 @@ void GTPApplication::add_downlink_tunnel_flow_ipv6( add_downlink_match_ipv6( downlink_fm, ev.get_ue_info().get_ipv6(), ingress_port); - add_downlink_tunnel_flow_action(ev, messenger, downlink_fm, passthrough); + add_downlink_tunnel_flow_action( + ev, messenger, downlink_fm, passthrough, from_pgw); } void GTPApplication::add_downlink_tunnel_flow_ded_brr( const AddGTPTunnelEvent& ev, const OpenflowMessenger& messenger, - uint32_t ingress_port, bool passthrough) { + uint32_t ingress_port, bool passthrough, bool from_pgw) { uint32_t flow_priority = convert_precedence_to_priority(ev.get_dl_flow_precedence()); of13::FlowMod downlink_fm = messenger.create_default_flow_mod(0, of13::OFPFC_ADD, flow_priority); add_ded_brr_dl_match(downlink_fm, ev.get_dl_flow(), ingress_port); - add_downlink_tunnel_flow_action(ev, messenger, downlink_fm, passthrough); + add_downlink_tunnel_flow_action( + ev, messenger, downlink_fm, passthrough, from_pgw); } void GTPApplication::add_downlink_tunnel_flow( const AddGTPTunnelEvent& ev, const OpenflowMessenger& messenger, - uint32_t ingress_port, bool passthrough) { + uint32_t ingress_port, bool passthrough, bool from_pgw) { if (ev.is_dl_flow_valid()) { - add_downlink_tunnel_flow_ded_brr(ev, messenger, ingress_port, passthrough); + add_downlink_tunnel_flow_ded_brr( + ev, messenger, ingress_port, passthrough, from_pgw); return; } UeNetworkInfo ue_info = ev.get_ue_info(); if (ue_info.is_ue_ipv4_addr_valid()) { - add_downlink_tunnel_flow_ipv4(ev, messenger, ingress_port, passthrough); + add_downlink_tunnel_flow_ipv4( + ev, messenger, ingress_port, passthrough, from_pgw); } if (ue_info.is_ue_ipv6_addr_valid()) { - add_downlink_tunnel_flow_ipv6(ev, messenger, ingress_port, passthrough); + add_downlink_tunnel_flow_ipv6( + ev, messenger, ingress_port, passthrough, from_pgw); } } diff --git a/lte/gateway/c/oai/lib/openflow/controller/GTPApplication.h b/lte/gateway/c/oai/lib/openflow/controller/GTPApplication.h index 69d7592605bb..a8e82a569573 100644 --- a/lte/gateway/c/oai/lib/openflow/controller/GTPApplication.h +++ b/lte/gateway/c/oai/lib/openflow/controller/GTPApplication.h @@ -63,7 +63,7 @@ class GTPApplication : public Application { */ void add_downlink_tunnel_flow( const AddGTPTunnelEvent& ev, const OpenflowMessenger& messenger, - uint32_t port_number, bool passthrough); + uint32_t port_number, bool passthrough, bool from_pgw); /* * Add downlink tunnel flow for S8 @@ -181,17 +181,17 @@ class GTPApplication : public Application { void add_downlink_tunnel_flow_action( const AddGTPTunnelEvent& ev, const OpenflowMessenger& messenger, - of13::FlowMod downlink_fm, bool passthrough); + of13::FlowMod downlink_fm, bool passthrough, bool from_pgw); void add_downlink_tunnel_flow_ipv4( const AddGTPTunnelEvent& ev, const OpenflowMessenger& messenger, - uint32_t port_number, bool passthrough); + uint32_t port_number, bool passthrough, bool from_pgw); void add_downlink_tunnel_flow_ipv6( const AddGTPTunnelEvent& ev, const OpenflowMessenger& messenger, - uint32_t port_number, bool passthrough); + uint32_t port_number, bool passthrough, bool from_pgw); void add_downlink_tunnel_flow_ded_brr( const AddGTPTunnelEvent& ev, const OpenflowMessenger& messenger, - uint32_t port_number, bool passthrough); + uint32_t port_number, bool passthrough, bool from_pgw); void delete_downlink_tunnel_flow_ipv4( const DeleteGTPTunnelEvent& ev, const OpenflowMessenger& messenger, diff --git a/lte/gateway/c/oai/tasks/gtpv1-u/gtp_tunnel_openflow.c b/lte/gateway/c/oai/tasks/gtpv1-u/gtp_tunnel_openflow.c index 0f6d6e9f27d5..118914a02a75 100644 --- a/lte/gateway/c/oai/tasks/gtpv1-u/gtp_tunnel_openflow.c +++ b/lte/gateway/c/oai/tasks/gtpv1-u/gtp_tunnel_openflow.c @@ -270,14 +270,16 @@ int openflow_del_tunnel( /* S8 tunnel related APIs */ int openflow_add_s8_tunnel( struct in_addr ue, struct in6_addr* ue_ipv6, int vlan, struct in_addr enb, - struct in_addr pgw, uint32_t i_tei, uint32_t o_tei, Imsi_t imsi, - struct ip_flow_dl* flow_dl, uint32_t flow_precedence_dl) { + struct in_addr pgw, uint32_t i_tei, uint32_t o_tei, uint32_t pgw_i_tei, + uint32_t pgw_o_tei, Imsi_t imsi, struct ip_flow_dl* flow_dl, + uint32_t flow_precedence_dl) { uint32_t enb_portno = find_gtp_port_no(enb); uint32_t pgw_portno = find_gtp_port_no(pgw); return openflow_controller_add_gtp_s8_tunnel( - ue, ue_ipv6, vlan, enb, pgw, i_tei, o_tei, (const char*) imsi.digit, - flow_dl, flow_precedence_dl, enb_portno, pgw_portno); + ue, ue_ipv6, vlan, enb, pgw, i_tei, o_tei, pgw_i_tei, pgw_o_tei, + (const char*) imsi.digit, flow_dl, flow_precedence_dl, enb_portno, + pgw_portno); } int openflow_del_s8_tunnel( diff --git a/lte/gateway/c/oai/tasks/gtpv1-u/gtpv1u.h b/lte/gateway/c/oai/tasks/gtpv1-u/gtpv1u.h index 2fe4745ef102..459265fdee23 100644 --- a/lte/gateway/c/oai/tasks/gtpv1-u/gtpv1u.h +++ b/lte/gateway/c/oai/tasks/gtpv1-u/gtpv1u.h @@ -145,8 +145,9 @@ struct gtp_tunnel_ops { uint32_t i_tei, uint32_t o_tei, struct ip_flow_dl* flow_dl); int (*add_s8_tunnel)( struct in_addr ue, struct in6_addr* ue_ipv6, int vlan, struct in_addr enb, - struct in_addr pgw, uint32_t i_tei, uint32_t o_tei, Imsi_t imsi, - struct ip_flow_dl* flow_dl, uint32_t flow_precedence_dl); + struct in_addr pgw, uint32_t i_tei, uint32_t o_tei, uint32_t pgw_i_tei, + uint32_t pgw_o_tei, Imsi_t imsi, struct ip_flow_dl* flow_dl, + uint32_t flow_precedence_dl); int (*del_s8_tunnel)( struct in_addr enb, struct in_addr pgw, struct in_addr ue, struct in6_addr* ue_ipv6, uint32_t i_tei, uint32_t o_tei, @@ -176,12 +177,7 @@ int gtpv1u_add_tunnel( int gtpv1u_add_s8_tunnel( struct in_addr ue, struct in6_addr* ue_ipv6, int vlan, struct in_addr enb, - struct in_addr pgw, uint32_t i_tei, uint32_t o_tei, Imsi_t imsi, - struct ip_flow_dl* flow_dl, uint32_t flow_precedence_dl); - -int gtpv1u_del_s8_tunnel( - struct in_addr enb, struct in_addr pgw, struct in_addr ue, - struct in6_addr* ue_ipv6, uint32_t i_tei, uint32_t o_tei, - struct ip_flow_dl* flow_dl); - + struct in_addr pgw, uint32_t i_tei, uint32_t o_tei, uint32_t pgw_i_tei, + uint32_t pgw_o_tei, Imsi_t imsi, struct ip_flow_dl* flow_dl, + uint32_t flow_precedence_dl); #endif /* FILE_GTPV1_U_SEEN */ diff --git a/lte/gateway/c/oai/tasks/gtpv1-u/gtpv1u_task.c b/lte/gateway/c/oai/tasks/gtpv1-u/gtpv1u_task.c index f492af7be8cf..dd3a99e284eb 100644 --- a/lte/gateway/c/oai/tasks/gtpv1-u/gtpv1u_task.c +++ b/lte/gateway/c/oai/tasks/gtpv1-u/gtpv1u_task.c @@ -202,13 +202,14 @@ int gtpv1u_add_tunnel( int gtpv1u_add_s8_tunnel( struct in_addr ue, struct in6_addr* ue_ipv6, int vlan, struct in_addr enb, - struct in_addr pgw, uint32_t i_tei, uint32_t o_tei, Imsi_t imsi, - struct ip_flow_dl* flow_dl, uint32_t flow_precedence_dl) { + struct in_addr pgw, uint32_t i_tei, uint32_t o_tei, uint32_t pgw_i_tei, + uint32_t pgw_o_tei, Imsi_t imsi, struct ip_flow_dl* flow_dl, + uint32_t flow_precedence_dl) { OAILOG_DEBUG(LOG_GTPV1U, "Add S8 tunnel ue %s", inet_ntoa(ue)); if (gtp_tunnel_ops->add_s8_tunnel) { return gtp_tunnel_ops->add_s8_tunnel( - ue, ue_ipv6, vlan, enb, pgw, i_tei, o_tei, imsi, flow_dl, - flow_precedence_dl); + ue, ue_ipv6, vlan, enb, pgw, i_tei, o_tei, pgw_i_tei, pgw_o_tei, imsi, + flow_dl, flow_precedence_dl); } else { return -EINVAL; } diff --git a/lte/gateway/c/oai/tasks/sgw_s8/sgw_s8_handlers.c b/lte/gateway/c/oai/tasks/sgw_s8/sgw_s8_handlers.c index 4c8d0e1fe37d..7470eb4cfb44 100644 --- a/lte/gateway/c/oai/tasks/sgw_s8/sgw_s8_handlers.c +++ b/lte/gateway/c/oai/tasks/sgw_s8/sgw_s8_handlers.c @@ -575,7 +575,8 @@ static int sgw_s8_add_gtp_tunnel( rv = gtpv1u_add_s8_tunnel( ue_ipv4, ue_ipv6, vlan, enb, pgw, eps_bearer_ctxt_p->s_gw_teid_S1u_S12_S4_up, - eps_bearer_ctxt_p->enb_teid_S1u, imsi, NULL, DEFAULT_PRECEDENCE); + eps_bearer_ctxt_p->enb_teid_S1u, 0, 0, imsi, NULL, + DEFAULT_PRECEDENCE); if (rv < 0) { OAILOG_ERROR_UE( LOG_SGW_S8, sgw_context_p->imsi64, diff --git a/lte/gateway/c/oai/test/openflow/test_gtp_app.cpp b/lte/gateway/c/oai/test/openflow/test_gtp_app.cpp index 80657d5ae2ba..977c4bd66c9d 100644 --- a/lte/gateway/c/oai/test/openflow/test_gtp_app.cpp +++ b/lte/gateway/c/oai/test/openflow/test_gtp_app.cpp @@ -911,17 +911,20 @@ TEST_F(GTPApplicationTest, TestAddTunnelS8) { struct in_addr enb_ip; enb_ip.s_addr = inet_addr("0.0.0.2"); struct in_addr pgw_ip; - enb_ip.s_addr = inet_addr("0.0.0.22"); - uint32_t in_tei = 1; - uint32_t out_tei = 2; - char imsi[] = "001010000000013"; - int vlan = 0; - int enb_port = 100; - int pgw_port = 200; + enb_ip.s_addr = inet_addr("0.0.0.22"); + uint32_t in_tei = 1; + uint32_t out_tei = 2; + uint32_t pgw_in_tei = 3; + uint32_t pgw_out_tei = 4; + + char imsi[] = "001010000000013"; + int vlan = 0; + int enb_port = 100; + int pgw_port = 200; AddGTPTunnelEvent add_tunnel( - ue_ip, NULL, vlan, enb_ip, pgw_ip, in_tei, out_tei, imsi, enb_port, - pgw_port); + ue_ip, NULL, vlan, enb_ip, pgw_ip, in_tei, out_tei, pgw_in_tei, + pgw_out_tei, imsi, enb_port, pgw_port); // Uplink EXPECT_CALL( *messenger, From 7943e55b564082afeb5bde7b3172e67d018cf027 Mon Sep 17 00:00:00 2001 From: Mykola Yurchenko Date: Fri, 9 Apr 2021 16:31:49 -0400 Subject: [PATCH 82/91] [Pipelined]Fix IPv6 Restart Flow Recovery (#6016) * [Pipelined]Fix IPv6 Restart Flow Recovery Signed-off-by: Nick Yurchenko * Fix lint Signed-off-by: Nick Yurchenko * ADD UNIT TEST Signed-off-by: Nick Yurchenko --- .../magma/pipelined/app/policy_mixin.py | 49 ++++++++---- ...cement_ipv6_restart.after_restart.snapshot | 5 ++ ...cement_ipv6_restart.default_flows.snapshot | 2 + .../magma/pipelined/tests/test_enforcement.py | 1 + .../pipelined/tests/test_enforcement_stats.py | 1 + .../python/magma/pipelined/tests/test_he.py | 1 + .../magma/pipelined/tests/test_redirect.py | 1 + .../tests/test_restart_resilience.py | 74 ++++++++++++++++++- 8 files changed, 116 insertions(+), 18 deletions(-) create mode 100644 lte/gateway/python/magma/pipelined/tests/snapshots/test_restart_resilience.RestartResilienceTest.test_enforcement_ipv6_restart.after_restart.snapshot create mode 100644 lte/gateway/python/magma/pipelined/tests/snapshots/test_restart_resilience.RestartResilienceTest.test_enforcement_ipv6_restart.default_flows.snapshot diff --git a/lte/gateway/python/magma/pipelined/app/policy_mixin.py b/lte/gateway/python/magma/pipelined/app/policy_mixin.py index 21c766bdf78f..c1ac04d0fa75 100644 --- a/lte/gateway/python/magma/pipelined/app/policy_mixin.py +++ b/lte/gateway/python/magma/pipelined/app/policy_mixin.py @@ -32,6 +32,7 @@ from magma.pipelined.policy_converters import ( FlowMatchError, convert_ipv4_str_to_ip_proto, + convert_ipv6_bytes_to_ip_proto, flow_match_to_magma_match, get_direction_for_match, get_flow_ip_dst, @@ -56,6 +57,7 @@ def __init__(self, *args, **kwargs): super(PolicyMixin, self).__init__(*args, **kwargs) self._datapath = None self._rule_mapper = kwargs['rule_id_mapper'] + self._setup_type = kwargs['config']['setup_type'] self._session_rule_version_mapper = kwargs[ 'session_rule_version_mapper'] if 'proxy' in kwargs['app_futures']: @@ -265,31 +267,46 @@ def _get_ue_specific_flow_msgs(self, requests: List[ActivateFlowsRequest]): msg_list = [] for add_flow_req in requests: imsi = add_flow_req.sid.id - ip_addr = convert_ipv4_str_to_ip_proto(add_flow_req.ip_addr) apn_ambr = add_flow_req.apn_ambr policies = add_flow_req.policies msisdn = add_flow_req.msisdn uplink_tunnel = add_flow_req.uplink_tunnel - msgs = self._get_default_flow_msgs_for_subscriber(imsi, ip_addr) - if msgs: - msg_list.extend(msgs) + if self._setup_type == 'CWF' or add_flow_req.ip_addr: + ipv4 = convert_ipv4_str_to_ip_proto(add_flow_req.ip_addr) + msgs = self._get_default_flow_msgs_for_subscriber(imsi, ipv4) + if msgs: + msg_list.extend(msgs) + + for policy in policies: + msg_list.extend(self._get_policy_flows(imsi, msisdn, uplink_tunnel, ipv4, apn_ambr, policy)) + if add_flow_req.ipv6_addr: + ipv6 = convert_ipv6_bytes_to_ip_proto(add_flow_req.ipv6_addr) + msgs = self._get_default_flow_msgs_for_subscriber(imsi, ipv6) + if msgs: + msg_list.extend(msgs) + + for policy in policies: + msg_list.extend(self._get_policy_flows(imsi, msisdn, uplink_tunnel, ipv6, apn_ambr, policy)) - for policy in policies: - # As the versions are managed by sessiond, save state here - self._service_manager.session_rule_version_mapper.save_version( - imsi, ip_addr, policy.rule.id, policy.version) - try: - if policy.rule.redirect.support == policy.rule.redirect.ENABLED: - continue - flow_adds = self._get_rule_match_flow_msgs(imsi, msisdn, uplink_tunnel, ip_addr, apn_ambr, policy.rule, policy.version) - msg_list.extend(flow_adds) - except FlowMatchError: - self.logger.error("Failed to verify rule_id: %s", - policy.rule.id) return {self.tbl_num: msg_list} + def _get_policy_flows(self, imsi, msisdn, uplink_tunnel, ip_addr, apn_ambr, + policy): + msg_list = [] + # As the versions are managed by sessiond, save state here + self._service_manager.session_rule_version_mapper.save_version( + imsi, ip_addr, policy.rule.id, policy.version) + try: + if policy.rule.redirect.support == policy.rule.redirect.ENABLED: + return msg_list + flow_adds = self._get_rule_match_flow_msgs(imsi, msisdn, uplink_tunnel, ip_addr, apn_ambr, policy.rule, policy.version) + msg_list.extend(flow_adds) + except FlowMatchError: + self.logger.error("Failed to verify rule_id: %s", policy.rule.id) + return msg_list + def _process_redirection_rules(self, requests): for add_flow_req in requests: imsi = add_flow_req.sid.id diff --git a/lte/gateway/python/magma/pipelined/tests/snapshots/test_restart_resilience.RestartResilienceTest.test_enforcement_ipv6_restart.after_restart.snapshot b/lte/gateway/python/magma/pipelined/tests/snapshots/test_restart_resilience.RestartResilienceTest.test_enforcement_ipv6_restart.after_restart.snapshot new file mode 100644 index 000000000000..cd1b36bfcfb4 --- /dev/null +++ b/lte/gateway/python/magma/pipelined/tests/snapshots/test_restart_resilience.RestartResilienceTest.test_enforcement_ipv6_restart.after_restart.snapshot @@ -0,0 +1,5 @@ + cookie=0x1, table=enforcement(main_table), n_packets=0, n_bytes=0, priority=65533,ipv6,reg1=0x1,metadata=0x48c274b89cc3,ipv6_src=fe80:24c3:d0ff:fef3:9d21:4407:d337:1928,ipv6_dst=fe80:: actions=note:b'ipv6_rule',set_field:0x1->reg2,set_field:0x1->reg4,resubmit(,enforcement_stats(main_table)),resubmit(,egress(main_table)) + cookie=0xfffffffffffffffe, table=enforcement(main_table), n_packets=0, n_bytes=0, priority=0 actions=resubmit(,enforcement_stats(main_table)),set_field:0->reg0,set_field:0->reg3 + cookie=0x0, table=enforcement_stats(main_table), n_packets=0, n_bytes=0, priority=1,ipv6,reg1=0x10,reg2=0,reg4=0,metadata=0x48c274b89cc3,ipv6_dst=fe80:24c3:d0ff:fef3:9d21:4407:d337:1928 actions=drop + cookie=0x0, table=enforcement_stats(main_table), n_packets=0, n_bytes=0, priority=1,ipv6,reg1=0x1,reg2=0,reg4=0,metadata=0x48c274b89cc3,ipv6_src=fe80:24c3:d0ff:fef3:9d21:4407:d337:1928 actions=drop + cookie=0xfffffffffffffffe, table=enforcement_stats(main_table), n_packets=0, n_bytes=0, priority=0 actions=drop diff --git a/lte/gateway/python/magma/pipelined/tests/snapshots/test_restart_resilience.RestartResilienceTest.test_enforcement_ipv6_restart.default_flows.snapshot b/lte/gateway/python/magma/pipelined/tests/snapshots/test_restart_resilience.RestartResilienceTest.test_enforcement_ipv6_restart.default_flows.snapshot new file mode 100644 index 000000000000..2b9a784e570c --- /dev/null +++ b/lte/gateway/python/magma/pipelined/tests/snapshots/test_restart_resilience.RestartResilienceTest.test_enforcement_ipv6_restart.default_flows.snapshot @@ -0,0 +1,2 @@ + cookie=0xfffffffffffffffe, table=enforcement(main_table), n_packets=0, n_bytes=0, priority=0 actions=resubmit(,enforcement_stats(main_table)),set_field:0->reg0,set_field:0->reg3 + cookie=0xfffffffffffffffe, table=enforcement_stats(main_table), n_packets=0, n_bytes=0, priority=0 actions=drop diff --git a/lte/gateway/python/magma/pipelined/tests/test_enforcement.py b/lte/gateway/python/magma/pipelined/tests/test_enforcement.py index 44c66be5b328..771cde7b3631 100644 --- a/lte/gateway/python/magma/pipelined/tests/test_enforcement.py +++ b/lte/gateway/python/magma/pipelined/tests/test_enforcement.py @@ -131,6 +131,7 @@ def setUpClass(cls): 'proxy_port_name': cls.VETH, 'enable_nat': True, 'ovs_gtp_port_number': 10, + 'setup_type': 'LTE', }, mconfig=PipelineD(), loop=None, diff --git a/lte/gateway/python/magma/pipelined/tests/test_enforcement_stats.py b/lte/gateway/python/magma/pipelined/tests/test_enforcement_stats.py index f67ec78a576f..88dcb75e5d86 100644 --- a/lte/gateway/python/magma/pipelined/tests/test_enforcement_stats.py +++ b/lte/gateway/python/magma/pipelined/tests/test_enforcement_stats.py @@ -125,6 +125,7 @@ def mock_thread_safe(cmd, body): 'qos': {'enable': False}, 'clean_restart': True, 'redis_enabled': False, + 'setup_type': 'LTE', }, mconfig=PipelineD(), loop=loop_mock, diff --git a/lte/gateway/python/magma/pipelined/tests/test_he.py b/lte/gateway/python/magma/pipelined/tests/test_he.py index fff36d4ef769..4898f0ad2ead 100644 --- a/lte/gateway/python/magma/pipelined/tests/test_he.py +++ b/lte/gateway/python/magma/pipelined/tests/test_he.py @@ -461,6 +461,7 @@ def setUpClass(cls): 'proxy_port_name': cls.VETH, 'enable_nat': True, 'ovs_gtp_port_number': 10, + 'setup_type': 'LTE', }, mconfig=PipelineD(), loop=None, diff --git a/lte/gateway/python/magma/pipelined/tests/test_redirect.py b/lte/gateway/python/magma/pipelined/tests/test_redirect.py index 30966497589f..4a057c1d5df6 100644 --- a/lte/gateway/python/magma/pipelined/tests/test_redirect.py +++ b/lte/gateway/python/magma/pipelined/tests/test_redirect.py @@ -98,6 +98,7 @@ def setUpClass(cls): 'enodeb_iface': 'eth1', 'qos': {'enable': False}, 'clean_restart': True, + 'setup_type': 'LTE', }, mconfig=PipelineD(), loop=None, diff --git a/lte/gateway/python/magma/pipelined/tests/test_restart_resilience.py b/lte/gateway/python/magma/pipelined/tests/test_restart_resilience.py index eb445c676d13..0d4145cbc6cf 100644 --- a/lte/gateway/python/magma/pipelined/tests/test_restart_resilience.py +++ b/lte/gateway/python/magma/pipelined/tests/test_restart_resilience.py @@ -34,8 +34,10 @@ from magma.pipelined.bridge_util import BridgeTools from magma.pipelined.policy_converters import ( convert_ipv4_str_to_ip_proto, + convert_ipv6_bytes_to_ip_proto, flow_match_to_magma_match, ) +from magma.pipelined.rule_mappers import RuleIDToNumMapper from magma.pipelined.tests.app.packet_builder import ( IPPacketBuilder, TCPPacketBuilder, @@ -135,6 +137,7 @@ def mock_thread_safe(cmd, body): 'enodeb_iface': 'eth1', 'qos': {'enable': False}, 'clean_restart': False, + 'setup_type': 'LTE', }, mconfig=PipelineD(), loop=loop_mock, @@ -161,6 +164,73 @@ def tearDownClass(cls): stop_ryu_app_thread(cls.thread) BridgeTools.destroy_bridge(cls.BRIDGE) + def test_enforcement_ipv6_restart(self): + """ + Adds rules using the setup feature + + 1) Empty SetupFlowsRequest + - assert default flows + 2) Add imsi with ipv6 policy + - assert everything is properly added + """ + fake_controller_setup( + enf_controller=self.enforcement_controller, + enf_stats_controller=self.enforcement_stats_controller, + startup_flow_controller=self.startup_flows_contoller) + snapshot_verifier = SnapshotVerifier(self, self.BRIDGE, + self.service_manager, + 'default_flows') + with snapshot_verifier: + pass + + imsi = 'IMSI010000002388888' + sub_ip = b'fe80:24c3:d0ff:fef3:9d21:4407:d337:1928' + + flow_list = [FlowDescription( + match=FlowMatch( + ip_dst=convert_ipv6_bytes_to_ip_proto(b'fe80::'), + direction=FlowMatch.UPLINK), + action=FlowDescription.PERMIT) + ] + policies = [ + VersionedPolicy( + rule=PolicyRule(id='ipv6_rule', priority=2, flow_list=flow_list), + version=1, + ), + ] + enf_stat_name = [imsi + '|ipv6_rule' + '|' + str(sub_ip)] + setup_flows_request = SetupFlowsRequest( + requests=[ActivateFlowsRequest( + sid=SIDUtils.to_pb(imsi), + ipv6_addr=sub_ip, + policies=policies, + )], + epoch=global_epoch + ) + + fake_controller_setup( + enf_controller=self.enforcement_controller, + enf_stats_controller=self.enforcement_stats_controller, + startup_flow_controller=self.startup_flows_contoller, + setup_flows_request=setup_flows_request) + snapshot_verifier = SnapshotVerifier(self, self.BRIDGE, + self.service_manager, + 'after_restart') + with snapshot_verifier: + pass + + fake_controller_setup( + enf_controller=self.enforcement_controller, + enf_stats_controller=self.enforcement_stats_controller, + startup_flow_controller=self.startup_flows_contoller) + snapshot_verifier = SnapshotVerifier(self, self.BRIDGE, + self.service_manager, + 'default_flows') + + with snapshot_verifier: + pass + + def test_enforcement_restart(self): """ Adds rules using the setup feature @@ -174,6 +244,8 @@ def test_enforcement_restart(self): 4) Empty SetupFlowsRequest - assert default flows """ + self.enforcement_controller._rule_mapper = RuleIDToNumMapper() + self.enforcement_stats_controller._rule_mapper = RuleIDToNumMapper() fake_controller_setup( enf_controller=self.enforcement_controller, enf_stats_controller=self.enforcement_stats_controller, @@ -286,8 +358,6 @@ def test_enforcement_restart(self): version=1, ), ] - self.service_manager.session_rule_version_mapper.save_version( - imsi2, convert_ipv4_str_to_ip_proto(sub2_ip), 'sub2_new_rule', 1) enf_stat_name = [imsi2 + '|sub2_new_rule' + '|' + sub2_ip, imsi2 + '|sub2_rule_keep' + '|' + sub2_ip] setup_flows_request = SetupFlowsRequest( From 9e0bd198d314b7825295515b8c54adfd423ee95b Mon Sep 17 00:00:00 2001 From: Ankit Kumar Aman <50101753+VinashakAnkitAman@users.noreply.github.com> Date: Sat, 10 Apr 2021 02:40:42 +0530 Subject: [PATCH 83/91] Fixed MME crash for stale enb cleanup issue (#5707) Signed-off-by: Ankit Kumar Aman --- lte/gateway/c/oai/tasks/s1ap/s1ap_mme_handlers.c | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/lte/gateway/c/oai/tasks/s1ap/s1ap_mme_handlers.c b/lte/gateway/c/oai/tasks/s1ap/s1ap_mme_handlers.c index 231c0324a8ba..5a9a69839fa5 100644 --- a/lte/gateway/c/oai/tasks/s1ap/s1ap_mme_handlers.c +++ b/lte/gateway/c/oai/tasks/s1ap/s1ap_mme_handlers.c @@ -362,13 +362,20 @@ void clean_stale_enb_state( ue_description_t* ue_ref = NULL; for (int i = 0; i < keys->num_keys; i++) { ue_ref = s1ap_state_get_ue_mmeid((mme_ue_s1ap_id_t) keys->keys[i]); + /* The function s1ap_remove_ue will take care of removing the enb also, + * when the last UE is removed + */ s1ap_remove_ue(state, ue_ref); } FREE_HASHTABLE_KEY_ARRAY(keys); + } else { + // Remove the old eNB association + OAILOG_INFO( + LOG_S1AP, "Deleting eNB: %s (Sctp_assoc_id = %u)", + stale_enb_association->enb_name, stale_enb_association->sctp_assoc_id); + s1ap_remove_enb(state, stale_enb_association); } - // Remove the old eNB association - s1ap_remove_enb(state, stale_enb_association); OAILOG_DEBUG(LOG_S1AP, "Removed stale eNB and all associated UEs."); } From 1963a0b62dd12d829fac0c772ce249f8c7f11c19 Mon Sep 17 00:00:00 2001 From: Pravin B Shelar Date: Fri, 9 Apr 2021 17:52:53 +0530 Subject: [PATCH 84/91] fix Signed-off-by: Pravin B Shelar --- lte/gateway/c/oai/lib/openflow/controller/GTPApplication.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lte/gateway/c/oai/lib/openflow/controller/GTPApplication.cpp b/lte/gateway/c/oai/lib/openflow/controller/GTPApplication.cpp index ceda943a36e5..4b1c64243600 100644 --- a/lte/gateway/c/oai/lib/openflow/controller/GTPApplication.cpp +++ b/lte/gateway/c/oai/lib/openflow/controller/GTPApplication.cpp @@ -421,7 +421,7 @@ void GTPApplication::add_downlink_tunnel_flow_action( of13::FlowMod downlink_fm, bool passthrough, bool from_pgw) { uint32_t in_teid; if (from_pgw) { - in_teid = ev.get_in_tei(); + in_teid = ev.get_pgw_in_tei(); } else { in_teid = ev.get_in_tei(); } From 601524df5bd4ecce4f52d2ef97016d9bb86a1abc Mon Sep 17 00:00:00 2001 From: rashmi Date: Sat, 10 Apr 2021 03:29:34 +0530 Subject: [PATCH 85/91] Modified code to use updated ovs api to add tunnel Signed-off-by: rashmi --- .../c/oai/tasks/sgw_s8/sgw_s8_handlers.c | 108 +++--------------- 1 file changed, 15 insertions(+), 93 deletions(-) diff --git a/lte/gateway/c/oai/tasks/sgw_s8/sgw_s8_handlers.c b/lte/gateway/c/oai/tasks/sgw_s8/sgw_s8_handlers.c index 7470eb4cfb44..6a703020a3db 100644 --- a/lte/gateway/c/oai/tasks/sgw_s8/sgw_s8_handlers.c +++ b/lte/gateway/c/oai/tasks/sgw_s8/sgw_s8_handlers.c @@ -28,12 +28,7 @@ limitations under the License. extern task_zmq_ctx_t sgw_s8_task_zmq_ctx; extern struct gtp_tunnel_ops* gtp_tunnel_ops; - -static int sgw_s8_add_gtp_tunnel( - sgw_eps_bearer_ctxt_t* eps_bearer_ctxt_p, - sgw_eps_bearer_context_information_t* sgw_context_p); - -static int sgw_s8_add_gtp_s8_tunnel( +static int sgw_s8_add_gtp_up_tunnel( sgw_eps_bearer_ctxt_t* eps_bearer_ctxt_p, sgw_eps_bearer_context_information_t* sgw_context_p); @@ -509,84 +504,6 @@ void sgw_s8_handle_create_session_response( OAILOG_FUNC_OUT(LOG_SGW_S8); } -// Helper function to add gtp tunnels for default bearers -static int sgw_s8_add_gtp_tunnel( - sgw_eps_bearer_ctxt_t* eps_bearer_ctxt_p, - sgw_eps_bearer_context_information_t* sgw_context_p) { - int rv = RETURNok; - struct in_addr enb = {.s_addr = 0}; - struct in_addr pgw = {.s_addr = 0}; - enb.s_addr = - eps_bearer_ctxt_p->enb_ip_address_S1u.address.ipv4_address.s_addr; - - pgw.s_addr = - eps_bearer_ctxt_p->p_gw_address_in_use_up.address.ipv4_address.s_addr; - struct in_addr ue_ipv4 = {.s_addr = 0}; - struct in6_addr* ue_ipv6 = NULL; - ue_ipv4.s_addr = eps_bearer_ctxt_p->paa.ipv4_address.s_addr; - if ((eps_bearer_ctxt_p->paa.pdn_type == IPv6) || - (eps_bearer_ctxt_p->paa.pdn_type == IPv4_AND_v6)) { - ue_ipv6 = &eps_bearer_ctxt_p->paa.ipv6_address; - } - - int vlan = eps_bearer_ctxt_p->paa.vlan; - Imsi_t imsi = sgw_context_p->imsi; - - char ip6_str[INET6_ADDRSTRLEN]; - if (ue_ipv6) { - inet_ntop(AF_INET6, ue_ipv6, ip6_str, INET6_ADDRSTRLEN); - } - /* UE is switching back to EPS services after the CS Fallback - * If Modify bearer Request is received in UE suspended mode, Resume PS - * data - */ - if (sgw_context_p->pdn_connection.ue_suspended_for_ps_handover) { - rv = gtp_tunnel_ops->forward_data_on_tunnel( - ue_ipv4, ue_ipv6, eps_bearer_ctxt_p->s_gw_teid_S1u_S12_S4_up, NULL, - DEFAULT_PRECEDENCE); - if (rv < 0) { - OAILOG_ERROR_UE( - LOG_SGW_S8, sgw_context_p->imsi64, - "ERROR in forwarding data on TUNNEL err=%d\n", rv); - } - } else { - OAILOG_DEBUG_UE( - LOG_SGW_S8, sgw_context_p->imsi64, - "Adding tunnel for bearer %u ue addr %x enb " - "%x,s_gw_teid_S1u_S12_S4_up %x, enb_teid_S1u %x pgw_up_ip %x " - "pgw_up_teid %x \n", - eps_bearer_ctxt_p->eps_bearer_id, ue_ipv4.s_addr, enb.s_addr, - eps_bearer_ctxt_p->s_gw_teid_S1u_S12_S4_up, - eps_bearer_ctxt_p->enb_teid_S1u, pgw.s_addr, - eps_bearer_ctxt_p->p_gw_teid_S5_S8_up); - if (eps_bearer_ctxt_p->eps_bearer_id == - sgw_context_p->pdn_connection.default_bearer) { - // Set default precedence and tft for default bearer - if (ue_ipv6) { - OAILOG_INFO_UE( - LOG_SGW_S8, sgw_context_p->imsi64, - "Adding tunnel for ipv6 ue addr %s, enb %x, " - "s_gw_teid_S1u_S12_S4_up %x, enb_teid_S1u %x pgw_up_ip %x " - "pgw_up_teid %x \n", - ip6_str, enb.s_addr, eps_bearer_ctxt_p->s_gw_teid_S1u_S12_S4_up, - eps_bearer_ctxt_p->enb_teid_S1u, pgw.s_addr, - eps_bearer_ctxt_p->p_gw_teid_S5_S8_up); - } - rv = gtpv1u_add_s8_tunnel( - ue_ipv4, ue_ipv6, vlan, enb, pgw, - eps_bearer_ctxt_p->s_gw_teid_S1u_S12_S4_up, - eps_bearer_ctxt_p->enb_teid_S1u, 0, 0, imsi, NULL, - DEFAULT_PRECEDENCE); - if (rv < 0) { - OAILOG_ERROR_UE( - LOG_SGW_S8, sgw_context_p->imsi64, - "ERROR in setting up TUNNEL err=%d\n", rv); - } - } - } - OAILOG_FUNC_RETURN(LOG_SGW_S8, rv); -} - void sgw_s8_handle_modify_bearer_request( sgw_state_t* state, const itti_s11_modify_bearer_request_t* const modify_bearer_pP, @@ -761,9 +678,8 @@ static void sgw_s8_populate_mbr_bearer_contexts_modified( .cause.cause_value = REQUEST_ACCEPTED; modify_response_p->bearer_contexts_modified.num_bearer_context++; - // setup GTPv1-U tunnel - sgw_s8_add_gtp_tunnel(eps_bearer_ctxt_p, sgw_context_p); - sgw_s8_add_gtp_s8_tunnel(eps_bearer_ctxt_p, sgw_context_p); + // setup GTPv1-U tunnels, both s1-u and s8-u tunnels + sgw_s8_add_gtp_up_tunnel(eps_bearer_ctxt_p, sgw_context_p); // may be removed TODO rashmi remove after testing if (TRAFFIC_FLOW_TEMPLATE_NB_PACKET_FILTERS_MAX > eps_bearer_ctxt_p->num_sdf) { @@ -783,7 +699,7 @@ static void sgw_s8_populate_mbr_bearer_contexts_modified( } // Helper function to add gtp tunnels for default bearers -static int sgw_s8_add_gtp_s8_tunnel( +static int sgw_s8_add_gtp_up_tunnel( sgw_eps_bearer_ctxt_t* eps_bearer_ctxt_p, sgw_eps_bearer_context_information_t* sgw_context_p) { int rv = RETURNok; @@ -819,12 +735,16 @@ static int sgw_s8_add_gtp_s8_tunnel( } OAILOG_DEBUG_UE( LOG_SGW_S8, sgw_context_p->imsi64, - "Adding tunnel for bearer_id %u ue addr %x enb %x s_gw_teid_S5_S8_up %x, " - "s_gw_ip_address_S5_S8_up %x pgw_up_ip %x pgw_up_teid %x \n", + "Adding tunnel for bearer_id %u ue addr %x enb %x " + "s_gw_teid_S1u_S12_S4_up %x, enb_teid_S1u %x pgw_up_ip %x pgw_up_teid %x " + "s_gw_ip_address_S5_S8_up %x" + "s_gw_teid_S5_S8_up %x \n ", eps_bearer_ctxt_p->eps_bearer_id, ue_ipv4.s_addr, enb.s_addr, - eps_bearer_ctxt_p->s_gw_teid_S5_S8_up, + eps_bearer_ctxt_p->s_gw_teid_S1u_S12_S4_up, + eps_bearer_ctxt_p->enb_teid_S1u, pgw.s_addr, + eps_bearer_ctxt_p->p_gw_teid_S5_S8_up, eps_bearer_ctxt_p->s_gw_ip_address_S5_S8_up.address.ipv4_address.s_addr, - pgw.s_addr, eps_bearer_ctxt_p->p_gw_teid_S5_S8_up); + eps_bearer_ctxt_p->s_gw_teid_S5_S8_up); if (eps_bearer_ctxt_p->eps_bearer_id == sgw_context_p->pdn_connection.default_bearer) { // Set default precedence and tft for default bearer @@ -840,7 +760,9 @@ static int sgw_s8_add_gtp_s8_tunnel( pgw.s_addr, eps_bearer_ctxt_p->p_gw_teid_S5_S8_up); } rv = gtpv1u_add_s8_tunnel( - ue_ipv4, ue_ipv6, vlan, enb, pgw, eps_bearer_ctxt_p->s_gw_teid_S5_S8_up, + ue_ipv4, ue_ipv6, vlan, enb, pgw, + eps_bearer_ctxt_p->s_gw_teid_S1u_S12_S4_up, + eps_bearer_ctxt_p->enb_teid_S1u, eps_bearer_ctxt_p->s_gw_teid_S5_S8_up, eps_bearer_ctxt_p->p_gw_teid_S5_S8_up, imsi, NULL, DEFAULT_PRECEDENCE); if (rv < 0) { OAILOG_ERROR_UE( From b8867cfceb4393dc5ed8fe48031ef4e70995bcb0 Mon Sep 17 00:00:00 2001 From: Ulas Kozat Date: Fri, 9 Apr 2021 15:10:57 -0700 Subject: [PATCH 86/91] Some optimizations and log level changes for blocked IMEI (#6075) Signed-off-by: Ulas Kozat --- lte/gateway/c/oai/common/conversions.h | 48 +++++++++---------- lte/gateway/c/oai/tasks/mme_app/mme_config.c | 6 +++ lte/gateway/c/oai/tasks/nas/emm/Attach.c | 4 +- .../c/oai/tasks/nas/emm/SecurityModeControl.c | 9 +++- 4 files changed, 38 insertions(+), 29 deletions(-) diff --git a/lte/gateway/c/oai/common/conversions.h b/lte/gateway/c/oai/common/conversions.h index 212763a19e6d..a5bca669fc2a 100644 --- a/lte/gateway/c/oai/common/conversions.h +++ b/lte/gateway/c/oai/common/conversions.h @@ -525,36 +525,32 @@ imsi64_t imsi_to_imsi64(const imsi_t* const imsi); #define IMEI_MOBID_TO_IMEI64(iMeI_t_PtR, iMEI64) \ { \ - /*len = TAC((8 digits + SNR (6 digits) - 1)*/ \ - int len = 13; \ - (*iMEI64) += (uint64_t)((iMeI_t_PtR)->u.num.tac1 * (pow(10, (len--)))); \ - (*iMEI64) += (uint64_t)((iMeI_t_PtR)->u.num.tac2 * (pow(10, (len--)))); \ - (*iMEI64) += (uint64_t)((iMeI_t_PtR)->u.num.tac3 * (pow(10, (len--)))); \ - (*iMEI64) += (uint64_t)((iMeI_t_PtR)->u.num.tac4 * (pow(10, (len--)))); \ - (*iMEI64) += (uint64_t)((iMeI_t_PtR)->u.num.tac5 * (pow(10, (len--)))); \ - (*iMEI64) += (uint64_t)((iMeI_t_PtR)->u.num.tac6 * (pow(10, (len--)))); \ - (*iMEI64) += (uint64_t)((iMeI_t_PtR)->u.num.tac7 * (pow(10, (len--)))); \ - (*iMEI64) += (uint64_t)((iMeI_t_PtR)->u.num.tac8 * (pow(10, (len--)))); \ - (*iMEI64) += (uint64_t)((iMeI_t_PtR)->u.num.snr1 * (pow(10, (len--)))); \ - (*iMEI64) += (uint64_t)((iMeI_t_PtR)->u.num.snr2 * (pow(10, (len--)))); \ - (*iMEI64) += (uint64_t)((iMeI_t_PtR)->u.num.snr3 * (pow(10, (len--)))); \ - (*iMEI64) += (uint64_t)((iMeI_t_PtR)->u.num.snr4 * (pow(10, (len--)))); \ - (*iMEI64) += (uint64_t)((iMeI_t_PtR)->u.num.snr5 * (pow(10, (len--)))); \ - (*iMEI64) += (uint64_t)((iMeI_t_PtR)->u.num.snr6 * (pow(10, (len--)))); \ + (*iMEI64) = (uint64_t)((iMeI_t_PtR)->u.num.tac1); \ + (*iMEI64) = (10 * (*iMEI64)) + ((uint64_t)((iMeI_t_PtR)->u.num.tac2)); \ + (*iMEI64) = (10 * (*iMEI64)) + ((uint64_t)((iMeI_t_PtR)->u.num.tac3)); \ + (*iMEI64) = (10 * (*iMEI64)) + ((uint64_t)((iMeI_t_PtR)->u.num.tac4)); \ + (*iMEI64) = (10 * (*iMEI64)) + ((uint64_t)((iMeI_t_PtR)->u.num.tac5)); \ + (*iMEI64) = (10 * (*iMEI64)) + ((uint64_t)((iMeI_t_PtR)->u.num.tac6)); \ + (*iMEI64) = (10 * (*iMEI64)) + ((uint64_t)((iMeI_t_PtR)->u.num.tac7)); \ + (*iMEI64) = (10 * (*iMEI64)) + ((uint64_t)((iMeI_t_PtR)->u.num.tac8)); \ + (*iMEI64) = (10 * (*iMEI64)) + ((uint64_t)((iMeI_t_PtR)->u.num.snr1)); \ + (*iMEI64) = (10 * (*iMEI64)) + ((uint64_t)((iMeI_t_PtR)->u.num.snr2)); \ + (*iMEI64) = (10 * (*iMEI64)) + ((uint64_t)((iMeI_t_PtR)->u.num.snr3)); \ + (*iMEI64) = (10 * (*iMEI64)) + ((uint64_t)((iMeI_t_PtR)->u.num.snr4)); \ + (*iMEI64) = (10 * (*iMEI64)) + ((uint64_t)((iMeI_t_PtR)->u.num.snr5)); \ + (*iMEI64) = (10 * (*iMEI64)) + ((uint64_t)((iMeI_t_PtR)->u.num.snr6)); \ } #define IMEI_MOBID_TO_IMEI_TAC64(iMeI_t_PtR, tAc_PtR) \ { \ - /* len = TAC length(8 digits) - 1*/ \ - int len = 7; \ - (*tAc_PtR) += (uint64_t)((iMeI_t_PtR)->u.num.tac1 * (pow(10, (len--)))); \ - (*tAc_PtR) += (uint64_t)((iMeI_t_PtR)->u.num.tac2 * (pow(10, (len--)))); \ - (*tAc_PtR) += (uint64_t)((iMeI_t_PtR)->u.num.tac3 * (pow(10, (len--)))); \ - (*tAc_PtR) += (uint64_t)((iMeI_t_PtR)->u.num.tac4 * (pow(10, (len--)))); \ - (*tAc_PtR) += (uint64_t)((iMeI_t_PtR)->u.num.tac5 * (pow(10, (len--)))); \ - (*tAc_PtR) += (uint64_t)((iMeI_t_PtR)->u.num.tac6 * (pow(10, (len--)))); \ - (*tAc_PtR) += (uint64_t)((iMeI_t_PtR)->u.num.tac7 * (pow(10, (len--)))); \ - (*tAc_PtR) += (uint64_t)((iMeI_t_PtR)->u.num.tac8 * (pow(10, (len)))); \ + (*tAc_PtR) = (uint64_t)((iMeI_t_PtR)->u.num.tac1); \ + (*tAc_PtR) = (10 * (*tAc_PtR)) + ((uint64_t)((iMeI_t_PtR)->u.num.tac2)); \ + (*tAc_PtR) = (10 * (*tAc_PtR)) + ((uint64_t)((iMeI_t_PtR)->u.num.tac3)); \ + (*tAc_PtR) = (10 * (*tAc_PtR)) + ((uint64_t)((iMeI_t_PtR)->u.num.tac4)); \ + (*tAc_PtR) = (10 * (*tAc_PtR)) + ((uint64_t)((iMeI_t_PtR)->u.num.tac5)); \ + (*tAc_PtR) = (10 * (*tAc_PtR)) + ((uint64_t)((iMeI_t_PtR)->u.num.tac6)); \ + (*tAc_PtR) = (10 * (*tAc_PtR)) + ((uint64_t)((iMeI_t_PtR)->u.num.tac7)); \ + (*tAc_PtR) = (10 * (*tAc_PtR)) + ((uint64_t)((iMeI_t_PtR)->u.num.tac8)); \ } #define IMSI_TO_OCTET_STRING(iMsI_sTr, iMsI_len, aSN) \ diff --git a/lte/gateway/c/oai/tasks/mme_app/mme_config.c b/lte/gateway/c/oai/tasks/mme_app/mme_config.c index cd13672d12a3..3d62d6afd98e 100644 --- a/lte/gateway/c/oai/tasks/mme_app/mme_config.c +++ b/lte/gateway/c/oai/tasks/mme_app/mme_config.c @@ -229,6 +229,11 @@ void service303_config_init(service303_data_t* service303_conf) { service303_conf->version = bfromcstr(SERVICE303_MME_PACKAGE_VERSION); } +void blocked_imei_config_init(blocked_imei_list_t* blocked_imeis) { + blocked_imeis->num = 0; + blocked_imeis->imei_htbl = NULL; +} + //------------------------------------------------------------------------------ void mme_config_init(mme_config_t* config) { memset(config, 0, sizeof(*config)); @@ -253,6 +258,7 @@ void mme_config_init(mme_config_t* config) { gummei_config_init(&config->gummei); served_tai_config_init(&config->served_tai); service303_config_init(&config->service303_config); + blocked_imei_config_init(&config->blocked_imei); } //------------------------------------------------------------------------------ diff --git a/lte/gateway/c/oai/tasks/nas/emm/Attach.c b/lte/gateway/c/oai/tasks/nas/emm/Attach.c index 8943477d2453..812389a59cdc 100644 --- a/lte/gateway/c/oai/tasks/nas/emm/Attach.c +++ b/lte/gateway/c/oai/tasks/nas/emm/Attach.c @@ -1304,7 +1304,7 @@ static int emm_attach_success_security_cb(emm_context_t* emm_context) { nas_emm_attach_proc_t* attach_proc = get_nas_specific_procedure_attach(emm_context); if (!attach_proc) { - OAILOG_INFO_UE( + OAILOG_ERROR_UE( LOG_NAS_EMM, emm_context->_imsi64, "EMM-PROC - attach_proc is NULL \n"); OAILOG_FUNC_RETURN(LOG_NAS_EMM, RETURNerror); @@ -1312,7 +1312,7 @@ static int emm_attach_success_security_cb(emm_context_t* emm_context) { if (emm_context->initiate_identity_after_smc) { emm_context->initiate_identity_after_smc = false; - OAILOG_INFO_UE( + OAILOG_DEBUG_UE( LOG_NAS_EMM, emm_context->_imsi64, "Trigger identity procedure\n"); rc = emm_proc_identification( emm_context, (nas_emm_proc_t*) attach_proc, IDENTITY_TYPE_2_IMEISV, diff --git a/lte/gateway/c/oai/tasks/nas/emm/SecurityModeControl.c b/lte/gateway/c/oai/tasks/nas/emm/SecurityModeControl.c index 88ae1b298111..eaf445c0fe6e 100644 --- a/lte/gateway/c/oai/tasks/nas/emm/SecurityModeControl.c +++ b/lte/gateway/c/oai/tasks/nas/emm/SecurityModeControl.c @@ -398,6 +398,13 @@ int validate_imei(imeisv_t* imeisv) { * the hashlist */ imei64_t tac64 = 0; + if (!mme_config.blocked_imei.num) { + OAILOG_DEBUG(LOG_NAS_EMM, "No Blocked IMEI exists, returning success!"); + OAILOG_FUNC_RETURN(LOG_NAS_EMM, EMM_CAUSE_SUCCESS); + } else { + OAILOG_DEBUG( + LOG_NAS_EMM, "Blocked IMEI exists, proceed with validation..."); + } IMEI_MOBID_TO_IMEI_TAC64(imeisv, &tac64); hashtable_rc_t h_rc = hashtable_uint64_ts_is_key_exists( mme_config.blocked_imei.imei_htbl, (const hash_key_t) tac64); @@ -405,7 +412,7 @@ int validate_imei(imeisv_t* imeisv) { if (HASH_TABLE_OK == h_rc) { OAILOG_FUNC_RETURN(LOG_NAS_EMM, EMM_CAUSE_IMEI_NOT_ACCEPTED); } else { - // Convert to imei to uint64_t + // Convert imei to uint64_t imei64_t imei64 = 0; IMEI_MOBID_TO_IMEI64(imeisv, &imei64); hashtable_rc_t h_rc = hashtable_uint64_ts_is_key_exists( From dd779f7b402c7641f25f809bc8c17b8cc9f6b32f Mon Sep 17 00:00:00 2001 From: Marie <37634144+themarwhal@users.noreply.github.com> Date: Fri, 9 Apr 2021 17:26:01 -0500 Subject: [PATCH 87/91] [Sentry][AGW] Handle env var lookup error better in sentry init (#6048) --- orc8r/gateway/python/magma/common/sentry.py | 41 ++++++++++++++------- 1 file changed, 28 insertions(+), 13 deletions(-) diff --git a/orc8r/gateway/python/magma/common/sentry.py b/orc8r/gateway/python/magma/common/sentry.py index 0a2e2f8270c5..9147d9525853 100644 --- a/orc8r/gateway/python/magma/common/sentry.py +++ b/orc8r/gateway/python/magma/common/sentry.py @@ -12,21 +12,36 @@ """ import os + import sentry_sdk import snowflake - from magma.configuration.service_configs import get_service_config_value +CONTROL_PROXY = 'control_proxy' +SENTRY_URL = 'sentry_url' +SENTRY_SAMPLE_RATE = 'sentry_sample_rate' +COMMIT_HASH = 'COMMIT_HASH' +HWID = 'hwid' + + def sentry_init(): - """ - Initialize connection and start piping errors to sentry.io - """ - sentry_url = get_service_config_value('control_proxy', 'sentry_url', default="") - if sentry_url: - sentry_sample_rate = get_service_config_value('control_proxy', 'sentry_sample_rate', default=1.0) - sentry_sdk.init( - dsn=sentry_url, - release=os.environ['COMMIT_HASH'], - traces_sample_rate=sentry_sample_rate, - ) - sentry_sdk.set_tag("hwid", snowflake.snowflake()) + """Initialize connection and start piping errors to sentry.io.""" + sentry_url = get_service_config_value( + CONTROL_PROXY, + SENTRY_URL, + default='', + ) + if not sentry_url: + return + + sentry_sample_rate = get_service_config_value( + CONTROL_PROXY, + SENTRY_SAMPLE_RATE, + default=1.0, + ) + sentry_sdk.init( + dsn=sentry_url, + release=os.getenv(COMMIT_HASH), + traces_sample_rate=sentry_sample_rate, + ) + sentry_sdk.set_tag(HWID, snowflake.snowflake()) From fb43400944be172c5f1fda4b9af7fd8000f2aec0 Mon Sep 17 00:00:00 2001 From: Andrei Lee Date: Fri, 9 Apr 2021 17:09:16 -0700 Subject: [PATCH 88/91] [nms] Update @fbcnms/sequelize-models to ^0.1.6 (#6072) Signed-off-by: Andrei Lee --- nms/app/packages/magmalte/package.json | 2 +- nms/app/yarn.lock | 47 ++++++++++++++++++++++++++ 2 files changed, 48 insertions(+), 1 deletion(-) diff --git a/nms/app/packages/magmalte/package.json b/nms/app/packages/magmalte/package.json index 9ea7590cc4fa..f6a8fd9d9bf0 100644 --- a/nms/app/packages/magmalte/package.json +++ b/nms/app/packages/magmalte/package.json @@ -33,7 +33,7 @@ "@fbcnms/magma-api": "^0.1.0", "@fbcnms/platform-server": "^0.1.23", "@fbcnms/projects": "^0.1.0", - "@fbcnms/sequelize-models": "^0.1.1", + "@fbcnms/sequelize-models": "^0.1.6", "@fbcnms/strings": "^0.1.0", "@fbcnms/types": "^0.1.11", "@fbcnms/ui": "^0.1.8", diff --git a/nms/app/yarn.lock b/nms/app/yarn.lock index 32f0c93cd47f..70986d5c8adf 100644 --- a/nms/app/yarn.lock +++ b/nms/app/yarn.lock @@ -1553,6 +1553,17 @@ dependencies: sequelize "^5.8.5" +"@fbcnms/sequelize-models@^0.1.6": + version "0.1.6" + resolved "https://registry.yarnpkg.com/@fbcnms/sequelize-models/-/sequelize-models-0.1.6.tgz#4d4278b9c59469f17e7851f514189cff703173ea" + integrity sha512-xQoF4w+6qo18gwS+kQYo5aCPpSvX9ZYIrAEp4EalQs+X5QkeKeHJs3a5c6SO8/wnMKsOX7Tdo0bg2Oa9Fgdc/w== + dependencies: + "@fbcnms/babel-register" "^0.1.0" + inquirer "^8.0.0" + mariadb "^2.4.2" + minimist "^1.2.5" + sequelize "^5.8.5" + "@fbcnms/strings@^0.1.0": version "0.1.0" resolved "https://registry.yarnpkg.com/@fbcnms/strings/-/strings-0.1.0.tgz#50dae22b4cb7cb7d1a30c013d0b40c96b741a1f1" @@ -4257,6 +4268,11 @@ cli-width@^2.0.0: resolved "https://registry.yarnpkg.com/cli-width/-/cli-width-2.2.1.tgz#b0433d0b4e9c847ef18868a4ef16fd5fc8271c48" integrity sha512-GRMWDxpOB6Dgk2E5Uo+3eEBvtOOlimMmpbFiKuLFnQzYDavtLFY3K5ona41jgN/WdRZtG7utuVSVTL4HbZHGkw== +cli-width@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/cli-width/-/cli-width-3.0.0.tgz#a2f48437a2caa9a22436e794bf071ec9e61cedf6" + integrity sha512-FxqpkPPwu1HjuN93Omfm4h8uIanXofW0RxVEW3k5RKx+mJJYSthzNhp32Kzxxy3YAEZ/Dc/EWN1vZRY0+kOhbw== + cli@~1.0.0: version "1.0.1" resolved "https://registry.yarnpkg.com/cli/-/cli-1.0.1.tgz#22817534f24bfa4950c34d532d48ecbc621b8c14" @@ -7600,6 +7616,25 @@ inquirer@^7.0.0: strip-ansi "^6.0.0" through "^2.3.6" +inquirer@^8.0.0: + version "8.0.0" + resolved "https://registry.yarnpkg.com/inquirer/-/inquirer-8.0.0.tgz#957a46db1abcf0fdd2ab82deb7470e90afc7d0ac" + integrity sha512-ON8pEJPPCdyjxj+cxsYRe6XfCJepTxANdNnTebsTuQgXpRyZRRT9t4dJwjRubgmvn20CLSEnozRUayXyM9VTXA== + dependencies: + ansi-escapes "^4.2.1" + chalk "^4.1.0" + cli-cursor "^3.1.0" + cli-width "^3.0.0" + external-editor "^3.0.3" + figures "^3.0.0" + lodash "^4.17.21" + mute-stream "0.0.8" + run-async "^2.4.0" + rxjs "^6.6.6" + string-width "^4.1.0" + strip-ansi "^6.0.0" + through "^2.3.6" + internal-slot@^1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/internal-slot/-/internal-slot-1.0.2.tgz#9c2e9fb3cd8e5e4256c6f45fe310067fcfa378a3" @@ -9140,6 +9175,11 @@ lodash@^4.17.10, lodash@^4.17.14, lodash@~4.17.11: resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.19.tgz#e48ddedbe30b3321783c5b4301fbd353bc1e4a4b" integrity sha512-JNvd8XER9GQX0v2qJgsaN/mzFCNA5BRe/j8JN9d+tWyGLSodKQHKFicdwNYzWwI3wjRnaKPsGj1XkBjx/F96DQ== +lodash@^4.17.21: + version "4.17.21" + resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.21.tgz#679591c564c3bffaae8454cf0b3df370c3d6911c" + integrity sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg== + logform@^2.2.0: version "2.2.0" resolved "https://registry.yarnpkg.com/logform/-/logform-2.2.0.tgz#40f036d19161fc76b68ab50fdc7fe495544492f2" @@ -12408,6 +12448,13 @@ rxjs@^6.5.3: dependencies: tslib "^1.9.0" +rxjs@^6.6.6: + version "6.6.7" + resolved "https://registry.yarnpkg.com/rxjs/-/rxjs-6.6.7.tgz#90ac018acabf491bf65044235d5863c4dab804c9" + integrity sha512-hTdwr+7yYNIT5n4AMYp85KA6yw2Va0FLa3Rguvbpa4W3I5xynaBZo41cM3XM+4Q6fRMj3sBYIR1VAmZMXYJvRQ== + dependencies: + tslib "^1.9.0" + safe-buffer@5.1.2, safe-buffer@~5.1.0, safe-buffer@~5.1.1: version "5.1.2" resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.1.2.tgz#991ec69d296e0313747d59bdfd2b745c35f8828d" From 0a96ca2d1b568f9f76b3ce80f728aaa8acb2df86 Mon Sep 17 00:00:00 2001 From: Ulas Kozat Date: Sat, 10 Apr 2021 21:35:34 -0700 Subject: [PATCH 89/91] [lte][agw] Generate final config file (#6078) * Generate final config file Signed-off-by: Ulas Kozat --- .../configs/templates/mme.conf.template | 13 +++- .../files/update_mme_config_for_sanity.sh | 2 +- .../python/scripts/generate_oai_config.py | 72 ++++++++++--------- 3 files changed, 51 insertions(+), 36 deletions(-) diff --git a/lte/gateway/configs/templates/mme.conf.template b/lte/gateway/configs/templates/mme.conf.template index 46ebf5265d93..e15a030a4e06 100644 --- a/lte/gateway/configs/templates/mme.conf.template +++ b/lte/gateway/configs/templates/mme.conf.template @@ -108,13 +108,20 @@ MME : # List of blocked IMEIs # By default this list is empty - # Max number of blocked IMEI is 10 + # Stored in a hash table on mme side # Length of IMEI=15 digits, length of IMEISV=16 digits BLOCKED_IMEI_LIST = ( # Sample IMEI: TAC(8 digits) + SNR (6 digits) - #{ TAC="99000482"; SNR="351037"} + #{ IMEI_TAC="99000482"; SNR="351037"} # Sample IMEI without SNR: TAC(8 digits) - #{ TAC="99000482";} + #{ IMEI_TAC="99000482";} + # ImeiConfig values can be found at magma/lte/protos/mconfig/mconfigs.proto + {% for imei_config in restrictedImeis -%} + { + IMEI_TAC = "{{ imei_config.tac }}" + SNR = "{{ imei_config.snr }}" + }{% if not loop.last %},{% endif %} + {% endfor %} ); CSFB : diff --git a/lte/gateway/deploy/roles/magma/files/update_mme_config_for_sanity.sh b/lte/gateway/deploy/roles/magma/files/update_mme_config_for_sanity.sh index 5378548d3a3c..669ee0776747 100755 --- a/lte/gateway/deploy/roles/magma/files/update_mme_config_for_sanity.sh +++ b/lte/gateway/deploy/roles/magma/files/update_mme_config_for_sanity.sh @@ -89,7 +89,7 @@ function configure_restricted_plmn { function configure_blocked_imei { # Remove default blocked imei(s) from MME configuration file - sed -i -e '/BLOCKED_IMEI_LIST/{n;N;N;N;d}' \ + sed -i -e '/BLOCKED_IMEI_LIST/{n;N;N;N;N;N;N;N;N;N;N;d}' \ "$mme_config_file" # Configure blocked imei(s) in MME configuration file diff --git a/lte/gateway/python/scripts/generate_oai_config.py b/lte/gateway/python/scripts/generate_oai_config.py index ca3018b66316..893766673e2c 100755 --- a/lte/gateway/python/scripts/generate_oai_config.py +++ b/lte/gateway/python/scripts/generate_oai_config.py @@ -177,18 +177,29 @@ def _get_apn_correction_map_list(service_mconfig): return service_mconfig.apn_correction_map_list return get_service_config_value("mme", "apn_correction_map_list", None) + def _get_federated_mode_map(service_mconfig): - if service_mconfig.federated_mode_map and \ - service_mconfig.federated_mode_map.enabled and \ - len(service_mconfig.federated_mode_map.mapping) != 0: + if ( + service_mconfig.federated_mode_map + and service_mconfig.federated_mode_map.enabled + and len(service_mconfig.federated_mode_map.mapping) != 0 + ): return service_mconfig.federated_mode_map.mapping return {} + def _get_restricted_plmns(service_mconfig): if service_mconfig.restricted_plmns: return service_mconfig.restricted_plmns return {} + +def _get_restricted_imeis(service_mconfig): + if service_mconfig.restricted_imeis: + return service_mconfig.restricted_imeis + return {} + + def _get_context(): """ Create the context which has the interface IP and the OAI log level to use. @@ -199,64 +210,61 @@ def _get_context(): "mme_s11_ip": _get_iface_ip("mme", "s11_iface_name"), "sgw_s11_ip": _get_iface_ip("spgw", "s11_iface_name"), "sgw_s5s8_up_ip": _get_iface_ip("spgw", "sgw_s5s8_up_iface_name"), - "remote_sgw_ip": get_service_config_value("mme", - "remote_sgw_ip", ""), + "remote_sgw_ip": get_service_config_value("mme", "remote_sgw_ip", ""), "s1ap_ip": _get_iface_ip("mme", "s1ap_iface_name"), "oai_log_level": _get_oai_log_level(), - "ipv4_dns": _get_primary_dns_ip(mme_service_config, - 'dns_iface_name'), + "ipv4_dns": _get_primary_dns_ip(mme_service_config, "dns_iface_name"), "ipv4_sec_dns": _get_secondary_dns_ip(mme_service_config), "ipv4_p_cscf_address": _get_ipv4_pcscf_ip(mme_service_config), "ipv6_dns": _get_ipv6_dns_ip(mme_service_config), "ipv6_p_cscf_address": _get_ipv6_pcscf_ip(mme_service_config), "identity": _get_identity(), "relay_enabled": _get_relay_enabled(mme_service_config), - "non_eps_service_control": _get_non_eps_service_control( - mme_service_config), + "non_eps_service_control": _get_non_eps_service_control(mme_service_config), "csfb_mcc": _get_csfb_mcc(mme_service_config), "csfb_mnc": _get_csfb_mnc(mme_service_config), "lac": _get_lac(mme_service_config), - "use_stateless": get_service_config_value("mme", - "use_stateless", ""), + "use_stateless": get_service_config_value("mme", "use_stateless", ""), "attached_enodeb_tacs": _get_attached_enodeb_tacs(mme_service_config), "enable_nat": _get_enable_nat(mme_service_config), - "federated_mode_map" : _get_federated_mode_map(mme_service_config), - "restricted_plmns" : _get_restricted_plmns(mme_service_config) + "federated_mode_map": _get_federated_mode_map(mme_service_config), + "restricted_plmns": _get_restricted_plmns(mme_service_config), + "restricted_imeis": _get_restricted_imeis(mme_service_config), } - context["s1u_ip"] = mme_service_config.ipv4_sgw_s1u_addr or \ - _get_iface_ip("spgw", "s1u_iface_name") + context["s1u_ip"] = mme_service_config.ipv4_sgw_s1u_addr or _get_iface_ip( + "spgw", "s1u_iface_name" + ) # set ovs params for key in ( - "ovs_bridge_name", - "ovs_gtp_port_number", - "ovs_mtr_port_number", - "ovs_internal_sampling_port_number", - "ovs_internal_sampling_fwd_tbl", - "ovs_uplink_port_number", - "ovs_uplink_mac", + "ovs_bridge_name", + "ovs_gtp_port_number", + "ovs_mtr_port_number", + "ovs_internal_sampling_port_number", + "ovs_internal_sampling_fwd_tbl", + "ovs_uplink_port_number", + "ovs_uplink_mac", ): context[key] = get_service_config_value("spgw", key, "") - context["enable_apn_correction"] = \ - get_service_config_value("mme", "enable_apn_correction", "") - context["apn_correction_map_list"] = \ - _get_apn_correction_map_list(mme_service_config) + context["enable_apn_correction"] = get_service_config_value( + "mme", "enable_apn_correction", "" + ) + context["apn_correction_map_list"] = _get_apn_correction_map_list( + mme_service_config + ) return context def main(): logging.basicConfig( - level=logging.INFO, - format="[%(asctime)s %(levelname)s %(name)s] %(message)s" + level=logging.INFO, format="[%(asctime)s %(levelname)s %(name)s] %(message)s" ) context = _get_context() - generate_template_config("spgw", "spgw", CONFIG_OVERRIDE_DIR, - context.copy()) + generate_template_config("spgw", "spgw", CONFIG_OVERRIDE_DIR, context.copy()) generate_template_config("mme", "mme", CONFIG_OVERRIDE_DIR, context.copy()) - generate_template_config("mme", "mme_fd", CONFIG_OVERRIDE_DIR, - context.copy()) + generate_template_config("mme", "mme_fd", CONFIG_OVERRIDE_DIR, context.copy()) cert_dir = get_service_config_value("mme", "cert_dir", "") generate_mme_certs(os.path.join(cert_dir, "freeDiameter")) From c10d36eeb2c027a35425b54dab7ea1cb8a0e8707 Mon Sep 17 00:00:00 2001 From: rsarwad <30685618+rsarwad@users.noreply.github.com> Date: Mon, 12 Apr 2021 05:38:04 +0530 Subject: [PATCH 90/91] Added code to handle delete session procedure (#5876) * Add code to handle create session response at sgw_s8 task Signed-off-by: rashmi --- lte/gateway/c/oai/common/CMakeLists.txt | 11 + .../c/oai/common/common_utility_funs.cpp | 60 ++ .../c/oai/common/common_utility_funs.h | 15 + .../c/oai/include/ip_forward_messages_types.h | 2 - .../c/oai/include/mme_app_ue_context.h | 1 + lte/gateway/c/oai/include/s8_messages_def.h | 2 + lte/gateway/c/oai/include/s8_messages_types.h | 7 +- .../c/oai/include/sgw_context_manager.h | 1 + lte/gateway/c/oai/lib/pcef/pcef_handlers.cpp | 2 +- lte/gateway/c/oai/lib/pcef/pcef_handlers.h | 2 + lte/gateway/c/oai/lib/s6a_proxy/S6aClient.cpp | 49 +- lte/gateway/c/oai/lib/s8_proxy/S8Client.cpp | 19 + lte/gateway/c/oai/lib/s8_proxy/S8Client.h | 5 + .../c/oai/lib/s8_proxy/s8_client_api.cpp | 151 ++++- .../c/oai/lib/s8_proxy/s8_client_api.h | 4 + .../c/oai/tasks/mme_app/mme_app_bearer.c | 58 +- .../c/oai/tasks/mme_app/mme_app_detach.c | 24 +- .../tasks/mme_app/mme_app_itti_messaging.c | 29 +- .../tasks/mme_app/mme_app_itti_messaging.h | 1 - .../c/oai/tasks/sgw/sgw_context_manager.c | 20 +- lte/gateway/c/oai/tasks/sgw/sgw_handlers.c | 58 +- lte/gateway/c/oai/tasks/sgw/sgw_handlers.h | 24 + .../c/oai/tasks/sgw_s8/sgw_s8_handlers.c | 548 ++++++++++++++++-- .../c/oai/tasks/sgw_s8/sgw_s8_s11_handlers.h | 9 + lte/gateway/c/oai/tasks/sgw_s8/sgw_s8_task.c | 16 + 25 files changed, 936 insertions(+), 182 deletions(-) create mode 100644 lte/gateway/c/oai/common/common_utility_funs.cpp create mode 100644 lte/gateway/c/oai/common/common_utility_funs.h diff --git a/lte/gateway/c/oai/common/CMakeLists.txt b/lte/gateway/c/oai/common/CMakeLists.txt index 9bd6534f6e4b..ff0c662e8915 100644 --- a/lte/gateway/c/oai/common/CMakeLists.txt +++ b/lte/gateway/c/oai/common/CMakeLists.txt @@ -9,20 +9,30 @@ include($ENV{MAGMA_ROOT}/orc8r/gateway/c/common/CMakeProtoMacros.txt) # compile the needed protos set(COMMON_CPP_PROTOS common_types) set(REDIS_CPP_PROTOS redis) +set(MCONFIG_PROTOS mconfig/mconfigs) list(APPEND PROTO_SRCS "") list(APPEND PROTO_HDRS "") +create_proto_dir("orc8r" ORC8R_CPP_OUT_DIR) set(LTE_OUT_DIR "${CMAKE_CURRENT_BINARY_DIR}/lte/protos/oai") generate_cpp_protos("${COMMON_CPP_PROTOS}" "${PROTO_SRCS}" "${PROTO_HDRS}" ${STATE_PROTO_DIR} ${LTE_OUT_DIR}) create_proto_dir("orc8r" ORC8R_CPP_OUT_DIR) +set(RPC_ORC8R_CPP_PROTOS common) +generate_cpp_protos("${RPC_ORC8R_CPP_PROTOS}" "${PROTO_SRCS}" + "${PROTO_HDRS}" ${ORC8R_PROTO_DIR} ${ORC8R_CPP_OUT_DIR}) generate_cpp_protos("${REDIS_CPP_PROTOS}" "${PROTO_SRCS}" "${PROTO_HDRS}" ${ORC8R_PROTO_DIR} ${ORC8R_CPP_OUT_DIR}) +set(MCONFIG_OUT_DIR "${CMAKE_CURRENT_BINARY_DIR}/lte/protos") +generate_cpp_protos("${MCONFIG_PROTOS}" "${PROTO_SRCS}" + "${PROTO_HDRS}" ${LTE_PROTO_DIR} ${MCONFIG_OUT_DIR}) + message("Proto_srcs are ${PROTO_SRCS}") include_directories(${CMAKE_CURRENT_BINARY_DIR}) include_directories(${LTE_OUT_DIR}) +include_directories(${MCONFIG_OUT_DIR}) include_directories(${ORC8R_CPP_OUT_DIR}) @@ -49,6 +59,7 @@ set(COMMON_SRC shared_ts_log.c log.c state_converter.cpp + common_utility_funs.cpp ${PROTO_SRCS} ${PROTO_HDRS} ) diff --git a/lte/gateway/c/oai/common/common_utility_funs.cpp b/lte/gateway/c/oai/common/common_utility_funs.cpp new file mode 100644 index 000000000000..f3adf07f45cd --- /dev/null +++ b/lte/gateway/c/oai/common/common_utility_funs.cpp @@ -0,0 +1,60 @@ +/* +Copyright 2020 The Magma Authors. + +This source code is licensed under the BSD-style license found in the +LICENSE file in the root directory of this source tree. + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +#include +#include +#include "common_utility_funs.h" +#include "lte/protos/mconfig/mconfigs.pb.h" + +// Extract MCC and MNC from the imsi received and match with +// configuration +extern "C" int match_fed_mode_map(const char* imsi, log_proto_t module) { + OAILOG_FUNC_IN(module); + imsi64_t imsi64; + IMSI_STRING_TO_IMSI64(imsi, &imsi64); + uint8_t mcc_d1 = imsi[0] - '0'; + uint8_t mcc_d2 = imsi[1] - '0'; + uint8_t mcc_d3 = imsi[2] - '0'; + uint8_t mnc_d1 = imsi[3] - '0'; + uint8_t mnc_d2 = imsi[4] - '0'; + uint8_t mnc_d3 = imsi[5] - '0'; + if ((mcc_d1 < 0 || mcc_d1 > 9) || (mcc_d2 < 0 || mcc_d2 > 9) || + (mcc_d3 < 0 || mcc_d3 > 9) || (mnc_d1 < 0 || mnc_d1 > 9) || + (mnc_d2 < 0 || mnc_d2 > 9) || (mnc_d3 < 0 || mnc_d3 > 9)) { + OAILOG_ERROR_UE(module, imsi64, "MCC/MNC is not a decimal digit \n"); + OAILOG_FUNC_RETURN(module, RETURNerror); + } + for (uint8_t itr = 0; itr < mme_config.mode_map_config.num; itr++) { + if (((mcc_d1 == mme_config.mode_map_config.mode_map[itr].plmn.mcc_digit1) && + (mcc_d2 == mme_config.mode_map_config.mode_map[itr].plmn.mcc_digit2) && + (mcc_d3 == mme_config.mode_map_config.mode_map[itr].plmn.mcc_digit3) && + (mnc_d1 == mme_config.mode_map_config.mode_map[itr].plmn.mnc_digit1) && + (mnc_d2 == + mme_config.mode_map_config.mode_map[itr].plmn.mnc_digit2))) { + if (mme_config.mode_map_config.mode_map[itr].plmn.mnc_digit3 != 0xf) { + if (mnc_d3 != + mme_config.mode_map_config.mode_map[itr].plmn.mnc_digit3) { + continue; + } + } + OAILOG_FUNC_RETURN(module, mme_config.mode_map_config.mode_map[itr].mode); + } + } + // If the plmn is not configured set the default mode as hss + spgw_task. + OAILOG_INFO_UE( + module, imsi64, + "PLMN is not configured. Selecting default mode: SPGW_SUBSCRIBER \n"); + OAILOG_FUNC_RETURN( + module, magma::mconfig::ModeMapItem_FederatedMode_SPGW_SUBSCRIBER); +} + diff --git a/lte/gateway/c/oai/common/common_utility_funs.h b/lte/gateway/c/oai/common/common_utility_funs.h new file mode 100644 index 000000000000..f173ba6b204c --- /dev/null +++ b/lte/gateway/c/oai/common/common_utility_funs.h @@ -0,0 +1,15 @@ +#define pragma once + +#ifdef __cplusplus +extern "C" { +#endif + +#include "mme_config.h" +#include "log.h" +#include "conversions.h" +#include "common_defs.h" + +int match_fed_mode_map(const char* imsi, log_proto_t module); +#ifdef __cplusplus +} +#endif diff --git a/lte/gateway/c/oai/include/ip_forward_messages_types.h b/lte/gateway/c/oai/include/ip_forward_messages_types.h index d15d93e6d9e9..45d2f7919508 100644 --- a/lte/gateway/c/oai/include/ip_forward_messages_types.h +++ b/lte/gateway/c/oai/include/ip_forward_messages_types.h @@ -80,8 +80,6 @@ typedef struct { typedef struct { teid_t context_teid; ///< Tunnel Endpoint Identifier S11 - SGIStatus_t status; ///< Status of endpoint creation (Failed = 0xFF or - ///< Success = 0x0) uint8_t num_bearers_modified; bearer_cxt_t bearer_contexts_to_be_modified[BEARERS_PER_UE]; uint8_t num_bearers_removed; diff --git a/lte/gateway/c/oai/include/mme_app_ue_context.h b/lte/gateway/c/oai/include/mme_app_ue_context.h index 51a002b48541..2c611059725e 100644 --- a/lte/gateway/c/oai/include/mme_app_ue_context.h +++ b/lte/gateway/c/oai/include/mme_app_ue_context.h @@ -239,6 +239,7 @@ typedef struct pdn_context_s { protocol_configuration_options_t* pco; bool ue_rej_act_def_ber_req; + bool route_s11_messages_to_s8_task; } pdn_context_t; typedef enum { diff --git a/lte/gateway/c/oai/include/s8_messages_def.h b/lte/gateway/c/oai/include/s8_messages_def.h index 790ca40cedd7..26082c5cb398 100644 --- a/lte/gateway/c/oai/include/s8_messages_def.h +++ b/lte/gateway/c/oai/include/s8_messages_def.h @@ -15,3 +15,5 @@ limitations under the License. // instead. MESSAGE_DEF( S8_CREATE_SESSION_RSP, s8_create_session_response_t, s8_create_session_rsp) +MESSAGE_DEF( + S8_DELETE_SESSION_RSP, s8_delete_session_response_t, s8_delete_session_rsp) diff --git a/lte/gateway/c/oai/include/s8_messages_types.h b/lte/gateway/c/oai/include/s8_messages_types.h index 04aa3906b11a..2f1281cd55c6 100644 --- a/lte/gateway/c/oai/include/s8_messages_types.h +++ b/lte/gateway/c/oai/include/s8_messages_types.h @@ -17,6 +17,7 @@ limitations under the License. #include "common_types.h" #define S8_CREATE_SESSION_RSP(mSGpTR) (mSGpTR)->ittiMsg.s8_create_session_rsp +#define S8_DELETE_SESSION_RSP(mSGpTR) (mSGpTR)->ittiMsg.s8_delete_session_rsp typedef struct s8_bearer_context_s { ebi_t eps_bearer_id; @@ -33,8 +34,12 @@ typedef struct s8_create_session_response_s { teid_t context_teid; // SGW_S11_teid, created per PDN ebi_t eps_bearer_id; s8_bearer_context_t bearer_context[BEARERS_PER_UE]; - uint8_t response_cause; uint8_t apn_restriction_value; fteid_t pgw_s8_cp_teid; uint32_t cause; } s8_create_session_response_t; + +typedef struct s8_delete_session_response_s { + teid_t context_teid; // SGW_S11_teid, created per PDN + uint32_t cause; +} s8_delete_session_response_t; diff --git a/lte/gateway/c/oai/include/sgw_context_manager.h b/lte/gateway/c/oai/include/sgw_context_manager.h index 969a2cf3dcaf..c7680e8cf184 100644 --- a/lte/gateway/c/oai/include/sgw_context_manager.h +++ b/lte/gateway/c/oai/include/sgw_context_manager.h @@ -38,6 +38,7 @@ void sgw_display_sgw_eps_bearer_context( const sgw_eps_bearer_ctxt_t* eps_bearer_ctxt); void sgw_display_s11teid2mme(mme_sgw_tunnel_t* mme_sgw_tunnel); void sgw_display_s11_bearer_context_information( + log_proto_t module, sgw_eps_bearer_context_information_t* sgw_context_information); void pgw_lite_cm_free_apn(pgw_apn_t** apnP); diff --git a/lte/gateway/c/oai/lib/pcef/pcef_handlers.cpp b/lte/gateway/c/oai/lib/pcef/pcef_handlers.cpp index 3bc9b0a9182f..c9cbcae591e6 100644 --- a/lte/gateway/c/oai/lib/pcef/pcef_handlers.cpp +++ b/lte/gateway/c/oai/lib/pcef/pcef_handlers.cpp @@ -313,7 +313,7 @@ int get_msisdn_from_session_req( return len; } -static int get_imeisv_from_session_req( +int get_imeisv_from_session_req( const itti_s11_create_session_request_t* saved_req, char* imeisv) { if (saved_req->mei.present & MEI_IMEISV) { // IMEISV as defined in 3GPP TS 23.003 MEI_IMEISV diff --git a/lte/gateway/c/oai/lib/pcef/pcef_handlers.h b/lte/gateway/c/oai/lib/pcef/pcef_handlers.h index 48c769601309..af7783ad255a 100644 --- a/lte/gateway/c/oai/lib/pcef/pcef_handlers.h +++ b/lte/gateway/c/oai/lib/pcef/pcef_handlers.h @@ -98,6 +98,8 @@ int get_msisdn_from_session_req( char convert_digit_to_char(char digit); +int get_imeisv_from_session_req( + const itti_s11_create_session_request_t* saved_req, char* imeisv); #ifdef __cplusplus } #endif diff --git a/lte/gateway/c/oai/lib/s6a_proxy/S6aClient.cpp b/lte/gateway/c/oai/lib/s6a_proxy/S6aClient.cpp index 01b2154b68fa..0f9801568f0f 100644 --- a/lte/gateway/c/oai/lib/s6a_proxy/S6aClient.cpp +++ b/lte/gateway/c/oai/lib/s6a_proxy/S6aClient.cpp @@ -27,6 +27,10 @@ #include "feg/protos/s6a_proxy.pb.h" #include "mme_config.h" #include "common_defs.h" +#include "common_utility_funs.h" +extern "C" { +#include "log.h" +} namespace grpc { class Status; @@ -99,45 +103,6 @@ S6aClient& S6aClient::get_subdb_instance() { static S6aClient subdb_instance(false); return subdb_instance; } - -// Extract MCC and MNC from the imsi received and match with -// configuration -int match_fed_mode_map(const char* imsi) { - uint8_t mcc_d1 = imsi[0] - '0'; - uint8_t mcc_d2 = imsi[1] - '0'; - uint8_t mcc_d3 = imsi[2] - '0'; - uint8_t mnc_d1 = imsi[3] - '0'; - uint8_t mnc_d2 = imsi[4] - '0'; - uint8_t mnc_d3 = imsi[5] - '0'; - if ((mcc_d1 < 0 || mcc_d1 > 9) || (mcc_d2 < 0 || mcc_d2 > 9) || - (mcc_d3 < 0 || mcc_d3 > 9) || (mnc_d1 < 0 || mnc_d1 > 9) || - (mnc_d2 < 0 || mnc_d2 > 9) || (mnc_d3 < 0 || mnc_d3 > 9)) { - std::cout << "[ERROR] MCC/MNC is not a decimal digit " << std::endl; - return -1; - } - for (uint8_t itr = 0; itr < mme_config.mode_map_config.num; itr++) { - if (((mcc_d1 == mme_config.mode_map_config.mode_map[itr].plmn.mcc_digit1) && - (mcc_d2 == mme_config.mode_map_config.mode_map[itr].plmn.mcc_digit2) && - (mcc_d3 == mme_config.mode_map_config.mode_map[itr].plmn.mcc_digit3) && - (mnc_d1 == mme_config.mode_map_config.mode_map[itr].plmn.mnc_digit1) && - (mnc_d2 == - mme_config.mode_map_config.mode_map[itr].plmn.mnc_digit2))) { - if (mme_config.mode_map_config.mode_map[itr].plmn.mnc_digit3 != 0xf) { - if (mnc_d3 != - mme_config.mode_map_config.mode_map[itr].plmn.mnc_digit3) { - continue; - } - } - return mme_config.mode_map_config.mode_map[itr].mode; - } - } - // If the plmn is not found/configured we still create a channel - // towards the FeG as the default mode is HSS + spgw_task. - std::cout << "[INFO] PLMN is not found/configured. Selecting default mode" - << std::endl; - return (magma::mconfig::ModeMapItem_FederatedMode_SPGW_SUBSCRIBER); -} - S6aClient::S6aClient(bool enable_s6a_proxy_channel) { // Create channel based on relay_enabled, enable_s6a_proxy_channel and // cloud_subscriberdb_enabled flags. @@ -165,7 +130,7 @@ S6aClient::S6aClient(bool enable_s6a_proxy_channel) { void S6aClient::purge_ue( const char* imsi, std::function callbk) { S6aClient* client_tmp; - int fed_mode = match_fed_mode_map(imsi); + int fed_mode = match_fed_mode_map(imsi, LOG_S6A); if ((fed_mode == magma::mconfig::ModeMapItem_FederatedMode_SPGW_SUBSCRIBER) || (fed_mode == magma::mconfig::ModeMapItem_FederatedMode_S8_SUBSCRIBER)) { client_tmp = &get_s6a_proxy_instance(); @@ -201,7 +166,7 @@ void S6aClient::authentication_info_req( const s6a_auth_info_req_t* const msg, std::function callbk) { S6aClient* client_tmp; - int fed_mode = match_fed_mode_map(msg->imsi); + int fed_mode = match_fed_mode_map(msg->imsi, LOG_S6A); if ((fed_mode == magma::mconfig::ModeMapItem_FederatedMode_SPGW_SUBSCRIBER) || (fed_mode == magma::mconfig::ModeMapItem_FederatedMode_S8_SUBSCRIBER)) { client_tmp = &get_s6a_proxy_instance(); @@ -237,7 +202,7 @@ void S6aClient::update_location_request( const s6a_update_location_req_t* const msg, std::function callbk) { S6aClient* client_tmp; - int fed_mode = match_fed_mode_map(msg->imsi); + int fed_mode = match_fed_mode_map(msg->imsi, LOG_S6A); if ((fed_mode == magma::mconfig::ModeMapItem_FederatedMode_SPGW_SUBSCRIBER) || (fed_mode == magma::mconfig::ModeMapItem_FederatedMode_S8_SUBSCRIBER)) { client_tmp = &get_s6a_proxy_instance(); diff --git a/lte/gateway/c/oai/lib/s8_proxy/S8Client.cpp b/lte/gateway/c/oai/lib/s8_proxy/S8Client.cpp index be649a823592..ec507d024ac7 100644 --- a/lte/gateway/c/oai/lib/s8_proxy/S8Client.cpp +++ b/lte/gateway/c/oai/lib/s8_proxy/S8Client.cpp @@ -60,4 +60,23 @@ void S8Client::s8_create_session_request( response->set_response_reader(std::move(response_reader)); } +void S8Client::s8_delete_session_request( + const DeleteSessionRequestPgw& dsr_req, + std::function callback) { + S8Client& client = get_instance(); + // Create a raw response pointer that stores a callback to be called when the + // gRPC call is answered + auto response = new AsyncLocalResponse( + std::move(callback), RESPONSE_TIMEOUT); + // Create a response reader for the `DeleteSession` RPC call. This reader + // stores the client context, the request to pass in, and the queue to add + // the response to when done + auto response_reader = client.stub_->AsyncDeleteSession( + response->get_context(), dsr_req, &client.queue_); + // Set the reader for the local response. This executes the `DeleteSession` + // response using the response reader. When it is done, the callback stored in + // `local_response` will be called + response->set_response_reader(std::move(response_reader)); +} + } // namespace magma diff --git a/lte/gateway/c/oai/lib/s8_proxy/S8Client.h b/lte/gateway/c/oai/lib/s8_proxy/S8Client.h index 0df1fcca8a83..64c91a4206cc 100644 --- a/lte/gateway/c/oai/lib/s8_proxy/S8Client.h +++ b/lte/gateway/c/oai/lib/s8_proxy/S8Client.h @@ -47,6 +47,11 @@ class S8Client : public GRPCReceiver { const CreateSessionRequestPgw& csr_req, std::function callback); + // Send Delete Session Request + static void s8_delete_session_request( + const DeleteSessionRequestPgw& dsr_req, + std::function callback); + public: S8Client(S8Client const&) = delete; void operator=(S8Client const&) = delete; diff --git a/lte/gateway/c/oai/lib/s8_proxy/s8_client_api.cpp b/lte/gateway/c/oai/lib/s8_proxy/s8_client_api.cpp index 3d64ea391e37..47a30b50b57e 100644 --- a/lte/gateway/c/oai/lib/s8_proxy/s8_client_api.cpp +++ b/lte/gateway/c/oai/lib/s8_proxy/s8_client_api.cpp @@ -28,7 +28,7 @@ extern task_zmq_ctx_t grpc_service_task_zmq_ctx; static void convert_proto_msg_to_itti_csr( magma::feg::CreateSessionResponsePgw& response, - s8_create_session_response_t* s5_response); + s8_create_session_response_t* s5_response, bearer_qos_t dflt_bearer_qos); static void get_qos_from_proto_msg( const magma::feg::QosInformation& proto_qos, bearer_qos_t* bearer_qos) { @@ -49,14 +49,16 @@ static void get_fteid_from_proto_msg( OAILOG_FUNC_IN(LOG_SGW_S8); pgw_fteid->teid = proto_fteid.teid(); if (proto_fteid.ipv4_address().c_str()) { - struct in_addr addr = {0}; - memcpy(&addr, proto_fteid.ipv4_address().c_str(), sizeof(in_addr)); - pgw_fteid->ipv4_address = addr; + pgw_fteid->ipv4 = true; + inet_pton( + AF_INET, proto_fteid.ipv4_address().c_str(), + &(pgw_fteid->ipv4_address)); } if (proto_fteid.ipv6_address().c_str()) { - struct in6_addr ip6_addr; - memcpy(&ip6_addr, proto_fteid.ipv6_address().c_str(), sizeof(in6_addr)); - pgw_fteid->ipv6_address = ip6_addr; + pgw_fteid->ipv6 = true; + inet_pton( + AF_INET6, proto_fteid.ipv6_address().c_str(), + &(pgw_fteid->ipv6_address)); } OAILOG_FUNC_OUT(LOG_SGW_S8); } @@ -69,22 +71,22 @@ static void get_paa_from_proto_msg( case magma::feg::PDNType::IPV4: { paa->pdn_type = IPv4; auto ip = proto_paa.ipv4_address(); - memcpy(&paa->ipv4_address, ip.c_str(), sizeof(ip.c_str())); + inet_pton(AF_INET, ip.c_str(), &(paa->ipv4_address)); break; } case magma::feg::PDNType::IPV6: { paa->pdn_type = IPv6; auto ip = proto_paa.ipv6_address(); - memcpy(&paa->ipv6_address, ip.c_str(), sizeof(ip.c_str())); + inet_pton(AF_INET6, ip.c_str(), &(paa->ipv6_address)); paa->ipv6_prefix_length = IPV6_PREFIX_LEN; break; } case magma::feg::PDNType::IPV4V6: { paa->pdn_type = IPv4_AND_v6; auto ip = proto_paa.ipv4_address(); - memcpy(&paa->ipv4_address, ip.c_str(), sizeof(ip.c_str())); + inet_pton(AF_INET, ip.c_str(), &(paa->ipv4_address)); auto ipv6 = proto_paa.ipv6_address(); - memcpy(&paa->ipv6_address, ipv6.c_str(), sizeof(ipv6.c_str())); + inet_pton(AF_INET6, ipv6.c_str(), &(paa->ipv6_address)); paa->ipv6_prefix_length = IPV6_PREFIX_LEN; break; } @@ -101,8 +103,59 @@ static void get_paa_from_proto_msg( OAILOG_FUNC_OUT(LOG_SGW_S8); } -static void recv_s8_create_session_response( +static void recv_s8_delete_session_response( imsi64_t imsi64, teid_t context_teid, const grpc::Status& status, + magma::feg::DeleteSessionResponsePgw& response) { + OAILOG_FUNC_IN(LOG_SGW_S8); + + s8_delete_session_response_t* s8_delete_session_rsp = NULL; + MessageDef* message_p = NULL; + message_p = itti_alloc_new_message(TASK_GRPC_SERVICE, S8_DELETE_SESSION_RSP); + if (!message_p) { + OAILOG_ERROR_UE( + LOG_SGW_S8, imsi64, + "Failed to allocate memory for S8_DELETE_SESSION_RSP for " + "context_teid" TEID_FMT "\n", + context_teid); + OAILOG_FUNC_OUT(LOG_SGW_S8); + } + s8_delete_session_rsp = &message_p->ittiMsg.s8_delete_session_rsp; + message_p->ittiMsgHeader.imsi = imsi64; + s8_delete_session_rsp->context_teid = context_teid; + + if (status.ok()) { + if (response.has_gtp_error()) { + s8_delete_session_rsp->cause = response.mutable_gtp_error()->cause(); + } else { + s8_delete_session_rsp->cause = REQUEST_ACCEPTED; + } + } else { + OAILOG_ERROR_UE( + LOG_SGW_S8, imsi64, + "Received gRPC error for delete session response for " + "context_teid " TEID_FMT "\n", + context_teid); + s8_delete_session_rsp->cause = REMOTE_PEER_NOT_RESPONDING; + } + OAILOG_INFO_UE( + LOG_UTIL, imsi64, + "Sending delete session response to sgw_s8 task for " + "context_teid " TEID_FMT "\n", + context_teid); + if ((send_msg_to_task(&grpc_service_task_zmq_ctx, TASK_SGW_S8, message_p)) != + RETURNok) { + OAILOG_ERROR_UE( + LOG_SGW_S8, imsi64, + "Failed to send delete session response to sgw_s8 task for" + "context_teid " TEID_FMT "\n", + context_teid); + } + OAILOG_FUNC_OUT(LOG_SGW_S8); +} + +static void recv_s8_create_session_response( + imsi64_t imsi64, teid_t context_teid, bearer_qos_t dflt_bearer_qos, + const grpc::Status& status, magma::feg::CreateSessionResponsePgw& response) { OAILOG_FUNC_IN(LOG_SGW_S8); s8_create_session_response_t* s5_response = NULL; @@ -120,7 +173,7 @@ static void recv_s8_create_session_response( message_p->ittiMsgHeader.imsi = imsi64; s5_response->context_teid = context_teid; if (status.ok()) { - convert_proto_msg_to_itti_csr(response, s5_response); + convert_proto_msg_to_itti_csr(response, s5_response, dflt_bearer_qos); } else { OAILOG_ERROR( LOG_SGW_S8, @@ -150,8 +203,8 @@ static void convert_uli_to_proto_msg( uli->set_sac(msg_uli.s.sai.sac); uli->set_rac(msg_uli.s.rai.rac); uli->set_tac(msg_uli.s.tai.tac); - uli->set_eci(msg_uli.s.ecgi.cell_identity.cell_id); - uli->set_menbi(msg_uli.s.ecgi.cell_identity.enb_id); + uli->set_eci(msg_uli.s.ecgi.cell_identity.enb_id); + uli->set_menbi(0); OAILOG_FUNC_OUT(LOG_SGW_S8); } @@ -281,6 +334,17 @@ static void get_msisdn_from_csr_req( OAILOG_FUNC_OUT(LOG_SGW_S8); } +static void convert_imeisv_to_string(char* imeisv) { + OAILOG_FUNC_IN(LOG_SGW_S8); + uint8_t idx = 0; + for (; idx < IMEISV_DIGITS_MAX; idx++) { + imeisv[idx] = convert_digit_to_char(imeisv[idx]); + } + imeisv[idx] = '\0'; + + OAILOG_FUNC_OUT(LOG_SGW_S8); +} + static void fill_s8_create_session_req( const itti_s11_create_session_request_t* msg, magma::feg::CreateSessionRequestPgw* csr, teid_t sgw_s8_teid) { @@ -305,7 +369,10 @@ static void fill_s8_create_session_req( mnc[2] = '\0'; mnc_len = 2; } - + char imeisv[IMEISV_DIGITS_MAX + 1]; + get_imeisv_from_session_req(msg, imeisv); + convert_imeisv_to_string(imeisv); + csr->set_mei(imeisv, IMEISV_DIGITS_MAX); magma::feg::ServingNetwork* serving_network = csr->mutable_serving_network(); serving_network->set_mcc(mcc, 3); serving_network->set_mnc(mnc, mnc_len); @@ -320,6 +387,9 @@ static void fill_s8_create_session_req( magma::feg::BearerContext* bc = csr->mutable_bearer_context(); convert_bearer_context_to_proto( &msg->bearer_contexts_to_be_created.bearer_contexts[0], bc); + // set the mbr within bearer qos same as apn-ambr + bc->mutable_qos()->mutable_mbr()->set_br_ul(msg->ambr.br_ul); + bc->mutable_qos()->mutable_mbr()->set_br_dl(msg->ambr.br_dl); } csr->set_c_agw_teid(sgw_s8_teid); csr->set_charging_characteristics( @@ -336,6 +406,7 @@ void send_s8_create_session_request( imsi64_t imsi64) { OAILOG_FUNC_IN(LOG_SGW_S8); magma::feg::CreateSessionRequestPgw csr_req; + bearer_qos_t dflt_bearer_qos = {0}; // teid shall remain same for both sgw's s11 interface and s8 interface as // teid is allocated per PDN @@ -345,18 +416,21 @@ void send_s8_create_session_request( sgw_s11_teid); fill_s8_create_session_req(msg, &csr_req, sgw_s11_teid); + dflt_bearer_qos = + msg->bearer_contexts_to_be_created.bearer_contexts[0].bearer_level_qos; magma::S8Client::s8_create_session_request( csr_req, - [imsi64, sgw_s11_teid]( + [imsi64, sgw_s11_teid, dflt_bearer_qos]( grpc::Status status, magma::feg::CreateSessionResponsePgw response) { - recv_s8_create_session_response(imsi64, sgw_s11_teid, status, response); + recv_s8_create_session_response( + imsi64, sgw_s11_teid, dflt_bearer_qos, status, response); }); } static void convert_proto_msg_to_itti_csr( magma::feg::CreateSessionResponsePgw& response, - s8_create_session_response_t* s5_response) { + s8_create_session_response_t* s5_response, bearer_qos_t dflt_bearer_qos) { OAILOG_FUNC_IN(LOG_SGW_S8); s5_response->apn_restriction_value = response.apn_restriction(); get_fteid_from_proto_msg( @@ -368,9 +442,44 @@ static void convert_proto_msg_to_itti_csr( s8_bc->eps_bearer_id = response.bearer_context().id(); s5_response->eps_bearer_id = s8_bc->eps_bearer_id; s8_bc->charging_id = response.bearer_context().charging_id(); - get_qos_from_proto_msg(response.bearer_context().qos(), &s8_bc->qos); + if (response.bearer_context().valid_qos()) { + get_qos_from_proto_msg(response.bearer_context().qos(), &s8_bc->qos); + } else { + // If qos is not received from PGW, set the qos that was sent in CS Req + s8_bc->qos = dflt_bearer_qos; + } get_fteid_from_proto_msg( response.bearer_context().user_plane_fteid(), &s8_bc->pgw_s8_up); - s5_response->cause = response.mutable_gtp_error()->cause(); + if (response.has_gtp_error()) { + s5_response->cause = response.mutable_gtp_error()->cause(); + } else { + s5_response->cause = REQUEST_ACCEPTED; + } + OAILOG_FUNC_OUT(LOG_SGW_S8); +} + +void send_s8_delete_session_request( + imsi64_t imsi64, Imsi_t imsi, teid_t sgw_s11_teid, teid_t pgw_s5_teid, + ebi_t bearer_id) { + OAILOG_FUNC_IN(LOG_SGW_S8); + OAILOG_INFO_UE( + LOG_SGW_S8, imsi64, + "Sending delete session request for context_teid:" TEID_FMT "\n", + sgw_s11_teid); + + magma::feg::DeleteSessionRequestPgw dsr_req; + + dsr_req.Clear(); + dsr_req.set_imsi((char*) imsi.digit, imsi.length); + dsr_req.set_bearer_id(bearer_id); + dsr_req.mutable_c_pgw_fteid()->set_teid(pgw_s5_teid); + dsr_req.set_c_agw_teid(sgw_s11_teid); + magma::S8Client::s8_delete_session_request( + dsr_req, + [imsi64, sgw_s11_teid]( + grpc::Status status, magma::feg::DeleteSessionResponsePgw response) { + recv_s8_delete_session_response(imsi64, sgw_s11_teid, status, response); + }); + OAILOG_FUNC_OUT(LOG_SGW_S8); } diff --git a/lte/gateway/c/oai/lib/s8_proxy/s8_client_api.h b/lte/gateway/c/oai/lib/s8_proxy/s8_client_api.h index 6bd66272e2f2..ac23e8e4c6d1 100644 --- a/lte/gateway/c/oai/lib/s8_proxy/s8_client_api.h +++ b/lte/gateway/c/oai/lib/s8_proxy/s8_client_api.h @@ -17,9 +17,13 @@ extern "C" { #endif #include "intertask_interface.h" #include "common_types.h" + void send_s8_create_session_request( teid_t sgw_s11_teid, const itti_s11_create_session_request_t* msg, imsi64_t imsi64); +void send_s8_delete_session_request( + imsi64_t imsi64, Imsi_t imsi, teid_t sgw_s11_teid, teid_t pgw_s5_teid, + ebi_t bearer_id); #ifdef __cplusplus } #endif diff --git a/lte/gateway/c/oai/tasks/mme_app/mme_app_bearer.c b/lte/gateway/c/oai/tasks/mme_app/mme_app_bearer.c index 98555be400d2..0c17df0845cc 100644 --- a/lte/gateway/c/oai/tasks/mme_app/mme_app_bearer.c +++ b/lte/gateway/c/oai/tasks/mme_app/mme_app_bearer.c @@ -84,6 +84,10 @@ extern task_zmq_ctx_t mme_app_task_zmq_ctx; extern int pdn_connectivity_delete(emm_context_t* emm_context, pdn_cid_t pid); +static void send_s11_modify_bearer_request( + ue_mm_context_t* ue_context_p, pdn_context_t* pdn_context_p, + MessageDef* message_p); + int send_modify_bearer_req(mme_ue_s1ap_id_t ue_id, ebi_t ebi) { OAILOG_FUNC_IN(LOG_MME_APP); @@ -187,11 +191,7 @@ int send_modify_bearer_req(mme_ue_s1ap_id_t ue_id, ebi_t ebi) { message_p->ittiMsgHeader.imsi = ue_context_p->emm_context._imsi64; - OAILOG_INFO_UE( - LOG_MME_APP, ue_context_p->emm_context._imsi64, - "Sending S11_MODIFY_BEARER_REQUEST to SGW for ue" MME_UE_S1AP_ID_FMT "\n", - ue_id); - send_msg_to_task(&mme_app_task_zmq_ctx, TASK_SPGW, message_p); + send_s11_modify_bearer_request(ue_context_p, pdn_context_p, message_p); OAILOG_FUNC_RETURN(LOG_MME_APP, RETURNok); } @@ -1527,7 +1527,9 @@ static int mme_app_send_modify_bearer_request_for_active_pdns( mme_app_build_modify_bearer_request_message( ue_context_p, initial_ctxt_setup_rsp_p, s11_modify_bearer_request, &pid, &bc_to_be_removed_idx); - send_msg_to_task(&mme_app_task_zmq_ctx, TASK_SPGW, message_p); + + send_s11_modify_bearer_request( + ue_context_p, ue_context_p->pdn_contexts[pid], message_p); } // end of for loop OAILOG_FUNC_RETURN(LOG_MME_APP, RETURNok); @@ -3431,11 +3433,9 @@ void mme_app_handle_path_switch_request( message_p->ittiMsgHeader.imsi = ue_context_p->emm_context._imsi64; - OAILOG_DEBUG_UE( - LOG_MME_APP, ue_context_p->emm_context._imsi64, - "MME_APP send S11_MODIFY_BEARER_REQUEST to teid %u \n", - s11_modify_bearer_request->teid); - send_msg_to_task(&mme_app_task_zmq_ctx, TASK_SPGW, message_p); + if (pdn_context) { + send_s11_modify_bearer_request(ue_context_p, pdn_context, message_p); + } ue_context_p->path_switch_req = true; OAILOG_FUNC_OUT(LOG_MME_APP); @@ -3941,12 +3941,9 @@ void mme_app_handle_e_rab_modification_ind( s11_modify_bearer_request->trxn = NULL; message_p->ittiMsgHeader.imsi = ue_context_p->emm_context._imsi64; - - OAILOG_DEBUG_UE( - LOG_MME_APP, ue_context_p->emm_context._imsi64, - "MME_APP send S11_MODIFY_BEARER_REQUEST to teid %u \n", - s11_modify_bearer_request->teid); - send_msg_to_task(&mme_app_task_zmq_ctx, TASK_SPGW, message_p); + if (pdn_context) { + send_s11_modify_bearer_request(ue_context_p, pdn_context, message_p); + } OAILOG_FUNC_OUT(LOG_MME_APP); } //------------------------------------------------------------------------------ @@ -4086,3 +4083,30 @@ void mme_app_handle_modify_bearer_rsp( } OAILOG_FUNC_OUT(LOG_MME_APP); } + +void send_s11_modify_bearer_request( + ue_mm_context_t* ue_context_p, pdn_context_t* pdn_context_p, + MessageDef* message_p) { + OAILOG_FUNC_IN(LOG_MME_APP); + Imsi_t imsi = {0}; + IMSI64_TO_STRING( + ue_context_p->emm_context._imsi64, (char*) (&imsi.digit), + ue_context_p->emm_context._imsi.length); + + if (pdn_context_p->route_s11_messages_to_s8_task) { + OAILOG_INFO_UE( + LOG_MME_APP, ue_context_p->emm_context._imsi64, + "Sending S11 modify bearer req message to SGW_s8 task for " + "ue_id " MME_UE_S1AP_ID_FMT "\n", + ue_context_p->mme_ue_s1ap_id); + send_msg_to_task(&mme_app_task_zmq_ctx, TASK_SGW_S8, message_p); + } else { + OAILOG_INFO_UE( + LOG_MME_APP, ue_context_p->emm_context._imsi64, + "Sending S11 modify bearer req message to SPGW task for " + "ue_id " MME_UE_S1AP_ID_FMT "\n", + ue_context_p->mme_ue_s1ap_id); + send_msg_to_task(&mme_app_task_zmq_ctx, TASK_SPGW, message_p); + } + OAILOG_FUNC_OUT(LOG_MME_APP); +} diff --git a/lte/gateway/c/oai/tasks/mme_app/mme_app_detach.c b/lte/gateway/c/oai/tasks/mme_app/mme_app_detach.c index 5a0ce522fc8e..7c53714b7571 100644 --- a/lte/gateway/c/oai/tasks/mme_app/mme_app_detach.c +++ b/lte/gateway/c/oai/tasks/mme_app/mme_app_detach.c @@ -32,6 +32,7 @@ #include #include "log.h" +#include "conversions.h" #include "intertask_interface.h" #include "gcc_diag.h" #include "mme_config.h" @@ -115,10 +116,25 @@ void mme_app_send_delete_session_request( message_p->ittiMsgHeader.imsi = ue_context_p->emm_context._imsi64; - send_msg_to_task(&mme_app_task_zmq_ctx, TASK_SPGW, message_p); - OAILOG_INFO( - LOG_MME_APP, "Send Delete session Req for teid " TEID_FMT "\n", - ue_context_p->mme_teid_s11); + Imsi_t imsi = {0}; + IMSI64_TO_STRING( + ue_context_p->emm_context._imsi64, (char*) (&imsi.digit), + ue_context_p->emm_context._imsi.length); + + if (ue_context_p->pdn_contexts[cid]->route_s11_messages_to_s8_task) { + OAILOG_INFO_UE( + LOG_MME_APP, ue_context_p->emm_context._imsi64, + "Send delete session Req for teid to sgw_s8 task " TEID_FMT "\n", + ue_context_p->mme_teid_s11); + send_msg_to_task(&mme_app_task_zmq_ctx, TASK_SGW_S8, message_p); + } else { + OAILOG_INFO_UE( + LOG_MME_APP, ue_context_p->emm_context._imsi64, + "Send delete session Req for teid to spgw task " TEID_FMT "\n", + ue_context_p->mme_teid_s11); + send_msg_to_task(&mme_app_task_zmq_ctx, TASK_SPGW, message_p); + } + increment_counter("mme_spgw_delete_session_req", 1, NO_LABELS); OAILOG_FUNC_OUT(LOG_MME_APP); } diff --git a/lte/gateway/c/oai/tasks/mme_app/mme_app_itti_messaging.c b/lte/gateway/c/oai/tasks/mme_app/mme_app_itti_messaging.c index f199cca7154a..615c2dcb7a66 100644 --- a/lte/gateway/c/oai/tasks/mme_app/mme_app_itti_messaging.c +++ b/lte/gateway/c/oai/tasks/mme_app/mme_app_itti_messaging.c @@ -48,6 +48,7 @@ #include "esm_data.h" #include "mme_app_desc.h" #include "s11_messages_types.h" +#include "common_utility_funs.h" #if EMBEDDED_SGW #define TASK_SPGW TASK_SPGW_APP @@ -236,7 +237,8 @@ int mme_app_send_s11_create_session_req( } else { session_request_p->msisdn.length = 0; } - + session_request_p->mei.present = MEI_IMEISV; + session_request_p->mei.choice.imeisv = ue_mm_context->emm_context._imeisv; // Fill User Location Information session_request_p->uli.present = 0; // initialize the presencemask mme_app_get_user_location_information(&session_request_p->uli, ue_mm_context); @@ -373,20 +375,23 @@ int mme_app_send_s11_create_session_req( session_request_p->serving_network.mnc[2] = ue_mm_context->e_utran_cgi.plmn.mnc_digit3; session_request_p->selection_mode = MS_O_N_P_APN_S_V; - - OAILOG_INFO_UE( - LOG_MME_APP, ue_mm_context->emm_context._imsi64, - "Sending S11 CREATE SESSION REQ message to SPGW for " - "ue_id " MME_UE_S1AP_ID_FMT "\n", - ue_mm_context->mme_ue_s1ap_id); - if ((send_msg_to_task(&mme_app_task_zmq_ctx, TASK_SPGW, message_p)) != - RETURNok) { - OAILOG_ERROR_UE( + int mode = + match_fed_mode_map((char*) session_request_p->imsi.digit, LOG_MME_APP); + if (mode == S8_SUBSCRIBER) { + OAILOG_INFO_UE( LOG_MME_APP, ue_mm_context->emm_context._imsi64, - "Failed to send S11 CREATE SESSION REQ message to SPGW for " + "Sending s11 create session req message to SGW_s8 task for " "ue_id " MME_UE_S1AP_ID_FMT "\n", ue_mm_context->mme_ue_s1ap_id); - OAILOG_FUNC_RETURN(LOG_MME_APP, RETURNerror); + send_msg_to_task(&mme_app_task_zmq_ctx, TASK_SGW_S8, message_p); + ue_mm_context->pdn_contexts[pdn_cid]->route_s11_messages_to_s8_task = true; + } else { + OAILOG_INFO_UE( + LOG_MME_APP, ue_mm_context->emm_context._imsi64, + "Sending s11 create session req message to SPGW task for " + "ue_id " MME_UE_S1AP_ID_FMT "\n", + ue_mm_context->mme_ue_s1ap_id); + send_msg_to_task(&mme_app_task_zmq_ctx, TASK_SPGW, message_p); } OAILOG_FUNC_RETURN(LOG_MME_APP, RETURNok); } diff --git a/lte/gateway/c/oai/tasks/mme_app/mme_app_itti_messaging.h b/lte/gateway/c/oai/tasks/mme_app/mme_app_itti_messaging.h index 377507743a0a..4f27d0dfbb53 100644 --- a/lte/gateway/c/oai/tasks/mme_app/mme_app_itti_messaging.h +++ b/lte/gateway/c/oai/tasks/mme_app/mme_app_itti_messaging.h @@ -131,5 +131,4 @@ void mme_app_itti_sgsap_tmsi_reallocation_comp( void mme_app_itti_sgsap_ue_activity_ind( const char* imsi, const unsigned int imsi_len); - #endif /* FILE_MME_APP_ITTI_MESSAGING_SEEN */ diff --git a/lte/gateway/c/oai/tasks/sgw/sgw_context_manager.c b/lte/gateway/c/oai/tasks/sgw/sgw_context_manager.c index 518d53c49439..49ae8700d9ac 100644 --- a/lte/gateway/c/oai/tasks/sgw/sgw_context_manager.c +++ b/lte/gateway/c/oai/tasks/sgw/sgw_context_manager.c @@ -65,41 +65,41 @@ void sgw_display_sgw_eps_bearer_context( //----------------------------------------------------------------------------- void sgw_display_s11_bearer_context_information( + log_proto_t module, sgw_eps_bearer_context_information_t* sgw_context_information) //----------------------------------------------------------------------------- { OAILOG_DEBUG( - LOG_SPGW_APP, "| KEY %" PRId64 ": \n", - sgw_context_information->imsi64); - OAILOG_DEBUG(LOG_SPGW_APP, "|\tsgw_eps_bearer_context_information: |\n"); + module, "| KEY %" PRId64 ": \n", sgw_context_information->imsi64); + OAILOG_DEBUG(module, "|\tsgw_eps_bearer_context_information: |\n"); // Imsi_t imsi; ///< IMSI // (International Mobile Subscriber Identity) is the subscriber permanent // identity. OAILOG_DEBUG( - LOG_SPGW_APP, "|\t\timsi_unauthenticated_indicator:\t%u\n", + module, "|\t\timsi_unauthenticated_indicator:\t%u\n", sgw_context_information->imsi_unauthenticated_indicator); // char msisdn[MSISDN_LENGTH]; ///< The basic MSISDN // of the UE. The presence is dictated by its storage in the HSS. OAILOG_DEBUG( - LOG_SPGW_APP, "|\t\tmme_teid_ S11: \t" TEID_FMT "\n", + module, "|\t\tmme_teid_ S11: \t" TEID_FMT "\n", sgw_context_information->mme_teid_S11); // ip_address_t mme_ip_address_for_S11; ///< MME IP address // the S11 interface. OAILOG_DEBUG( - LOG_SPGW_APP, "|\t\ts_gw_teid_S11_S4: \t" TEID_FMT "\n", + module, "|\t\ts_gw_teid_S11_S4: \t" TEID_FMT "\n", sgw_context_information->s_gw_teid_S11_S4); // ip_address_t s_gw_ip_address_for_S11_S4; ///< S-GW IP address // for the S11 interface and the S4 Interface (control plane). cgi_t // last_known_cell_Id; ///< This is the last location of the UE // known by the network - OAILOG_DEBUG(LOG_SPGW_APP, "|\t\tpdn_connection:\n"); + OAILOG_DEBUG(module, "|\t\tpdn_connection:\n"); OAILOG_DEBUG( - LOG_SPGW_APP, "|\t\t\tapn_in_use: %s\n", + module, "|\t\t\tapn_in_use: %s\n", sgw_context_information->pdn_connection.apn_in_use); OAILOG_DEBUG( - LOG_SPGW_APP, "|\t\t\tdefault_bearer: %u\n", + module, "|\t\t\tdefault_bearer: %u\n", sgw_context_information->pdn_connection.default_bearer); - OAILOG_DEBUG(LOG_SPGW_APP, "|\t\t\teps_bearers:\n"); + OAILOG_DEBUG(module, "|\t\t\teps_bearers:\n"); for (int ebix = 0; ebix < BEARERS_PER_UE; ebix++) { sgw_display_sgw_eps_bearer_context( sgw_context_information->pdn_connection.sgw_eps_bearers_array[ebix]); diff --git a/lte/gateway/c/oai/tasks/sgw/sgw_handlers.c b/lte/gateway/c/oai/tasks/sgw/sgw_handlers.c index 49dd3bb4410c..84aa1e6d25e3 100644 --- a/lte/gateway/c/oai/tasks/sgw/sgw_handlers.c +++ b/lte/gateway/c/oai/tasks/sgw/sgw_handlers.c @@ -78,9 +78,6 @@ static void generate_dl_flow( packet_filter_contents_t* packet_filter, in_addr_t ipv4_s_addr, struct in6_addr* ue_ipv6, struct ip_flow_dl* dlflow); -static bool does_bearer_context_hold_valid_enb_ip( - ip_address_t enb_ip_address_S1u); - static void add_tunnel_helper( s_plus_p_gw_eps_bearer_context_information_t* spgw_context, sgw_eps_bearer_ctxt_t* eps_bearer_ctxt_entry_p, imsi64_t imsi64); @@ -255,8 +252,8 @@ int sgw_handle_s11_create_session_request( session_req_pP->bearer_contexts_to_be_created.bearer_contexts[0] .eps_bearer_id); sgw_display_s11_bearer_context_information( - &s_plus_p_gw_eps_bearer_ctxt_info_p - ->sgw_eps_bearer_context_information); + LOG_SPGW_APP, &s_plus_p_gw_eps_bearer_ctxt_info_p + ->sgw_eps_bearer_context_information); if (eps_bearer_ctxt_p == NULL) { OAILOG_ERROR_UE( @@ -439,10 +436,11 @@ int sgw_handle_sgi_endpoint_created( /* Populates bearer contexts marked for removal structure in * modify bearer rsp message. */ -static void sgw_populate_mbr_bearer_contexts_not_found( +void sgw_populate_mbr_bearer_contexts_not_found( + log_proto_t module, const itti_sgi_update_end_point_response_t* const resp_pP, itti_s11_modify_bearer_response_t* modify_response_p) { - OAILOG_FUNC_IN(LOG_SPGW_APP); + OAILOG_FUNC_IN(module); uint8_t rsp_idx = 0; for (uint8_t idx = 0; idx < resp_pP->num_bearers_not_found; idx++) { modify_response_p->bearer_contexts_marked_for_removal @@ -453,23 +451,22 @@ static void sgw_populate_mbr_bearer_contexts_not_found( .cause.cause_value = CONTEXT_NOT_FOUND; modify_response_p->bearer_contexts_marked_for_removal.num_bearer_context++; } - OAILOG_FUNC_OUT(LOG_SPGW_APP); + OAILOG_FUNC_OUT(module); } //------------------------------------------------------------------------------ /* Populates bearer contexts marked for removal structure in * modify bearer rsp message */ -static void sgw_populate_mbr_bearer_contexts_removed( +void sgw_populate_mbr_bearer_contexts_removed( const itti_sgi_update_end_point_response_t* const resp_pP, imsi64_t imsi64, - s_plus_p_gw_eps_bearer_context_information_t* new_bearer_ctxt_info_p, + sgw_eps_bearer_context_information_t* sgw_context_p, itti_s11_modify_bearer_response_t* modify_response_p) { OAILOG_FUNC_IN(LOG_SPGW_APP); uint8_t rsp_idx = 0; sgw_eps_bearer_ctxt_t* eps_bearer_ctxt_p = NULL; for (uint8_t idx = 0; idx < resp_pP->num_bearers_removed; idx++) { eps_bearer_ctxt_p = sgw_cm_get_eps_bearer_entry( - &new_bearer_ctxt_info_p->sgw_eps_bearer_context_information - .pdn_connection, + &(sgw_context_p->pdn_connection), resp_pP->bearer_contexts_to_be_removed[idx]); /* If context is found, delete the context and set cause as * REQUEST_ACCEPTED. If context is not found set the cause as @@ -478,9 +475,8 @@ static void sgw_populate_mbr_bearer_contexts_removed( */ if (NULL != eps_bearer_ctxt_p) { sgw_free_eps_bearer_context( - &new_bearer_ctxt_info_p->sgw_eps_bearer_context_information - .pdn_connection.sgw_eps_bearers_array[EBI_TO_INDEX( - eps_bearer_ctxt_p->eps_bearer_id)]); + &(sgw_context_p->pdn_connection.sgw_eps_bearers_array[EBI_TO_INDEX( + eps_bearer_ctxt_p->eps_bearer_id)])); modify_response_p->bearer_contexts_marked_for_removal .bearer_contexts[rsp_idx] .cause.cause_value = REQUEST_ACCEPTED; @@ -672,8 +668,8 @@ void sgw_handle_sgi_endpoint_updated( OAILOG_DEBUG_UE( LOG_SPGW_APP, imsi64, - "Rx SGI_UPDATE_ENDPOINT_RESPONSE, Context teid " TEID_FMT " status %d\n", - resp_pP->context_teid, resp_pP->status); + "Rx SGI_UPDATE_ENDPOINT_RESPONSE, Context teid " TEID_FMT "\n", + resp_pP->context_teid); message_p = itti_alloc_new_message(TASK_SPGW_APP, S11_MODIFY_BEARER_RESPONSE); if (!message_p) { @@ -698,8 +694,11 @@ void sgw_handle_sgi_endpoint_updated( sgw_populate_mbr_bearer_contexts_modified( resp_pP, imsi64, new_bearer_ctxt_info_p, modify_response_p); sgw_populate_mbr_bearer_contexts_removed( - resp_pP, imsi64, new_bearer_ctxt_info_p, modify_response_p); - sgw_populate_mbr_bearer_contexts_not_found(resp_pP, modify_response_p); + resp_pP, imsi64, + &new_bearer_ctxt_info_p->sgw_eps_bearer_context_information, + modify_response_p); + sgw_populate_mbr_bearer_contexts_not_found( + LOG_SPGW_APP, resp_pP, modify_response_p); send_msg_to_task(&spgw_app_task_zmq_ctx, TASK_MME, message_p); } OAILOG_FUNC_OUT(LOG_SPGW_APP); @@ -860,7 +859,7 @@ int sgw_handle_sgi_endpoint_deleted( //------------------------------------------------------------------------------ // This function populates itti_sgi_update_end_point_response_t message -static void populate_sgi_end_point_update( +void populate_sgi_end_point_update( uint8_t sgi_rsp_idx, uint8_t idx, const itti_s11_modify_bearer_request_t* const modify_bearer_pP, sgw_eps_bearer_ctxt_t* eps_bearer_ctxt_p, @@ -887,18 +886,19 @@ static void populate_sgi_end_point_update( //------------------------------------------------------------------------------ // This function populates and sends MBR failure message to MME APP -static int send_mbr_failure( +int send_mbr_failure( + log_proto_t module, const itti_s11_modify_bearer_request_t* const modify_bearer_pP, imsi64_t imsi64) { int rv = RETURNok; - OAILOG_FUNC_IN(LOG_SPGW_APP); + OAILOG_FUNC_IN(module); MessageDef* message_p = itti_alloc_new_message(TASK_SPGW_APP, S11_MODIFY_BEARER_RESPONSE); if (!message_p) { OAILOG_ERROR( - LOG_SPGW_APP, "S11_MODIFY_BEARER_RESPONSE memory allocation failed\n"); - OAILOG_FUNC_RETURN(LOG_SPGW_APP, RETURNerror); + module, "S11_MODIFY_BEARER_RESPONSE memory allocation failed\n"); + OAILOG_FUNC_RETURN(module, RETURNerror); } itti_s11_modify_bearer_response_t* modify_response_p = @@ -921,13 +921,13 @@ static int send_mbr_failure( modify_response_p->cause.cause_value = CONTEXT_NOT_FOUND; modify_response_p->trxn = modify_bearer_pP->trxn; OAILOG_DEBUG_UE( - LOG_SPGW_APP, imsi64, + module, imsi64, "Rx MODIFY_BEARER_REQUEST, teid " TEID_FMT " CONTEXT_NOT_FOUND\n", modify_bearer_pP->teid); message_p->ittiMsgHeader.imsi = imsi64; rv = send_msg_to_task(&spgw_app_task_zmq_ctx, TASK_MME, message_p); - OAILOG_FUNC_RETURN(LOG_SPGW_APP, rv); + OAILOG_FUNC_RETURN(module, rv); } //------------------------------------------------------------------------------ @@ -956,7 +956,6 @@ int sgw_handle_modify_bearer_request( modify_bearer_pP->trxn; sgi_update_end_point_resp.context_teid = modify_bearer_pP->teid; - sgi_update_end_point_resp.status = 0; uint8_t sgi_rsp_idx = 0; for (idx = 0; idx < @@ -1031,7 +1030,7 @@ int sgw_handle_modify_bearer_request( } sgw_handle_sgi_endpoint_updated(&sgi_update_end_point_resp, imsi64); } else { // bearer_ctxt_info_p not found - rv = send_mbr_failure(modify_bearer_pP, imsi64); + rv = send_mbr_failure(LOG_SPGW_APP, modify_bearer_pP, imsi64); if (rv != RETURNok) { OAILOG_ERROR( LOG_SPGW_APP, @@ -2227,8 +2226,7 @@ static void add_tunnel_helper( } } } -static bool does_bearer_context_hold_valid_enb_ip( - ip_address_t enb_ip_address_S1u) { +bool does_bearer_context_hold_valid_enb_ip(ip_address_t enb_ip_address_S1u) { OAILOG_FUNC_IN(LOG_SPGW_APP); static struct in6_addr ipv6_address = {0}; switch (enb_ip_address_S1u.pdn_type) { diff --git a/lte/gateway/c/oai/tasks/sgw/sgw_handlers.h b/lte/gateway/c/oai/tasks/sgw/sgw_handlers.h index 21f40af3d490..658164070882 100644 --- a/lte/gateway/c/oai/tasks/sgw/sgw_handlers.h +++ b/lte/gateway/c/oai/tasks/sgw/sgw_handlers.h @@ -69,4 +69,28 @@ int sgw_handle_ip_allocation_rsp( const itti_ip_allocation_response_t* ip_allocation_rsp, imsi64_t imsi64); bool is_enb_ip_address_same(const fteid_t* fte_p, ip_address_t* ip_p); uint32_t spgw_get_new_s1u_teid(spgw_state_t* state); +int send_mbr_failure( + log_proto_t module, + const itti_s11_modify_bearer_request_t* const modify_bearer_pP, + imsi64_t imsi64); +void sgw_populate_mbr_bearer_contexts_removed( + const itti_sgi_update_end_point_response_t* const resp_pP, imsi64_t imsi64, + sgw_eps_bearer_context_information_t* sgw_context_p, + itti_s11_modify_bearer_response_t* modify_response_p); +void sgw_populate_mbr_bearer_contexts_not_found( + log_proto_t module, + const itti_sgi_update_end_point_response_t* const resp_pP, + itti_s11_modify_bearer_response_t* modify_response_p); +void populate_sgi_end_point_update( + uint8_t sgi_rsp_idx, uint8_t idx, + const itti_s11_modify_bearer_request_t* const modify_bearer_pP, + sgw_eps_bearer_ctxt_t* eps_bearer_ctxt_p, + itti_sgi_update_end_point_response_t* sgi_update_end_point_resp); +bool does_bearer_context_hold_valid_enb_ip(ip_address_t enb_ip_address_S1u); +void populate_sgi_end_point_update( + uint8_t sgi_rsp_idx, uint8_t idx, + const itti_s11_modify_bearer_request_t* const modify_bearer_pP, + sgw_eps_bearer_ctxt_t* eps_bearer_ctxt_p, + itti_sgi_update_end_point_response_t* sgi_update_end_point_resp); + #endif /* FILE_SGW_HANDLERS_SEEN */ diff --git a/lte/gateway/c/oai/tasks/sgw_s8/sgw_s8_handlers.c b/lte/gateway/c/oai/tasks/sgw_s8/sgw_s8_handlers.c index ff0642e03bd2..6a703020a3db 100644 --- a/lte/gateway/c/oai/tasks/sgw_s8/sgw_s8_handlers.c +++ b/lte/gateway/c/oai/tasks/sgw_s8/sgw_s8_handlers.c @@ -24,14 +24,24 @@ limitations under the License. #include "s8_client_api.h" #include "gtpv1u.h" #include "dynamic_memory_check.h" +#include "sgw_handlers.h" extern task_zmq_ctx_t sgw_s8_task_zmq_ctx; extern struct gtp_tunnel_ops* gtp_tunnel_ops; - -static int sgw_s8_add_gtp_tunnel( +static int sgw_s8_add_gtp_up_tunnel( sgw_eps_bearer_ctxt_t* eps_bearer_ctxt_p, sgw_eps_bearer_context_information_t* sgw_context_p); +static void sgw_send_modify_bearer_response( + sgw_eps_bearer_context_information_t* sgw_context_p, + const itti_sgi_update_end_point_response_t* const resp_pP, imsi64_t imsi64); + +static void sgw_s8_send_failed_delete_session_response( + sgw_eps_bearer_context_information_t* sgw_context_p, + gtpv2c_cause_value_t cause, sgw_state_t* sgw_state, + const itti_s11_delete_session_request_t* const delete_session_req_p, + imsi64_t imsi64); + uint32_t sgw_get_new_s1u_teid(sgw_state_t* state) { if (state->s1u_teid == 0) { state->s1u_teid = INITIAL_SGW_S8_S1U_TEID; @@ -44,6 +54,10 @@ uint32_t sgw_get_new_s5s8u_teid(sgw_state_t* state) { __sync_fetch_and_add(&state->s5s8u_teid, 1); return (state->s5s8u_teid); } +static void sgw_s8_populate_mbr_bearer_contexts_modified( + const itti_sgi_update_end_point_response_t* const resp_pP, imsi64_t imsi64, + sgw_eps_bearer_context_information_t* sgw_context_p, + itti_s11_modify_bearer_response_t* modify_response_p); void sgw_remove_sgw_bearer_context_information( sgw_state_t* sgw_state, teid_t teid, imsi64_t imsi64) { @@ -254,7 +268,8 @@ void sgw_s8_handle_s11_create_session_request( memcpy( new_sgw_eps_context->imsi.digit, session_req_pP->imsi.digit, IMSI_BCD_DIGITS_MAX); - new_sgw_eps_context->imsi64 = imsi64; + new_sgw_eps_context->imsi.length = session_req_pP->imsi.length; + new_sgw_eps_context->imsi64 = imsi64; new_sgw_eps_context->imsi_unauthenticated_indicator = 1; new_sgw_eps_context->mme_teid_S11 = session_req_pP->sender_fteid_for_cp.teid; @@ -300,7 +315,7 @@ void sgw_s8_handle_s11_create_session_request( send_s8_create_session_request( sgw_s11_tunnel.local_teid, session_req_pP, imsi64); - sgw_display_s11_bearer_context_information(new_sgw_eps_context); + sgw_display_s11_bearer_context_information(LOG_SGW_S8, new_sgw_eps_context); OAILOG_FUNC_OUT(LOG_SGW_S8); } @@ -338,7 +353,6 @@ static int update_bearer_context_info( memcpy( &default_bearer_ctx_p->eps_bearer_qos, &s5s8_bearer_context.qos, sizeof(bearer_qos_t)); - sgw_s8_add_gtp_tunnel(default_bearer_ctx_p, sgw_context_p); OAILOG_FUNC_RETURN(LOG_SGW_S8, RETURNok); } @@ -490,18 +504,220 @@ void sgw_s8_handle_create_session_response( OAILOG_FUNC_OUT(LOG_SGW_S8); } +void sgw_s8_handle_modify_bearer_request( + sgw_state_t* state, + const itti_s11_modify_bearer_request_t* const modify_bearer_pP, + imsi64_t imsi64) { + OAILOG_FUNC_IN(LOG_SGW_S8); + + uint8_t idx = 0; + uint8_t sgi_rsp_idx = 0; + itti_sgi_update_end_point_response_t sgi_update_end_point_resp = {0}; + struct in_addr enb = {.s_addr = 0}; + struct in_addr pgw = {.s_addr = 0}; + sgw_eps_bearer_ctxt_t* bearer_ctx_p = NULL; + + OAILOG_INFO_UE( + LOG_SGW_S8, imsi64, "Rx MODIFY_BEARER_REQUEST, teid " TEID_FMT "\n", + modify_bearer_pP->teid); + + sgw_eps_bearer_context_information_t* sgw_context_p = + sgw_get_sgw_eps_bearer_context(modify_bearer_pP->teid); + if (!sgw_context_p) { + OAILOG_ERROR_UE( + LOG_SGW_S8, imsi64, + "Failed to fetch sgw_eps_bearer_context_info from " + "context_teid " TEID_FMT " \n", + modify_bearer_pP->teid); + if ((send_mbr_failure(LOG_SGW_S8, modify_bearer_pP, imsi64) != RETURNok)) { + OAILOG_ERROR( + LOG_SGW_S8, + "Error in sending modify bearer response to MME App for context " + "teid " TEID_FMT "\n", + modify_bearer_pP->teid); + } + OAILOG_FUNC_OUT(LOG_SGW_S8); + } + sgw_context_p->trxn = modify_bearer_pP->trxn; + sgi_update_end_point_resp.context_teid = modify_bearer_pP->teid; + // Traversing through the list of bearers to be modified + for (; idx < + modify_bearer_pP->bearer_contexts_to_be_modified.num_bearer_context; + idx++) { + bearer_context_to_be_modified_t mbr_bearer_ctxt_p = + modify_bearer_pP->bearer_contexts_to_be_modified.bearer_contexts[idx]; + bearer_ctx_p = sgw_cm_get_eps_bearer_entry( + &sgw_context_p->pdn_connection, mbr_bearer_ctxt_p.eps_bearer_id); + if (!bearer_ctx_p) { + OAILOG_ERROR_UE( + LOG_SGW_S8, imsi64, + "Failed to get eps bearer context for context teid " TEID_FMT + "and bearer_id :%u \n", + modify_bearer_pP->teid, mbr_bearer_ctxt_p.eps_bearer_id); + sgi_update_end_point_resp.bearer_contexts_not_found[sgi_rsp_idx++] = + mbr_bearer_ctxt_p.eps_bearer_id; + sgi_update_end_point_resp.num_bearers_not_found++; + } else { + enb.s_addr = bearer_ctx_p->enb_ip_address_S1u.address.ipv4_address.s_addr; + pgw.s_addr = + bearer_ctx_p->p_gw_address_in_use_up.address.ipv4_address.s_addr; + + // Send end marker to eNB and then delete the tunnel if enb_ip is + // different + if (does_bearer_context_hold_valid_enb_ip( + bearer_ctx_p->enb_ip_address_S1u) && + is_enb_ip_address_same( + &mbr_bearer_ctxt_p.s1_eNB_fteid, + &bearer_ctx_p->enb_ip_address_S1u) == false) { + struct in_addr ue_ipv4 = bearer_ctx_p->paa.ipv4_address; + struct in6_addr* ue_ipv6 = NULL; + if ((bearer_ctx_p->paa.pdn_type == IPv6) || + (bearer_ctx_p->paa.pdn_type == IPv4_AND_v6)) { + ue_ipv6 = &bearer_ctx_p->paa.ipv6_address; + } + + OAILOG_DEBUG_UE( + LOG_SGW_S8, imsi64, + "Delete GTPv1-U tunnel for sgw_teid:" TEID_FMT "for bearer %u\n", + bearer_ctx_p->s_gw_teid_S1u_S12_S4_up, bearer_ctx_p->eps_bearer_id); + // This is best effort, ignore return code. + gtp_tunnel_ops->send_end_marker(enb, modify_bearer_pP->teid); + // delete GTPv1-U tunnel + gtpv1u_del_s8_tunnel( + enb, pgw, ue_ipv4, ue_ipv6, bearer_ctx_p->s_gw_teid_S1u_S12_S4_up, + bearer_ctx_p->enb_teid_S1u, NULL); + } + populate_sgi_end_point_update( + sgi_rsp_idx, idx, modify_bearer_pP, bearer_ctx_p, + &sgi_update_end_point_resp); + sgi_rsp_idx++; + } + } // for loop + + sgi_rsp_idx = 0; + for (idx = 0; + idx < modify_bearer_pP->bearer_contexts_to_be_removed.num_bearer_context; + idx++) { + bearer_ctx_p = sgw_cm_get_eps_bearer_entry( + &sgw_context_p->pdn_connection, + modify_bearer_pP->bearer_contexts_to_be_removed.bearer_contexts[idx] + .eps_bearer_id); + if (bearer_ctx_p) { + sgi_update_end_point_resp.bearer_contexts_to_be_removed[sgi_rsp_idx++] = + bearer_ctx_p->eps_bearer_id; + sgi_update_end_point_resp.num_bearers_removed++; + } + } + sgw_send_modify_bearer_response( + sgw_context_p, &sgi_update_end_point_resp, imsi64); + OAILOG_FUNC_OUT(LOG_SGW_S8); +} + +static void sgw_send_modify_bearer_response( + sgw_eps_bearer_context_information_t* sgw_context_p, + const itti_sgi_update_end_point_response_t* const resp_pP, + imsi64_t imsi64) { + OAILOG_FUNC_IN(LOG_SGW_S8); + itti_s11_modify_bearer_response_t* modify_response_p = NULL; + MessageDef* message_p = NULL; + + OAILOG_DEBUG_UE( + LOG_SGW_S8, imsi64, + "send modify bearer response for Context teid " TEID_FMT "\n", + resp_pP->context_teid); + message_p = itti_alloc_new_message(TASK_SGW_S8, S11_MODIFY_BEARER_RESPONSE); + + if (!message_p) { + OAILOG_ERROR_UE( + LOG_SGW_S8, imsi64, + "Failed to allocate memory for S11_MODIFY_BEARER_RESPONSE\n"); + OAILOG_FUNC_OUT(LOG_SGW_S8); + } + + modify_response_p = &message_p->ittiMsg.s11_modify_bearer_response; + + if (sgw_context_p) { + modify_response_p->teid = sgw_context_p->mme_teid_S11; + modify_response_p->cause.cause_value = REQUEST_ACCEPTED; + modify_response_p->trxn = sgw_context_p->trxn; + message_p->ittiMsgHeader.imsi = imsi64; + + sgw_s8_populate_mbr_bearer_contexts_modified( + resp_pP, imsi64, sgw_context_p, modify_response_p); + sgw_populate_mbr_bearer_contexts_removed( + resp_pP, imsi64, sgw_context_p, modify_response_p); + sgw_populate_mbr_bearer_contexts_not_found( + LOG_SGW_S8, resp_pP, modify_response_p); + send_msg_to_task(&sgw_s8_task_zmq_ctx, TASK_MME_APP, message_p); + } + OAILOG_FUNC_OUT(LOG_SGW_S8); +} + +static void sgw_s8_populate_mbr_bearer_contexts_modified( + const itti_sgi_update_end_point_response_t* const resp_pP, imsi64_t imsi64, + sgw_eps_bearer_context_information_t* sgw_context_p, + itti_s11_modify_bearer_response_t* modify_response_p) { + OAILOG_FUNC_IN(LOG_SGW_S8); + uint8_t rsp_idx = 0; + sgw_eps_bearer_ctxt_t* eps_bearer_ctxt_p = NULL; + + for (uint8_t idx = 0; idx < resp_pP->num_bearers_modified; idx++) { + eps_bearer_ctxt_p = sgw_cm_get_eps_bearer_entry( + &sgw_context_p->pdn_connection, + resp_pP->bearer_contexts_to_be_modified[idx].eps_bearer_id); + + if (NULL != eps_bearer_ctxt_p) { + OAILOG_DEBUG_UE( + LOG_SGW_S8, imsi64, + "Modify bearer request is accepted for bearer_id :%u\n", + resp_pP->bearer_contexts_to_be_modified[idx].eps_bearer_id); + modify_response_p->bearer_contexts_modified.bearer_contexts[rsp_idx] + .eps_bearer_id = + resp_pP->bearer_contexts_to_be_modified[idx].eps_bearer_id; + modify_response_p->bearer_contexts_modified.bearer_contexts[rsp_idx++] + .cause.cause_value = REQUEST_ACCEPTED; + modify_response_p->bearer_contexts_modified.num_bearer_context++; + + // setup GTPv1-U tunnels, both s1-u and s8-u tunnels + sgw_s8_add_gtp_up_tunnel(eps_bearer_ctxt_p, sgw_context_p); + // may be removed TODO rashmi remove after testing + if (TRAFFIC_FLOW_TEMPLATE_NB_PACKET_FILTERS_MAX > + eps_bearer_ctxt_p->num_sdf) { + int i = 0; + while ((i < eps_bearer_ctxt_p->num_sdf) && + (SDF_ID_NGBR_DEFAULT != eps_bearer_ctxt_p->sdf_id[i])) + i++; + if (i >= eps_bearer_ctxt_p->num_sdf) { + eps_bearer_ctxt_p->sdf_id[eps_bearer_ctxt_p->num_sdf] = + SDF_ID_NGBR_DEFAULT; + eps_bearer_ctxt_p->num_sdf += 1; + } + } + } + } + OAILOG_FUNC_OUT(LOG_SGW_S8); +} + // Helper function to add gtp tunnels for default bearers -static int sgw_s8_add_gtp_tunnel( +static int sgw_s8_add_gtp_up_tunnel( sgw_eps_bearer_ctxt_t* eps_bearer_ctxt_p, sgw_eps_bearer_context_information_t* sgw_context_p) { int rv = RETURNok; struct in_addr enb = {.s_addr = 0}; struct in_addr pgw = {.s_addr = 0}; + pgw.s_addr = + eps_bearer_ctxt_p->p_gw_address_in_use_up.address.ipv4_address.s_addr; + if ((pgw.s_addr == 0) && (eps_bearer_ctxt_p->p_gw_teid_S5_S8_up == 0)) { + OAILOG_ERROR_UE( + LOG_SGW_S8, sgw_context_p->imsi64, + "bearer context has invalid pgw_s8_teid " TEID_FMT + "pgw_ip address :%x \n", + eps_bearer_ctxt_p->p_gw_teid_S5_S8_up, pgw.s_addr); + OAILOG_FUNC_RETURN(LOG_SGW_S8, RETURNerror); + } enb.s_addr = eps_bearer_ctxt_p->enb_ip_address_S1u.address.ipv4_address.s_addr; - pgw.s_addr = - eps_bearer_ctxt_p->p_gw_address_in_use_up.address.ipv4_address.s_addr; struct in_addr ue_ipv4 = {.s_addr = 0}; struct in6_addr* ue_ipv6 = NULL; ue_ipv4.s_addr = eps_bearer_ctxt_p->paa.ipv4_address.s_addr; @@ -517,48 +733,298 @@ static int sgw_s8_add_gtp_tunnel( if (ue_ipv6) { inet_ntop(AF_INET6, ue_ipv6, ip6_str, INET6_ADDRSTRLEN); } - /* UE is switching back to EPS services after the CS Fallback - * If Modify bearer Request is received in UE suspended mode, Resume PS - * data - */ - if (sgw_context_p->pdn_connection.ue_suspended_for_ps_handover) { - rv = gtp_tunnel_ops->forward_data_on_tunnel( - ue_ipv4, ue_ipv6, eps_bearer_ctxt_p->s_gw_teid_S1u_S12_S4_up, NULL, - DEFAULT_PRECEDENCE); + OAILOG_DEBUG_UE( + LOG_SGW_S8, sgw_context_p->imsi64, + "Adding tunnel for bearer_id %u ue addr %x enb %x " + "s_gw_teid_S1u_S12_S4_up %x, enb_teid_S1u %x pgw_up_ip %x pgw_up_teid %x " + "s_gw_ip_address_S5_S8_up %x" + "s_gw_teid_S5_S8_up %x \n ", + eps_bearer_ctxt_p->eps_bearer_id, ue_ipv4.s_addr, enb.s_addr, + eps_bearer_ctxt_p->s_gw_teid_S1u_S12_S4_up, + eps_bearer_ctxt_p->enb_teid_S1u, pgw.s_addr, + eps_bearer_ctxt_p->p_gw_teid_S5_S8_up, + eps_bearer_ctxt_p->s_gw_ip_address_S5_S8_up.address.ipv4_address.s_addr, + eps_bearer_ctxt_p->s_gw_teid_S5_S8_up); + if (eps_bearer_ctxt_p->eps_bearer_id == + sgw_context_p->pdn_connection.default_bearer) { + // Set default precedence and tft for default bearer + if (ue_ipv6) { + OAILOG_INFO_UE( + LOG_SGW_S8, sgw_context_p->imsi64, + "Adding tunnel for ipv6 ue addr %s, enb %x, " + "s_gw_teid_S5_S8_up %x, s_gw_ip_address_S5_S8_up %x pgw_up_ip %x " + "pgw_up_teid %x \n", + ip6_str, enb.s_addr, eps_bearer_ctxt_p->s_gw_teid_S5_S8_up, + eps_bearer_ctxt_p->s_gw_ip_address_S5_S8_up.address.ipv4_address + .s_addr, + pgw.s_addr, eps_bearer_ctxt_p->p_gw_teid_S5_S8_up); + } + rv = gtpv1u_add_s8_tunnel( + ue_ipv4, ue_ipv6, vlan, enb, pgw, + eps_bearer_ctxt_p->s_gw_teid_S1u_S12_S4_up, + eps_bearer_ctxt_p->enb_teid_S1u, eps_bearer_ctxt_p->s_gw_teid_S5_S8_up, + eps_bearer_ctxt_p->p_gw_teid_S5_S8_up, imsi, NULL, DEFAULT_PRECEDENCE); if (rv < 0) { OAILOG_ERROR_UE( LOG_SGW_S8, sgw_context_p->imsi64, - "ERROR in forwarding data on TUNNEL err=%d\n", rv); + "ERROR in setting up TUNNEL err=%d\n", rv); } - } else { + } + OAILOG_FUNC_RETURN(LOG_SGW_S8, rv); +} + +void sgw_s8_handle_s11_delete_session_request( + sgw_state_t* sgw_state, + const itti_s11_delete_session_request_t* const delete_session_req_p, + imsi64_t imsi64) { + OAILOG_FUNC_IN(LOG_SGW_S8); + gtpv2c_cause_value_t gtpv2c_cause = 0; + if (!delete_session_req_p) { + OAILOG_ERROR_UE( + LOG_SGW_S8, imsi64, + "Received NULL delete_session_req_p from mme app \n"); + OAILOG_FUNC_OUT(LOG_SGW_S8); + } + OAILOG_INFO_UE( + LOG_SGW_S8, imsi64, + "Received S11 DELETE SESSION REQUEST for sgw_s11_teid " TEID_FMT "\n", + delete_session_req_p->teid); + increment_counter("sgw_delete_session", 1, NO_LABELS); + if (delete_session_req_p->indication_flags.oi) { OAILOG_DEBUG_UE( - LOG_SGW_S8, sgw_context_p->imsi64, - "Adding tunnel for bearer %u ue addr %x\n", - eps_bearer_ctxt_p->eps_bearer_id, ue_ipv4.s_addr); - if (eps_bearer_ctxt_p->eps_bearer_id == - sgw_context_p->pdn_connection.default_bearer) { - // Set default precedence and tft for default bearer - if (ue_ipv6) { - OAILOG_INFO_UE( - LOG_SGW_S8, sgw_context_p->imsi64, - "Adding tunnel for ipv6 ue addr %s, enb %x, " - "s_gw_teid_S1u_S12_S4_up %x, enb_teid_S1u %x pgw_up_ip %x " - "pgw_up_teid %x \n", - ip6_str, enb.s_addr, eps_bearer_ctxt_p->s_gw_teid_S1u_S12_S4_up, - eps_bearer_ctxt_p->enb_teid_S1u, pgw.s_addr, - eps_bearer_ctxt_p->p_gw_teid_S5_S8_up); + LOG_SPGW_APP, imsi64, + "OI flag is set for this message indicating the request" + "should be forwarded to P-GW entity\n"); + } + + sgw_eps_bearer_context_information_t* sgw_context_p = + sgw_get_sgw_eps_bearer_context(delete_session_req_p->teid); + if (!sgw_context_p) { + OAILOG_ERROR_UE( + LOG_SGW_S8, imsi64, + "Failed to fetch sgw_eps_bearer_context_info from " + "sgw_s11_teid " TEID_FMT " \n", + delete_session_req_p->teid); + gtpv2c_cause = CONTEXT_NOT_FOUND; + sgw_s8_send_failed_delete_session_response( + sgw_context_p, gtpv2c_cause, sgw_state, delete_session_req_p, imsi64); + OAILOG_FUNC_OUT(LOG_SGW_S8); + } + if ((delete_session_req_p->sender_fteid_for_cp.ipv4) && + (delete_session_req_p->sender_fteid_for_cp.ipv6)) { + // Sender F-TEID IE present + if (delete_session_req_p->teid != sgw_context_p->mme_teid_S11) { + OAILOG_ERROR_UE( + LOG_SPGW_APP, imsi64, + "Mismatch in MME Teid for CP teid recevied in delete session " + "req: " TEID_FMT " teid present in sgw_context :" TEID_FMT "\n", + delete_session_req_p->teid, sgw_context_p->mme_teid_S11); + gtpv2c_cause = INVALID_PEER; + sgw_s8_send_failed_delete_session_response( + sgw_context_p, gtpv2c_cause, sgw_state, delete_session_req_p, imsi64); + OAILOG_FUNC_OUT(LOG_SGW_S8); + } + } + if (delete_session_req_p->lbi != + sgw_context_p->pdn_connection.default_bearer) { + OAILOG_ERROR_UE( + LOG_SPGW_APP, imsi64, + "Mismatch in default eps bearer_id, bearer_id recevied in delete " + "session req :%d and bearer_id present in sgw_context :%d \n", + delete_session_req_p->lbi, + sgw_context_p->pdn_connection.default_bearer); + sgw_s8_send_failed_delete_session_response( + sgw_context_p, gtpv2c_cause, sgw_state, delete_session_req_p, imsi64); + OAILOG_FUNC_OUT(LOG_SGW_S8); + } + + send_s8_delete_session_request( + sgw_context_p->imsi64, sgw_context_p->imsi, + sgw_context_p->s_gw_teid_S11_S4, + sgw_context_p->pdn_connection.p_gw_teid_S5_S8_cp, + sgw_context_p->pdn_connection.default_bearer); + OAILOG_FUNC_OUT(LOG_SGW_S8); +} + +static void delete_userplane_tunnels( + sgw_eps_bearer_context_information_t* sgw_context_p) { + OAILOG_FUNC_IN(LOG_SGW_S8); + struct in_addr enb = {.s_addr = 0}; + struct in_addr pgw = {.s_addr = 0}; + sgw_eps_bearer_ctxt_t* bearer_ctxt_p = NULL; + int rv = RETURNerror; + + for (int ebix = 0; ebix < BEARERS_PER_UE; ebix++) { + ebi_t ebi = INDEX_TO_EBI(ebix); + bearer_ctxt_p = + sgw_cm_get_eps_bearer_entry(&sgw_context_p->pdn_connection, ebi); + + if (bearer_ctxt_p) { + enb.s_addr = + bearer_ctxt_p->enb_ip_address_S1u.address.ipv4_address.s_addr; + pgw.s_addr = + bearer_ctxt_p->p_gw_address_in_use_up.address.ipv4_address.s_addr; + struct in6_addr* ue_ipv6 = NULL; + if ((bearer_ctxt_p->paa.pdn_type == IPv6) || + (bearer_ctxt_p->paa.pdn_type == IPv4_AND_v6)) { + ue_ipv6 = &bearer_ctxt_p->paa.ipv6_address; } - rv = gtpv1u_add_s8_tunnel( - ue_ipv4, ue_ipv6, vlan, enb, pgw, - eps_bearer_ctxt_p->s_gw_teid_S1u_S12_S4_up, - eps_bearer_ctxt_p->enb_teid_S1u, 0, 0, imsi, NULL, - DEFAULT_PRECEDENCE); + // Delete S1-U tunnel + rv = openflow_del_s8_tunnel( + enb, pgw, bearer_ctxt_p->paa.ipv4_address, ue_ipv6, + bearer_ctxt_p->s_gw_teid_S1u_S12_S4_up, bearer_ctxt_p->enb_teid_S1u, + NULL); if (rv < 0) { OAILOG_ERROR_UE( - LOG_SGW_S8, sgw_context_p->imsi64, - "ERROR in setting up TUNNEL err=%d\n", rv); + LOG_SPGW_APP, sgw_context_p->imsi64, + "ERROR in deleting S1-U TUNNEL " TEID_FMT + " (eNB) <-> (SGW) " TEID_FMT "\n", + bearer_ctxt_p->enb_teid_S1u, + bearer_ctxt_p->s_gw_teid_S1u_S12_S4_up); + } + // Delete S8-U tunnel + rv = openflow_del_s8_tunnel( + enb, pgw, bearer_ctxt_p->paa.ipv4_address, ue_ipv6, + bearer_ctxt_p->s_gw_teid_S5_S8_up, bearer_ctxt_p->p_gw_teid_S5_S8_up, + NULL); + if (rv < 0) { + OAILOG_ERROR_UE( + LOG_SPGW_APP, sgw_context_p->imsi64, + "ERROR in deleting S8-U TUNNEL " TEID_FMT + " (PGW) <-> (SGW) " TEID_FMT "\n", + bearer_ctxt_p->p_gw_teid_S5_S8_up, + bearer_ctxt_p->s_gw_teid_S5_S8_up); } } } - OAILOG_FUNC_RETURN(LOG_SGW_S8, rv); + OAILOG_FUNC_OUT(LOG_SGW_S8); +} + +void sgw_s8_handle_delete_session_response( + sgw_state_t* sgw_state, s8_delete_session_response_t* session_rsp_p, + imsi64_t imsi64) { + OAILOG_FUNC_IN(LOG_SGW_S8); + MessageDef* message_p = NULL; + itti_s11_delete_session_response_t* delete_session_response_p = NULL; + + if (!session_rsp_p) { + OAILOG_ERROR_UE( + LOG_SGW_S8, imsi64, + "Received null delete session response from s8_proxy\n"); + OAILOG_FUNC_OUT(LOG_SGW_S8); + } + OAILOG_INFO_UE( + LOG_SGW_S8, imsi64, + " Rx S5S8_DELETE_SESSION_RSP from s8_proxy for context_teid " TEID_FMT + "\n", + session_rsp_p->context_teid); + + sgw_eps_bearer_context_information_t* sgw_context_p = + sgw_get_sgw_eps_bearer_context(session_rsp_p->context_teid); + if (!sgw_context_p) { + OAILOG_ERROR_UE( + LOG_SGW_S8, imsi64, + "Failed to fetch sgw_eps_bearer_context_info from " + "context_teid " TEID_FMT " \n", + session_rsp_p->context_teid); + OAILOG_FUNC_OUT(LOG_SGW_S8); + } + message_p = itti_alloc_new_message(TASK_SGW_S8, S11_DELETE_SESSION_RESPONSE); + if (message_p == NULL) { + OAILOG_CRITICAL_UE( + LOG_SGW_S8, sgw_context_p->imsi64, + "Failed to allocate memory for S11_delete_session_response for " + "context_teid " TEID_FMT "\n", + session_rsp_p->context_teid); + OAILOG_FUNC_OUT(LOG_SGW_S8); + } + + delete_session_response_p = &message_p->ittiMsg.s11_delete_session_response; + message_p->ittiMsgHeader.imsi = imsi64; + if (!delete_session_response_p) { + OAILOG_ERROR_UE( + LOG_SGW_S8, sgw_context_p->imsi64, + "delete_session_response_p is NULL \n"); + OAILOG_FUNC_OUT(LOG_SGW_S8); + } + delete_session_response_p->teid = sgw_context_p->mme_teid_S11; + delete_session_response_p->cause.cause_value = session_rsp_p->cause; + delete_session_response_p->trxn = sgw_context_p->trxn; + delete_session_response_p->lbi = sgw_context_p->pdn_connection.default_bearer; + + // Delete ovs rules + delete_userplane_tunnels(sgw_context_p); + sgw_remove_sgw_bearer_context_information( + sgw_state, session_rsp_p->context_teid, imsi64); + // send delete session response to mme + if (send_msg_to_task(&sgw_s8_task_zmq_ctx, TASK_MME_APP, message_p) != + RETURNok) { + OAILOG_ERROR_UE( + LOG_SGW_S8, imsi64, + "Failed to send delete session response to mme for " + "sgw_s11_teid " TEID_FMT "\n", + session_rsp_p->context_teid); + } + increment_counter("sgw_delete_session", 1, 1, "result", "success"); + OAILOG_FUNC_OUT(LOG_SGW_S8); +} + +static void sgw_s8_send_failed_delete_session_response( + sgw_eps_bearer_context_information_t* sgw_context_p, + gtpv2c_cause_value_t cause, sgw_state_t* sgw_state, + const itti_s11_delete_session_request_t* const delete_session_req_p, + imsi64_t imsi64) { + OAILOG_FUNC_IN(LOG_SGW_S8); + MessageDef* message_p = NULL; + itti_s11_delete_session_response_t* delete_session_response_p = NULL; + teid_t teid = 0; + + message_p = itti_alloc_new_message(TASK_SGW_S8, S11_DELETE_SESSION_RESPONSE); + if (message_p == NULL) { + OAILOG_CRITICAL_UE( + LOG_SGW_S8, sgw_context_p->imsi64, + "Failed to allocate memory for S11_delete_session_response \n"); + OAILOG_FUNC_OUT(LOG_SGW_S8); + } + + delete_session_response_p = &message_p->ittiMsg.s11_delete_session_response; + message_p->ittiMsgHeader.imsi = imsi64; + if (!delete_session_response_p) { + OAILOG_ERROR_UE( + LOG_SGW_S8, sgw_context_p->imsi64, + "delete_session_response_p is NULL \n"); + OAILOG_FUNC_OUT(LOG_SGW_S8); + } + if (sgw_context_p) { + delete_session_response_p->teid = sgw_context_p->mme_teid_S11; + delete_session_response_p->cause.cause_value = cause; + delete_session_response_p->trxn = sgw_context_p->trxn; + delete_session_response_p->lbi = + sgw_context_p->pdn_connection.default_bearer; + + // Delete ovs rules + delete_userplane_tunnels(sgw_context_p); + sgw_remove_sgw_bearer_context_information( + sgw_state, sgw_context_p->s_gw_teid_S11_S4, imsi64); + teid = sgw_context_p->s_gw_teid_S11_S4; + } else { + if (delete_session_req_p) { + delete_session_response_p->teid = delete_session_req_p->local_teid; + delete_session_response_p->cause.cause_value = cause; + delete_session_response_p->trxn = delete_session_req_p->trxn; + delete_session_response_p->lbi = delete_session_req_p->lbi; + teid = delete_session_req_p->teid; + } + } + increment_counter("sgw_delete_session", 1, 1, "result", "failed"); + // send delete session response to mme + if (send_msg_to_task(&sgw_s8_task_zmq_ctx, TASK_MME_APP, message_p) != + RETURNok) { + OAILOG_ERROR_UE( + LOG_SGW_S8, imsi64, + "Failed to send delete session response to mme for " + "sgw_s11_teid " TEID_FMT "\n", + teid); + } + OAILOG_FUNC_OUT(LOG_SGW_S8); } diff --git a/lte/gateway/c/oai/tasks/sgw_s8/sgw_s8_s11_handlers.h b/lte/gateway/c/oai/tasks/sgw_s8/sgw_s8_s11_handlers.h index 059f69b5064e..71659b15f910 100644 --- a/lte/gateway/c/oai/tasks/sgw_s8/sgw_s8_s11_handlers.h +++ b/lte/gateway/c/oai/tasks/sgw_s8/sgw_s8_s11_handlers.h @@ -24,3 +24,12 @@ void sgw_s8_handle_s11_create_session_request( void sgw_s8_handle_create_session_response( sgw_state_t* sgw_state, s8_create_session_response_t* session_rsp_p, imsi64_t imsi64); + +void sgw_s8_handle_modify_bearer_request( + sgw_state_t* state, + const itti_s11_modify_bearer_request_t* const modify_bearer_pP, + imsi64_t imsi64); + +void sgw_s8_handle_delete_session_response( + sgw_state_t* sgw_state, s8_delete_session_response_t* session_rsp_p, + imsi64_t imsi64); diff --git a/lte/gateway/c/oai/tasks/sgw_s8/sgw_s8_task.c b/lte/gateway/c/oai/tasks/sgw_s8/sgw_s8_task.c index d48e6780b42b..4afac9be6bfa 100644 --- a/lte/gateway/c/oai/tasks/sgw_s8/sgw_s8_task.c +++ b/lte/gateway/c/oai/tasks/sgw_s8/sgw_s8_task.c @@ -76,7 +76,23 @@ static int handle_message(zloop_t* loop, zsock_t* reader, void* arg) { sgw_state, &received_message_p->ittiMsg.s8_create_session_rsp, imsi64); } break; + case S11_MODIFY_BEARER_REQUEST: { + sgw_s8_handle_modify_bearer_request( + sgw_state, &received_message_p->ittiMsg.s11_modify_bearer_request, + imsi64); + } break; + case S11_DELETE_SESSION_REQUEST: { + sgw_s8_handle_s11_delete_session_request( + sgw_state, &received_message_p->ittiMsg.s11_delete_session_request, + imsi64); + } break; + + case S8_DELETE_SESSION_RSP: { + sgw_s8_handle_delete_session_response( + sgw_state, &received_message_p->ittiMsg.s8_delete_session_rsp, + imsi64); + } break; default: { OAILOG_DEBUG( LOG_SGW_S8, "Unkwnon message ID %d: %s\n", From e9884e7823e2d1626c8804eedbeeac04e76f4974 Mon Sep 17 00:00:00 2001 From: Ulas Kozat Date: Sun, 11 Apr 2021 20:43:56 -0700 Subject: [PATCH 91/91] Bug fix for missing IE in attach accept (#6084) Signed-off-by: Ulas Kozat --- lte/gateway/c/oai/tasks/nas/emm/Attach.c | 19 +++++-------------- 1 file changed, 5 insertions(+), 14 deletions(-) diff --git a/lte/gateway/c/oai/tasks/nas/emm/Attach.c b/lte/gateway/c/oai/tasks/nas/emm/Attach.c index 812389a59cdc..b958b3f94654 100644 --- a/lte/gateway/c/oai/tasks/nas/emm/Attach.c +++ b/lte/gateway/c/oai/tasks/nas/emm/Attach.c @@ -1797,11 +1797,8 @@ static int emm_send_attach_accept(emm_context_t* emm_context) { //---------------------------------------- REQUIREMENT_3GPP_24_301(R10_5_5_1_2_4__14); emm_sap.u.emm_as.u.establish.eps_network_feature_support = - calloc(1, sizeof(eps_network_feature_support_t)); - emm_sap.u.emm_as.u.establish.eps_network_feature_support->b1 = - _emm_data.conf.eps_network_feature_support[0]; - emm_sap.u.emm_as.u.establish.eps_network_feature_support->b2 = - _emm_data.conf.eps_network_feature_support[1]; + (eps_network_feature_support_t*) &_emm_data.conf + .eps_network_feature_support; /* * Delete any preexisting UE radio capabilities, pursuant to @@ -1810,8 +1807,7 @@ static int emm_send_attach_accept(emm_context_t* emm_context) { // Note: this is safe from double-free errors because it sets to NULL // after freeing, which free treats as a no-op. bdestroy_wrapper(&ue_mm_context_p->ue_radio_capability); - free_wrapper( - (void**) &emm_sap.u.emm_as.u.establish.eps_network_feature_support); + /* * Setup EPS NAS security data */ @@ -1972,14 +1968,9 @@ static int emm_attach_accept_retx(emm_context_t* emm_context) { "message\n", ue_id); emm_sap.u.emm_as.u.establish.eps_network_feature_support = - calloc(1, sizeof(eps_network_feature_support_t)); + (eps_network_feature_support_t*) &_emm_data.conf + .eps_network_feature_support; emm_sap.u.emm_as.u.data.new_guti = &emm_context->_guti; - emm_sap.u.emm_as.u.establish.eps_network_feature_support->b1 = - _emm_data.conf.eps_network_feature_support[0]; - emm_sap.u.emm_as.u.establish.eps_network_feature_support->b2 = - _emm_data.conf.eps_network_feature_support[1]; - free_wrapper( - (void**) &emm_sap.u.emm_as.u.establish.eps_network_feature_support); /* * Setup EPS NAS security data