Implementing Managed Modules
Applies To: System Center 2012 - Operations Manager
[This topic is pre-release documentation and is subject to change in future releases. Blank topics are included as placeholders.]
This section contains conceptual information about developing managed modules by using the System Center 2012 – Operations Manager software development kit (SDK). The topics in this section describe how your custom module will interact with the health service.
You should create a custom module only when the modules that ship with System Center 2012 – Operations Manager are insufficient for your needs. Consult the Module Types Reference to see if a pre-existing module will satisfy your needs before you begin to develop your own modules.
In order to develop a custom managed module, you implement a .NET assembly that contains the managed definition of your module and you define the new module type in a management pack. For procedures for implementing managed modules, see How to Implement a Write Action Module, How to Implement a Probe Module, How to Implement a Data Source Module, or How to Implement a Condition Detection Module.
For a full .NET code sample, download the System Center 2012 – Operations Manager Managed Module Samples source code.
Managed Module Types
This section describes the different types of managed modules that you can create.
Data Source Module Type
A data source module type generates data using some form of instrumentation of some timed trigger action. For example, a data source may provide events from a specific Windows event log, or it could poll Windows performance counters every 10 minutes for a specific performance counter.
A data source takes no input data, and it provides one output data stream. The module type normally requires some configuration to specify the behavior, for example, a computer name and log name, in the case of the Windows event log data source.
You should always try to use one of the existing data source modules before implementing your own. One of the scenarios where you would need to implement your own data source module would be collecting instrumentation information from a component that exposes a C++ or .NET interface only, rather than performance counters or event log entries. Consult the Module Types Reference to see if a pre-existing module will satisfy your needs before you begin to develop your own modules.
Condition Detection Module Type
A condition detection module type is used to filter the incoming data in some way. There are many types of possible filters, for example, a simple filter on the input data, consolidation of like data items, correlation between multiple inputs, averaging and optimization of performance data, and so on. A condition detection module type can take one or more input data streams and provide a single output data steam.
Many of the data sources that ship “out of the box” already contain an expression filter in their composite definition. Therefore, it may not be necessary to use a condition detection module in your workflow definitions.
Probe Action Module Type
A probe action module type uses some input data to provide some output data, and it usually requires some configuration to specify its behavior. A probe action interrogates a monitored entity in some way, but it should not affect system state in any way. An example would be running a script that uses an application API to generate data or querying Windows Management Instrumentation (WMI) to get data. Often, a probe action is used in conjunction with a scheduler data source to run an action on a timed basis. The probe action module type may or may not use the input data item to affect the behavior. If the data item is not used, the input data should be defined as trigger only. An incoming data item triggers the probe to execute, but the data is not used. Sometimes, the data may be used; for example, if the probe is triggered by a Windows event, it may be required to pass the event ID to the probe action as configuration.
A probe action provides a single output data stream.
Operations Manager cannot determine if the probe action is being used to change system state in some way; for example, if you run a script that is defined using a probe action module type, you could be changing state in some way in your script. It is up to the management pack author to adhere to the module type definition guidance. If you are changing system state, use a write action module type instead.
Write Action Module Type
A write action module type takes a single input data stream and uses it in conjunction with a configuration to affect system state in some way. This change can be in the monitored system or in Operations Manager itself. For example, this module may be used to run a script that changes something, write data into the Operations Manager database, or generate an alert.
A write action may or may not output data. This data cannot be passed to any other module because the write action is the last module in a workflow. However, the data may be sent to the Operations Manager database. An example is running a command that outputs data to standard output. This data may be useful to the operator that executed the command; it is returned to the operator and stored as task output.
Managed Module Development Concepts
This section provides guidance regarding how your custom module should behave while running in the Operations Manager environment.
All modules run in a shared environment with many other modules. Your module should help minimize the system resources it uses so that the health service does not have a large impact on the system. Your module should not hold on to threads because the supply of threads for modules is limited and much smaller than the number of modules that are being run. Whenever your module performs an I/O bound operation, it should use an asynchronous version of that operation. This makes it possible for other modules to perform work on the thread that is released by the blocked module. If your module has CPU bound work to perform, it should always do this work on the thread that the health service called it on. When the health service calls in to your module, it will always do so on a worker thread from the set of threads that is allocated to modules. If your module requires dedicated threads for some purpose, it may create its own threads.
The health service gives your module the ability to persist its state. Persistence is necessary so that information about what data has already been processed is not lost when the module is not running, for example, when the computer is rebooted. The ModuleHost.SaveState method makes it possible for your module to save its state whenever it needs to. This method takes an opaque buffer that will be safely written to disk by the health service. When your module starts up, it may be provided a previously saved buffer from which to initialize its state. If the health service provides your module with a state block at startup, it must use that block to initialize its state. The health service decides how the state is actually persisted to disk and what the lifetime of the persisted data is. You do not have to consider the versioning of persistence data. If your module or its configuration changes, the health service will clear the persistence data for the module. The persistence support that the health service provides does not allow incremental updates of state. Your module must always save its entire state. Your module should not implement its own persistence support.
The health service does not support a transaction model for data flowing through the system. Instead, a model guaranteeing that every piece of data is processed at least once is supported. When your module produces a piece of data, it is responsible for remembering that data until it receives an acknowledgment that the consumer of that data has accepted responsibility for the data. When the producer receives acknowledgment for a piece of data, it is no longer responsible for maintaining state about that data item. A consumer of a data item must signal acknowledgment only when it has ensured that the data item it accepted has been persisted or processed. A stateless module should not persist a data item itself but instead acknowledge a data item after its own output has been acknowledged. A module must not request a new data item from its input streams until it has acknowledged the current data item. A module must not post a new data item to its output stream until it has received the acknowledgment for the last data item it sent. Check the ModuleHost.IsGuaranteedDeliveryRequested flag to see if persistence has been requested for a particular workflow. If persistence has not been requested, the module should not request acknowledgments on data items that it generates.
An example of a persisted data source is the event log. The state that is associated with the module is the bookmark for the next event to read. Initially, the event log data source points itself at the most recent event in the log, which we call Event 42. The data source saves that it is at Event 42, and it posts that event to its output port. When it receives the acknowledgment for that data item, it updates its bookmark to Event 43, saves its state, and then sends the next event out. It is possible that duplicate data will be sent. If the health service were to crash right before the data source got the acknowledgment for Event 42, it would send that event again after startup, even though the next module already processed that data item. It is not possible to lose data if modules correctly persist the data before acknowledging it.
When your module receives a data item, the health service may ask it to report when it has completed processing the data item. A stateful module may report that it has completed processing the data item at any time. A stateless module must report completion when it has finished all processing of the data item. This includes posting any output data items, sending acknowledgments for received data items, and waiting for any forwarded acknowledgments to complete. After the stateless module has signaled completion, it may be unloaded by the engine if the engine has no more data items for that module to process. A module must not request another data item from its input ports until it signals completion on the last data item that it received.
Startup and Shutdown Sequences
The first thing the health service will do when starting your module is to create an instance of the object implementing your module. Within the constructor, modules should perform whatever actions are necessary to prepare to start running. At this point the module may not request data from its input ports or post data to its output port. After the constructor method has completed, the engine will call the Start method on your module. At this point the module may request data from its input ports and may post data to its output ports.
A graceful shutdown begins by the engine calling NotifyStop on your module. This call may happen at any time after your module returns from its Start method. Your module must be thread-safe between this call and receiving data on its input ports because both may happen in parallel, and the module must at this point begin whatever operations are necessary to shut it down. Your module may still process any outstanding data items and post data to its output stream, and it should not request any more data through its input ports, although it may still receive data through its input port if the data was already scheduled by the engine to be delivered. When your module has finished stopping, it must signal this to the engine through the callback that is provided in the NotifyStop method. After your module has signaled that it has completed stopping, it must not post any data from its input ports or post data to its output port, although it may still receive an acknowledgment for an outstanding data item that is sent through its output port.
After your module is in a stopped state, the engine will notify it that it is about to be unloaded by calling Shutdown on the module. After Shutdown has been called, your module can no longer expect to receive acknowledgments for its output port.
During the shutdown call, your module should save any state that it wants to have persisted by calling the ModuleHost.SaveState method.
After the module returns from shutdown, it must not call in any methods on the interfaces that are provided by the engine. If your module calls into these methods after shutdown, it will throw an InvalidOperationException exception in managed code. After your module has returned from shutdown, it will be unloaded by the engine. Because of the asynchronous nature of the health service, it is possible that your module may receive data items or acknowledgment callbacks after it has been stopped or shut down. If this happens, your module must correctly ignore the stale data.
The engine may immediately unload your module rather than going through the full shutdown process. If this is the case, the engine will simply unload your module. Your module must not corrupt the process that it is running in if the engine chooses to shut down the module in this fashion.