Sensors are a way to integrate external systems and events with BWC.
Sensors are pieces of Python code that either periodically poll some
external system, or passively wait for inbound events. They then inject
triggers into BWC, which can be matched by rules, for potential
action execution.

Sensors are written in Python, and have to follow the BWC-defined
sensor interface requirements to run successfully.

Triggers are BWC constructs that identify the incoming events to BWC.
A trigger is a tuple of type (string) and optional parameters (object).
Rules are written to work with triggers. Sensors typically register triggers
though this is not strictly required. For example, there is a generic
webhooks trigger registered with BWC, which does not require
a custom sensor.

fromst2reactor.sensor.baseimportSensorclassSampleSensor(Sensor):""" * self.sensor_service - provides utilities like - get_logger() - returns logger instance specific to this sensor. - dispatch() for dispatching triggers into the system. * self._config - contains parsed configuration that was specified as config.yaml in the pack. """defsetup(self):# Setup stuff goes here. For example, you might establish connections# to external system once and reuse it. This is called only once by the system.passdefrun(self):# This is where the crux of the sensor work goes.# This is called once by the system.# (If you want to sleep for regular intervals and keep# interacting with your external system, you'd inherit from PollingSensor.)# For example, let's consider a simple flask app. You'd run the flask app here.# You can dispatch triggers using sensor_service like so:# self.sensor_service(trigger, payload, trace_tag)# # You can refer to the trigger as dict# # { "name": ${trigger_name}, "pack": ${trigger_pack} }# # or just simply by reference as string.# # i.e. dispatch(${trigger_pack}.${trigger_name}, payload)# # E.g.: dispatch('examples.foo_sensor', {'k1': 'stuff', 'k2': 'foo'})# # trace_tag is a tag you would like to associate with the dispacthed TriggerInstance# # Typically the trace_tag is unique and a reference to an external event.passdefcleanup(self):# This is called when the st2 system goes down. You can perform cleanup operations like# closing the connections to external system here.passdefadd_trigger(self,trigger):# This method is called when trigger is createdpassdefupdate_trigger(self,trigger):# This method is called when trigger is updatedpassdefremove_trigger(self,trigger):# This method is called when trigger is deletedpass

This is a bare minimum version of what a sensor looks like. For a more
complete implementation of a sensor that actually injects triggers
into the system, look at the examples section below.

If you want a sensor that polls an external system at regular intervals, you
can use a PollingSensor instead of Sensor as the base class.

fromst2reactor.sensor.baseimportPollingSensorclassSamplePollingSensor(PollingSensor):""" * self.sensor_service - provides utilities like get_logger() for writing to logs. dispatch() for dispatching triggers into the system. * self._config - contains configuration that was specified as config.yaml in the pack. * self._poll_interval - indicates the interval between two successive poll() calls. """defsetup(self):# Setup stuff goes here. For example, you might establish connections# to external system once and reuse it. This is called only once by the system.passdefpoll(self):# This is where the crux of the sensor work goes.# This is called every self._poll_interval.# For example, let's assume you want to query ec2 and get# health information about your instances:# some_data = aws_client.get('')# payload = self._to_payload(some_data)# # _to_triggers is something you'd write to convert the data format you have# # into a standard python dictionary. This should follow the payload schema# # registered for the trigger.# self.sensor_service.dispatch(trigger, payload)# # You can refer to the trigger as dict# # { "name": ${trigger_name}, "pack": ${trigger_pack} }# # or just simply by reference as string.# # i.e. dispatch(${trigger_pack}.${trigger_name}, payload)# # E.g.: dispatch('examples.foo_sensor', {'k1': 'stuff', 'k2': 'foo'})# # trace_tag is a tag you would like to associate with the dispacthed TriggerInstance# # Typically the trace_tag is unique and a reference to an external event.passdefcleanup(self):# This is called when the st2 system goes down. You can perform cleanup operations like# closing the connections to external system here.passdefadd_trigger(self,trigger):# This method is called when trigger is createdpassdefupdate_trigger(self,trigger):# This method is called when trigger is updatedpassdefremove_trigger(self,trigger):# This method is called when trigger is deletedpass

Polling Sensors also require a poll_interval parameter in the metadata file.
This defines (in seconds) how frequently the poll() method is called.

In addition to the trigger injection, the sensor service also provides
functionality for reading and manipulating the datastore.

Each sensor has a namespace which is local to it and by default, all the
datastore operations operate on the keys in that sensor-local namespace.
If you want to operate on a global namespace, you need to pass the local=False
argument to the datastore manipulation method.

Among other reasons, this functionality is useful if you want to persist
temporary data between sensor runs.

A good example of this functionality in action is TwitterSensor. The Twitter
sensor persists the ID of the last processed tweet after every poll in the
datastore. This way if the sensor is restarted or if it crashes, the sensor
can resume from where it left off without injecting duplicate triggers into
the system.

Once you write your own sensor, the following steps can be used to run
your sensor for the first time:

Place the sensor Python file and YAML metadata in the default pack in
/opt/stackstorm/packs/default/sensors/. Alternatively, you can create a custom
pack in /opt/stackstorm/packs/ with the appropriate pack structure (see
Create and Contribute a Pack) and place the sensor artifacts there.

Register the sensor with st2ctl. Look out for any errors in sensor registration.

st2ctl reload --register-all

If there are errors in registration, fix the errors and re-register them using
st2ctlreload--register-all.

If registration is successful, the sensor will run automatically.

Once you like your sensor, you can promote it to a pack (if required) by creating a pack in
/opt/stackstorm/packs/${pack_name} and moving the sensor artifacts (YAML and Python) to
/opt/stackstorm/packs/${pack_name}/sensors/. See Create and Contribute a Pack for how to create a pack.