Policies

Policies are JSON documents that define the following:

  • The states that an index can be in, including the default state for new indexes. For example, you might name your states “hot,” “warm,” “delete,” and so on. For more information, see States.
  • Any actions that you want the plugin to take when an index enters a state, such as performing a rollover. For more information, see Actions.
  • The conditions that must be met for an index to move into a new state, known as transitions. For example, if an index is more than eight weeks old, you might want to move it to the “delete” state. For more information, see Transitions.

In other words, a policy defines the states that an index can be in, the actions to perform when in a state, and the conditions that must be met to transition between states.

You have complete flexibility in the way you can design your policies. You can create any state, transition to any other state, and specify any number of actions in each state.

This table lists the relevant fields of a policy.

FieldDescriptionTypeRequiredRead Only
policy_idThe name of the policy.stringYesYes
descriptionA human-readable description of the policy.stringYesNo
ism_templateSpecify an ISM template pattern that matches the index to apply the policy.nested list of objectsNoNo
last_updated_timeThe time the policy was last updated.timestampYesYes
error_notificationThe destination and message template for error notifications. The destination could be Amazon Chime, Slack, or a webhook URL.objectNoNo
default_stateThe default starting state for each index that uses this policy.stringYesNo
statesThe states that you define in the policy.nested list of objectsYesNo


States

A state is the description of the status that the managed index is currently in. A managed index can be in only one state at a time. Each state has associated actions that are executed sequentially on entering a state and transitions that are checked after all the actions have been completed.

This table lists the parameters that you can define for a state.

FieldDescriptionTypeRequired
nameThe name of the state.stringYes
actionsThe actions to execute after entering a state. For more information, see Actions.nested list of objectsYes
transitionsThe next states and the conditions required to transition to those states. If no transitions exist, the policy assumes that it’s complete and can now stop managing the index. For more information, see Transitions.nested list of objectsYes

Actions

Actions are the steps that the policy sequentially executes on entering a specific state.

ISM executes actions in the order in which they are defined. For example, if you define actions [A,B,C,D], ISM executes action A, and then goes into a sleep period based on the cluster setting plugins.index_state_management.job_interval. Once the sleep period ends, ISM continues to execute the remaining actions. However, if ISM cannot successfully execute action A, the operation ends, and actions B, C, and D do not get executed.

Optionally, you can define an action’s timeout period, which, if exceeded, forcibly fails the action. For example, if timeout is set to 1d, and ISM has not completed the action within one day, even after retries, the action fails.

This table lists the parameters that you can define for an action.

ParameterDescriptionTypeRequiredDefault
timeoutThe timeout period for the action. Accepts time units for minutes, hours, and days.time unitNo-
retryThe retry configuration for the action.objectNoSpecific to action

The retry operation has the following parameters:

ParameterDescriptionTypeRequiredDefault
countThe number of retry counts.numberYes-
backoffThe backoff policy type to use when retrying. Valid values are Exponential, Constant, and Linear.stringNoExponential
delayThe time to wait between retries. Accepts time units for minutes, hours, and days.time unitNo1 minute

The following example action has a timeout period of one hour. The policy retries this action three times with an exponential backoff policy, with a delay of 10 minutes between each retry:

  1. "actions": {
  2. "timeout": "1h",
  3. "retry": {
  4. "count": 3,
  5. "backoff": "exponential",
  6. "delay": "10m"
  7. }
  8. }

For a list of available unit types, see Supported units.

ISM supported operations

ISM supports the following operations:

force_merge

Reduces the number of Lucene segments by merging the segments of individual shards. This operation attempts to set the index to a read-only state before starting the merging process.

ParameterDescriptionTypeRequired
max_num_segmentsThe number of segments to reduce the shard to.numberYes
  1. {
  2. "force_merge": {
  3. "max_num_segments": 1
  4. }
  5. }

read_only

Sets a managed index to be read only.

  1. {
  2. "read_only": {}
  3. }

read_write

Sets a managed index to be writeable.

  1. {
  2. "read_write": {}
  3. }

replica_count

Sets the number of replicas to assign to an index.

ParameterDescriptionTypeRequired
number_of_replicasDefines the number of replicas to assign to an index.numberYes
  1. {
  2. "replica_count": {
  3. "number_of_replicas": 2
  4. }
  5. }

For information about setting replicas, see Primary and replica shards.

shrink

Allows you to reduce the number of primary shards in your indexes. With this action, you can specify:

  • The number of primary shards that the target index should contain.
  • A max shard size for the primary shards in the target index.
  • Specify a percentage to shrink the number of primary shards in the target index.
  1. "shrink": {
  2. "num_new_shards": 1,
  3. "target_index_name_template": {
  4. "source": "_shrunken"
  5. },
  6. "aliases": [
  7. {
  8. "my-alias": {}
  9. }
  10. ],
  11. "force_unsafe": false
  12. }
ParameterDescriptionTypeExampleRequired
num_new_shardsThe maximum number of primary shards in the shrunken index.integer5Yes, however it cannot be used with max_shard_size or percentage_of_source_shards
max_shard_sizeThe maximum size in bytes of a shard for the target index.keyword5gbYes, however it cannot be used with num_new_shards or percentage_of_source_shards
percentage_of_source_shardsPercentage of the number of original primary shards to shrink. This parameter indicates the minimum percentage to use when shrinking the number of primary shards. Must be between 0.0 and 1.0, exclusive.Percentage0.5Yes, however it cannot be used with max_shard_size or num_new_shards
target_index_name_templateThe name of the shrunken index. Accepts strings and the Mustache variables and.string or Mustache template{“source”: “_shrunken”}No
aliasesAliases to add to the new index.objectmyaliasNo, but must be an array of alias objects
force_unsafeIf true, executes the shrink action even if there are no replicas.booleanfalseNo

If you want to add aliases to the action, the parameter must include an array of alias objects. For example,

  1. "aliases": [
  2. {
  3. "my-alias": {}
  4. },
  5. {
  6. "my-second-alias": {
  7. "is_write_index": false,
  8. "filter": {
  9. "multi_match": {
  10. "query": "QUEEN",
  11. "fields": ["speaker", "text_entry"]
  12. }
  13. },
  14. "index_routing" : "1",
  15. "search_routing" : "1"
  16. }
  17. },
  18. ]

close

Closes the managed index.

  1. {
  2. "close": {}
  3. }

Closed indexes remain on disk, but consume no CPU or memory. You can’t read from, write to, or search closed indexes.

Closing an index is a good option if you need to retain data for longer than you need to actively search it and have sufficient disk space on your data nodes. If you need to search the data again, reopening a closed index is simpler than restoring an index from a snapshot.

open

Opens a managed index.

  1. {
  2. "open": {}
  3. }

delete

Deletes a managed index.

  1. {
  2. "delete": {}
  3. }

rollover

Rolls an alias over to a new index when the managed index meets one of the rollover conditions.

Important: ISM checks the conditions for operations on every execution of the policy based on the set interval, not continuously. The rollover will be performed if the value has reached or exceeded the configured limit when the check is performed. For example with min_size configured to a value of 100GiB, ISM might check the index at 99 GiB and not perform the rollover. However, if the index has grown past the limit (e.g. 105GiB) by the next check, the operation is performed.

The index format must match the pattern: ^.*-\d+$. For example, (logs-000001). Set index.plugins.index_state_management.rollover_alias as the alias to rollover.

ParameterDescriptionTypeExampleRequired
min_sizeThe minimum size of the total primary shard storage (not counting replicas) required to roll over the index. For example, if you set min_size to 100 GiB and your index has 5 primary shards and 5 replica shards of 20 GiB each, the total size of all primary shards is 100 GiB, so the rollover occurs. See Important note above.string20gb or 5mbNo
min_primary_shard_sizeThe minimum storage size of a single primary shard required to roll over the index. For example, if you set min_primary_shard_size to 30 GiB and one of the primary shards in the index has a size greater than the condition, the rollover occurs. See Important note above.string20gb or 5mbNo
min_doc_countThe minimum number of documents required to roll over the index. See Important note above.number2000000No
min_index_ageThe minimum age required to roll over the index. Index age is the time between its creation and the present. Supported units are d (days), h (hours), m (minutes), s (seconds), ms (milliseconds), and micros (microseconds). See Important note above.string5d or 7hNo
  1. {
  2. "rollover": {
  3. "min_size": "50gb"
  4. }
  5. }
  1. {
  2. "rollover": {
  3. "min_primary_shard_size": "30gb"
  4. }
  5. }
  1. {
  2. "rollover": {
  3. "min_doc_count": 100000000
  4. }
  5. }
  1. {
  2. "rollover": {
  3. "min_index_age": "30d"
  4. }
  5. }

notification

Sends you a notification.

ParameterDescriptionTypeRequired
destinationThe destination URL.Slack, Amazon Chime, or webhook URLYes
message_templateThe text of the message. You can add variables to your messages using Mustache templates.objectYes

The destination system must return a response otherwise the notification operation throws an error.

Example 1: Chime notification

  1. {
  2. "notification": {
  3. "destination": {
  4. "chime": {
  5. "url": "<url>"
  6. }
  7. },
  8. "message_template": {
  9. "source": "the index is {{ctx.index}}"
  10. }
  11. }
  12. }

Example 2: Custom webhook notification

  1. {
  2. "notification": {
  3. "destination": {
  4. "custom_webhook": {
  5. "url": "https://<your_webhook>"
  6. }
  7. },
  8. "message_template": {
  9. "source": "the index is {{ctx.index}}"
  10. }
  11. }
  12. }

Example 3: Slack notification

  1. {
  2. "notification": {
  3. "destination": {
  4. "slack": {
  5. "url": "https://hooks.slack.com/services/xxx/xxxxxx"
  6. }
  7. },
  8. "message_template": {
  9. "source": "the index is {{ctx.index}}"
  10. }
  11. }
  12. }

You can use ctx variables in your message to represent a number of policy parameters based on the past executions of your policy. For example, if your policy has a rollover action, you can use {{ctx.action.name}} in your message to represent the name of the rollover.

The following ctx variable options are available for every policy:

Guaranteed variables

ParameterDescriptionType
indexThe name of the index.string
index_uuidThe uuid of the index.string
policy_idThe name of the policy.string

snapshot

Back up your cluster’s indexes and state. For more information about snapshots, see Take and restore snapshots.

The snapshot operation has the following parameters:

ParameterDescriptionTypeRequiredDefault
repositoryThe repository name that you register through the native snapshot API operations.stringYes-
snapshotThe name of the snapshot. Accepts strings and the Mustache variables and. If the Mustache variables are invalid, then the snapshot name defaults to the index’s name.string or Mustache templateYes-
  1. {
  2. "snapshot": {
  3. "repository": "my_backup",
  4. "snapshot": ""
  5. }
  6. }

index_priority

Set the priority for the index in a specific state. Unallocated shards of indexes are recovered in the order of their priority, whenever possible. The indexes with higher priority values are recovered first followed by the indexes with lower priority values.

The index_priority operation has the following parameter:

ParameterDescriptionTypeRequiredDefault
priorityThe priority for the index as soon as it enters a state.numberYes1
  1. "actions": [
  2. {
  3. "index_priority": {
  4. "priority": 50
  5. }
  6. }
  7. ]

allocation

Allocate the index to a node with a specific attribute set like this. For example, setting require to warm moves your data only to “warm” nodes.

The allocation operation has the following parameters:

ParameterDescriptionTypeRequired
requireAllocate the index to a node with a specified attribute.stringYes
includeAllocate the index to a node with any of the specified attributes.stringYes
excludeDon’t allocate the index to a node with any of the specified attributes.stringYes
wait_forWait for the policy to execute before allocating the index to a node with a specified attribute.stringYes
  1. "actions": [
  2. {
  3. "allocation": {
  4. "require": { "temp": "warm" }
  5. }
  6. }
  7. ]

rollup

Index rollup lets you periodically reduce data granularity by rolling up old data into summarized indexes.

Rollup jobs can be continuous or non-continuous. A rollup job created using an ISM policy can only be non-continuous.

Path and HTTP methods

  1. PUT _plugins/_rollup/jobs/<rollup_id>
  2. GET _plugins/_rollup/jobs/<rollup_id>
  3. DELETE _plugins/_rollup/jobs/<rollup_id>
  4. POST _plugins/_rollup/jobs/<rollup_id>/_start
  5. POST _plugins/_rollup/jobs/<rollup_id>/_stop
  6. GET _plugins/_rollup/jobs/<rollup_id>/_explain

Sample ISM rollup policy

  1. {
  2. "policy": {
  3. "description": "Sample rollup" ,
  4. "default_state": "rollup",
  5. "states": [
  6. {
  7. "name": "rollup",
  8. "actions": [
  9. {
  10. "rollup": {
  11. "ism_rollup": {
  12. "description": "Creating rollup through ISM",
  13. "target_index": "target",
  14. "page_size": 1000,
  15. "dimensions": [
  16. {
  17. "date_histogram": {
  18. "fixed_interval": "60m",
  19. "source_field": "order_date",
  20. "target_field": "order_date",
  21. "timezone": "America/Los_Angeles"
  22. }
  23. },
  24. {
  25. "terms": {
  26. "source_field": "customer_gender",
  27. "target_field": "customer_gender"
  28. }
  29. },
  30. {
  31. "terms": {
  32. "source_field": "day_of_week",
  33. "target_field": "day_of_week"
  34. }
  35. }
  36. ],
  37. "metrics": [
  38. {
  39. "source_field": "taxless_total_price",
  40. "metrics": [
  41. {
  42. "sum": {}
  43. }
  44. ]
  45. },
  46. {
  47. "source_field": "total_quantity",
  48. "metrics": [
  49. {
  50. "avg": {}
  51. },
  52. {
  53. "max": {}
  54. }
  55. ]
  56. }
  57. ]
  58. }
  59. }
  60. }
  61. ],
  62. "transitions": []
  63. }
  64. ]
  65. }
  66. }

Request fields

Request fields are required when creating an ISM policy. You can reference the Index rollups API page for request field options.

Adding a rollup policy in Dashboards

To add a rollup policy in Dashboards, follow the steps below.

  • Select the menu button on the top-left of the Dashboards user interface.
  • In the Dashboards menu, select Index Management.
  • On the next screen select Rollup jobs.
  • Select the Create rollup button.
  • Follow the steps in the Create rollup job wizard.
  • Add a name for the policy in the Name box.
  • You can reference the Index rollups API page to configure the rollup policy.
  • Finally, select the Create button on the bottom-right of the Dashboards user interface.

Transitions

Transitions define the conditions that need to be met for a state to change. After all actions in the current state are completed, the policy starts checking the conditions for transitions.

ISM evaluates transitions in the order in which they are defined. For example, if you define transitions: [A,B,C,D], ISM iterates through this list of transitions until it finds a transition that evaluates to true, it then stops and sets the next state to the one defined in that transition. On its next execution, ISM dismisses the rest of the transitions and starts in that new state.

If you don’t specify any conditions in a transition and leave it empty, then it’s assumed to be the equivalent of always true. This means that the policy transitions the index to this state the moment it checks.

This table lists the parameters you can define for transitions.

ParameterDescriptionTypeRequired
state_nameThe name of the state to transition to if the conditions are met.stringYes
conditionsList the conditions for the transition.listYes

The conditions object has the following parameters:

ParameterDescriptionTypeRequired
min_index_ageThe minimum age of the index required to transition.stringNo
min_rollover_ageThe minimum age required after a rollover has occurred to transition to the next state.stringNo
min_doc_countThe minimum document count of the index required to transition.numberNo
min_sizeThe minimum size of the total primary shard storage (not counting replicas) required to transition. For example, if you set min_size to 100 GiB and your index has 5 primary shards and 5 replica shards of 20 GiB each, the total size of all primary shards is 100 GiB, so your index is transitioned to the next state.stringNo
cronThe cron job that triggers the transition if no other transition happens first.objectNo
cron.cron.expressionThe cron expression that triggers the transition.stringYes
cron.cron.timezoneThe timezone that triggers the transition.stringYes

The following example transitions the index to a cold state after a period of 30 days:

  1. "transitions": [
  2. {
  3. "state_name": "cold",
  4. "conditions": {
  5. "min_index_age": "30d"
  6. }
  7. }
  8. ]

ISM checks the conditions on every execution of the policy based on the set interval.

This example uses the cron condition to transition indexes every Saturday at 5:00 PT:

  1. "transitions": [
  2. {
  3. "state_name": "cold",
  4. "conditions": {
  5. "cron": {
  6. "cron": {
  7. "expression": "* 17 * * SAT",
  8. "timezone": "America/Los_Angeles"
  9. }
  10. }
  11. }
  12. }
  13. ]

Note that this condition does not execute at exactly 5:00 PM; the job still executes based off the job_interval setting. Due to this variance in start time and the amount of time that it can take for actions to complete prior to checking transition conditions, we recommend against overly narrow cron expressions. For example, don’t use 15 17 * * SAT (5:15 PM on Saturday).

A window of an hour, which this example uses, is generally sufficient, but you might increase it to 2–3 hours to avoid missing the window and having to wait a week for the transition to occur. Alternately, you could use a broader expression such as * * * * SAT,SUN to have the transition occur at any time during the weekend.

For information on writing cron expressions, see Cron expression reference.


Error notifications

The error_notification operation sends you a notification if your managed index fails. It notifies a single destination or notification channel with a custom message.

Set up error notifications at the policy level:

  1. {
  2. "policy": {
  3. "description": "hot warm delete workflow",
  4. "default_state": "hot",
  5. "schema_version": 1,
  6. "error_notification": { },
  7. "states": [ ]
  8. }
  9. }
ParameterDescriptionTypeRequired
destinationThe destination URL.Slack, Amazon Chime, or webhook URLYes if channel isn’t specified
channelA notification channel’s IDstringYes if destination isn’t specified
message_templateThe text of the message. You can add variables to your messages using Mustache templates.objectYes

The destination system must return a response otherwise the error_notification operation throws an error.

Example 1: Chime notification

  1. {
  2. "error_notification": {
  3. "destination": {
  4. "chime": {
  5. "url": "<url>"
  6. }
  7. },
  8. "message_template": {
  9. "source": "The index {{ctx.index}} failed during policy execution."
  10. }
  11. }
  12. }

Example 2: Custom webhook notification

  1. {
  2. "error_notification": {
  3. "destination": {
  4. "custom_webhook": {
  5. "url": "https://<your_webhook>"
  6. }
  7. },
  8. "message_template": {
  9. "source": "The index {{ctx.index}} failed during policy execution."
  10. }
  11. }
  12. }

Example 3: Slack notification

  1. {
  2. "error_notification": {
  3. "destination": {
  4. "slack": {
  5. "url": "https://hooks.slack.com/services/xxx/xxxxxx"
  6. }
  7. },
  8. "message_template": {
  9. "source": "The index {{ctx.index}} failed during policy execution."
  10. }
  11. }
  12. }

Example 4: Using a notification channel

  1. {
  2. "error_notification": {
  3. "channel": {
  4. "id": "some-channel-config-id"
  5. },
  6. "message_template": {
  7. "source": "The index {{ctx.index}} failed during policy execution."
  8. }
  9. }
  10. }

You can use the same options for ctx variables as the notification operation.

Sample policy with ISM template for auto rollover

The following sample template policy is for a rollover use case.

If you want to skip rollovers for an index, set index.plugins.index_state_management.rollover_skip to true in the settings of that index.

  1. Create a policy with an ism_template field:

    1. PUT _plugins/_ism/policies/rollover_policy
    2. {
    3. "policy": {
    4. "description": "Example rollover policy.",
    5. "default_state": "rollover",
    6. "states": [
    7. {
    8. "name": "rollover",
    9. "actions": [
    10. {
    11. "rollover": {
    12. "min_doc_count": 1
    13. }
    14. }
    15. ],
    16. "transitions": []
    17. }
    18. ],
    19. "ism_template": {
    20. "index_patterns": ["log*"],
    21. "priority": 100
    22. }
    23. }
    24. }

    You need to specify the index_patterns field. If you don’t specify a value for priority, it defaults to 0.

  2. Set up a template with the rollover_alias as log :

    1. PUT _index_template/ism_rollover
    2. {
    3. "index_patterns": ["log*"],
    4. "template": {
    5. "settings": {
    6. "plugins.index_state_management.rollover_alias": "log"
    7. }
    8. }
    9. }
  3. Create an index with the log alias:

    1. PUT log-000001
    2. {
    3. "aliases": {
    4. "log": {
    5. "is_write_index": true
    6. }
    7. }
    8. }
  4. Index a document to trigger the rollover condition:

    1. POST log/_doc
    2. {
    3. "message": "dummy"
    4. }
  5. Verify if the policy is attached to the log-000001 index:

    1. GET _plugins/_ism/explain/log-000001?pretty

Example policy with ISM templates for the alias action

The following example policy is for an alias action use case.

In the following example, the first job will trigger the rollover action, and a new index will be created. Next, another document is added to the two indexes. The new job will then cause the second index to point to the log alias, and the older index will be removed due to the alias action.

First, create an ISM policy:

  1. PUT /_plugins/_ism/policies/rollover_policy?pretty
  2. {
  3. "policy": {
  4. "description": "Example rollover policy.",
  5. "default_state": "rollover",
  6. "states": [
  7. {
  8. "name": "rollover",
  9. "actions": [
  10. {
  11. "rollover": {
  12. "min_doc_count": 1
  13. }
  14. }
  15. ],
  16. "transitions": [{
  17. "state_name": "alias",
  18. "conditions": {
  19. "min_doc_count": "2"
  20. }
  21. }]
  22. },
  23. {
  24. "name": "alias",
  25. "actions": [
  26. {
  27. "alias": {
  28. "actions": [
  29. {
  30. "remove": {
  31. "alias": "log"
  32. }
  33. }
  34. ]
  35. }
  36. }
  37. ]
  38. }
  39. ],
  40. "ism_template": {
  41. "index_patterns": ["log*"],
  42. "priority": 100
  43. }
  44. }
  45. }

Next, create an index template on which to enable the policy:

  1. PUT /_index_template/ism_rollover?
  2. {
  3. "index_patterns": ["log*"],
  4. "template": {
  5. "settings": {
  6. "plugins.index_state_management.rollover_alias": "log"
  7. }
  8. }
  9. }

copy

Next, change the cluster settings to trigger jobs every minute:

  1. PUT /_cluster/settings?pretty=true
  2. {
  3. "persistent" : {
  4. "plugins.index_state_management.job_interval" : 1
  5. }
  6. }

copy

Next, create a new index:

  1. PUT /log-000001
  2. {
  3. "aliases": {
  4. "log": {
  5. "is_write_index": true
  6. }
  7. }
  8. }

copy

Finally, add a document to the index to trigger the job:

  1. POST /log-000001/_doc
  2. {
  3. "message": "dummy"
  4. }

copy

You can verify these steps using the Alias and Index API:

  1. GET /_cat/indices?pretty

copy

  1. GET /_cat/aliases?pretty

copy

Note: The index and remove_index parameters are not allowed with alias action policies. Only the add and remove alias action parameters are allowed.

Example policy

The following example policy implements a hot, warm, and delete workflow. You can use this policy as a template to prioritize resources to your indexes based on their levels of activity.

In this case, an index is initially in a hot state. After a day, it changes to a warm state, where the number of replicas increases to 5 to improve the read performance.

After 30 days, the policy moves this index into a delete state. The service sends a notification to a Chime room that the index is being deleted, and then permanently deletes it.

  1. {
  2. "policy": {
  3. "description": "hot warm delete workflow",
  4. "default_state": "hot",
  5. "schema_version": 1,
  6. "states": [
  7. {
  8. "name": "hot",
  9. "actions": [
  10. {
  11. "rollover": {
  12. "min_index_age": "1d",
  13. "min_primary_shard_size": "30gb"
  14. }
  15. }
  16. ],
  17. "transitions": [
  18. {
  19. "state_name": "warm"
  20. }
  21. ]
  22. },
  23. {
  24. "name": "warm",
  25. "actions": [
  26. {
  27. "replica_count": {
  28. "number_of_replicas": 5
  29. }
  30. }
  31. ],
  32. "transitions": [
  33. {
  34. "state_name": "delete",
  35. "conditions": {
  36. "min_index_age": "30d"
  37. }
  38. }
  39. ]
  40. },
  41. {
  42. "name": "delete",
  43. "actions": [
  44. {
  45. "notification": {
  46. "destination": {
  47. "chime": {
  48. "url": "<URL>"
  49. }
  50. },
  51. "message_template": {
  52. "source": "The index {{ctx.index}} is being deleted"
  53. }
  54. }
  55. },
  56. {
  57. "delete": {}
  58. }
  59. ]
  60. }
  61. ],
  62. "ism_template": {
  63. "index_patterns": ["log*"],
  64. "priority": 100
  65. }
  66. }
  67. }

This diagram shows the states, transitions, and actions of the above policy as a finite-state machine. For more information about finite-state machines, see Wikipedia.

Policy State Machine