Browse Source

Fuel HA Fencing plugin for puppet

All documentation provided in README.md

Implements blueprint fencing-in-puppet-manifests

* Use Fuel corosync from 5.1.1
* Add cluster-recheck-interval 3 min setting
* Add parser functions and facts from Fuel library
* Add pre-build hook for dependencies:
  * puppetlabs/stdlib v 4.5.0
  * Fuel corosync v 5.1.1
* Add examples of YAML for fence_virsh, fence_ipmilan,
  fence_apc_snmp and fence topology

Change-Id: I15dc9ff747957f7d22ca3ccd12628423c3c5c8cc
Signed-off-by: Bogdan Dobrelya <bdobrelia@mirantis.com>
Bogdan Dobrelya 4 years ago
commit
7382d88ccf
39 changed files with 2085 additions and 0 deletions
  1. 5
    0
      .gitignore
  2. 202
    0
      LICENSE
  3. 178
    0
      README.md
  4. 21
    0
      deployment_scripts/puppet/manifests/site.pp
  5. 9
    0
      deployment_scripts/puppet/modules/pcs_fencing/.fixtures.yml
  6. 3
    0
      deployment_scripts/puppet/modules/pcs_fencing/.gitignore
  7. 16
    0
      deployment_scripts/puppet/modules/pcs_fencing/Gemfile
  8. 56
    0
      deployment_scripts/puppet/modules/pcs_fencing/Gemfile.lock
  9. 201
    0
      deployment_scripts/puppet/modules/pcs_fencing/LICENSE
  10. 87
    0
      deployment_scripts/puppet/modules/pcs_fencing/README.md
  11. 7
    0
      deployment_scripts/puppet/modules/pcs_fencing/Rakefile
  12. 117
    0
      deployment_scripts/puppet/modules/pcs_fencing/examples/pcs_fencing.yaml
  13. 43
    0
      deployment_scripts/puppet/modules/pcs_fencing/examples/pcs_fencing_virsh.yaml
  14. 15
    0
      deployment_scripts/puppet/modules/pcs_fencing/lib/facter/fencing_config.rb
  15. 16
    0
      deployment_scripts/puppet/modules/pcs_fencing/lib/facter/naily.rb
  16. 9
    0
      deployment_scripts/puppet/modules/pcs_fencing/lib/facter/pacemaker_hostname.rb
  17. 13
    0
      deployment_scripts/puppet/modules/pcs_fencing/lib/puppet/parser/functions/filter_hash.rb
  18. 9
    0
      deployment_scripts/puppet/modules/pcs_fencing/lib/puppet/parser/functions/filter_nodes.rb
  19. 24
    0
      deployment_scripts/puppet/modules/pcs_fencing/lib/puppet/parser/functions/parseyaml.rb
  20. 144
    0
      deployment_scripts/puppet/modules/pcs_fencing/lib/puppet/provider/corosync.rb
  21. 134
    0
      deployment_scripts/puppet/modules/pcs_fencing/lib/puppet/provider/cs_fencetopo/crm.rb
  22. 55
    0
      deployment_scripts/puppet/modules/pcs_fencing/lib/puppet/type/cs_fencetopo.rb
  23. 44
    0
      deployment_scripts/puppet/modules/pcs_fencing/manifests/fencing.pp
  24. 41
    0
      deployment_scripts/puppet/modules/pcs_fencing/manifests/fencing_primitives.pp
  25. 36
    0
      deployment_scripts/puppet/modules/pcs_fencing/metadata.json
  26. 66
    0
      deployment_scripts/puppet/modules/pcs_fencing/spec/classes/fencing_primitives_spec.rb
  27. 87
    0
      deployment_scripts/puppet/modules/pcs_fencing/spec/defines/fencing_spec.rb
  28. 60
    0
      deployment_scripts/puppet/modules/pcs_fencing/spec/fixtures/cib/cib.xml
  29. 54
    0
      deployment_scripts/puppet/modules/pcs_fencing/spec/fixtures/cib/cib_no_topo.xml
  30. 1
    0
      deployment_scripts/puppet/modules/pcs_fencing/spec/spec_helper.rb
  31. 150
    0
      deployment_scripts/puppet/modules/pcs_fencing/spec/unit/puppet/provider/cs_fencetopo/crm_spec.rb
  32. 92
    0
      deployment_scripts/puppet/modules/pcs_fencing/spec/unit/puppet/type/cs_fencetopo_spec.rb
  33. 24
    0
      deployment_scripts/puppet/modules/pcs_fencing/test/site.pp
  34. 7
    0
      environment_config.yaml
  35. 25
    0
      metadata.yaml
  36. 26
    0
      pre_build_hook
  37. 0
    0
      repositories/centos/.gitkeep
  38. 0
    0
      repositories/ubuntu/.gitkeep
  39. 8
    0
      tasks.yaml

+ 5
- 0
.gitignore View File

@@ -0,0 +1,5 @@
1
+deployment_scripts/puppet/modules/corosync
2
+deployment_scripts/puppet/modules/stdlib
3
+.build
4
+tmp
5
+*.fp

+ 202
- 0
LICENSE View File

@@ -0,0 +1,202 @@
1
+Apache License
2
+                           Version 2.0, January 2004
3
+                        http://www.apache.org/licenses/
4
+
5
+   TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
6
+
7
+   1. Definitions.
8
+
9
+      "License" shall mean the terms and conditions for use, reproduction,
10
+      and distribution as defined by Sections 1 through 9 of this document.
11
+
12
+      "Licensor" shall mean the copyright owner or entity authorized by
13
+      the copyright owner that is granting the License.
14
+
15
+      "Legal Entity" shall mean the union of the acting entity and all
16
+      other entities that control, are controlled by, or are under common
17
+      control with that entity. For the purposes of this definition,
18
+      "control" means (i) the power, direct or indirect, to cause the
19
+      direction or management of such entity, whether by contract or
20
+      otherwise, or (ii) ownership of fifty percent (50%) or more of the
21
+      outstanding shares, or (iii) beneficial ownership of such entity.
22
+
23
+      "You" (or "Your") shall mean an individual or Legal Entity
24
+      exercising permissions granted by this License.
25
+
26
+      "Source" form shall mean the preferred form for making modifications,
27
+      including but not limited to software source code, documentation
28
+      source, and configuration files.
29
+
30
+      "Object" form shall mean any form resulting from mechanical
31
+      transformation or translation of a Source form, including but
32
+      not limited to compiled object code, generated documentation,
33
+      and conversions to other media types.
34
+
35
+      "Work" shall mean the work of authorship, whether in Source or
36
+      Object form, made available under the License, as indicated by a
37
+      copyright notice that is included in or attached to the work
38
+      (an example is provided in the Appendix below).
39
+
40
+      "Derivative Works" shall mean any work, whether in Source or Object
41
+      form, that is based on (or derived from) the Work and for which the
42
+      editorial revisions, annotations, elaborations, or other modifications
43
+      represent, as a whole, an original work of authorship. For the purposes
44
+      of this License, Derivative Works shall not include works that remain
45
+      separable from, or merely link (or bind by name) to the interfaces of,
46
+      the Work and Derivative Works thereof.
47
+
48
+      "Contribution" shall mean any work of authorship, including
49
+      the original version of the Work and any modifications or additions
50
+      to that Work or Derivative Works thereof, that is intentionally
51
+      submitted to Licensor for inclusion in the Work by the copyright owner
52
+      or by an individual or Legal Entity authorized to submit on behalf of
53
+      the copyright owner. For the purposes of this definition, "submitted"
54
+      means any form of electronic, verbal, or written communication sent
55
+      to the Licensor or its representatives, including but not limited to
56
+      communication on electronic mailing lists, source code control systems,
57
+      and issue tracking systems that are managed by, or on behalf of, the
58
+      Licensor for the purpose of discussing and improving the Work, but
59
+      excluding communication that is conspicuously marked or otherwise
60
+      designated in writing by the copyright owner as "Not a Contribution."
61
+
62
+      "Contributor" shall mean Licensor and any individual or Legal Entity
63
+      on behalf of whom a Contribution has been received by Licensor and
64
+      subsequently incorporated within the Work.
65
+
66
+   2. Grant of Copyright License. Subject to the terms and conditions of
67
+      this License, each Contributor hereby grants to You a perpetual,
68
+      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
69
+      copyright license to reproduce, prepare Derivative Works of,
70
+      publicly display, publicly perform, sublicense, and distribute the
71
+      Work and such Derivative Works in Source or Object form.
72
+
73
+   3. Grant of Patent License. Subject to the terms and conditions of
74
+      this License, each Contributor hereby grants to You a perpetual,
75
+      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
76
+      (except as stated in this section) patent license to make, have made,
77
+      use, offer to sell, sell, import, and otherwise transfer the Work,
78
+      where such license applies only to those patent claims licensable
79
+      by such Contributor that are necessarily infringed by their
80
+      Contribution(s) alone or by combination of their Contribution(s)
81
+      with the Work to which such Contribution(s) was submitted. If You
82
+      institute patent litigation against any entity (including a
83
+      cross-claim or counterclaim in a lawsuit) alleging that the Work
84
+      or a Contribution incorporated within the Work constitutes direct
85
+      or contributory patent infringement, then any patent licenses
86
+      granted to You under this License for that Work shall terminate
87
+      as of the date such litigation is filed.
88
+
89
+   4. Redistribution. You may reproduce and distribute copies of the
90
+      Work or Derivative Works thereof in any medium, with or without
91
+      modifications, and in Source or Object form, provided that You
92
+      meet the following conditions:
93
+
94
+      (a) You must give any other recipients of the Work or
95
+          Derivative Works a copy of this License; and
96
+
97
+      (b) You must cause any modified files to carry prominent notices
98
+          stating that You changed the files; and
99
+
100
+      (c) You must retain, in the Source form of any Derivative Works
101
+          that You distribute, all copyright, patent, trademark, and
102
+          attribution notices from the Source form of the Work,
103
+          excluding those notices that do not pertain to any part of
104
+          the Derivative Works; and
105
+
106
+      (d) If the Work includes a "NOTICE" text file as part of its
107
+          distribution, then any Derivative Works that You distribute must
108
+          include a readable copy of the attribution notices contained
109
+          within such NOTICE file, excluding those notices that do not
110
+          pertain to any part of the Derivative Works, in at least one
111
+          of the following places: within a NOTICE text file distributed
112
+          as part of the Derivative Works; within the Source form or
113
+          documentation, if provided along with the Derivative Works; or,
114
+          within a display generated by the Derivative Works, if and
115
+          wherever such third-party notices normally appear. The contents
116
+          of the NOTICE file are for informational purposes only and
117
+          do not modify the License. You may add Your own attribution
118
+          notices within Derivative Works that You distribute, alongside
119
+          or as an addendum to the NOTICE text from the Work, provided
120
+          that such additional attribution notices cannot be construed
121
+          as modifying the License.
122
+
123
+      You may add Your own copyright statement to Your modifications and
124
+      may provide additional or different license terms and conditions
125
+      for use, reproduction, or distribution of Your modifications, or
126
+      for any such Derivative Works as a whole, provided Your use,
127
+      reproduction, and distribution of the Work otherwise complies with
128
+      the conditions stated in this License.
129
+
130
+   5. Submission of Contributions. Unless You explicitly state otherwise,
131
+      any Contribution intentionally submitted for inclusion in the Work
132
+      by You to the Licensor shall be under the terms and conditions of
133
+      this License, without any additional terms or conditions.
134
+      Notwithstanding the above, nothing herein shall supersede or modify
135
+      the terms of any separate license agreement you may have executed
136
+      with Licensor regarding such Contributions.
137
+
138
+   6. Trademarks. This License does not grant permission to use the trade
139
+      names, trademarks, service marks, or product names of the Licensor,
140
+      except as required for reasonable and customary use in describing the
141
+      origin of the Work and reproducing the content of the NOTICE file.
142
+
143
+   7. Disclaimer of Warranty. Unless required by applicable law or
144
+      agreed to in writing, Licensor provides the Work (and each
145
+      Contributor provides its Contributions) on an "AS IS" BASIS,
146
+      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
147
+      implied, including, without limitation, any warranties or conditions
148
+      of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
149
+      PARTICULAR PURPOSE. You are solely responsible for determining the
150
+      appropriateness of using or redistributing the Work and assume any
151
+      risks associated with Your exercise of permissions under this License.
152
+
153
+   8. Limitation of Liability. In no event and under no legal theory,
154
+      whether in tort (including negligence), contract, or otherwise,
155
+      unless required by applicable law (such as deliberate and grossly
156
+      negligent acts) or agreed to in writing, shall any Contributor be
157
+      liable to You for damages, including any direct, indirect, special,
158
+      incidental, or consequential damages of any character arising as a
159
+      result of this License or out of the use or inability to use the
160
+      Work (including but not limited to damages for loss of goodwill,
161
+      work stoppage, computer failure or malfunction, or any and all
162
+      other commercial damages or losses), even if such Contributor
163
+      has been advised of the possibility of such damages.
164
+
165
+   9. Accepting Warranty or Additional Liability. While redistributing
166
+      the Work or Derivative Works thereof, You may choose to offer,
167
+      and charge a fee for, acceptance of support, warranty, indemnity,
168
+      or other liability obligations and/or rights consistent with this
169
+      License. However, in accepting such obligations, You may act only
170
+      on Your own behalf and on Your sole responsibility, not on behalf
171
+      of any other Contributor, and only if You agree to indemnify,
172
+      defend, and hold each Contributor harmless for any liability
173
+      incurred by, or claims asserted against, such Contributor by reason
174
+      of your accepting any such warranty or additional liability.
175
+
176
+   END OF TERMS AND CONDITIONS
177
+
178
+   APPENDIX: How to apply the Apache License to your work.
179
+
180
+      To apply the Apache License to your work, attach the following
181
+      boilerplate notice, with the fields enclosed by brackets "{}"
182
+      replaced with your own identifying information. (Don't include
183
+      the brackets!)  The text should be enclosed in the appropriate
184
+      comment syntax for the file format. We also recommend that a
185
+      file or class name and description of purpose be included on the
186
+      same "printed page" as the copyright notice for easier
187
+      identification within third-party archives.
188
+
189
+   Copyright {yyyy} {name of copyright owner}
190
+
191
+   Licensed under the Apache License, Version 2.0 (the "License");
192
+   you may not use this file except in compliance with the License.
193
+   You may obtain a copy of the License at
194
+
195
+       http://www.apache.org/licenses/LICENSE-2.0
196
+
197
+   Unless required by applicable law or agreed to in writing, software
198
+   distributed under the License is distributed on an "AS IS" BASIS,
199
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
200
+   See the License for the specific language governing permissions and
201
+   limitations under the License.
202
+

+ 178
- 0
README.md View File

@@ -0,0 +1,178 @@
1
+Fuel Fencing Plugin
2
+===================
3
+
4
+#### Table of Contents
5
+
6
+1. [Overview - What is the Fuel fencing plugin?](#overview)
7
+2. [Plugin Description - What does the plugin do?](#plugin-description)
8
+3. [Setup - The basics of getting started with Fuel fencing plugin](#setup)
9
+4. [Implementation - An under-the-hood peek at what the plugin is doing](#implementation)
10
+5. [Limitations - OS compatibility, etc.](#limitations)
11
+6. [Development - Guide for contributing to the plugin](#development)
12
+7. [Contributors - Those with commits](#contributors)
13
+8. [Release Notes - Notes on the most recent updates to the plugin](#release-notes)
14
+
15
+Overview
16
+--------
17
+
18
+The Fuel fencing plugin is a Puppet configuration module being executed as an
19
+additional post deployment step in order to provide HA fencing (STONITH based)
20
+of a failed cluster nodes.
21
+The plugin itself is used to describe a datacenter's HW power management
22
+configuration - such as PDU outlets, IPMI and other agents - and represent
23
+it as a fencing topology for Corosync & Pacemaker cluster.
24
+
25
+[About Fuel plugins](https://software.mirantis.com/mirantis-openstack-fuel-plug-in-development/)
26
+
27
+Plugin Description
28
+------------------
29
+
30
+The Fuel fencing plugin is intended to provide STONITHing of the failed nodes
31
+in Corosync & Pacemaker cluster.
32
+Fencing plugin operates the YAML data structures and extends the Fuel YAML
33
+configuration file.
34
+
35
+It suggests the manual definition of the YAML data structures with all required
36
+parameters for existing power management (PM) devices for every controller node.
37
+It is up to the user to collect and verify all the needed IP adresses, credentials,
38
+power outlets layouts, BM hosts to PM devices mappings and other parameters.
39
+
40
+This plugin also installs fence-agents package and assumes
41
+there is the one avaiable in the OS repositories.
42
+
43
+Setup
44
+-----
45
+
46
+### Installing Fencing plugin
47
+
48
+Please refer to the [plugins dev guide](http://docs.mirantis.com/fuel/fuel-6.0/plugin-dev.html#what-is-pluggable-architecture)
49
+Note that in order to build this plugin the following tools must present:
50
+* rsync
51
+* wget
52
+
53
+### Beginning with Fencing plugin
54
+
55
+* Create an HA environment and select the fencing policy (reboot, poweroff or
56
+  disabled) at the settings tab.
57
+
58
+* Assign roles to the nodes as always, but use Fuel CLI instead of Deploy button
59
+  to provision all nodes in the environment. Please note, that the power management
60
+  devices should be reachable from the management network via TCP protocol.
61
+
62
+* Define YAML configuration files for controller nodes and existing power management
63
+  (PM aka STONITH) devices. See an example in
64
+  ``deployment_scripts/puppet/modules/pcs_fencing/examples/pcs_fencing.yaml``.
65
+
66
+  In the given example we assume 'reboot' policy, which is a hard resetting of
67
+  the failed nodes in Pacemaker cluster. We define IPMI reset action and PSU OFF/ON
68
+  actions for ``fence_ipmilan`` and ``fence_apc_snmp`` agent types.
69
+  These agents will be contacted by Pacemaker stonith-ng daemon to STONITH controller
70
+  nodes (there are 3 of them according to the given fence topology) in the following
71
+  order:
72
+
73
+  * If IPMI device reported OK on reset action requested, STONITH is completed.
74
+  * if IPMI device cannot succeed on reset action for some reason, PSU OFF action
75
+    will be requested.
76
+  * In the case of PSU OFF action success, PSU ON action will be requested as well.
77
+  * In the case of both OFF and ON actions success, STONITH is completed OK.
78
+  * If either of them failed, repeat from the step 1, untill timeout exceeded.
79
+    (if timeout exceeded, STONITH is failed)
80
+
81
+  For other controllers, the same configuration stanza should be manually populated.
82
+  IP addresses, credentials, delay and indexes of the power outlets (in case of PDU/PSU)
83
+  of STONISH devices being connected by these agents should be updated as well as
84
+  node names.
85
+
86
+  Please note, that each controller node should have configured all of its fence agent
87
+  types ``delay`` parameters with an increased values. That is required in order to
88
+  resolve a mutual fencing situations then the nodes are triyng to STONITH each other.
89
+  For example, if you have 3 controllers, set all delay values as 0 for 1st controller,
90
+  10 - for 2nd one and 20 seconds - for the last one. The other timeouts should be
91
+  as well adjusted as the following:
92
+  ```
93
+  delay + shell_timeout + login_timeout < power_wait < power_timeout
94
+  ```
95
+
96
+  Fencing topology could vary for controller nodes but usually it is the same.
97
+  It provides an ordering of STONITH agents to call in case of the fencing actions.
98
+  It is recommended to configure several types of the fencing devices and put
99
+  them to the dedicated admin network. This network should be either directly connected
100
+  or reached from the management interfaces of controller nodes in order to provide a
101
+  connectivity to the fencing devices.
102
+
103
+  In the given example we define the same topology for node-10 and node-11 and slightly
104
+  different one for node-12 - just to illustrate that each node could have a different
105
+  fence agent types configured, hence, the different topology as well. So, we configure
106
+  nodes 10 and 11 to rely on IPMI and PSU devices, while the node 12 is a virtual node
107
+  and relies on virsh agent.
108
+
109
+  Please also note, that the names of nodes in fence topology stanza and ``pcmk_*``
110
+  parameters should be specified as FQDN names in case of RedHat OS family and as a
111
+  short names in case of Debian OS family. That is related to the node naming rules in
112
+  Pacemaker cluster in different OS types.
113
+ 
114
+* Put created fencing configuration YAML files as ``/etc/pcs_fencing.yaml``
115
+  for corresponding controller nodes.
116
+
117
+* Deploy HA environment either by CLI command or Deploy button
118
+
119
+TODO(bogdando) finish the guide, add agents and devices verification commands
120
+
121
+Implementation
122
+--------------
123
+
124
+### Fuel Fencing plugin
125
+
126
+This plugin is a combination of Puppet module and metadata required to
127
+describe and configure the fencing topology for Corosync & Pacemaker
128
+cluster. The plugin includes custom puppet module pcs_fencing and as a dependencies,
129
+custom corosync module and puppetlabs/stdlib module v4.5.0.
130
+
131
+It changes global cluster properties:
132
+* cluster-recheck-interval = 3 minutes
133
+* stonith-enabled = True
134
+
135
+It creates a set of STONITH primitives in Pacemaker cluster and runs them in a way,
136
+that ensures the node will never try to shoot itself (-inf location constraint).
137
+It configures a fencing topology singleton primitive in Pacemaker cluster.
138
+It uses crm command line tool which is deprecated and will be replaced to pcs later.
139
+
140
+Limitations
141
+-----------
142
+
143
+* It is not recommended to use this plugin, if controller nodes contain any additional
144
+  roles (such as storage, monitoring, compute) in Openstack environment, because
145
+  STONITH'ed node in Pacemaker cluster will bring these additional roles residing at
146
+  this node down as well.
147
+* Can be used only with the Debian and RedHat OS families with crm command line tool
148
+  available.
149
+
150
+Development
151
+-----------
152
+
153
+Developer documentation for the entire Fuel project.
154
+
155
+* https://wiki.openstack.org/wiki/Fuel#Where_can_documentation_be_found
156
+
157
+Contributors
158
+------------
159
+
160
+Will be added later
161
+
162
+Versioning
163
+----------
164
+
165
+This module has been given version 6 to track the Fuel releases. The
166
+versioning for plugin releases are as follows:
167
+
168
+```
169
+Plugin :: Fuel version
170
+6.0.0  -> 6.0
171
+```
172
+
173
+Release Notes
174
+-------------
175
+
176
+*** 6.0.0 *** 
177
+
178
+* This is the initial release of this plugin.

+ 21
- 0
deployment_scripts/puppet/manifests/site.pp View File

@@ -0,0 +1,21 @@
1
+$fuel_settings = parseyaml($astute_settings_yaml)
2
+
3
+# Fetch fencing policy and settings
4
+$fence_policy = $::fuel_settings['ha_fencing']['fence_policy']
5
+$fencing_enabled  = $fence_policy ? { 'disabled'=>false, 'reboot'=>true, 'poweroff'=>true, default=>false }
6
+
7
+if $fencing_enabled {
8
+  $fencing_settings = parseyaml($fencing_settings_yaml)
9
+  $fence_primitives = $::fencing_settings['fence_primitives']
10
+  $fence_topology   = $::fencing_settings['fence_topology']
11
+
12
+  $nodes_hash = $::fuel_settings['nodes']
13
+  $controllers = concat(filter_nodes($nodes_hash,'role','primary-controller'), filter_nodes($nodes_hash,'role','controller'))
14
+
15
+  include stdlib
16
+  class { '::pcs_fencing::fencing_primitives':
17
+    fence_primitives => $fence_primitives,
18
+    fence_topology   => $fence_topology,
19
+    nodes            => $controllers,
20
+  }
21
+}

+ 9
- 0
deployment_scripts/puppet/modules/pcs_fencing/.fixtures.yml View File

@@ -0,0 +1,9 @@
1
+fixtures:
2
+  repositories:
3
+    #corosync: 'https://github.com/puppetlabs/puppetlabs-corosync.git'
4
+    #stdlib: 'https://github.com/puppetlabs/puppetlabs-stdlib.git'
5
+      
6
+  symlinks:
7
+    pcs_fencing: "#{source_dir}"
8
+    corosync: "#{source_dir}/../corosync"
9
+    stdlib: "#{source_dir}/../stdlib"

+ 3
- 0
deployment_scripts/puppet/modules/pcs_fencing/.gitignore View File

@@ -0,0 +1,3 @@
1
+Gemfile.lock
2
+spec/fixtures
3
+.bundle

+ 16
- 0
deployment_scripts/puppet/modules/pcs_fencing/Gemfile View File

@@ -0,0 +1,16 @@
1
+source 'https://rubygems.org'
2
+
3
+group :development, :test do
4
+  gem 'puppetlabs_spec_helper', :require => false
5
+  gem 'puppet-lint', '~> 0.3.2'
6
+  gem 'rake', '10.1.1'
7
+  gem 'rspec', '< 2.99'
8
+  gem 'json'
9
+  gem 'webmock'
10
+end
11
+
12
+if puppetversion = ENV['PUPPET_GEM_VERSION']
13
+  gem 'puppet', puppetversion, :require => false
14
+else
15
+  gem 'puppet', :require => false
16
+end

+ 56
- 0
deployment_scripts/puppet/modules/pcs_fencing/Gemfile.lock View File

@@ -0,0 +1,56 @@
1
+GEM
2
+  remote: https://rubygems.org/
3
+  specs:
4
+    addressable (2.3.6)
5
+    crack (0.4.2)
6
+      safe_yaml (~> 1.0.0)
7
+    diff-lcs (1.2.5)
8
+    facter (2.3.0)
9
+    hiera (1.3.4)
10
+      json_pure
11
+    json (1.8.1)
12
+    json_pure (1.8.1)
13
+    metaclass (0.0.4)
14
+    mocha (1.1.0)
15
+      metaclass (~> 0.0.1)
16
+    puppet (3.7.3)
17
+      facter (> 1.6, < 3)
18
+      hiera (~> 1.0)
19
+      json_pure
20
+    puppet-lint (0.3.2)
21
+    puppet-syntax (1.3.0)
22
+      rake
23
+    puppetlabs_spec_helper (0.8.2)
24
+      mocha
25
+      puppet-lint
26
+      puppet-syntax
27
+      rake
28
+      rspec
29
+      rspec-puppet
30
+    rake (10.1.1)
31
+    rspec (2.14.1)
32
+      rspec-core (~> 2.14.0)
33
+      rspec-expectations (~> 2.14.0)
34
+      rspec-mocks (~> 2.14.0)
35
+    rspec-core (2.14.8)
36
+    rspec-expectations (2.14.5)
37
+      diff-lcs (>= 1.1.3, < 2.0)
38
+    rspec-mocks (2.14.6)
39
+    rspec-puppet (1.0.1)
40
+      rspec
41
+    safe_yaml (1.0.4)
42
+    webmock (1.20.4)
43
+      addressable (>= 2.3.6)
44
+      crack (>= 0.3.2)
45
+
46
+PLATFORMS
47
+  ruby
48
+
49
+DEPENDENCIES
50
+  json
51
+  puppet
52
+  puppet-lint (~> 0.3.2)
53
+  puppetlabs_spec_helper
54
+  rake (= 10.1.1)
55
+  rspec (< 2.99)
56
+  webmock

+ 201
- 0
deployment_scripts/puppet/modules/pcs_fencing/LICENSE View File

@@ -0,0 +1,201 @@
1
+                                 Apache License
2
+                           Version 2.0, January 2004
3
+                        http://www.apache.org/licenses/
4
+
5
+   TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
6
+
7
+   1. Definitions.
8
+
9
+      "License" shall mean the terms and conditions for use, reproduction,
10
+      and distribution as defined by Sections 1 through 9 of this document.
11
+
12
+      "Licensor" shall mean the copyright owner or entity authorized by
13
+      the copyright owner that is granting the License.
14
+
15
+      "Legal Entity" shall mean the union of the acting entity and all
16
+      other entities that control, are controlled by, or are under common
17
+      control with that entity. For the purposes of this definition,
18
+      "control" means (i) the power, direct or indirect, to cause the
19
+      direction or management of such entity, whether by contract or
20
+      otherwise, or (ii) ownership of fifty percent (50%) or more of the
21
+      outstanding shares, or (iii) beneficial ownership of such entity.
22
+
23
+      "You" (or "Your") shall mean an individual or Legal Entity
24
+      exercising permissions granted by this License.
25
+
26
+      "Source" form shall mean the preferred form for making modifications,
27
+      including but not limited to software source code, documentation
28
+      source, and configuration files.
29
+
30
+      "Object" form shall mean any form resulting from mechanical
31
+      transformation or translation of a Source form, including but
32
+      not limited to compiled object code, generated documentation,
33
+      and conversions to other media types.
34
+
35
+      "Work" shall mean the work of authorship, whether in Source or
36
+      Object form, made available under the License, as indicated by a
37
+      copyright notice that is included in or attached to the work
38
+      (an example is provided in the Appendix below).
39
+
40
+      "Derivative Works" shall mean any work, whether in Source or Object
41
+      form, that is based on (or derived from) the Work and for which the
42
+      editorial revisions, annotations, elaborations, or other modifications
43
+      represent, as a whole, an original work of authorship. For the purposes
44
+      of this License, Derivative Works shall not include works that remain
45
+      separable from, or merely link (or bind by name) to the interfaces of,
46
+      the Work and Derivative Works thereof.
47
+
48
+      "Contribution" shall mean any work of authorship, including
49
+      the original version of the Work and any modifications or additions
50
+      to that Work or Derivative Works thereof, that is intentionally
51
+      submitted to Licensor for inclusion in the Work by the copyright owner
52
+      or by an individual or Legal Entity authorized to submit on behalf of
53
+      the copyright owner. For the purposes of this definition, "submitted"
54
+      means any form of electronic, verbal, or written communication sent
55
+      to the Licensor or its representatives, including but not limited to
56
+      communication on electronic mailing lists, source code control systems,
57
+      and issue tracking systems that are managed by, or on behalf of, the
58
+      Licensor for the purpose of discussing and improving the Work, but
59
+      excluding communication that is conspicuously marked or otherwise
60
+      designated in writing by the copyright owner as "Not a Contribution."
61
+
62
+      "Contributor" shall mean Licensor and any individual or Legal Entity
63
+      on behalf of whom a Contribution has been received by Licensor and
64
+      subsequently incorporated within the Work.
65
+
66
+   2. Grant of Copyright License. Subject to the terms and conditions of
67
+      this License, each Contributor hereby grants to You a perpetual,
68
+      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
69
+      copyright license to reproduce, prepare Derivative Works of,
70
+      publicly display, publicly perform, sublicense, and distribute the
71
+      Work and such Derivative Works in Source or Object form.
72
+
73
+   3. Grant of Patent License. Subject to the terms and conditions of
74
+      this License, each Contributor hereby grants to You a perpetual,
75
+      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
76
+      (except as stated in this section) patent license to make, have made,
77
+      use, offer to sell, sell, import, and otherwise transfer the Work,
78
+      where such license applies only to those patent claims licensable
79
+      by such Contributor that are necessarily infringed by their
80
+      Contribution(s) alone or by combination of their Contribution(s)
81
+      with the Work to which such Contribution(s) was submitted. If You
82
+      institute patent litigation against any entity (including a
83
+      cross-claim or counterclaim in a lawsuit) alleging that the Work
84
+      or a Contribution incorporated within the Work constitutes direct
85
+      or contributory patent infringement, then any patent licenses
86
+      granted to You under this License for that Work shall terminate
87
+      as of the date such litigation is filed.
88
+
89
+   4. Redistribution. You may reproduce and distribute copies of the
90
+      Work or Derivative Works thereof in any medium, with or without
91
+      modifications, and in Source or Object form, provided that You
92
+      meet the following conditions:
93
+
94
+      (a) You must give any other recipients of the Work or
95
+          Derivative Works a copy of this License; and
96
+
97
+      (b) You must cause any modified files to carry prominent notices
98
+          stating that You changed the files; and
99
+
100
+      (c) You must retain, in the Source form of any Derivative Works
101
+          that You distribute, all copyright, patent, trademark, and
102
+          attribution notices from the Source form of the Work,
103
+          excluding those notices that do not pertain to any part of
104
+          the Derivative Works; and
105
+
106
+      (d) If the Work includes a "NOTICE" text file as part of its
107
+          distribution, then any Derivative Works that You distribute must
108
+          include a readable copy of the attribution notices contained
109
+          within such NOTICE file, excluding those notices that do not
110
+          pertain to any part of the Derivative Works, in at least one
111
+          of the following places: within a NOTICE text file distributed
112
+          as part of the Derivative Works; within the Source form or
113
+          documentation, if provided along with the Derivative Works; or,
114
+          within a display generated by the Derivative Works, if and
115
+          wherever such third-party notices normally appear. The contents
116
+          of the NOTICE file are for informational purposes only and
117
+          do not modify the License. You may add Your own attribution
118
+          notices within Derivative Works that You distribute, alongside
119
+          or as an addendum to the NOTICE text from the Work, provided
120
+          that such additional attribution notices cannot be construed
121
+          as modifying the License.
122
+
123
+      You may add Your own copyright statement to Your modifications and
124
+      may provide additional or different license terms and conditions
125
+      for use, reproduction, or distribution of Your modifications, or
126
+      for any such Derivative Works as a whole, provided Your use,
127
+      reproduction, and distribution of the Work otherwise complies with
128
+      the conditions stated in this License.
129
+
130
+   5. Submission of Contributions. Unless You explicitly state otherwise,
131
+      any Contribution intentionally submitted for inclusion in the Work
132
+      by You to the Licensor shall be under the terms and conditions of
133
+      this License, without any additional terms or conditions.
134
+      Notwithstanding the above, nothing herein shall supersede or modify
135
+      the terms of any separate license agreement you may have executed
136
+      with Licensor regarding such Contributions.
137
+
138
+   6. Trademarks. This License does not grant permission to use the trade
139
+      names, trademarks, service marks, or product names of the Licensor,
140
+      except as required for reasonable and customary use in describing the
141
+      origin of the Work and reproducing the content of the NOTICE file.
142
+
143
+   7. Disclaimer of Warranty. Unless required by applicable law or
144
+      agreed to in writing, Licensor provides the Work (and each
145
+      Contributor provides its Contributions) on an "AS IS" BASIS,
146
+      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
147
+      implied, including, without limitation, any warranties or conditions
148
+      of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
149
+      PARTICULAR PURPOSE. You are solely responsible for determining the
150
+      appropriateness of using or redistributing the Work and assume any
151
+      risks associated with Your exercise of permissions under this License.
152
+
153
+   8. Limitation of Liability. In no event and under no legal theory,
154
+      whether in tort (including negligence), contract, or otherwise,
155
+      unless required by applicable law (such as deliberate and grossly
156
+      negligent acts) or agreed to in writing, shall any Contributor be
157
+      liable to You for damages, including any direct, indirect, special,
158
+      incidental, or consequential damages of any character arising as a
159
+      result of this License or out of the use or inability to use the
160
+      Work (including but not limited to damages for loss of goodwill,
161
+      work stoppage, computer failure or malfunction, or any and all
162
+      other commercial damages or losses), even if such Contributor
163
+      has been advised of the possibility of such damages.
164
+
165
+   9. Accepting Warranty or Additional Liability. While redistributing
166
+      the Work or Derivative Works thereof, You may choose to offer,
167
+      and charge a fee for, acceptance of support, warranty, indemnity,
168
+      or other liability obligations and/or rights consistent with this
169
+      License. However, in accepting such obligations, You may act only
170
+      on Your own behalf and on Your sole responsibility, not on behalf
171
+      of any other Contributor, and only if You agree to indemnify,
172
+      defend, and hold each Contributor harmless for any liability
173
+      incurred by, or claims asserted against, such Contributor by reason
174
+      of your accepting any such warranty or additional liability.
175
+
176
+   END OF TERMS AND CONDITIONS
177
+
178
+   APPENDIX: How to apply the Apache License to your work.
179
+
180
+      To apply the Apache License to your work, attach the following
181
+      boilerplate notice, with the fields enclosed by brackets "[]"
182
+      replaced with your own identifying information. (Don't include
183
+      the brackets!) The text should be enclosed in the appropriate
184
+      comment syntax for the file format. We also recommend that a
185
+      file or class name and description of purpose be included on the
186
+      same "printed page" as the copyright notice for easier
187
+      identification within third-party archives.
188
+
189
+   Copyright 2014 OpenStack Foundation
190
+
191
+   Licensed under the Apache License, Version 2.0 (the "License");
192
+   you may not use this file except in compliance with the License.
193
+   You may obtain a copy of the License at
194
+
195
+       http://www.apache.org/licenses/LICENSE-2.0
196
+
197
+   Unless required by applicable law or agreed to in writing, software
198
+   distributed under the License is distributed on an "AS IS" BASIS,
199
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
200
+   See the License for the specific language governing permissions and
201
+   limitations under the License.

+ 87
- 0
deployment_scripts/puppet/modules/pcs_fencing/README.md View File

@@ -0,0 +1,87 @@
1
+pcs_fencing
2
+===========
3
+
4
+#### Table of Contents
5
+
6
+1. [Overview - What is the pcs_fencing module?](#overview)
7
+2. [Module Description - What does the module do?](#module-description)
8
+3. [Setup - The basics of getting started with pcs_fencing](#setup)
9
+4. [Implementation - An under-the-hood peek at what the module is doing](#implementation)
10
+5. [Limitations - OS compatibility, etc.](#limitations)
11
+6. [Development - Guide for contributing to the module](#development)
12
+7. [Contributors - Those with commits](#contributors)
13
+8. [Release Notes - Notes on the most recent updates to the module](#release-notes)
14
+
15
+Overview
16
+--------
17
+
18
+TODO(bogdando) provide a link to Fuel plugin repo then ready.
19
+The pcs_fencing module is a part of Fencing plugin for Fuel and have no
20
+a separate code repository.
21
+The module itself is used to configure fencing primitives in Pacemaker
22
+and combine them into the Fencing topology.
23
+
24
+Module Description
25
+------------------
26
+
27
+The pcs_fencing module is intended to provide STONITH based HA fencing
28
+of the failed nodes in Corosync & Pacemaker cluster. This module
29
+cannot be used separately from Fuel Fencing plugin.
30
+Pcs_fencing module operates the data structures which are tight to the ones
31
+in Fuel YAML configuration file and has no its own parameters.
32
+This module also installs fence-agents package and assumes there is
33
+the one avaiable in the OS repositories.
34
+
35
+Setup
36
+-----
37
+
38
+### Installing pcs_fencing
39
+
40
+This module is being installed automatically as a part of Fuel Fencing
41
+plugin.
42
+The module's rspec tests could be run only after the plugin is built as
43
+its pre build hook will download required Fuel custom puppet module for corosync
44
+and puppetlabs/stdlib module.
45
+
46
+### Beginning with pcs_fencing
47
+
48
+Instructions for beginning with pcs_fencing will be added later.
49
+
50
+Implementation
51
+--------------
52
+
53
+### pcs_fencing
54
+
55
+pcs_fencing is a combination of Puppet manifest and ruby code to delivery
56
+configuration and extra functionality through custom types, providers, parser
57
+functions and facts from Fuel library of puppet manifests.
58
+Note that it requires a custom module for corosync and includes a custom
59
+provider for fencing topology.
60
+
61
+Limitations
62
+-----------
63
+
64
+Limitations will be added as they are discovered.
65
+
66
+Development
67
+-----------
68
+
69
+Developer documentation for the entire Fuel project.
70
+
71
+* https://wiki.openstack.org/wiki/Fuel#Where_can_documentation_be_found
72
+
73
+Contributors
74
+------------
75
+
76
+Will be added later
77
+
78
+Versioning
79
+----------
80
+
81
+This module is being versioned as well as Fuel Fencing plugin.
82
+
83
+Release Notes
84
+-------------
85
+
86
+This module has no a separate release notes. See the release notes for
87
+Fuel Fencing plugin.

+ 7
- 0
deployment_scripts/puppet/modules/pcs_fencing/Rakefile View File

@@ -0,0 +1,7 @@
1
+require 'rubygems'
2
+require 'puppetlabs_spec_helper/rake_tasks'
3
+require 'puppet-lint/tasks/puppet-lint'
4
+
5
+PuppetLint.configuration.fail_on_warnings = true
6
+PuppetLint.configuration.send('disable_80chars')
7
+PuppetLint.configuration.ignore_paths = ["spec/**/*.pp", "pkg/**/*.pp"]

+ 117
- 0
deployment_scripts/puppet/modules/pcs_fencing/examples/pcs_fencing.yaml View File

@@ -0,0 +1,117 @@
1
+#fence_policy: reboot
2
+fence_topology:
3
+  node-10.test.local:
4
+    '1':
5
+     - ipmi_reset
6
+    '2':
7
+     - psu_off
8
+     - psu_on
9
+  node-11.test.local:
10
+    '1':
11
+     - ipmi_reset
12
+    '2':
13
+     - psu_off
14
+     - psu_on
15
+  node-12.test.local:
16
+    '1':
17
+     - virsh_reset
18
+fence_primitives:
19
+  ipmi_reset:
20
+    agent_type: fence_ipmilan
21
+    operations:
22
+      monitor:
23
+        interval: 3600s
24
+        timeout: 120s
25
+      start:
26
+        interval: '0'
27
+        timeout: 120s
28
+        on-fail: restart
29
+      stop:
30
+        interval: '0'
31
+        timeout: 1800s
32
+        on-fail: restart
33
+    meta:
34
+      migration-threshold: '5'
35
+      failure-timeout: '180'
36
+    parameters:
37
+      ipaddr: 10.11.12.13
38
+      login: ipmi_user
39
+      passwd: ipmi_pass
40
+      privlvl: operator
41
+      auth: password
42
+      power_wait: '15'
43
+      delay: '300'
44
+      action: reboot
45
+      pcmk_reboot_action: reboot
46
+      pcmk_off_action: reboot
47
+      pcmk_host_list: node-10.test.local
48
+  psu_off:
49
+    agent_type: fence_apc_snmp
50
+    operations:
51
+      monitor:
52
+        interval: 3600s
53
+        timeout: 120s
54
+      start:
55
+        interval: '0'
56
+        timeout: 120s
57
+        on-fail: restart
58
+      stop:
59
+        interval: '0'
60
+        timeout: 1800s
61
+        on-fail: fence
62
+    meta:
63
+      migration-threshold: '5'
64
+      failure-timeout: '180'
65
+    parameters:
66
+      ipaddr: 10.11.12.14
67
+      login: tripplite
68
+      community: QWErty123
69
+      snmp_auth_prot: MD5
70
+      snmp_priv_prot: DES
71
+      port: '5'
72
+      snmp_sec_level: authPriv
73
+      passwd: QWErty123
74
+      power_timeout: '30'
75
+      shell_timeout: '10'
76
+      login_timeout: '10'
77
+      power_wait: '15'
78
+      delay: '300'
79
+      action: 'off'
80
+      pcmk_reboot_action: 'off'
81
+      pcmk_off_action: 'off'
82
+      pcmk_host_list: node-10.test.local
83
+  psu_on:
84
+    agent_type: fence_apc_snmp
85
+    operations:
86
+      monitor:
87
+        interval: 3600s
88
+        timeout: 120s
89
+      start:
90
+        interval: '0'
91
+        timeout: 120s
92
+        on-fail: restart
93
+      stop:
94
+        interval: '0'
95
+        timeout: 1800s
96
+        on-fail: fence
97
+    meta:
98
+      migration-threshold: '5'
99
+      failure-timeout: '180'
100
+    parameters:
101
+      ipaddr: 10.11.12.14
102
+      login: tripplite
103
+      community: QWErty123
104
+      snmp_auth_prot: MD5
105
+      snmp_priv_prot: DES
106
+      port: '5'
107
+      snmp_sec_level: authPriv
108
+      passwd: QWErty123
109
+      power_timeout: '30'
110
+      shell_timeout: '10'
111
+      login_timeout: '10'
112
+      power_wait: '15'
113
+      delay: '300'
114
+      action: 'on'
115
+      pcmk_reboot_action: 'on'
116
+      pcmk_off_action: 'on'
117
+      pcmk_host_list: node-10.test.local

+ 43
- 0
deployment_scripts/puppet/modules/pcs_fencing/examples/pcs_fencing_virsh.yaml View File

@@ -0,0 +1,43 @@
1
+#fence_policy: reboot
2
+fence_topology:
3
+  node-7:
4
+    '1':
5
+     - virsh_reset
6
+  node-8:
7
+    '1':
8
+     - virsh_reset
9
+  node-9:
10
+    '1':
11
+     - virsh_reset
12
+fence_primitives:
13
+  virsh_reset:
14
+    agent_type: fence_virsh
15
+    operations:
16
+      monitor:
17
+        interval: 3600s
18
+        timeout: 120s
19
+      start:
20
+        interval: '0'
21
+        timeout: 120s
22
+        on-fail: restart
23
+      stop:
24
+        interval: '0'
25
+        timeout: 1800s
26
+        on-fail: restart
27
+    meta:
28
+      migration-threshold: '5'
29
+      failure-timeout: '180'
30
+    parameters:
31
+      ipaddr: 10.108.5.1
32
+      login: virsh_ssh_user
33
+      passwd: virsh_ssh_pass
34
+      power_wait: '15'
35
+      power_timeout: '20'
36
+      shell_timeout: '10'
37
+      login_timeout: '5'
38
+      secure: true
39
+      delay: '300'
40
+      action: reboot
41
+      pcmk_reboot_action: reboot
42
+      pcmk_off_action: reboot
43
+      pcmk_host_map: 'node-7:env60_slave-07'

+ 15
- 0
deployment_scripts/puppet/modules/pcs_fencing/lib/facter/fencing_config.rb View File

@@ -0,0 +1,15 @@
1
+require 'facter'
2
+
3
+fencing_settings_path = ['/etc/pcs_fencing.yaml']
4
+
5
+fencing_settings_path.each do |fencing_file|
6
+  if File.exist?(fencing_file)
7
+    Facter.add('fencing_settings_file') do
8
+      setcode { fencing_file }
9
+    end
10
+    Facter.add('fencing_settings_yaml') do
11
+      setcode { File.read(fencing_file) }
12
+    end
13
+    break
14
+  end
15
+end

+ 16
- 0
deployment_scripts/puppet/modules/pcs_fencing/lib/facter/naily.rb View File

@@ -0,0 +1,16 @@
1
+require 'facter'
2
+
3
+# This file is created and managed by Astute
4
+astute_settings_path = ['/etc/fuel/astute.yaml', '/etc/astute.yaml']
5
+
6
+astute_settings_path.each do |astute_file|
7
+  if File.exist?(astute_file)
8
+    Facter.add('astute_settings_file') do
9
+      setcode { astute_file }
10
+    end
11
+    Facter.add('astute_settings_yaml') do
12
+      setcode { File.read(astute_file) }
13
+    end
14
+    break
15
+  end
16
+end

+ 9
- 0
deployment_scripts/puppet/modules/pcs_fencing/lib/facter/pacemaker_hostname.rb View File

@@ -0,0 +1,9 @@
1
+# Fact: pacemaker_hostname
2
+#
3
+# Purpose: Return name of the node used by Pacemaker
4
+#
5
+Facter.add(:pacemaker_hostname) do
6
+  setcode do
7
+    rv = Facter::Util::Resolution.exec('crm_node -n')
8
+  end
9
+end

+ 13
- 0
deployment_scripts/puppet/modules/pcs_fencing/lib/puppet/parser/functions/filter_hash.rb View File

@@ -0,0 +1,13 @@
1
+module Puppet::Parser::Functions
2
+  newfunction(:filter_hash, :type => :rvalue,  :doc => <<-EOS
3
+    Map array of hashes $arg0 to an array yielding 
4
+    an element from each hash by key $arg1
5
+    EOS
6
+ ) do |args|
7
+    hash = args[0]
8
+    field  = args[1]
9
+    hash.map do |e|
10
+      e[field]
11
+    end
12
+  end
13
+end

+ 9
- 0
deployment_scripts/puppet/modules/pcs_fencing/lib/puppet/parser/functions/filter_nodes.rb View File

@@ -0,0 +1,9 @@
1
+module Puppet::Parser::Functions
2
+  newfunction(:filter_nodes, :type => :rvalue) do |args|
3
+    name = args[1]
4
+    value  = args[2]
5
+    args[0].select do |it|
6
+      it[name] == value
7
+    end
8
+  end
9
+end

+ 24
- 0
deployment_scripts/puppet/modules/pcs_fencing/lib/puppet/parser/functions/parseyaml.rb View File

@@ -0,0 +1,24 @@
1
+#
2
+# parseyaml.rb
3
+#
4
+
5
+module Puppet::Parser::Functions
6
+  newfunction(:parseyaml, :type => :rvalue, :doc => <<-EOS
7
+This function accepts YAML as a string and converts it into the correct
8
+Puppet structure.
9
+    EOS
10
+  ) do |arguments|
11
+
12
+    if (arguments.size != 1) then
13
+      raise(Puppet::ParseError, "parseyaml(): Wrong number of arguments "+
14
+        "given #{arguments.size} for 1")
15
+    end
16
+
17
+    require 'yaml'
18
+
19
+    YAML::load(arguments[0])
20
+
21
+  end
22
+end
23
+
24
+# vim: set ts=2 sw=2 et :

+ 144
- 0
deployment_scripts/puppet/modules/pcs_fencing/lib/puppet/provider/corosync.rb View File

@@ -0,0 +1,144 @@
1
+require 'pp'
2
+require 'open3'
3
+require 'rexml/document'
4
+
5
+class Puppet::Provider::Corosync < Puppet::Provider
6
+
7
+  def self.dump_cib
8
+    self.block_until_ready
9
+    stdout = Open3.popen3("#{command(:crm)} configure show xml")[1].read
10
+    return stdout, nil
11
+  end
12
+
13
+  def try_command(command,resource_name,should=nil,cib=nil,timeout=120)
14
+    cmd = "#{command(:crm)} configure #{command} #{resource_name} #{should} ".rstrip
15
+    env = {}
16
+      if cib
17
+        env["CIB_shadow"]=cib.to_s
18
+      end
19
+    Timeout::timeout(timeout) do
20
+      debug("Issuing  #{cmd} for CIB #{cib}  ")
21
+      loop do
22
+        break  if exec_withenv(cmd,env) == 0
23
+        sleep 2
24
+      end
25
+    end
26
+  end
27
+
28
+  def exec_withenv(cmd,env=nil)
29
+    self.class.exec_withenv(cmd,env)
30
+  end
31
+
32
+  def self.exec_withenv(cmd,env=nil)
33
+    Process.fork  do
34
+      ENV.update(env) if !env.nil?
35
+      Process.exec(cmd)
36
+    end
37
+    Process.wait
38
+    $?.exitstatus
39
+  end
40
+
41
+  # Corosync takes a while to build the initial CIB configuration once the
42
+  # service is started for the first time.  This provides us a way to wait
43
+  # until we're up so we can make changes that don't disappear in to a black
44
+  # hole.
45
+
46
+  def self.block_until_ready(timeout = 120)
47
+    cmd = "#{command(:crm_attribute)} --type crm_config --query --name dc-version 2>/dev/null"
48
+    Timeout::timeout(timeout) do
49
+      until exec_withenv(cmd) == 0
50
+        debug('Corosync not ready, retrying')
51
+        sleep 2
52
+      end
53
+      # Sleeping a spare two since it seems that dc-version is returning before
54
+      # It is really ready to take config changes, but it is close enough.
55
+      # Probably need to find a better way to check for reediness.
56
+      sleep 2
57
+    end
58
+  end
59
+
60
+  def self.prefetch(resources)
61
+    instances.each do |prov|
62
+      if res = resources[prov.name.to_s]
63
+        res.provider = prov
64
+      end
65
+    end
66
+  end
67
+
68
+  def exists?
69
+    self.class.block_until_ready
70
+    Puppet.debug "Call exists? on cs_resource '#{@resource[:name]}'"
71
+    out = !(@property_hash[:ensure] == :absent or @property_hash.empty?)
72
+    Puppet.debug "Return: #{out}"
73
+    Puppet.debug "Current state:\n#{@property_hash.pretty_inspect}" if @property_hash.any?
74
+    out
75
+  end
76
+
77
+  def get_scope(type)
78
+      case type
79
+      when 'resource'
80
+          scope='resources'
81
+      when /^(colocation|order|location)$/
82
+          scope='constraints'
83
+      when 'rsc_defaults'
84
+          scope='rsc_defaults'
85
+      else
86
+          fail('unknown resource type')
87
+          scope=nil
88
+      end
89
+      return scope
90
+  end
91
+
92
+  def apply_changes(res_name,tmpfile,res_type)
93
+      env={}
94
+      shadow_name="#{res_type}_#{res_name}"
95
+      original_cib="/tmp/#{shadow_name}_orig.xml"
96
+      new_cib="/tmp/#{shadow_name}_new.xml"
97
+      begin
98
+        debug('trying to delete old shadow if exists')
99
+        crm_shadow("-b","-f","-D",shadow_name)
100
+      rescue Puppet::ExecutionFailure
101
+        debug('delete failed but proceeding anyway')
102
+      end
103
+      if !get_scope(res_type).nil?
104
+          cibadmin_scope = "-o #{get_scope(res_type)}"
105
+      else
106
+          cibadmin_scope = nil
107
+      end
108
+      crm_shadow("-b","-c",shadow_name)
109
+      env["CIB_shadow"] = shadow_name
110
+      orig_status = exec_withenv("#{command(:cibadmin)} #{cibadmin_scope} -Q > /tmp/#{shadow_name}_orig.xml", env)
111
+      #cibadmin returns code 6 if scope is empty
112
+      #in this case write empty file
113
+      if orig_status == 6 or File.open("/tmp/#{shadow_name}_orig.xml").read.empty?
114
+          cur_scope=REXML::Element.new(get_scope(res_type)).to_s
115
+          emptydoc=REXML::Document.new(cur_scope)
116
+          emptydoc.write(File.new("/tmp/#{shadow_name}_orig.xml",'w'))
117
+      end
118
+      exec_withenv("#{command(:crm)} configure load update #{tmpfile.path.to_s}",env)
119
+      exec_withenv("#{command(:cibadmin)} #{cibadmin_scope} -Q > /tmp/#{shadow_name}_new.xml",env)
120
+      patch = Open3.popen3("#{command(:crm_diff)} --original #{original_cib} --new #{new_cib}")[1].read
121
+      if patch.empty?
122
+          debug("no difference - nothing to apply")
123
+          return
124
+      end
125
+      xml_patch = REXML::Document.new(patch)
126
+      wrap_cib=REXML::Element.new('cib')
127
+      wrap_configuration=REXML::Element.new('configuration')
128
+      wrap_cib.add_element(wrap_configuration)
129
+      wrap_cib_a=Marshal.load(Marshal.dump(wrap_cib))
130
+      wrap_cib_r=Marshal.load(Marshal.dump(wrap_cib))
131
+      diff_a=REXML::XPath.first(xml_patch,'//diff-added')
132
+      diff_r=REXML::XPath.first(xml_patch,'//diff-removed')
133
+      diff_a_elements=diff_a.elements
134
+      diff_r_elements=diff_r.elements
135
+      wrap_configuration_a=REXML::XPath.first(wrap_cib_a,'//configuration')
136
+      wrap_configuration_r=REXML::XPath.first(wrap_cib_r,'//configuration')
137
+      diff_a_elements.each {|element| wrap_configuration_a.add_element(element)}
138
+      diff_r_elements.each {|element| wrap_configuration_r.add_element(element)}
139
+      diff_a.add_element(wrap_cib_a)
140
+      diff_r.add_element(wrap_cib_r)
141
+      cibadmin '--patch', '--sync-call', '--xml-text', xml_patch
142
+  end
143
+
144
+end

+ 134
- 0
deployment_scripts/puppet/modules/pcs_fencing/lib/puppet/provider/cs_fencetopo/crm.rb View File

@@ -0,0 +1,134 @@
1
+require 'pathname'
2
+require 'open3'
3
+require Pathname.new(__FILE__).dirname.dirname.expand_path + 'corosync'
4
+require 'rexml/document'
5
+
6
+Puppet::Type.type(:cs_fencetopo).provide(:crm, :parent => Puppet::Provider::Corosync) do
7
+  desc 'Specific provider for a rather specific type since I currently have no plan to
8
+        abstract corosync/pacemaker vs. keepalived. This provider will create or destroy
9
+        a singleton for fencing topology configuration.'
10
+
11
+  # Path to the crm binary for interacting with the cluster configuration.
12
+  commands :crm           => 'crm'
13
+  commands :cibadmin      => 'cibadmin'
14
+  commands :crm_attribute => 'crm_attribute'
15
+
16
+  def self.instances
17
+
18
+    block_until_ready
19
+
20
+    raw, status = dump_cib
21
+    doc = REXML::Document.new(raw)
22
+    nodes = []
23
+    fence_topology = {}
24
+    # return empty array, if there is no topology singleton configured in cib
25
+    stanzas = doc.root.elements['configuration/fencing-topology'] rescue nil
26
+    return [] if stanzas.nil?
27
+    # otherwise, parse cib for existing topology singleton and return it as provider instance
28
+    stanzas.each_element do |e|
29
+      items = e.attributes
30
+      line = { :fence_primitives => items['devices'], :node => items['target'], :index => items['index'] }
31
+      primitives = line[:fence_primitives].split(',')
32
+      if primitives.length > 1 then
33
+        agents = []
34
+        primitives.each { |primitive| agents << (/^stonith__([^__].+)__.*$/.match(primitive)[1] rescue 'primitive_name_parse_error') }
35
+      else
36
+        agents = [(/^stonith__([^__].+)__.*$/.match(primitives[0])[1] rescue 'primitive_name_parse_error')]
37
+      end
38
+      nodes.push(line[:node]) unless nodes.include?(line[:node])
39
+      fence_topology[line[:node]] = {} if fence_topology[line[:node]].nil?
40
+      fence_topology[line[:node]][line[:index]] = agents
41
+    end
42
+    property_instance = {
43
+      :name       => 'myfencetopo',
44
+      :ensure     => :present,
45
+      :fence_topology      => fence_topology,
46
+      :nodes      => nodes,
47
+      :provider   => self.name
48
+    }
49
+    [new(property_instance)]
50
+  end
51
+
52
+  # SET
53
+  def nodes=(should)
54
+    @property_hash[:nodes] = should
55
+  end
56
+
57
+  def fence_topology=(should)
58
+    @property_hash[:fence_topology] = should
59
+  end
60
+  #GET
61
+  def nodes
62
+    @property_hash[:nodes]
63
+  end
64
+
65
+  def fence_topology
66
+    @property_hash[:fence_topology]
67
+  end
68
+
69
+  def create
70
+    @property_hash = {
71
+      :name   => @resource[:name],
72
+      :ensure => :present,
73
+      :fence_topology  => @resource[:fence_topology],
74
+      :nodes => @resource[:nodes]
75
+    }
76
+    @property_hash[:cib] = @resource[:cib] if ! @resource[:cib].nil?
77
+  end
78
+
79
+  def destroy
80
+    debug("Removing fencing topology")
81
+    env = {}
82
+    env["CIB_shadow"] = @resource[:cib].to_s if !@resource[:cib].nil?
83
+    commands_to_exec = ''
84
+    commands_to_exec << "#{command(:cibadmin)} --scope fencing-topology --delete-all --force --xpath //fencing-level 2>&1"
85
+    commands_to_exec << "\n"
86
+    commands_to_exec << "#{command(:cibadmin)} --delete --xml-text '<fencing-topology/>' 2>&1"
87
+    exec_withenv(commands_to_exec, env)
88
+    @property_hash.clear
89
+  end
90
+
91
+  def exists?
92
+    self.class.block_until_ready
93
+    debug(@property_hash.inspect)
94
+    env = {}
95
+    env["CIB_shadow"] = @resource[:cib].to_s if !@resource[:cib].nil?
96
+    commands_to_exec = "#{command(:cibadmin)} --query --scope fencing-topology"
97
+    exec_withenv(commands_to_exec, env) == 0
98
+  end
99
+
100
+  def flush
101
+    unless @property_hash.empty? or self.class.instances != []
102
+      self.class.block_until_ready
103
+      args = ''
104
+      @property_hash[:nodes].each do |node|
105
+        # extract node's short name from its fqdn, if defined
106
+        shortname = /^([^.]+)\..*$/.match(node)[1] rescue node
107
+        pos = 1
108
+        # start crafting node's topology from position #1,
109
+        # nodes' topology lines should be separated by whitespace
110
+        line = " #{node}: "
111
+        @property_hash[:fence_topology][node].sort.each do |index, primitives|
112
+           primitives.each do |primitive|
113
+             line += case pos
114
+               # first primitive should be put after its node fqdn
115
+               when 1 then "stonith__#{primitive}__#{shortname}"
116
+               # all primitives with the same indexes should be grouped together, coma separated
117
+               when index then ",stonith__#{primitive}__#{shortname}"
118
+               # all groups with different indexes should be separated by whitespace
119
+               else " stonith__#{primitive}__#{shortname}"
120
+             end
121
+             pos = index if index != pos
122
+           end
123
+        end
124
+        args += line
125
+        # proceed to the next node
126
+      end
127
+      # send topology lines crafted for all nodes to crm
128
+      env = {}
129
+      env["CIB_shadow"] = @resource[:cib].to_s if !@resource[:cib].nil?
130
+      command_to_exec = "#{command(:crm)} --force configure fencing_topology#{args} 2>&1"
131
+      exec_withenv(command_to_exec, env)
132
+    end
133
+  end
134
+end

+ 55
- 0
deployment_scripts/puppet/modules/pcs_fencing/lib/puppet/type/cs_fencetopo.rb View File

@@ -0,0 +1,55 @@
1
+module Puppet
2
+  newtype(:cs_fencetopo) do
3
+    @doc = "Type for manipulating corosync/pacemaker configuration for fencing topology.
4
+      More information on fencing topologies can be found here:
5
+      * http://clusterlabs.org/wiki/Fencing_topology
6
+      "
7
+
8
+    ensurable
9
+
10
+    newparam(:name) do
11
+      desc "Fencing topology name reference."
12
+
13
+      isnamevar
14
+    end
15
+
16
+    newparam(:cib) do
17
+      desc "Corosync applies its configuration immediately. Using a CIB allows
18
+            you to group multiple primitives and relationships to be applied at
19
+            once. This can be necessary to insert complex configurations into
20
+            Corosync correctly.
21
+
22
+            This paramater sets the CIB this order should be created in. A
23
+            cs_shadow resource with a title of the same name as this value should
24
+            also be added to your manifest."
25
+    end
26
+
27
+    newproperty(:nodes, :array_matching=>:all) do
28
+      desc "An array with cluster nodes' fqdns"
29
+      isrequired
30
+    end
31
+
32
+    newproperty(:fence_topology) do
33
+      desc "A hash with predefined fence topology."
34
+      isrequired
35
+      validate do |fence_topology|
36
+        raise Puppet::Error, "Puppet::Type::Cs_FenceTopo: fencing topology entries must be a hashes." unless fence_topology.is_a? Hash
37
+      end
38
+      defaultto Hash.new
39
+    end
40
+
41
+    autorequire(:service) do
42
+      [ 'corosync' ]
43
+    end
44
+
45
+    autorequire(:cs_shadow) do
46
+      autos = []
47
+      if @parameters[:cib]
48
+        autos << @parameters[:cib].value
49
+      end
50
+
51
+      autos
52
+    end
53
+
54
+  end
55
+end

+ 44
- 0
deployment_scripts/puppet/modules/pcs_fencing/manifests/fencing.pp View File

@@ -0,0 +1,44 @@
1
+# == Define: pcs_fencing::fencing
2
+#
3
+# Configure STONITH resources for corosync/pacemaker.
4
+#
5
+define pcs_fencing::fencing (
6
+  $agent_type,
7
+  $parameters    = false,
8
+  $operations    = false,
9
+  $meta          = false,
10
+){
11
+  $res_name = "stonith__${title}__${::hostname}"
12
+
13
+  cs_resource { $res_name:
14
+    ensure              => present,
15
+    provided_by         => 'pacemaker',
16
+    primitive_class     => 'stonith',
17
+    primitive_type      => $agent_type,
18
+    parameters          => $parameters,
19
+    operations          => $operations,
20
+    metadata            => $meta,
21
+  }
22
+
23
+  cs_location {"location__prohibit__${res_name}":
24
+    node_name  => $::pacemaker_hostname,
25
+    node_score => '-INFINITY',
26
+    primitive  => $res_name,
27
+  }
28
+
29
+  cs_location {"location__allow__${res_name}":
30
+    primitive  => $res_name,
31
+    rules     => [
32
+      {
33
+        'score'   => '100',
34
+        'boolean' => '',
35
+        'expressions' => [
36
+          {'attribute'=>"#uname",'operation'=>'ne','value'=>$::pacemaker_hostname},
37
+        ],
38
+      },
39
+    ],
40
+  }
41
+  
42
+  Cs_resource[$res_name] ->
43
+  Cs_location<||>
44
+}

+ 41
- 0
deployment_scripts/puppet/modules/pcs_fencing/manifests/fencing_primitives.pp View File

@@ -0,0 +1,41 @@
1
+# Creates fencing primitives and topology for given nodes.
2
+# Assumes all nodes have the same OS installed
3
+#
4
+class pcs_fencing::fencing_primitives (
5
+  $fence_primitives,
6
+  $fence_topology,
7
+  $nodes,
8
+) {
9
+  case $::osfamily {
10
+    'RedHat': {
11
+       $names = filter_hash($nodes, 'fqdn')
12
+    }
13
+    'Debian': {
14
+       $names = filter_hash($nodes, 'name')
15
+    }
16
+    default: {
17
+      fail("Unsupported osfamily: ${::osfamily} operatingsystem: ${::operatingsystem}, module ${module_name} only support osfamily RedHat and Debian")
18
+    }
19
+  }
20
+
21
+  anchor {'Fencing primitives start':}
22
+  anchor {'Fencing primitives end':}
23
+
24
+  create_resources('::pcs_fencing::fencing', $fence_primitives)
25
+
26
+  cs_fencetopo { 'fencing_topology':
27
+    ensure         => present,
28
+    fence_topology => $fence_topology,
29
+    nodes          => $names,
30
+  }
31
+  cs_property { 'stonith-enabled': value  => 'true' }
32
+  cs_property { 'cluster-recheck-interval':  value  => '3min' }
33
+  package {'fence-agents':}
34
+
35
+  Anchor['Fencing primitives start'] ->
36
+  Package['fence-agents'] ->
37
+  Pcs_fencing::Fencing<||> ->
38
+  Cs_fencetopo['fencing_topology'] ->
39
+  Cs_property<||> ->
40
+  Anchor['Fencing primitives end']
41
+}

+ 36
- 0
deployment_scripts/puppet/modules/pcs_fencing/metadata.json View File

@@ -0,0 +1,36 @@
1
+{
2
+  "name": "pcs_fencing",
3
+  "version": "6.1.0",
4
+  "author": "Bogdan Dobrelya <bdobrelia@mirantis.com>",
5
+  "summary": "Puppet Pacemaker fencing Module for Fuel fencing plugin",
6
+  "license": "Apache License 2.0",
7
+  "source": "git://github.com/bogdando/pcs_fencing.git",
8
+  "project_page": "none",
9
+  "issues_url": "none",
10
+  "requirements": [
11
+    { "name": "pe","version_requirement": "3.x" },
12
+    { "name": "puppet","version_requirement": "3.x" }
13
+  ],
14
+  "operatingsystem_support": [
15
+    {
16
+      "operatingsystem": "Debian",
17
+      "operatingsystemrelease": ["7"]
18
+    },
19
+    {
20
+      "operatingsystem": "Fedora",
21
+      "operatingsystemrelease": ["20"]
22
+    },
23
+    {
24
+      "operatingsystem": "RedHat",
25
+      "operatingsystemrelease": ["6.5","7"]
26
+    },
27
+    {
28
+      "operatingsystem": "Ubuntu",
29
+      "operatingsystemrelease": ["12.04","14.04"]
30
+    }
31
+  ],
32
+  "description": "Puppet module for configuring Pacemaker HA fencing in Fuel plugin",
33
+  "dependencies": [
34
+    { "name":"puppetlabs/stdlib","version_requirement": "4.x" }
35
+  ]
36
+}

+ 66
- 0
deployment_scripts/puppet/modules/pcs_fencing/spec/classes/fencing_primitives_spec.rb View File

@@ -0,0 +1,66 @@
1
+require 'spec_helper'
2
+
3
+describe 'pcs_fencing::fencing_primitives' do
4
+
5
+  let :params do
6
+    {
7
+      :fence_primitives => {
8
+        'ipmi_off' => {
9
+          'agent_type' => 'fence_ipmilan',
10
+          'operations' => false,
11
+          'meta'       => false,
12
+          'parameters' => false
13
+        }
14
+      },
15
+      :fence_topology => {
16
+        'node-1.foo.bar' => {
17
+          '1' => [ 'ipmi_off' ]
18
+        }
19
+      },
20
+      :nodes => [
21
+        {
22
+          'fqdn' => 'node-1.foo.bar',
23
+          'name' => 'node-1',
24
+          'role' => 'primary-controller'
25
+        }
26
+      ]
27
+    }
28
+  end
29
+  let(:names) { [ 'node-1.foo.bar' ] }
30
+  let(:facts) {{ :osfamily => 'RedHat' }}
31
+
32
+  context 'then configuring fencing' do
33
+
34
+    it 'should install fence-agents' do
35
+      should contain_package('fence-agents')
36
+    end
37
+
38
+    it 'should contain its class' do
39
+      should contain_class('pcs_fencing::fencing_primitives').with(params)
40
+    end
41
+
42
+    it 'should create fencing primitives' do
43
+      should contain_pcs_fencing__fencing('ipmi_off').with(
44
+        params[:fence_primitives]['ipmi_off']
45
+      )
46
+    end
47
+
48
+    it 'should enable fencing' do
49
+      should contain_cs_property('stonith-enabled')
50
+    end
51
+
52
+    it 'should update cluster recheck interval' do
53
+      should contain_cs_property('cluster-recheck-interval')
54
+    end
55
+
56
+    it 'should create a topology' do
57
+      should contain_cs_fencetopo('fencing_topology').with(
58
+        {
59
+          :ensure         => 'present',
60
+          :fence_topology => params[:fence_topology],
61
+          :nodes          => names,
62
+        }
63
+      )
64
+    end
65
+  end
66
+end

+ 87
- 0
deployment_scripts/puppet/modules/pcs_fencing/spec/defines/fencing_spec.rb View File

@@ -0,0 +1,87 @@
1
+require 'spec_helper'
2
+
3
+describe 'pcs_fencing::fencing', :type => :define do
4
+
5
+  let (:title) { 'virsh_off' }
6
+  let (:node)  { 'node-1' }
7
+  let (:res_name) { "stonith__#{title}__#{node}" }
8
+  let :params do
9
+    {
10
+      :agent_type => 'fence_virsh',
11
+      :parameters => false,
12
+      :operations => false,
13
+      :meta => false
14
+    }
15
+  end
16
+  let :primitive_params do
17
+    {
18
+      :ensure => 'present',
19
+      :provided_by => 'pacemaker',
20
+      :primitive_class => 'stonith',
21
+      :primitive_type => params[:agent_type],
22
+      :parameters => params[:parameters],
23
+      :operations => params[:operations],
24
+      :metadata => params[:meta]
25
+    }
26
+  end
27
+  let :location_prohibit_params do
28
+    {
29
+      :node_name => node,
30
+      :score => '-INFINITY',
31
+      :primitive => res_name
32
+    }
33
+  end
34
+  let :location_allow_params do
35
+    {
36
+      :primitive => res_name,
37
+      :rules => [
38
+        {
39
+          'score'   => '100',
40
+          'boolean' => '',
41
+          'expressions' => [
42
+            {'attribute'=>"#uname",'operation'=>'ne','value'=>node}
43
+          ]
44
+        }
45
+      ]
46
+    }
47
+  end
48
+  let(:facts) {{ :osfamily => 'Debian' }}
49
+  let(:facts) {{ :pacemaker_hostname => node }}
50
+
51
+  context 'then configuring STONITH primitive' do
52
+    it 'should contain its definition' do
53
+      should contain_pcs_fencing__fencing(title).with(params)
54
+    end
55
+
56
+    it 'should create a pacemaker primitive' do
57
+      should contain_cs_resource(res_name).with(
58
+        {
59
+          'ensure' => primitive_params[:ensure],
60
+          'primitive_class' => primitive_params[:primitive_class],
61
+          'primitive_type' => primitive_params[:primitive_type],
62
+          'provided_by' => primitive_params[:provided_by],
63
+          'parameters' => primitive_params[:parameters],
64
+          'operations' => primitive_params[:operations],
65
+          'metadata' => primitive_params[:metadata]
66
+        }
67
+      )
68
+    end
69
+    it 'should create a prohibit location' do
70
+      should contain_cs_location("location__prohibit__#{res_name}").with(
71
+        {
72
+          'node_name' => location_prohibit_params[:node_name],
73
+          'node_score' => location_prohibit_params[:score],
74
+          'primitive' => location_prohibit_params[:primitive]
75
+        }
76
+      )
77
+    end
78
+    it 'should create an allow location' do
79
+      should contain_cs_location("location__allow__#{res_name}").with(
80
+        {
81
+          'primitive' => location_allow_params[:primitive],
82
+          'rules' => location_allow_params[:rules]
83
+        }
84
+      )
85
+    end
86
+  end
87
+end

+ 60
- 0
deployment_scripts/puppet/modules/pcs_fencing/spec/fixtures/cib/cib.xml View File

@@ -0,0 +1,60 @@
1
+<cib epoch="521" num_updates="0" admin_epoch="0" validate-with="pacemaker-1.2" crm_feature_set="3.0.5" update-origin="ubuntu-1" update-client="crmd" cib-last-written="Mon Jan 28 20:34:13 2013" have-quorum="1" dc-uuid="ubuntu-1">
2
+	<configuration>
3
+		<crm_config>
4
+			<cluster_property_set id="cib-bootstrap-options">
5
+				<nvpair id="cib-bootstrap-options-dc-version" name="dc-version" value="1.1.6-9971ebba4494012a93c03b40a2c58ec0eb60f50c"/>
6
+			</cluster_property_set>
7
+		</crm_config>
8
+		<nodes>
9
+			<node id="ubuntu-1" type="normal" uname="ubuntu-1"/>
10
+			<node id="ubuntu-2" type="normal" uname="ubuntu-2"/>
11
+			<node id="vvk-workstation" type="normal" uname="vvk-workstation"/>
12
+		</nodes>
13
+		<resources>
14
+			<primitive class="ocf" id="bar" provider="pacemaker" type="Dummy">
15
+				<operations>
16
+					<op id="bar-monitor-20" interval="20" name="monitor"/>
17
+				</operations>
18
+			</primitive>
19
+			<primitive class="ocf" id="foo" provider="pacemaker" type="Dummy"/>
20
+			<clone id="clone_blort">
21
+				<primitive class="ocf" id="blort" provider="pacemaker" type="Dummy">
22
+					<operations>
23
+						<op id="blort-start-0" interval="0" name="start" timeout="20"/>
24
+						<op id="blort-monitor-20" interval="20" name="monitor"/>
25
+					</operations>
26
+				</primitive>
27
+			</clone>
28
+			<group id="mygroup">
29
+				<primitive class="ocf" id="baz_1" provider="pacemaker" type="Dummy"/>
30
+				<primitive class="ocf" id="baz_2" provider="pacemaker" type="Dummy"/>
31
+			</group>
32
+		</resources>
33
+		<constraints>
34
+			<rsc_order first="foo" id="foo-before-bar" score="INFINITY" then="bar"/>
35
+			<rsc_colocation id="foo-with-bar" rsc="foo" score="INFINITY" with-rsc="bar"/>
36
+			<rsc_location id="l_11" rsc="master_bar">
37
+				<rule id="l_11-rule" score="INFINITY">
38
+					<expression attribute="#uname" id="l_11-expression" operation="ne" value="ubuntu-1"/>
39
+					<date_expression id="l_11-expression-0" operation="date_spec">
40
+						<date_spec hours="10" id="l_11-date_spec" weeks="5"/>
41
+					</date_expression>
42
+					<date_expression id="l_11-expression-1" operation="in_range" start="20121212" end="20131212" />
43
+					<date_expression id="l_11-expression-2" operation="gt" start="20121212"/>
44
+					<date_expression id="l_11-expression-3" operation="lt" end="20131212" />
45
+					<date_expression id="l_11-expression-4" operation="in_range" end="" start="20121212">
46
+						<duration id="l_11-duration" years="10"/>
47
+					</date_expression>
48
+				</rule>
49
+			</rsc_location>
50
+			<rsc_location id="l_12" rsc="master_bar" node="ubuntu-1" score="INFINITY"/>
51
+
52
+		</constraints>
53
+		<fencing-topology>
54
+			<fencing-level devices="stonith__ipmi_reset__node-1" id="fencing" index="1" target="node-1.test.local"/>
55
+			<fencing-level devices="stonith__psu_off__node-1,stonith__psu_on__node-1" id="fencing-0" index="2" target="node-1.test.local"/>
56
+			<fencing-level devices="stonith__ilo_reset__node-2" id="fencing-1" index="1" target="node-2.test.local"/>
57
+			<fencing-level devices="stonith__psu_snmp_off__node-2,stonith__psu_snmp_on__node-2" id="fencing-2" index="2" target="node-2.test.local"/>
58
+		</fencing-topology>
59
+	</configuration>
60
+</cib>

+ 54
- 0
deployment_scripts/puppet/modules/pcs_fencing/spec/fixtures/cib/cib_no_topo.xml View File

@@ -0,0 +1,54 @@
1
+<cib epoch="521" num_updates="0" admin_epoch="0" validate-with="pacemaker-1.2" crm_feature_set="3.0.5" update-origin="ubuntu-1" update-client="crmd" cib-last-written="Mon Jan 28 20:34:13 2013" have-quorum="1" dc-uuid="ubuntu-1">
2
+	<configuration>
3
+		<crm_config>
4
+			<cluster_property_set id="cib-bootstrap-options">
5
+				<nvpair id="cib-bootstrap-options-dc-version" name="dc-version" value="1.1.6-9971ebba4494012a93c03b40a2c58ec0eb60f50c"/>
6
+			</cluster_property_set>
7
+		</crm_config>
8
+		<nodes>
9
+			<node id="ubuntu-1" type="normal" uname="ubuntu-1"/>
10
+			<node id="ubuntu-2" type="normal" uname="ubuntu-2"/>
11
+			<node id="vvk-workstation" type="normal" uname="vvk-workstation"/>
12
+		</nodes>
13
+		<resources>
14
+			<primitive class="ocf" id="bar" provider="pacemaker" type="Dummy">
15
+				<operations>
16
+					<op id="bar-monitor-20" interval="20" name="monitor"/>
17
+				</operations>
18
+			</primitive>
19
+			<primitive class="ocf" id="foo" provider="pacemaker" type="Dummy"/>
20
+			<clone id="clone_blort">
21
+				<primitive class="ocf" id="blort" provider="pacemaker" type="Dummy">
22
+					<operations>
23
+						<op id="blort-start-0" interval="0" name="start" timeout="20"/>
24
+						<op id="blort-monitor-20" interval="20" name="monitor"/>
25
+					</operations>
26
+				</primitive>
27
+			</clone>
28
+			<group id="mygroup">
29
+				<primitive class="ocf" id="baz_1" provider="pacemaker" type="Dummy"/>
30
+				<primitive class="ocf" id="baz_2" provider="pacemaker" type="Dummy"/>
31
+			</group>
32
+		</resources>
33
+		<constraints>
34
+			<rsc_order first="foo" id="foo-before-bar" score="INFINITY" then="bar"/>
35
+			<rsc_colocation id="foo-with-bar" rsc="foo" score="INFINITY" with-rsc="bar"/>
36
+			<rsc_location id="l_11" rsc="master_bar">
37
+				<rule id="l_11-rule" score="INFINITY">
38
+					<expression attribute="#uname" id="l_11-expression" operation="ne" value="ubuntu-1"/>
39
+					<date_expression id="l_11-expression-0" operation="date_spec">
40
+						<date_spec hours="10" id="l_11-date_spec" weeks="5"/>
41
+					</date_expression>
42
+					<date_expression id="l_11-expression-1" operation="in_range" start="20121212" end="20131212" />
43
+					<date_expression id="l_11-expression-2" operation="gt" start="20121212"/>
44
+					<date_expression id="l_11-expression-3" operation="lt" end="20131212" />
45
+					<date_expression id="l_11-expression-4" operation="in_range" end="" start="20121212">
46
+						<duration id="l_11-duration" years="10"/>
47
+					</date_expression>
48
+				</rule>
49
+			</rsc_location>
50
+			<rsc_location id="l_12" rsc="master_bar" node="ubuntu-1" score="INFINITY"/>
51
+
52
+		</constraints>
53
+	</configuration>
54
+</cib>

+ 1
- 0
deployment_scripts/puppet/modules/pcs_fencing/spec/spec_helper.rb View File

@@ -0,0 +1 @@
1
+require 'puppetlabs_spec_helper/module_spec_helper'

+ 150
- 0
deployment_scripts/puppet/modules/pcs_fencing/spec/unit/puppet/provider/cs_fencetopo/crm_spec.rb View File

@@ -0,0 +1,150 @@
1
+require 'spec_helper'
2
+
3
+describe Puppet::Type.type(:cs_fencetopo).provider(:crm) do
4
+
5
+  $fence_topology = {
6
+    'node-1.test.local' => {
7
+      '1' => [
8
+        'ipmi_reset',
9
+      ],
10
+      '2' => [
11
+        'psu_off','psu_on'
12
+      ],
13
+    },
14
+    'node-2.test.local' => {
15
+      '1' => [
16
+        'ilo_reset',
17
+      ],
18
+      '2' => [
19
+        'psu_snmp_off','psu_snmp_on'
20
+      ],
21
+    },
22
+  }
23
+  $nodes = [ 'node-1.test.local', 'node-2.test.local' ]
24
+
25
+  $foo_topology = {
26
+    'node-1.foo-test.local' => {
27
+      '1' => [
28
+        'ipmi_off', 'dirac_off', 'ilo_off'
29
+      ],
30
+      '2' => [
31
+        'psu1_off','psu2_off'
32
+      ],
33
+    },
34
+    'node-2.foo-test.local' => {
35
+      '1' => [
36
+        'ipmi_off', 'dirac_off', 'ilo_off'
37
+      ],
38
+      '2' => [
39
+        'psu1_off','psu2_off'
40
+      ],
41
+    },
42
+    'node-3.foo-test.local' => {
43
+      '1' => [
44
+        'ipmi_off', 'dirac_off', 'ilo_off'
45
+      ],
46
+      '2' => [
47
+        'psu1_off','psu2_off'
48
+      ],
49
+    },
50
+  }
51
+  $foo_nodes = [ 'node-1.foo-test.local', 'node-2.foo-test.local', 'node-3.foo-test.local' ]
52
+
53
+ let(:resource) { Puppet::Type.type(:cs_fencetopo).new(
54
+   :name=>'myfencetopo',
55
+   :provider=>:crm,
56
+   :ensure=>:present,
57
+   :nodes=>$nodes,
58
+   :fence_topology=>$fence_topology) }
59
+
60
+ let(:provider) { resource.provider }
61
+ let(:instance) { provider.class.instances }
62
+
63
+ let(:foo_resource) { Puppet::Type.type(:cs_fencetopo).new(
64
+   :name=>'myfootopo',
65
+   :provider=>:crm,
66
+   :ensure=>:present,
67
+   :nodes=>$foo_nodes,
68
+   :fence_topology=>$foo_topology) }
69
+
70
+ let(:foo_provider) { foo_resource.provider }
71
+ let(:foo_instance) { foo_provider.class.instances }
72
+
73
+  describe "#create" do
74
+    before(:each) do
75
+      provider.class.stubs(:exec_withenv).returns(0)
76
+    end
77
+
78
+    it "should create topology singleton with corresponding nodes list and fence primitives" do
79
+      provider.class.stubs(:block_until_ready).returns(true)
80
+      provider.class.stubs(:instances).returns([])
81
+      provider.expects(:exec_withenv).with(' --force configure fencing_topology node-1.test.local: stonith__ipmi_reset__node-1 stonith__psu_off__node-1,stonith__psu_on__node-1 node-2.test.local: stonith__ilo_reset__node-2 stonith__psu_snmp_off__node-2,stonith__psu_snmp_on__node-2 2>&1', {})
82
+      provider.create
83
+      provider.flush
84
+    end
85
+
86
+    it "should not try to recreate the same topology (idempotency test)" do
87
+      provider.class.stubs(:block_until_ready).returns(true)
88
+      provider.class.stubs(:instances).returns([instance])
89
+      provider.create
90
+      provider.flush
91
+      instance.instance_eval{@property_hash}.should be_nil
92
+    end
93
+
94
+    it "should not create new topology, if one already exists (singleton test)" do
95
+      foo_provider.class.stubs(:block_until_ready).returns(true)
96
+      foo_provider.class.stubs(:instances).returns([instance])
97
+      foo_provider.create
98
+      foo_provider.flush
99
+      foo_instance.instance_eval{@property_hash}.should be_nil
100
+    end
101
+  end
102
+
103
+  describe "#destroy" do
104
+    it "should destroy topology singleton" do
105
+      expected = ''
106
+      expected << ' --scope fencing-topology --delete-all --force --xpath //fencing-level 2>&1'
107
+      expected << "\n"
108
+      expected << " --delete --xml-text '<fencing-topology/>' 2>&1"
109
+      provider.expects(:exec_withenv).with(expected, {})
110
+      provider.destroy
111
+    end
112
+  end
113
+
114
+  describe "#instances" do
115
+    it "should find topology singleton" do
116
+      provider.class.stubs(:block_until_ready).returns(true)
117
+      out=File.open(File.dirname(__FILE__) + '/../../../../fixtures/cib/cib.xml')
118
+      provider.class.stubs(:dump_cib).returns(out,nil)
119
+      expected = {:name=>"myfencetopo", :fence_topology=>$fence_topology, :nodes=>$nodes, :ensure=>:present, :provider=>:crm}
120
+      instance[0].instance_eval{@property_hash}.should eql(expected)
121
+    end
122
+
123
+    it "should not find topology singleton" do
124
+      provider.class.stubs(:block_until_ready).returns(true)
125
+      out=File.open(File.dirname(__FILE__) + '/../../../../fixtures/cib/cib_no_topo.xml')
126
+      provider.class.stubs(:dump_cib).returns(out,nil)
127
+      instance[0].instance_eval{@property_hash}.should be_nil
128
+    end
129
+
130
+  end
131
+
132
+  describe '#exists?' do
133
+    it 'checks if topology singleton exists' do
134
+      provider.class.stubs(:block_until_ready).returns(true)
135
+      out=File.open(File.dirname(__FILE__) + '/../../../../fixtures/cib/cib.xml')
136
+      provider.class.stubs(:dump_cib).returns(out,nil)
137
+      provider.class.stubs(:exec_withenv).with(' --query --scope fencing-topology', {}).returns(0)
138
+      provider.exists?.should be_true
139
+    end
140
+
141
+    it 'checks if topology singleton does not exist' do
142
+      provider.class.stubs(:block_until_ready).returns(true)
143
+      out=File.open(File.dirname(__FILE__) + '/../../../../fixtures/cib/cib_no_topo.xml')
144
+      provider.class.stubs(:dump_cib).returns(out,nil)
145
+      provider.class.stubs(:exec_withenv).with(' --query --scope fencing-topology', {}).returns(6)
146
+      provider.exists?.should be_false
147
+    end
148
+  end
149
+end
150
+

+ 92
- 0
deployment_scripts/puppet/modules/pcs_fencing/spec/unit/puppet/type/cs_fencetopo_spec.rb View File

@@ -0,0 +1,92 @@
1
+require 'spec_helper'
2
+
3
+describe Puppet::Type.type(:cs_fencetopo) do
4
+  subject do
5
+    Puppet::Type.type(:cs_fencetopo)
6
+  end
7
+
8
+  $fence_topology = {
9
+    'node-1.test.local' => {
10
+      '1' => [
11
+        'ipmi_reset',
12
+      ],
13
+      '2' => [
14
+        'psu_off','psu_on'
15
+      ],
16
+    },
17
+    'node-2.test.local' => {
18
+      '1' => [
19
+        'ilo_reset',
20
+      ],
21
+      '2' => [
22
+        'psu_snmp_off','psu_snmp_on'
23
+      ],
24
+    }
25
+  }
26
+  $nodes = [ 'node-1.test.local', 'node-2.test.local' ]
27
+
28
+  $foo_topology = {
29
+    'node-1.foo-test.local' => {
30
+      '1' => [
31
+        'ipmi_off', 'dirac_off', 'ilo_off'
32
+      ],
33
+      '2' => [
34
+        'psu1_off','psu2_off'
35
+      ],
36
+    },
37
+    'node-2.foo-test.local' => {
38
+      '1' => [
39
+        'ipmi_off', 'dirac_off', 'ilo_off'
40
+      ],
41
+      '2' => [
42
+        'psu1_off','psu2_off'
43
+      ],
44
+    },
45
+    'node-3.foo-test.local' => {
46
+      '1' => [
47
+        'ipmi_off', 'dirac_off', 'ilo_off'
48
+      ],
49
+      '2' => [
50
+        'psu1_off','psu2_off'
51
+      ],
52
+    },
53
+  }
54
+  $foo_nodes = [ 'node-1.foo-test.local', 'node-2.foo-test.local', 'node-3.foo-test.local' ]
55
+
56
+  it "should have a 'name' parameter" do
57
+    subject.new(:name => 'mock_resource')[:name].should == 'mock_resource'
58
+  end
59
+
60
+  describe "basic structure" do
61
+    it "should be able to create a singleton instance" do
62
+      provider_class = Puppet::Type::Cs_fencetopo.provider(Puppet::Type::Cs_fencetopo.providers[0])
63
+      Puppet::Type::Cs_fencetopo.expects(:defaultprovider).returns(provider_class)
64
+
65
+      subject.new(:name => "mock_resource").should_not be_nil
66
+    end
67
+
68
+    #it "should not be able to create other instances" do
69
+      #TODO verify if fencetopo has a singleton nature
70
+    #end
71
+
72
+    [:cib, :name ].each do |param|
73
+      it "should have a #{param} parameter" do
74
+        subject.validparameter?(param).should be_true
75
+      end
76
+
77
+      it "should have documentation for its #{param} parameter" do
78
+        subject.paramclass(param).doc.should be_instance_of(String)
79
+      end
80
+    end
81
+
82
+    [ :nodes, :fence_topology ].each do |prop|
83
+      it "should have a #{prop} property" do
84
+        subject.validproperty?(prop).should be_true
85
+      end
86
+
87
+      it "should have documentation for its #{prop} property" do
88
+        subject.propertybyname(prop).doc.should be_instance_of(String)
89
+      end
90
+    end
91
+  end
92
+end

+ 24
- 0
deployment_scripts/puppet/modules/pcs_fencing/test/site.pp View File

@@ -0,0 +1,24 @@
1
+node default {
2
+  class { 'pcs_fencing::fencing_primitives':
3
+    fence_primitives => {
4
+      'ipmi_off' => {
5
+        'agent_type' => 'fence_ipmilan',
6
+        'operations' => false,
7
+        'meta'       => false,
8
+        'parameters' => false,
9
+      },
10
+    },
11
+    fence_topology => {
12
+      'node-1' => {
13
+        '1' => [ 'ipmi_off' ],
14
+      }
15
+    },
16
+    nodes => [
17
+      {
18
+        'name' => 'node-1',
19
+        'fqdn' => 'node-1.foo.bar',
20
+        'role' => 'primary-controller',
21
+      },
22
+    ],
23
+  }
24
+}

+ 7
- 0
environment_config.yaml View File

@@ -0,0 +1,7 @@
1
+attributes:
2
+  fence_policy:
3
+    value: 'reboot'
4
+    label: 'Policy for HA fencing [disabled, reboot, poweroff]'
5
+    description: 'Pick a type of HA fencing policy'
6
+    weight: 25
7
+    type: "text"

+ 25
- 0
metadata.yaml View File

@@ -0,0 +1,25 @@
1
+# Plugin name
2
+name: ha_fencing
3
+title: HA fencing for Pacemaker cluster
4
+# Plugin version
5
+version: 6.0.0
6
+# Description
7
+description: Enables STONITH of the failed nodes in Corosync & Pacemaker cluster
8
+# Required fuel version
9
+fuel_version: ['6.0']
10
+
11
+# The plugin is compatible with releases in the list
12
+releases:
13
+  - os: ubuntu
14
+    version: 2014.2-6.0
15
+    mode: ['ha']
16
+    deployment_scripts_path: deployment_scripts/
17
+    repository_path: repositories/ubuntu
18
+  - os: centos
19
+    version: 2014.2-6.0
20
+    mode: ['ha']
21
+    deployment_scripts_path: deployment_scripts/
22
+    repository_path: repositories/centos
23
+
24
+# Version of plugin package
25
+package_version: '1.0.0'

+ 26
- 0
pre_build_hook View File

@@ -0,0 +1,26 @@
1
+#!/bin/bash
2
+
3
+set -eux
4
+
5
+ROOT="$(dirname `readlink -f $0`)"
6
+MODULES="${ROOT}"/deployment_scripts/puppet/modules/
7
+TMP_DIR="${ROOT}"/tmp
8
+mkdir -p "${MODULES}"
9
+mkdir -p "${TMP_DIR}"
10
+#Puppetlabs/stdlib 4.5.0
11
+REPO_PATH='https://github.com/puppetlabs/puppetlabs-stdlib/tarball/80f09623b63cf6946b5913b629911e2c49b5d1dd'
12
+
13
+wget -qO- "${REPO_PATH}" | \
14
+    tar -C "${MODULES}" -zxvf - \
15
+    puppetlabs-puppetlabs-stdlib-80f0962 && \
16
+    mv "${MODULES}puppetlabs-puppetlabs-stdlib-80f0962" "${MODULES}stdlib"
17
+
18
+#Fuel 5.1.1 puppet-corosync
19
+REPO_PATH='https://github.com/stackforge/fuel-library/tarball/a3043477337b4a0a8fd166dc83d6cd5d504f5da8'
20
+MODULES="${ROOT}"/deployment_scripts/puppet/modules/
21
+
22
+wget -qO- "${REPO_PATH}" | \
23
+    tar -C "${MODULES}" --strip-components=3 -zxvf - \
24
+    stackforge-fuel-library-a304347/deployment/puppet/corosync
25
+
26
+

+ 0
- 0
repositories/centos/.gitkeep View File


+ 0
- 0
repositories/ubuntu/.gitkeep View File


+ 8
- 0
tasks.yaml View File

@@ -0,0 +1,8 @@
1
+# Deployment is required for controllers
2
+- role: ['controller']
3
+  stage: post_deployment
4
+  type: puppet
5
+  parameters:
6
+    puppet_manifest: puppet/manifests/site.pp
7
+    puppet_modules: puppet/modules
8
+    timeout: 720

Loading…
Cancel
Save