Red Hat OpenShift 4.16 has a new templating mechanism called Red Hat Advanced Cluster Management for Kubernetes (RHACM) Policy Generator that will eventually replace Policy Generator Template (PGT) as the standard way to generate Red Hat Advanced Cluster Management for Kubernetes policies. The new templates are now available as a technology preview.
Although RHACM Policy Generator has been available for some time, this marks its first availability as part of our zero touch provisioning (ZTP) reference configuration for the radio access network (RAN) distributed unit (DU) use case.
The RHACM Policy Generator is similar to PGT and uses a Kustomize-based tool that utilizes the PolicyGenerator
Custom Resource to create RHACM policies. Its primary goal is to convert reference Kubernetes manifest files into RHACM policies. Any deviations from the reference manifest, such as ethernet interface names, are captured in the patch section of the PolicyGenerator
Custom Resource.
The RHACM Policy Generator enables policy providers to create libraries of shareable and patchable policies, streamlining the process of managing and updating RHACM policies. This feature makes it easier for users to customize and extend existing policies to suit their specific needs.
In this article, we will highlight the key differences between PGT and RHACM Policy Generator Custom Resources, as well as provide guidance on how to convert PGT to RHACM Policy Generator using the cnf-features-deploy project as an example.
Comparing PGT and RHACM formats
PGT and RHACM Policy Generator are similar in achieving the same goal of building policies from a set of CRs.
Figure 1 illustrates a few of the basic fields. The PolicyGenTemplate
example is on the left, and the PolicyGenerator
CR on the right. The apiVersion, kind, metadata, name, namespace, and binding rules are mapped as follows between the PGT and RHACM Policy Generator templates.

By contrast, PGT organizes policies by associating each source CR filename
with a single policyName
, so a policy can be mapped to multiple source CR files. RHACM Policy Generator templates achieve the same goal but instead define the policies
field. You'll find a list of policies, and within each policy, you'll see the source CR files that implement them listed under manifests
. The advantage of this grouping by policy is improved access to specify other per-policy configurations, such as evaluationIntervals
. Figure 2 shows an example of PGT syntax to RHACM Policy Generator policy section conversion.

Sometimes PGT templates contain an MCP field indicating whether it applies to worker or master nodes. RHACM Policy Generator does not support a global MCP variable equivalent. When converting PGT to RHACM Policy Generator, $mcp
should be replaced by master or worker depending on the node role.
This could be achieved through two approaches: either creating separate reference files for master and worker nodes (this is the approach taken in the example RHACM Policy Generator files), or patching "master"
or "worker"
in the appropriate location. The following example shows a PGT CR containing the $mcp
variable before conversion to RHACM Policy Generator. Then show the equivalent CR using RHACM Policy Generator. This is for illustration purposes only. The RHACM Policy Generator templates provided with OpenShift 4.16 already include these modifications and require no modifications.
The following shows the PGT content with mcp
field defined:
---
apiVersion: ran.openshift.io/v1
kind: PolicyGenTemplate
metadata:
name: "group-du-standard-latest"
namespace: "ztp-group"
spec:
bindingRules:
# These policies will correspond to all clusters with this label:
group-du-standard: ""
du-profile: "latest"
mcp: "worker"
sourceFiles:
- fileName: TunedPerformancePatch.yaml
policyName: "config-policy"
The content of TunedPerformancePatch.yaml
is shown here. Note that the $mcp
text is replaced, but the mcp
value previously defined to "worker"
during rendering of the template:
apiVersion: tuned.openshift.io/v1
kind: Tuned
metadata:
name: performance-patch
namespace: openshift-cluster-node-tuning-operator
annotations:
ran.openshift.io/ztp-deploy-wave: "10"
spec:
profile:
- name: performance-patch
data: |
[main]
summary=Configuration changes profile inherited from performance created tuned
include=openshift-node-performance-openshift-node-performance-profile
[scheduler]
group.ice-ptp=0:f:10:*:ice-ptp.*
group.ice-gnss=0:f:10:*:ice-gnss.*
group.ice-dplls=0:f:10:*:ice-dplls.*
[service]
service.stalld=start,enable
service.chronyd=stop,disable
recommend:
- machineConfigLabels:
machineconfiguration.openshift.io/role: "$mcp"
priority: 19
profile: performance-patch
The equivalent RHACM Policy Generator template is shown next. In this case, the whole recommend
section is patched with RHACM Policy Generator, including the "worker"
(machineconfiguration.openshift.io/role: "worker"
) role:
apiVersion: policy.open-cluster-management.io/v1
kind: PolicyGenerator
metadata:
name: group-du-3node-latest
placementBindingDefaults:
name: group-du-3node-placement-binding
policyDefaults:
namespace: ztp-group
...
policies:
- name: group-du-3node-latest-config-policy
policyAnnotations:
ran.openshift.io/ztp-deploy-wave: "10"
manifests:
- path: source-crs/TunedPerformancePatch.yaml
patches:
- spec:
recommend:
- machineConfigLabels:
machineconfiguration.openshift.io/role: "worker"
priority: 19
profile: performance-patch
...
A few source CR files contain variables prefixed with the dollar sign, such as $name
, $namespace
, and etc. These keywords indicate where the user needs to include cluster or environment specific values. The appropriate values should be provided in the RHACM Policy Generator which will overwrite them via the patch merging mechanism defined by policy-generator-plugin
.
The RHACM Policy Generator also adds fine tuning generated underlying policies parameters. For instance, with RHACM Policy Generator, it is possible to change the underlying policies soak-seconds
by setting the evaluationInterval
in the RHACM Policy Generator CR. You can find a list of other fine tuning options in the policy generator documentation.
More placement options
In the recommended use, RHACM Policy Generator creates Placement
resources instead of PlacementRules
to bind the generated policies to clusters. The role of both Placement
and PlacementRules
is to filter the full list of ManagedCluster
objects available based on specific criteria. For instance, both objects can filter ManagedClusters
based on labels and conditions/status. Placement adds support for filtering based on ClusterSet
, and a system of predicates allowing more granular selection by OR-ing requirements, including labels and ManagedCluster
claims.
In addition, with Placement
, tolerations can consider managed clusters that have certain taints. A prioritizer's scheme to select clusters with competing placement requests is also supported, but not discussed in this document.
With PolicyGenTemplate
, the bindingRules
field is used to create a PlacementRules
object:
---
apiVersion: ran.openshift.io/v1
kind: PolicyGenTemplate
metadata:
name: "test"
spec:
bindingRules:
environment: "dev"
...
policies:
- name: policy-role
The generated PlacementRule
and PlacementBinding
corresponding to the previous PolicyGenTemplate
are:
---
apiVersion: policy.open-cluster-management.io/v1
kind: PlacementBinding
metadata:
name: binding-policy-role
placementRef:
name: placement-policy-role
kind: PlacementRule
apiGroup: apps.open-cluster-management.io
subjects:
- name: policy-role
kind: Policy
apiGroup: policy.open-cluster-management.io
---
apiVersion: apps.open-cluster-management.io/v1
kind: PlacementRule
metadata:
name: placement-policy-role
spec:
clusterConditions:
- status: "True"
type: ManagedClusterConditionAvailable
clusterSelector:
matchExpressions:
- {key: environment, operator: In, values: ["dev"]}
The equivalent field in the PolicyGenerator
object is policyDefaults.placement
:
---
apiVersion: policy.open-cluster-management.io/v1
kind: PolicyGenerator
metadata:
name: test
policyDefaults:
namespace: default
placement:
labelSelector:
environment: "dev"
...
policies:
- name: policy-role
This entry generates the following Placement and PlacementBinding objects:
---
apiVersion: policy.open-cluster-management.io/v1
kind: PlacementBinding
metadata:
name: binding-policy-role
placementRef:
name: placement-policy-role
kind: Placement
apiGroup: cluster.open-cluster-management.io
subjects:
- name: policy-role
kind: Policy
apiGroup: policy.open-cluster-management.io
---
apiVersion: cluster.open-cluster-management.io/v1alpha1
kind: Placement
metadata:
name: placement-policy-role
spec:
predicates:
- requiredClusterSelector:
labelSelector:
matchExpressions:
- {key: environment, operator: In, values: ["dev"]}
The RHACM Policy Generator also adds support for regrouping policies under ManagedClusterSets
. The goal of the ManagedClusterSet
is to regroup policies that apply to similar clusters, for instance production vs. staging. Red Hat OpenShift creates a default ManagedClusterSet
called global
. To allow policies to bind to clusters using the Placement
API, a ManagedClusterSetBinding
should be created to add the policies namespace to the global ManagedClusterSet
. The following creates a ManagedClusterSetBinding
adding the ztp-policies
namespace to the global ManagedClusterSet
:
apiVersion: cluster.open-cluster-management.io/v1beta2
kind: ManagedClusterSetBinding
metadata:
name: global
namespace: ztp-policies
spec:
clusterSet: global
More flexible patching strategy
RHACM Policy Generator leverages Kubernetes' strategic merge functionality to customize reference manifests, as detailed in the documentation.
One benefit of using this method is patching lists of objects into CRs, which is not possible with PGT. The next example describes updating a specific profile in the list of profiles of a ptpconfig
object using a patch applied on the source-crs/PtpConfigFollower.yaml
CR. With this patch only the highlighted “follower” profile is targeted and the data is updated. Other profiles are left untouched:
interface: ens5f0
phc2sysOpts: -a -r -n 24
ptp4lOpts: -2 -s --summary_interval -4
apiVersion: policy.open-cluster-management.io/v1
kind: PolicyGenerator
metadata:
name: group-du-3node-latest
placementBindingDefaults:
name: group-du-3node-placement-binding
policyDefaults:
...
policies:
- name: group-du-3node-latest-config-policy
policyAnnotations:
ran.openshift.io/ztp-deploy-wave: "10"
manifests:
- path: source-crs/PtpConfigFollower.yaml
patches:
- metadata:
name: du-ptp-follower
spec:
profile:
- interface: ens5f0
name: follower
phc2sysOpts: -a -r -n 24
ptp4lOpts: -2 -s --summary_interval -4
openapi:
path: schema.openapi
When working with CRs without lists, strategic merge is supported by default. In this case, if the manifest contains only one CR, the namespace, and name of the object in the patch can be used to optionally rename the patched object. However, if multiple objects are described in the manifest, the namespace and name of the object will be used to identify the object to patch.
Note that all examples provided in this documentation use one CR per manifest, so it is possible to rename the patched object.
In the following example, the metadata section in the patch name: du-ptp-follower
optionally renames the CR in this case since the manifest source-crs/PtpConfigFollower.yaml
only defines a single CR:
policies:
- name: group-du-3node-latest-config-policy
policyAnnotations:
ran.openshift.io/ztp-deploy-wave: "10"
manifests:
- path: source-crs/PtpConfigFollower.yaml
patches:
- metadata:
name: du-ptp-follower
spec:
...
OpenAPI schema support
When dealing with resources containing lists, there are two ways to accomplish patching with cluster specific content. In one strategy, the base CR has an empty list, and the entire content of the list can be supplied via the patch. The other strategy is to include the majority of the list content in the base CR and use the patch to fill in specific entries in the list. In this second case, RHACM Policy Generator requires knowledge of the OpenAPI schema that defines the structure of the resource. The schema dictates which fields can be used as keys to identify list objects in the patch. For Kubernetes resources such as pods, the schema is known by Kustomize and does not need to be provided. For Custom Resources, the Open API schema can be provided as an optional field in the PolicyGenerator.
The following illustrates an RHACM Policy Generator patch with the OpenAPI schema. The openapi.path
indicates the path of the schema file (schema.openapi
) relative to the kustomization.yaml
. The profile list section of the patch specifically targets and patches a single profile within the manifest, namely the one identified by the name clock1
. In this case, Kustomize will apply the patch values only to the clock1
profile:
policies:
- name: group-du-3node-latest-config-policy
policyAnnotations:
ran.openshift.io/ztp-deploy-wave: "10"
manifests:
- path: source-crs/PtpConfigFollower.yaml
patches:
- metadata:
name: du-ptp-follower
spec:
profile:
- interface: ens5f0
name: clock1
phc2sysOpts: -a -r -n 24
ptp4lOpts: -2 -s --summary_interval -4
openapi:
path: schema.openapi
Ideally, the developer of the Custom Resource (CR) should provide the OpenAPI schema. However, if this information is not available, you can manually retrieve the schema from a running Kubernetes cluster by following these steps:
Run
kustomize openapi fetch
to obtain the schema.Identify the subset of resources that need to be patched and extract the relevant sections of the schema.
Determine the list objects in the schema and select a key (e.g.,
"name"
) from the fields of the object that can be used to index the list.
After defining the list, add the following text:
"x-kubernetes-patch-merge-key": "name",
"x-kubernetes-patch-strategy": "merge"
The "x-kubernetes-patch-merge-key"
specifies the field in the object that is used to uniquely identify it in the list (in this case, the name field). The "x-kubernetes-patch-strategy"
indicates the patch strategy. For example, a merge strategy will merge fields, while a replace strategy would replace the object identified by the key with patch content.
The key selected in this step is used in patches to uniquely identify a list object. The following is an example of schema:
{
"definitions": {
"io.myorg.myapp.mycr": {
"description": "MyCr is the Schema for the MyCrs API",
"properties": {
"apiVersion": {
"description": "APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources",
"type": "string"
},
"kind": {
"description": "Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds",
"type": "string"
},
"metadata": {
"$ref": "#/definitions/io.k8s.apimachinery.pkg.apis.meta.v1.ObjectMeta_v2",
"description": "Standard object's metadata. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#metadata"
},
"spec": {
"description": "MyCrSpec defines the desired state of MyCr",
"properties": {
"listOfStuff": {
"x-kubernetes-patch-merge-key": "name",
"x-kubernetes-patch-strategy": "merge",
"items": {
"properties": {
"myattribute": {
"type": "string"
},
"name": {
"type": "string"
},
"plugins": {
"additionalProperties": {
"x-kubernetes-preserve-unknown-fields": true
},
"type": "object"
}
},
"required": [
"name"
],
"type": "object"
},
"type": "array"
}
},
"type": "object"
}
},
"type": "object",
"x-kubernetes-group-version-kind": [
{
"group": "myapp.myorg.io",
"kind": "MyCr",
"version": "v1"
}
]
}
}
}
RHACM policy generator is better
The RHACM Policy Generator offers functionality comparable to PGT with improvements in tuning policy fields and merging capabilities. It is available and supported as part of RHACM (via Open Cluster Management). Besides a different format, RHACM Policy Generator introduces the following notable differences compared to PGT:
PGT | RHACM Policy Generator | ||
Underlying generated Policy tuning | Set additional policy fields | Via extra patches in | Directly in RHACM Policy Generator |
Placement | Creating placement | Yes, using deprecated placement rules | Yes, using placement object |
Patching | Patching CRs containing list of objects | yes, but only one object in the list | Yes, using OpenAPI |
Patching object names and namespace | Yes | Yes |
Migrate to RHACM Policy Generator
Red Hat OpenShift 4.16 introduces RHACM Policy Generator templates as the preferred way to generate RHACM policies. It will eventually replace the current Policy Generator Template (PGT). It is essential for users to understand the differences between PGT and RHACM Policy Generator.
The main difference lies in the improved patching capabilities of the RHACM Policy Generator. Additionally, RHACM Policy Generator offers a more structured approach to organizing policies, allowing for easier customization and extension. To successfully migrate to RHACM Policy Generator, users must familiarize themselves with the new template structure, patching mechanisms, and OpenAPI schema support. By doing so, they can take advantage of the improved capabilities and ensure seamless integration with their existing policy management workflows.