Puppet Class: sap::cluster

Defined in:
manifests/cluster.pp

Summary

Configures a node with the appropriate SAP cluster resources

Overview

Cluster class which configures a PCS/Corosync based ha-environment. Note that it is assumed that the puppet-corosync class is defined and the cluster is configured before this class is configured.

Additionally, the cluster must be an Asymmetrical “Opt-In” cluster. This configuration operates under that assumption and sets cluster level parameters appropriately.

Parameters:

  • cib_name (String) (defaults to: undef)

    Name of the temporary Cluster Information Base to use during the building and commiting of the rule changes incorporated in this module.

  • resource_stickiness (Integer) (defaults to: 500)

    Global configuration setting controlling how much a given resource wants to ‘stay’ where it is as opposed to moving to a ‘home’ node.

  • enable_fencing (Boolean) (defaults to: false)

    Controls whether the cluster assumes Stonith should be enabled or not.

  • sid_rules (Sap::SIDClusterData) (defaults to: {})

    A hash mapping each cluster relevant SID to two mandatory components: node_roles: a complete list of PCS nodes mapped to their roles components: cluster elements for this instance with their data

  • fence_agents (Optional[Array[Sap::ClusterFence]]) (defaults to: [])

    An array of Sap::ClusterFence definitions which provide fencing coverage for all nodes defined by this cluster. Note that this parameter becomes mandatory when enable_fencing is true. Additionally, every node referenced within the sid_rules structure must have exactly one corresponding entry in the pcmk_host_map structure of a single agent defined here.



33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
# File 'manifests/cluster.pp', line 33

class sap::cluster (
  String $cib_name                                 = undef,
  Integer $resource_stickiness                     = 500,
  Boolean $enable_fencing                          = false,
  Sap::SIDClusterData $sid_rules                   = {},
  Optional[Array[Sap::ClusterFence]] $fence_agents = [],
){
  $cluster_sid_list = $sid_rules.map |$entry| { $entry[0] }

  # Validate the SID Rules structure and build a list of nodes
  $node_list_tmp = $sid_rules.map | $entry | {
    $sid = $entry[0]
    $sid_data = $entry[1]

    # Ensure we have the necessary parameters
    ['node_roles', 'components'].each |$key| {
      unless( $key in $sid_data ) {
        fail("cluster ${sid}: '${key}' parameter is mandatory!")
      }
    }
    $sid_data['node_roles'].keys()
  }
  $node_list = unique(flatten($node_list_tmp))

  class { 'sap::cluster::prepare':
    cluster_sid_list => $cluster_sid_list,
  }
  contain 'sap::cluster::prepare'

  # Start building the cluster information base
  cs_shadow { $cib_name: }

  # Opt-in cluster - resources can only run on nodes we explicitly specify
  cs_property { 'symmetric-cluster':
    value => 0,
    cib   => $cib_name,
  }

  # Management of STONITH for fencing
  if $enable_fencing {
    $stonith_enabled_value = 'true' # lint:ignore:quoted_booleans

    # Ensure at least one agent is defined
    if empty($fence_agents) {
      $message = @("EOT"/L)
        cluster: at least one fence agent must be defined when fencing is \
        enabled!
        |-EOT
      fail($message)
    }

    # Ensure that every node is fenced by exactly one agent
    $fence_node_tmp = $fence_agents.map |$entry| {
      # Ensure both the type and data are present
      ['type', 'data'].each |$key| {
        unless($key in $entry) {
          $message = @("EOT"/L)
            cluster: fence_agent entry missing '${key}'!
            |-EOT
          fail($message)
        }
      }

      unless('pcmk_host_map' in $entry['data']) {
        $message = @("EOT"/L)
          cluster: all currently supported fence agents require \
          'pcmk_host_map' to be defined!
          |-EOT
        fail($message)
      }
      $entry['data']['pcmk_host_map'].keys()
    }
    $fence_node_list = flatten($fence_node_tmp)

    $node_list.each |$node| {
      $node_count = count($fence_node_list, $node)
      if $node_count == 0 {
        $fail = true
        $message = @("EOT"/L)
          cluster: node '${node}' must be targeted by exactly one fence agent!
          |-EOT
      } elsif $node_count > 1 {
        $fail = true
        $message = @("EOT"/L)
          cluster: node '${node}' cannot be targeted by more than one fence \
          agent!
          |-EOT
      } else {
        $fail = false
      }
      if $fail { fail($message) }
    }

    # Assuming everything is good, construct the fencing!
    $fence_agents.each |$index, $entry| {
      $type = $entry['type']
      $data = $entry['data']
      case $type {
        'fence_lpar': {
          sap::cluster::fence_lpar { "${type}_${index}":
            cib_name  => $cib_name,
            node_list => $node_list,
            *         => $data,
          }
        }
        default: {} # do nothing if we don't understand - this is impossible
      }
    }
  } else {
    $stonith_enabled_value = 'false' # lint:ignore:quoted_booleans
  }

  cs_property { 'stonith-enabled':
    value => $stonith_enabled_value,
    cib   => $cib_name,
  }

  # Configure how much resources want to stick in place by default
  cs_rsc_defaults { 'resource-stickiness':
    value => $resource_stickiness,
    cib   => $cib_name,
  }

  # Construct the appropriate ruleset for each SID
  unless(empty($sid_rules)) {
    $sid_rules.each | $sid, $sid_data | {
      $node_roles = $sid_data['node_roles']
      $components = $sid_data['components']

      # Ensure we have the required roles defined
      $defined_cluster_types = $components.map |$entry| {
        # Every component must have a type!
        if ! ( 'type' in $entry) {
          fail("cluster ${sid}: component missing 'type'!")
        }
        $entry['type']
      }
      # Validate AS dependencies
      if 'As' in $defined_cluster_types{
        if ! ('ScsErs' in $defined_cluster_types) {
          fail("cluster ${sid}: when As type is defined ScsErs is mandatory!")
        } elsif ! ('Db2' in $defined_cluster_types) {
          fail("cluster ${sid}: when As type is defined Db2 is mandatory!")
        }
      }

      unless(empty($components)) {
        $components.each | $component | {
          # Every component must have corresponding data
          if ! ( 'data' in $component ) {
            fail("cluster ${sid}: component missing 'data'!")
          }

          $comp_type = $component['type']
          $comp_data = $component['data']

          case $comp_type {
            'ScsErs': {
              sap::cluster::scsers { "${sid}_ScsErs":
                sid      => $sid,
                cib_name => $cib_name,
                nodes    => $node_roles,
                before   => Cs_commit[$cib_name],
                *        => $comp_data,
              }
            }
            'As': {
              # Retrieve the DB detail and pass it's mode to the As resources
              $db_component = $components.filter |$component| {
                $component['type'] =~ /Db2/
              }
              $db_cluster_mode = $db_component[0]['data']['mode']
              sap::cluster::as { "${sid}_As":
                sid      => $sid,
                cib_name => $cib_name,
                nodes    => $node_roles,
                before   => Cs_commit[$cib_name],
                db_mode  => $db_cluster_mode,
                *        => $comp_data,
              }
            }
            'Db2': {
              sap::cluster::db2 { "${sid}_Db2":
                sid      => $sid,
                cib_name => $cib_name,
                nodes    => $node_roles,
                before   => Cs_commit[$cib_name],
                *        => $comp_data,
              }
            }
            default: {}
          }
        }
      }
    }
  }

  # Finalize all CIB changes
  # Ensure the prepare steps happen before these changes are merged
  cs_commit { $cib_name:
    require => Class['sap::cluster::prepare'],
  }
}