From: Pieter Lexis Date: Wed, 26 Nov 2025 13:38:02 +0000 (+0100) Subject: feat(dnsdist): Allow passing RemoteLoggers to SetTrace action X-Git-Tag: rec-5.4.0-beta1~65^2~11 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=ec1267f47208c6166955310e8f0a916c2dcc37ba;p=thirdparty%2Fpdns.git feat(dnsdist): Allow passing RemoteLoggers to SetTrace action --- diff --git a/pdns/dnsdistdist/dnsdist-actions-definitions.yml b/pdns/dnsdistdist/dnsdist-actions-definitions.yml index a9ea1c3ca6..e80e3f990f 100644 --- a/pdns/dnsdistdist/dnsdist-actions-definitions.yml +++ b/pdns/dnsdistdist/dnsdist-actions-definitions.yml @@ -402,10 +402,18 @@ are processed after this action" description: "The TTL to use" - name: "SetTrace" description: "Enable or disable OpenTelemetry tracing for this query. Don't forget to also use a RemoteLog action to actually send the trace. Subsequent rules are processed after this action" + skip-cpp: true + skip-rust: true parameters: - name: "value" type: "bool" description: "Whether or not to enable tracing" + - name: remote_loggers + type: "Vec" + default: "" + description: | + The names of all the loggers that should be sent the protobuf messages that includes the tracing information. + This message will be sent once dnsdist is finished with the DNS transaction (i.e. the response has been sent, the query was dropped, or when a timeout occured). - name: "use_incoming_traceid" type: "bool" default: "false" diff --git a/pdns/dnsdistdist/dnsdist-actions-factory.cc b/pdns/dnsdistdist/dnsdist-actions-factory.cc index 1bd2a4ed42..ba3245acb0 100644 --- a/pdns/dnsdistdist/dnsdist-actions-factory.cc +++ b/pdns/dnsdistdist/dnsdist-actions-factory.cc @@ -23,6 +23,7 @@ #include #include #include +#include #include "dnsdist-actions-factory.hh" @@ -1680,8 +1681,8 @@ private: class SetTraceAction : public DNSAction { public: - SetTraceAction(bool value, std::optional useIncomingTraceID, std::optional incomingTraceIDOptionCode) : - d_value{value}, d_useIncomingTraceID(useIncomingTraceID), d_incomingTraceIDOptionCode(incomingTraceIDOptionCode) {}; + SetTraceAction(SetTraceActionConfiguration& config) : + d_value{config.value}, d_loggers(config.remote_loggers), d_useIncomingTraceID(config.use_incoming_traceid), d_incomingTraceIDOptionCode(config.trace_edns_option) {}; DNSAction::Action operator()([[maybe_unused]] DNSQuestion* dnsquestion, [[maybe_unused]] std::string* ruleresult) const override { @@ -1716,6 +1717,7 @@ public: } } } + dnsquestion->ids.ottraceLoggers = d_loggers; } #endif return Action::None; @@ -1728,6 +1730,9 @@ public: private: bool d_value; + + std::vector> d_loggers; + std::optional d_useIncomingTraceID; std::optional d_incomingTraceIDOptionCode; }; @@ -2506,6 +2511,11 @@ std::shared_ptr getLuaFFIResponseAction(dnsdist::actions::Lua } #ifndef DISABLE_PROTOBUF +std::shared_ptr getSetTraceAction(SetTraceActionConfiguration& config) +{ + return std::shared_ptr(new SetTraceAction(config)); +} + std::shared_ptr getRemoteLogAction(RemoteLogActionConfiguration& config) { return std::shared_ptr(new RemoteLogAction(config)); diff --git a/pdns/dnsdistdist/dnsdist-actions-factory.hh b/pdns/dnsdistdist/dnsdist-actions-factory.hh index 9c0a441aff..041d9c7fe1 100644 --- a/pdns/dnsdistdist/dnsdist-actions-factory.hh +++ b/pdns/dnsdistdist/dnsdist-actions-factory.hh @@ -122,5 +122,14 @@ std::shared_ptr getRemoteLogAction(RemoteLogActionConfiguration& conf std::shared_ptr getRemoteLogResponseAction(RemoteLogActionConfiguration& config); std::shared_ptr getDnstapLogAction(const std::string& identity, std::shared_ptr logger, std::optional alterFunc); std::shared_ptr getDnstapLogResponseAction(const std::string& identity, std::shared_ptr logger, std::optional alterFunc); + +struct SetTraceActionConfiguration +{ + bool value = false; + std::vector> remote_loggers; + bool use_incoming_traceid = false; + std::uint16_t trace_edns_option = 0; +}; +std::shared_ptr getSetTraceAction(SetTraceActionConfiguration& config); #endif /* DISABLE_PROTOBUF */ } diff --git a/pdns/dnsdistdist/dnsdist-configuration-yaml.cc b/pdns/dnsdistdist/dnsdist-configuration-yaml.cc index 112de6227c..b156353254 100644 --- a/pdns/dnsdistdist/dnsdist-configuration-yaml.cc +++ b/pdns/dnsdistdist/dnsdist-configuration-yaml.cc @@ -19,7 +19,9 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ +#include #include +#include #include "dnsdist-configuration-yaml.hh" @@ -1687,6 +1689,33 @@ std::shared_ptr getDnstapLogResponseAction([[maybe_unu #endif } +std::shared_ptr getSetTraceAction(const SetTraceActionConfiguration& config) +{ +#if defined(DISABLE_PROTOBUF) + throw std::runtime_error("Unable to create set trace action: protobuf support is disabled"); +#else + std::vector> loggers; + for (auto const& logger_name : config.remote_loggers) { + auto logger = dnsdist::configuration::yaml::getRegisteredTypeByName(std::string(logger_name)); + if (!logger && !(dnsdist::configuration::yaml::s_inClientMode || dnsdist::configuration::yaml::s_inConfigCheckMode)) { + throw std::runtime_error("Unable to find the protobuf logger named '" + std::string(logger_name) + "'"); + } + loggers.push_back(logger); + } + + dnsdist::actions::SetTraceActionConfiguration actionConfig{ + .value = config.value, + .remote_loggers = std::move(loggers), + .use_incoming_traceid = config.use_incoming_traceid, + .trace_edns_option = config.trace_edns_option, + }; + + auto action = dnsdist::actions::getSetTraceAction(actionConfig); + + return newDNSActionWrapper(std::move(action), config.name); +#endif +} + std::shared_ptr getRemoteLogAction(const RemoteLogActionConfiguration& config) { #if defined(DISABLE_PROTOBUF) diff --git a/pdns/dnsdistdist/dnsdist-idstate.cc b/pdns/dnsdistdist/dnsdist-idstate.cc index efb2c0af37..37fb3a8e1d 100644 --- a/pdns/dnsdistdist/dnsdist-idstate.cc +++ b/pdns/dnsdistdist/dnsdist-idstate.cc @@ -22,8 +22,10 @@ #include "dnsdist-idstate.hh" #include "dnsdist-doh-common.hh" +#include "dnsdist-protobuf.hh" #include "doh3.hh" #include "doq.hh" +#include "protozero.hh" #include #include @@ -63,11 +65,15 @@ InternalQueryState InternalQueryState::partialCloneForXFR() const InternalQueryState::~InternalQueryState() { #ifndef DISABLE_PROTOBUF + static thread_local string otPBBuf; otPBBuf.clear(); + std::string OTData; if (tracingEnabled && d_OTTracer != nullptr) { + pdns::ProtoZero::Message msg{otPBBuf}; - msg.setOpenTelemetryData(d_OTTracer->getOTProtobuf()); + OTData = d_OTTracer->getOTProtobuf(); + msg.setOpenTelemetryData(OTData); } for (auto const& msg_logger : delayedResponseMsgs) { @@ -80,6 +86,15 @@ InternalQueryState::~InternalQueryState() // that only contains the OTTrace data as a single bytes field msg_logger.second->queueData(msg_logger.first + otPBBuf); } + + static thread_local string minimalPBBuf; + minimalPBBuf.clear(); + pdns::ProtoZero::Message minimalMsg{minimalPBBuf}; + minimalMsg.setType(pdns::ProtoZero::Message::MessageType::DNSQueryType); + minimalMsg.setOpenTelemetryData(OTData); + for (auto const& msg_logger : ottraceLoggers) { + msg_logger->queueData(minimalPBBuf); + } #endif } diff --git a/pdns/dnsdistdist/dnsdist-idstate.hh b/pdns/dnsdistdist/dnsdist-idstate.hh index 7259f96f6a..fec10cefdb 100644 --- a/pdns/dnsdistdist/dnsdist-idstate.hh +++ b/pdns/dnsdistdist/dnsdist-idstate.hh @@ -201,6 +201,7 @@ public: std::unique_ptr d_protoBufData{nullptr}; #ifndef DISABLE_PROTOBUF std::vector>> delayedResponseMsgs; + std::vector> ottraceLoggers; #endif std::unique_ptr d_extendedError{nullptr}; std::optional tempFailureTTL{std::nullopt}; // 8 diff --git a/pdns/dnsdistdist/dnsdist-lua-actions.cc b/pdns/dnsdistdist/dnsdist-lua-actions.cc index 73a2ed4d23..3aeb391743 100644 --- a/pdns/dnsdistdist/dnsdist-lua-actions.cc +++ b/pdns/dnsdistdist/dnsdist-lua-actions.cc @@ -29,7 +29,10 @@ #include "dnsdist-rule-chains.hh" #include "dnstap.hh" #include "remote_logger.hh" +#include +#include #include +#include using responseParams_t = std::unordered_map>; @@ -235,6 +238,31 @@ void setupLuaActions(LuaContext& luaCtx) }); #ifndef DISABLE_PROTOBUF + luaCtx.writeFunction("SetTraceAction", [](bool value, std::optional>> remote_loggers, std::optional use_incoming_traceid, std::optional trace_edns_option) { + dnsdist::actions::SetTraceActionConfiguration config; + + if (remote_loggers) { + std::vector> loggers; + for (auto& remote_logger : remote_loggers.value()) { + if (remote_logger.second != nullptr) { + // avoids potentially-evaluated-expression warning with clang. + RemoteLoggerInterface& remoteLoggerRef = *remote_logger.second; + if (typeid(remoteLoggerRef) != typeid(RemoteLogger)) { + // We could let the user do what he wants, but wrapping PowerDNS Protobuf inside a FrameStream tagged as dnstap is logically wrong. + throw std::runtime_error(std::string("SetTraceAction only takes RemoteLogger.")); + } + loggers.push_back(remote_logger.second); + } + } + config.remote_loggers = loggers; + } + config.value = value; + config.trace_edns_option = trace_edns_option.value_or(65500); + config.use_incoming_traceid = use_incoming_traceid.value_or(false); + + return dnsdist::actions::getSetTraceAction(config); + }); + // Used for both RemoteLogAction and RemoteLogResponseAction static const std::array s_validIpEncryptMethods = {"legacy", "ipcrypt-pfx"}; diff --git a/pdns/dnsdistdist/docs/reference/ottrace.rst b/pdns/dnsdistdist/docs/reference/ottrace.rst index 61a00f0e56..9424c0b5a1 100644 --- a/pdns/dnsdistdist/docs/reference/ottrace.rst +++ b/pdns/dnsdistdist/docs/reference/ottrace.rst @@ -15,16 +15,15 @@ Per-query tracing can be enabled using the :func:`SetTraceAction`. However, :pro When tracing is enabled in the query, :program:`dnsdist` stores start and end times of certain (but not all) functions that are called during the lifetime of the query and the response. It is recommended to send the traces out through a RemoteLogger in ResponseRules, to capture as much information as possible. -.. note:: - At the moment, the ProtoBuf message is sent out **during** the processing of the response rules. - Hence, the traces are not complete. - There are plans to remedy this, but no timeline to do so. - Tracing uses more memory and CPU than usual query processing and it is recommended to enable tracing only for certain queries using specific :doc:`selectors `. Example configuration ===================== +In this configuration, the RemoteLogger is passed directly to the ``SetTrace`` action. +Doing this ensures that no matter what happens with the query (timeout, self-answered, cache-hit, dropped, answered by the backend), the trace will be sent out. +When sending the trace in this way, the Protobuf message is essentially empty apart from the OpenTelemetry Trace. + .. code-block:: yaml logging: @@ -41,18 +40,39 @@ Example configuration action: type: SetTrace value: true - response_rules: - - name: Do PB logging + remote_loggers: + - pblog + +Should you only want to receive the trace, including a fully filled Protobuf message, a `RemoteLog` can be used: + +.. code-block:: yaml + + logging: + open_telemetry_tracing: true + remote_logging: + protobuf_loggers: + - name: pblog + address: 127.0.0.1:5301 + query_rules: + - name: Enable tracing selector: + # Just as an example, in production don't trace all the queries type: All action: - type: RemoteLog - logger_name: pblog - # Delay ensures that the PB message is sent - # after the response is sent to client, instead - # of immediately. This ensures all Trace Spans - # have proper end timestamps. - delay: true + type: SetTrace + value: true + response_rules: + - name: Send PB log + selector: + type: All + action: + type: RemoteLog + logger_name: pblog + # Delay ensures that the PB message is sent + # after the response is sent to client, instead + # of immediately. This ensures all Trace Spans + # have proper end timestamps. + delay: true Passing Trace ID and Span ID to downstream servers ================================================== diff --git a/regression-tests.dnsdist/test_OpenTelemetryTracing.py b/regression-tests.dnsdist/test_OpenTelemetryTracing.py index 50a8aea89d..8a78372617 100644 --- a/regression-tests.dnsdist/test_OpenTelemetryTracing.py +++ b/regression-tests.dnsdist/test_OpenTelemetryTracing.py @@ -21,7 +21,13 @@ class DNSDistOpenTelemetryProtobufTest(test_Protobuf.DNSDistProtobufTest): self.assertTrue(msg.HasField("openTelemetry")) def sendQueryAndGetProtobuf( - self, useTCP=False, traceID="", spanID="", ednsTraceIDOpt=65500 + self, + useTCP=False, + traceID="", + spanID="", + ednsTraceIDOpt=65500, + dropped=False, + querySentByDNSDist=True, ): name = "query.ot.tests.powerdns.com." @@ -54,11 +60,14 @@ class DNSDistOpenTelemetryProtobufTest(test_Protobuf.DNSDistProtobufTest): else: (receivedQuery, receivedResponse) = self.sendUDPQuery(query, response) - self.assertTrue(receivedQuery) - self.assertTrue(receivedResponse) - receivedQuery.id = query.id - self.assertEqual(query, receivedQuery) - self.assertEqual(response, receivedResponse) + if querySentByDNSDist: + self.assertTrue(receivedQuery) + receivedQuery.id = query.id + self.assertEqual(query, receivedQuery) + + if not dropped: + self.assertTrue(receivedResponse) + self.assertEqual(response, receivedResponse) if self._protobufQueue.empty(): # let the protobuf messages the time to get there @@ -67,36 +76,28 @@ class DNSDistOpenTelemetryProtobufTest(test_Protobuf.DNSDistProtobufTest): # check the protobuf message corresponding to the UDP query return self.getFirstProtobufMessage() - -class DNSDistOpenTelemetryProtobufBaseTest(DNSDistOpenTelemetryProtobufTest): - def doTest(self, wasDelayed=False, useTCP=False, traceID="", spanID=""): - msg = self.sendQueryAndGetProtobuf(useTCP, traceID, spanID) - - self.assertTrue(msg.HasField("openTelemetryTraceID")) - self.assertNotEqual(msg.openTelemetryTraceID, "") - - if traceID != "": - self.assertEqual(msg.openTelemetryTraceID, binascii.a2b_hex(traceID)) - - self.assertTrue(msg.HasField("openTelemetryData")) - traces_data = opentelemetry.proto.trace.v1.trace_pb2.TracesData() - traces_data.ParseFromString(msg.openTelemetryData) - ot_data = google.protobuf.json_format.MessageToDict( - traces_data, preserving_proto_field_name=True - ) - - self.assertEqual(len(ot_data["resource_spans"]), 1) - self.assertEqual(len(ot_data["resource_spans"][0]["resource"]["attributes"]), 1) + def checkOTData( + self, + otData, + hasProcessResponseAfterRules=False, + useTCP=False, + hasRemoteLogResponseAction=True, + hasSelectBackendForOutgoingQuery=True, + hasResponse=True, + extraFunctions=set(), + ): + self.assertEqual(len(otData["resource_spans"]), 1) + self.assertEqual(len(otData["resource_spans"][0]["resource"]["attributes"]), 1) # Ensure all attributes exist - for field in ot_data["resource_spans"][0]["resource"]["attributes"]: + for field in otData["resource_spans"][0]["resource"]["attributes"]: self.assertIn(field["key"], ["service.name"]) # Ensure the values are correct # TODO: query.remote with port msg_scope_attr_keys = [ v["key"] - for v in ot_data["resource_spans"][0]["scope_spans"][0]["scope"][ + for v in otData["resource_spans"][0]["scope_spans"][0]["scope"][ "attributes" ] ] @@ -104,7 +105,7 @@ class DNSDistOpenTelemetryProtobufBaseTest(DNSDistOpenTelemetryProtobufTest): root_span_attr_keys = [ v["key"] - for v in ot_data["resource_spans"][0]["scope_spans"][0]["spans"][0][ + for v in otData["resource_spans"][0]["scope_spans"][0]["spans"][0][ "attributes" ] ] @@ -116,7 +117,7 @@ class DNSDistOpenTelemetryProtobufBaseTest(DNSDistOpenTelemetryProtobufTest): # No way to guess the test port, but check the rest of the values root_span_attrs = { v["key"]: v["value"]["string_value"] - for v in ot_data["resource_spans"][0]["scope_spans"][0]["spans"][0][ + for v in otData["resource_spans"][0]["scope_spans"][0]["spans"][0][ "attributes" ] if v["key"] not in ["query.remote.port"] @@ -131,7 +132,7 @@ class DNSDistOpenTelemetryProtobufBaseTest(DNSDistOpenTelemetryProtobufTest): ) msg_span_name = { - v["name"] for v in ot_data["resource_spans"][0]["scope_spans"][0]["spans"] + v["name"] for v in otData["resource_spans"][0]["scope_spans"][0]["spans"] } funcs = { @@ -139,23 +140,54 @@ class DNSDistOpenTelemetryProtobufBaseTest(DNSDistOpenTelemetryProtobufTest): "applyRulesToQuery", "Rule: Enable tracing", "applyRulesChainToQuery", - "selectBackendForOutgoingQuery", - "processResponse", "applyRulesToResponse", - "Rule: Do PB logging", } + if hasSelectBackendForOutgoingQuery: + funcs.add("selectBackendForOutgoingQuery") + + if hasResponse: + funcs.add("processResponse") + + if hasRemoteLogResponseAction: + funcs.add("Rule: Do PB logging") + if useTCP: funcs.add("IncomingTCPConnectionState::handleQuery") else: funcs.add("processUDPQuery") - funcs.add("assignOutgoingUDPQueryToBackend") + if hasSelectBackendForOutgoingQuery: + funcs.add("assignOutgoingUDPQueryToBackend") - if wasDelayed: + if hasProcessResponseAfterRules: funcs.add("processResponseAfterRules") + funcs = funcs.union(extraFunctions) + self.assertSetEqual(msg_span_name, funcs) + +class DNSDistOpenTelemetryProtobufBaseTest(DNSDistOpenTelemetryProtobufTest): + def doTest( + self, hasProcessResponseAfterRules=False, useTCP=False, traceID="", spanID="" + ): + msg = self.sendQueryAndGetProtobuf(useTCP, traceID, spanID) + + self.assertTrue(msg.HasField("openTelemetryTraceID")) + self.assertNotEqual(msg.openTelemetryTraceID, "") + + if traceID != "": + self.assertEqual(msg.openTelemetryTraceID, binascii.a2b_hex(traceID)) + + self.assertTrue(msg.HasField("openTelemetryData")) + traces_data = opentelemetry.proto.trace.v1.trace_pb2.TracesData() + traces_data.ParseFromString(msg.openTelemetryData) + ot_data = google.protobuf.json_format.MessageToDict( + traces_data, preserving_proto_field_name=True + ) + + self.checkOTData(ot_data, hasProcessResponseAfterRules, useTCP) + traceId = base64.b64encode(msg.openTelemetryTraceID).decode() for msg_span in ot_data["resource_spans"][0]["scope_spans"][0]["spans"]: self.assertEqual( @@ -275,7 +307,7 @@ response_rules: self.doTest(True) def testTCP(self): - self.doTest(wasDelayed=True, useTCP=True) + self.doTest(hasProcessResponseAfterRules=True, useTCP=True) class TestOpenTelemetryTracingBaseDelayLua(DNSDistOpenTelemetryProtobufBaseTest): @@ -297,7 +329,7 @@ addResponseAction(AllRule(), RemoteLogResponseAction(rl, nil, false, {}, {}, tru self.doTest(True) def testTCP(self): - self.doTest(wasDelayed=True, useTCP=True) + self.doTest(hasProcessResponseAfterRules=True, useTCP=True) class TestOpenTelemetryTracingUseIncomingYAML(DNSDistOpenTelemetryProtobufBaseTest): @@ -359,7 +391,7 @@ newServer{address="127.0.0.1:%d"} rl = newRemoteLogger('127.0.0.1:%d') setOpenTelemetryTracing(true) -addAction(AllRule(), SetTraceAction(true, true), {name="Enable tracing"}) +addAction(AllRule(), SetTraceAction(true, {}, true), {name="Enable tracing"}) addResponseAction(AllRule(), RemoteLogResponseAction(rl, nil, false, {}, {}, false), {name="Do PB logging"}) """ @@ -494,3 +526,153 @@ addResponseAction(AllRule(), RemoteLogResponseAction(rl)) def testEnabledButUnset(self): self.doTest() + + +class TestOpenTelemetryTracingBaseYAMLIncludedRemoteLoggerDropped( + DNSDistOpenTelemetryProtobufTest +): + _yaml_config_params = [ + "_testServerPort", + "_protobufServerPort", + ] + _yaml_config_template = """--- +logging: + open_telemetry_tracing: true + +backends: + - address: 127.0.0.1:%d + protocol: Do53 + +remote_logging: + protobuf_loggers: + - name: pblog + address: 127.0.0.1:%d + +query_rules: + - name: Enable tracing + selector: + type: All + action: + type: SetTrace + value: true + remote_loggers: + - pblog + +response_rules: + - name: Drop + selector: + type: All + action: + type: Drop +""" + + def doTest(self, useTCP=False): + msg = self.sendQueryAndGetProtobuf(useTCP=useTCP, dropped=True) + traces_data = opentelemetry.proto.trace.v1.trace_pb2.TracesData() + traces_data.ParseFromString(msg.openTelemetryData) + ot_data = google.protobuf.json_format.MessageToDict( + traces_data, preserving_proto_field_name=True + ) + + self.checkOTData( + ot_data, + hasProcessResponseAfterRules=False, + hasRemoteLogResponseAction=False, + useTCP=useTCP, + extraFunctions={ + "Rule: Drop", + }, + ) + + def testBasic(self): + self.doTest(False) + + def testTCP(self): + self.doTest(True) + + +class TestOpenTelemetryTracingBaseLuaIncludedRemoteLoggerDropped( + TestOpenTelemetryTracingBaseYAMLIncludedRemoteLoggerDropped +): + _yaml_config_params = [] + _yaml_config_template = "" + + _config_params = [ + "_testServerPort", + "_protobufServerPort", + ] + _config_template = """ +newServer{address="127.0.0.1:%d"} +rl = newRemoteLogger('127.0.0.1:%d') +setOpenTelemetryTracing(true) + +addAction(AllRule(), SetTraceAction(true, {rl}), {name="Enable tracing"}) +addResponseAction(AllRule(), DropResponseAction(), {name="Drop"}) +""" + + +class TestOpenTelemetryTracingBaseYAMLIncludedRemoteLoggerSpoofed( + DNSDistOpenTelemetryProtobufTest +): + _yaml_config_params = [ + "_testServerPort", + "_protobufServerPort", + ] + _yaml_config_template = """--- +logging: + open_telemetry_tracing: true + +backends: + - address: 127.0.0.1:%d + protocol: Do53 + +remote_logging: + protobuf_loggers: + - name: pblog + address: 127.0.0.1:%d + +query_rules: + - name: Enable tracing + selector: + type: All + action: + type: SetTrace + value: true + remote_loggers: + - pblog + - name: Spoof A record + selector: + type: All + action: + type: Spoof + ips: + - 192.0.2.1 +""" + + def doTest(self, useTCP=False): + msg = self.sendQueryAndGetProtobuf( + useTCP=useTCP, querySentByDNSDist=False, dropped=True + ) + traces_data = opentelemetry.proto.trace.v1.trace_pb2.TracesData() + traces_data.ParseFromString(msg.openTelemetryData) + ot_data = google.protobuf.json_format.MessageToDict( + traces_data, preserving_proto_field_name=True + ) + + self.checkOTData( + ot_data, + hasProcessResponseAfterRules=False, + hasRemoteLogResponseAction=False, + useTCP=useTCP, + hasSelectBackendForOutgoingQuery=False, + hasResponse=False, + extraFunctions={ + "Rule: Spoof A record", + }, + ) + + def testBasic(self): + self.doTest() + + def testTCP(self): + self.doTest(useTCP=True)