Policy Types¶
A policy policy is a set of rules that are checked and enforced. The checking can be done before or after an action’s execution or both. Policies are of different policy types, each of which is designed to make sure that a cluster’s behavior follows certain patterns or complies with certain restrictions.
When released, Senlin comes with some built-in policy types to meet the requirements found in some typical use cases. However, the distributors or the users can always augment their collection of policy types by implementing their own ones.
Policy type implementations are managed as Senlin plugins. The plan is to have
Senlin engine support dynamical loading of plugins from user specified modules
and classes. Currently, this can be achieved by adding new senlin.policies
entries in the entry_points
section in the setup.cfg
file, followed by
a reinstall of the Senlin service, i.e. sudo pip install
command.
The Base Class Policy
¶
The base class Policy
provides some common logics regarding the following
operations:
The initialization of the
spec_data
property, based on thespec_schema
definition and thespec
input.The serialization and deserialization of a policy object into/from database.
The serialization and deserialization of a policy object into/from a dict.
The default validation operation for the
spec_data
property.Default implementations for the following methods which are to be overridden by a policy type implementation:
attach(cluster_id, action)
: a method that will be invoked when a policy object of this type is attached to a cluster.detach(cluster_id, action)
: a method that will be invoked when a policy object of this type is detached from a cluster.pre_op(cluster_id, action)
: a method that will be invoked before an action is executed;post_op(cluster_id, action)
: a method that will be invoked after an action is executed.
The VERSIONS
Property¶
Each policy type class has a VERSIONS
class property that documents the
changes to the policy type. This information is returned when users request
to list all policy types supported.
The VERSIONS
property is a dict with version numbers as keys. For each
specific version, the value is list of support status changes made to the
policy type. Each change record contains a status
key whose value is one
of EXPERIMENTAL
, SUPPORTED
, DEPRECATED
or UNSUPPORTED
, and a
since
key whose value is of format yyyy.mm
where yyyy
and mm
are the year and month of the release that bears the change to the support
status. For example, the following record indicates that the specific policy
type was introduced in April, 2016 (i.e. version 1.0 release of Senlin) as
an experimental feature; later, in October, 2016 (i.e. version 2.0 release of
Senlin) it has graduated into a mature feature supported by the developer
team.
VERSIONS = {
'1.0': [
{
"status": "EXPERIMENTAL",
"since": "2016.04"
},
{
"status": "SUPPORTED",
"since": "2016.10"
}
]
}
Providing New Policy Types¶
Adding new policy type implementations is an easy task with only a few steps to follow.
Develop A New Policy Type¶
The first step for adding a new policy type is to create a new file containing
a subclass of Policy
. Then you will define the spec schema for the new
policy type in a Python dictionary named spec_schema
.
Defining Spec Schema¶
Each key in this dictionary represents a property name; the value of it is an object of one of the schema types listed below:
String
: A string property.Boolean
: A boolean property.Integer
: An integer property.List
: A property containing a list of values.Map
: A property containing a map of key-value pairs.
For example:
spec_schema = {
'destroy_after_delete': schema.Boolean(
'Boolean indicating whether object will be destroyed after deletion.',
default=True,
),
...
}
If a property value will be a list, you can further define the type of items the list can accept. For example:
spec_schema = {
'criteria': schema.List(
'Criteria for object selection that will be evaluated in order.',
schema=schema.String('Name of a criterion'),
),
...
}
If a property value will be a map of key-value pairs, you can define the schema of the map, which is another Python dictionary containing definitions of properties. For example:
spec_schema = {
'strategy': schema.Map(
'Strategy for dealing with servers with different states.',
schema={
'inactive': 'boot',
'deleted': 'create',
'suspended': 'resume',
},
),
...
}
When creating a schema type object, you can specify the following keyword arguments to gain a better control of the property:
default
: a default value of the expected data type;required
: a boolean value indicating whether a missing of the property is acceptable when validating the policy spec;constraints
: a list ofConstraint
objects each of which defines a constraint to be checked. Senlin currently only supportAllowedValues
constraint.
Applicable Profile Types¶
Not all policy types can be used on all profile types. For example, a policy about load-balancing is only meaningful for objects that can handle workloads, or more specifically, objects that expose service access point on an IP port.
You can define what are the profile types your new policy type can handle by
specifying the PROFILE_TYPE
property of your policy type class. The value
of PROFILE_TYPE
is a list of profile type names. If a policy type is
designed to handle all profile types, you can specify a single entry ANY
as the value. See profile types for profile type related
operations.
Policy Targets¶
A policy type is usually defined to handle certain operations. The rules
embedded in the implementation may need to be checked before the execution of
an action or they may need to be enforced after the execution
of the action. When an action is about to be executed or an action has
finished execution, the Senlin engine will check if any policy objects
attached to a cluster is interested in the action. If the answer is yes, the
engine will invoke the pre_op
function or the post_op
function
respectively, thus giving the policy object a chance to adjust the action’s
behavior.
You can define a TARGET
property for the policy type implementation to
indicate the actions your policy type want to subscribe to. The TARGET
property is a list of tuple (WHEN
, ACTION
). For example, the following
property definition indicates that the policy type is interested in the action
CLUSTER_SCALE_IN
and CLUSTER_DEL_NODES
. The policy type wants itself
be consulted before these actions are performed.
class MyPolicyType(Policy):
...
TARGET = [
(BEFORE, consts.CLUSTER_SCALE_IN),
(BEFORE, consts.CLUSTER_DEL_NODES),
]
...
When the corresponding actions are about to be executed, the pre_op
function of this policy object will be invoked.
Passing Data Between Policies¶
Each policy type may decide to send some data as additional inputs or
constraints for the action to consume. This is done by modifying the data
property of an Action
object (see action).
A policy type may want to check if there are other policy objects leaving some
policy decisions in the data
property of an action object.
Senlin allows for more than one policy to be attached to the same cluster. Each policy, when enabled, is supposed to check a specific subset of cluster actions. In other words, different policies may get checked before/after the engine executes a specific cluster action. This design is effectively forming a chain of policies for checking. The decisions (outcomes) from one policy sometimes impact other policies that are checked later.
To help other developers to understand how a specific policy type is designed to work in concert with others, we require all policy type implementations shipped with Senlin accompanied by a documentation about:
the
action data
items the policy type will consume, including how these data will impact the policy decisions.the
action.data
items the policy type will produce, thus consumable by any policies downstream.
For built-in policy types, the protocol is documented below:
Registering The New Policy Type¶
For Senlin service to be aware of and thus to make use of the new policy type you have just developed, you will register it to the Senlin service. Currently, this is done through a manual process shown below. In future, Senlin will provide dynamical loading support to policy type plugins.
To register a new plugin type, you will add a line to the setup.cfg
file
that can be found at the root directory of Senlin code base. For example:
[entry_points]
senlin.policies =
ScalingPolicy = senlin.policies.scaling_policy:ScalingPolicy
MyCoolPolicy = <path to the policy module>:<policy class name>
Finally, save that file and do a reinstall of the Senlin service, followed
by a restart of the senlin-engine
process.
$ sudo pip install -e .
Now, when you do a openstack cluster policy type list, you will see your policy type listed along with other existing policy types.