Using the Python OpenDXL Client
- 0 Comments
- 28,693 Views
This article walks through the process of installing, provisioning, and running a subset of the samples that are included with the OpenDXL Python Client.
The samples examined in this article demonstrate how to use the two distinct messaging models that are supported by DXL. A publish/subscribe event-based model and a service-based model with point-to-point (request/response) communication.
Client Installation and Provisioning
The following steps detail how to install and provision the OpenDXL Python Client:
- Download the latest version of the OpenDXL Python Client
- Extract the release .zip file
- Follow the client installation instructions to install the client
- The client libraries are in the lib folder of the extracted release
- Follow the steps to provision the client for use with the samples
- The steps will vary based on the type of DXL fabric being connected to
The DXL fabric supports an event-based communication model. This model is typically referred to as “publish/subscribe” wherein clients register interest by subscribing to a particular topic and publishers periodically send events to that topic. The event is delivered by the DXL fabric to all of the currently subscribed clients for the topic. Therefore, a single event sent can reach multiple clients (one-to-many). It is important to note that in this model the client passively receives events when they are sent by a publisher.
For example, McAfee Advanced Threat Defense (ATD) servers send events to the topic /mcafee/event/atd/file/report when they have successfully determined the reputation of a submitted file. Any clients currently subscribed to this topic will receive the report and can take immediate action.
A "basic event sample" is included with the OpenDXL Python Client SDK that demonstrates sending and receiving 1000 events via the Python client.
To run the sample execute the following from the root directory of the extracted OpenDXL Python Client SDK:
The output should appear similar to the following (1000 events are received and timing information is displayed).
This section walks through the different portions of code that comprise the "basic event sample" in detail. Full code for the sample is available within the extracted SDK or via the OpenDXL Python Client GitHub Repository.
As shown above, several variables are defined in the header of the "basic event sample":
EVENT_TOPIC: The topic that the events will be sent (and received) on:
- TOTAL_EVENTS: The total number of events to send (1000)
- event_count_condition: While not critical to understanding the basic DXL event concepts, this condition (and associated lock) is used to control how incoming events are tracked (they will be received on multiple threads)
- event_count: Tracks the number of events that have been received
The next portion of code (shown above) connects to the DXL fabric. The first step is to create a DxlClientConfig object that contains the information necessary to connect to a DXL fabric. In this particular sample, the create_dxl_config_from_file method was used to create a configuration object from a configuration file.
The next line of code creates a DxlClient object by passing the previously created configuration object to its constructor. It is also important to note that the client is created using the Python with statement. While not necessary, the withstatement provides a simple way to control the lifetime of the client object and will automatically clean up any client-related resources when the client block ends (you do not need to explicitly call disconnect or destroy).
In the final line of code above, the connect method is invoked to connect to the DXL fabric.
- # Create and add event listener
- class MyEventCallback(EventCallback):
- def on_event(self, event):
- with event_count_condition:
- # Print the payload for the received event
- print "Received event: " + event.payload.decode()
- # Increment the count
- event_count += 1
- # Notify that the count was increment
- # Register the callback with the client
- client.add_event_callback(EVENT_TOPIC, MyEventCallback())
The next portion of code (shown above) registers an event callback that will receive events that are published to the /isecg/sample/basiceventtopic.
The first step is to derive an event callback (MyEventCallback) from the EventCallback base class and override the on_event method. This method will be invoked for each event that is sent to the DXL fabric for topics that are associated with the callback. The main portion of this method displays the payload of the event received and increments the total count of events. While not critical to understanding the basic DXL event concepts, the event_count_condition condition is used to limit the increment code to one thread at a time. Further, the condition is used to notify that another event has been received.
The final line in this portion of code registers the newly created callback with the /isecg/sample/basicevent topic by invoking the add_event_callback method on the client.
The next portion of code (shown above) sends out 1000 events on the /isecg/sample/basicevent topic.
The body of the for loop is iterated for TOTAL_EVENTS times (1000). The first step in the body of the for loop is to construct a new Event that will be sent to the /isecg/sample/basicevent topic. Next, a payload is set on the event that contains the index of the loop (from 0 to 999). The final line of the loop body sends the event to the DXL fabric by invoking the send_event method on the client.
The final portion of code waits for all of the events to be received by the registered callback.
While not critical to understanding the basic DXL event concepts, the event_count_condition condition is used to wait for TOTAL_EVENTS to be received. Once all of the events have been received the elapsed time for the sample is displayed.
The DXL fabric allows for “services” to be registered and exposed that respond to requests sent by invoking clients. This communication is point-to-point (one-to-one), meaning the communication is solely between an invoking client and the service that is being invoked. It is important to note that in this model the client actively invokes the service by sending it requests.
For example, the McAfee Threat Intelligence Exchange (TIE) service is exposed via DXL allowing for DXL clients to actively request reputations for files and certificates.
A "basic service sample" is included with the OpenDXL Python Client SDK that demonstrates registering and invoking a DXL service.
To run the sample execute the following from the root directory of the extracted OpenDXL Python Client SDK:
The output should appear similar to the following (a request is received by the service and it delivers a response back to the invoking client).
This section walks through the different portions of code that comprise the "basic service sample" in detail. Full code for the sample is available within the extracted SDK or via the OpenDXL Python Client GitHub Repository.
As shown above, a variable is defined that contains a topic to associate with the service. Multiple topics can be associated with a service, each topic represents a different "method" that can be invoked by remote DXL clients.
The next portion of the sample connects to the DXL fabric. This code is identical to the "Connecting to the DXL fabric" portion detailed in the "Event-based Communication" sample above and therefore is not detailed here.
- # Create incoming request callback
- class MyRequestCallback(RequestCallback):
- def on_request(self, request):
- # Extract information from request
- print "Service received request payload: " + request.payload.decode()
- # Create the response message
- res = Response(request)
- # Populate the response payload
- res.payload = "pong".encode()
- # Send the response
- # Create service registration object
- info = ServiceRegistrationInfo(client, "myService")
- # Add a topic for the service to respond to
- info.add_topic(SERVICE_TOPIC, MyRequestCallback())
- # Register the service with the fabric (wait up to 10 seconds for registration to complete)
- client.register_service_sync(info, 10)
The next portion of code (shown above) registers a DXL service that will respond to requests that are received on the /isecg/sample/basicservice topic.
The first step is to derive a request callback (MyRequestCallback) from the RequestCallback base class and override the on_request method. This method will be invoked for each request that is sent to the DXL fabric for the service topic that is associated with the callback.
The implementation of the on_request method displays the payload of the Request message that was received. Next, it creates a Response message that will be sent back to the invoking client. The payload of the Response message is set to "pong". The last line of this method sends the response back to the invoking client by passing the newly created Response object to the send_response method of the client.
The final three lines in this portion create a service registration, associate the newly created request callback with a service topic, and register the service with the DXL fabric. A ServiceRegistrationInfo object is created which represents the service (named "myService") to register. The service topic /isecg/sample/basicservice is associated with the request callback by invoking the add_topic method of the service registration object. Finally, the service is registered with the DXL fabric by passing the service registration object to the register_service_sync method of the client.
- # Create the request message
- req = Request(SERVICE_TOPIC)
- # Populate the request payload
- req.payload = "ping".encode()
- # Send the request and wait for a response (synchronous)
- res = client.sync_request(req)
- # Extract information from the response (if an error did not occur)
- if res.message_type != Message.MESSAGE_TYPE_ERROR:
- print "Client received response payload: " + res.payload.decode()
The next portion of code (shown above) invokes the DXL service that was registered in the previous code portion and displays the information contained in the received response.
The first step is to create a Request message that will be sent to the /isecg/sample/basicservice topic. The payload of the Request object is set to "ping".
Next, the service is invoked by passing the newly created Request object to the sync_request method of the client. This method will wait until a Response is received from the service or a timeout occurs.
Finally, the message_type is checked to ensure the Response received is not an ErrorResponse. If it is not an error, the payload associated with the Response message is displayed.
Refer to the Python OpenDXL Bootstrap Application guide to learn how you can significantly reduce the time necessary to create an OpenDXL solution.