summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorBogdan Dobrelya <bdobrelia@mirantis.com>2014-12-19 13:50:10 +0100
committerBogdan Dobrelya <bdobrelia@mirantis.com>2014-12-30 13:10:31 +0100
commit7382d88ccf1721f055a8f1530bf52709332b0e41 (patch)
tree30e45a08b80dd1ada77c4a858fa5058521ffe5ed
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>
-rw-r--r--.gitignore5
-rw-r--r--LICENSE202
-rw-r--r--README.md178
-rw-r--r--deployment_scripts/puppet/manifests/site.pp21
-rw-r--r--deployment_scripts/puppet/modules/pcs_fencing/.fixtures.yml9
-rw-r--r--deployment_scripts/puppet/modules/pcs_fencing/.gitignore3
-rw-r--r--deployment_scripts/puppet/modules/pcs_fencing/Gemfile16
-rw-r--r--deployment_scripts/puppet/modules/pcs_fencing/Gemfile.lock56
-rw-r--r--deployment_scripts/puppet/modules/pcs_fencing/LICENSE201
-rw-r--r--deployment_scripts/puppet/modules/pcs_fencing/README.md87
-rw-r--r--deployment_scripts/puppet/modules/pcs_fencing/Rakefile7
-rw-r--r--deployment_scripts/puppet/modules/pcs_fencing/examples/pcs_fencing.yaml117
-rw-r--r--deployment_scripts/puppet/modules/pcs_fencing/examples/pcs_fencing_virsh.yaml43
-rw-r--r--deployment_scripts/puppet/modules/pcs_fencing/lib/facter/fencing_config.rb15
-rw-r--r--deployment_scripts/puppet/modules/pcs_fencing/lib/facter/naily.rb16
-rw-r--r--deployment_scripts/puppet/modules/pcs_fencing/lib/facter/pacemaker_hostname.rb9
-rw-r--r--deployment_scripts/puppet/modules/pcs_fencing/lib/puppet/parser/functions/filter_hash.rb13
-rw-r--r--deployment_scripts/puppet/modules/pcs_fencing/lib/puppet/parser/functions/filter_nodes.rb9
-rw-r--r--deployment_scripts/puppet/modules/pcs_fencing/lib/puppet/parser/functions/parseyaml.rb24
-rw-r--r--deployment_scripts/puppet/modules/pcs_fencing/lib/puppet/provider/corosync.rb144
-rw-r--r--deployment_scripts/puppet/modules/pcs_fencing/lib/puppet/provider/cs_fencetopo/crm.rb134
-rw-r--r--deployment_scripts/puppet/modules/pcs_fencing/lib/puppet/type/cs_fencetopo.rb55
-rw-r--r--deployment_scripts/puppet/modules/pcs_fencing/manifests/fencing.pp44
-rw-r--r--deployment_scripts/puppet/modules/pcs_fencing/manifests/fencing_primitives.pp41
-rw-r--r--deployment_scripts/puppet/modules/pcs_fencing/metadata.json36
-rw-r--r--deployment_scripts/puppet/modules/pcs_fencing/spec/classes/fencing_primitives_spec.rb66
-rw-r--r--deployment_scripts/puppet/modules/pcs_fencing/spec/defines/fencing_spec.rb87
-rw-r--r--deployment_scripts/puppet/modules/pcs_fencing/spec/fixtures/cib/cib.xml60
-rw-r--r--deployment_scripts/puppet/modules/pcs_fencing/spec/fixtures/cib/cib_no_topo.xml54
-rw-r--r--deployment_scripts/puppet/modules/pcs_fencing/spec/spec_helper.rb1
-rw-r--r--deployment_scripts/puppet/modules/pcs_fencing/spec/unit/puppet/provider/cs_fencetopo/crm_spec.rb150
-rw-r--r--deployment_scripts/puppet/modules/pcs_fencing/spec/unit/puppet/type/cs_fencetopo_spec.rb92
-rw-r--r--deployment_scripts/puppet/modules/pcs_fencing/test/site.pp24
-rw-r--r--environment_config.yaml7
-rw-r--r--metadata.yaml25
-rwxr-xr-xpre_build_hook26
-rw-r--r--repositories/centos/.gitkeep0
-rw-r--r--repositories/ubuntu/.gitkeep0
-rw-r--r--tasks.yaml8
39 files changed, 2085 insertions, 0 deletions
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..ae7297c
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,5 @@
1deployment_scripts/puppet/modules/corosync
2deployment_scripts/puppet/modules/stdlib
3.build
4tmp
5*.fp
diff --git a/LICENSE b/LICENSE
new file mode 100644
index 0000000..e06d208
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,202 @@
1Apache 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
diff --git a/README.md b/README.md
new file mode 100644
index 0000000..77530f3
--- /dev/null
+++ b/README.md
@@ -0,0 +1,178 @@
1Fuel Fencing Plugin
2===================
3
4#### Table of Contents
5
61. [Overview - What is the Fuel fencing plugin?](#overview)
72. [Plugin Description - What does the plugin do?](#plugin-description)
83. [Setup - The basics of getting started with Fuel fencing plugin](#setup)
94. [Implementation - An under-the-hood peek at what the plugin is doing](#implementation)
105. [Limitations - OS compatibility, etc.](#limitations)
116. [Development - Guide for contributing to the plugin](#development)
127. [Contributors - Those with commits](#contributors)
138. [Release Notes - Notes on the most recent updates to the plugin](#release-notes)
14
15Overview
16--------
17
18The Fuel fencing plugin is a Puppet configuration module being executed as an
19additional post deployment step in order to provide HA fencing (STONITH based)
20of a failed cluster nodes.
21The plugin itself is used to describe a datacenter's HW power management
22configuration - such as PDU outlets, IPMI and other agents - and represent
23it as a fencing topology for Corosync & Pacemaker cluster.
24
25[About Fuel plugins](https://software.mirantis.com/mirantis-openstack-fuel-plug-in-development/)
26
27Plugin Description
28------------------
29
30The Fuel fencing plugin is intended to provide STONITHing of the failed nodes
31in Corosync & Pacemaker cluster.
32Fencing plugin operates the YAML data structures and extends the Fuel YAML
33configuration file.
34
35It suggests the manual definition of the YAML data structures with all required
36parameters for existing power management (PM) devices for every controller node.
37It is up to the user to collect and verify all the needed IP adresses, credentials,
38power outlets layouts, BM hosts to PM devices mappings and other parameters.
39
40This plugin also installs fence-agents package and assumes
41there is the one avaiable in the OS repositories.
42
43Setup
44-----
45
46### Installing Fencing plugin
47
48Please refer to the [plugins dev guide](http://docs.mirantis.com/fuel/fuel-6.0/plugin-dev.html#what-is-pluggable-architecture)
49Note 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
119TODO(bogdando) finish the guide, add agents and devices verification commands
120
121Implementation
122--------------
123
124### Fuel Fencing plugin
125
126This plugin is a combination of Puppet module and metadata required to
127describe and configure the fencing topology for Corosync & Pacemaker
128cluster. The plugin includes custom puppet module pcs_fencing and as a dependencies,
129custom corosync module and puppetlabs/stdlib module v4.5.0.
130
131It changes global cluster properties:
132* cluster-recheck-interval = 3 minutes
133* stonith-enabled = True
134
135It creates a set of STONITH primitives in Pacemaker cluster and runs them in a way,
136that ensures the node will never try to shoot itself (-inf location constraint).
137It configures a fencing topology singleton primitive in Pacemaker cluster.
138It uses crm command line tool which is deprecated and will be replaced to pcs later.
139
140Limitations
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
150Development
151-----------
152
153Developer documentation for the entire Fuel project.
154
155* https://wiki.openstack.org/wiki/Fuel#Where_can_documentation_be_found
156
157Contributors
158------------
159
160Will be added later
161
162Versioning
163----------
164
165This module has been given version 6 to track the Fuel releases. The
166versioning for plugin releases are as follows:
167
168```
169Plugin :: Fuel version
1706.0.0 -> 6.0
171```
172
173Release Notes
174-------------
175
176*** 6.0.0 ***
177
178* This is the initial release of this plugin.
diff --git a/deployment_scripts/puppet/manifests/site.pp b/deployment_scripts/puppet/manifests/site.pp
new file mode 100644
index 0000000..01a264e
--- /dev/null
+++ b/deployment_scripts/puppet/manifests/site.pp
@@ -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
7if $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}
diff --git a/deployment_scripts/puppet/modules/pcs_fencing/.fixtures.yml b/deployment_scripts/puppet/modules/pcs_fencing/.fixtures.yml
new file mode 100644
index 0000000..9d3d8c3
--- /dev/null
+++ b/deployment_scripts/puppet/modules/pcs_fencing/.fixtures.yml
@@ -0,0 +1,9 @@
1fixtures:
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"
diff --git a/deployment_scripts/puppet/modules/pcs_fencing/.gitignore b/deployment_scripts/puppet/modules/pcs_fencing/.gitignore
new file mode 100644
index 0000000..353d51f
--- /dev/null
+++ b/deployment_scripts/puppet/modules/pcs_fencing/.gitignore
@@ -0,0 +1,3 @@
1Gemfile.lock
2spec/fixtures
3.bundle
diff --git a/deployment_scripts/puppet/modules/pcs_fencing/Gemfile b/deployment_scripts/puppet/modules/pcs_fencing/Gemfile
new file mode 100644
index 0000000..2797c77
--- /dev/null
+++ b/deployment_scripts/puppet/modules/pcs_fencing/Gemfile
@@ -0,0 +1,16 @@
1source 'https://rubygems.org'
2
3group :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'
10end
11
12if puppetversion = ENV['PUPPET_GEM_VERSION']
13 gem 'puppet', puppetversion, :require => false
14else
15 gem 'puppet', :require => false
16end
diff --git a/deployment_scripts/puppet/modules/pcs_fencing/Gemfile.lock b/deployment_scripts/puppet/modules/pcs_fencing/Gemfile.lock
new file mode 100644
index 0000000..a730a2f
--- /dev/null
+++ b/deployment_scripts/puppet/modules/pcs_fencing/Gemfile.lock
@@ -0,0 +1,56 @@
1GEM
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
46PLATFORMS
47 ruby
48
49DEPENDENCIES
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
diff --git a/deployment_scripts/puppet/modules/pcs_fencing/LICENSE b/deployment_scripts/puppet/modules/pcs_fencing/LICENSE
new file mode 100644
index 0000000..96f12d3
--- /dev/null
+++ b/deployment_scripts/puppet/modules/pcs_fencing/LICENSE
@@ -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.
diff --git a/deployment_scripts/puppet/modules/pcs_fencing/README.md b/deployment_scripts/puppet/modules/pcs_fencing/README.md
new file mode 100644
index 0000000..b7a13ee
--- /dev/null
+++ b/deployment_scripts/puppet/modules/pcs_fencing/README.md
@@ -0,0 +1,87 @@
1pcs_fencing
2===========
3
4#### Table of Contents
5
61. [Overview - What is the pcs_fencing module?](#overview)
72. [Module Description - What does the module do?](#module-description)
83. [Setup - The basics of getting started with pcs_fencing](#setup)
94. [Implementation - An under-the-hood peek at what the module is doing](#implementation)
105. [Limitations - OS compatibility, etc.](#limitations)
116. [Development - Guide for contributing to the module](#development)
127. [Contributors - Those with commits](#contributors)
138. [Release Notes - Notes on the most recent updates to the module](#release-notes)
14
15Overview
16--------
17
18TODO(bogdando) provide a link to Fuel plugin repo then ready.
19The pcs_fencing module is a part of Fencing plugin for Fuel and have no
20a separate code repository.
21The module itself is used to configure fencing primitives in Pacemaker
22and combine them into the Fencing topology.
23
24Module Description
25------------------
26
27The pcs_fencing module is intended to provide STONITH based HA fencing
28of the failed nodes in Corosync & Pacemaker cluster. This module
29cannot be used separately from Fuel Fencing plugin.
30Pcs_fencing module operates the data structures which are tight to the ones
31in Fuel YAML configuration file and has no its own parameters.
32This module also installs fence-agents package and assumes there is
33the one avaiable in the OS repositories.
34
35Setup
36-----
37
38### Installing pcs_fencing
39
40This module is being installed automatically as a part of Fuel Fencing
41plugin.
42The module's rspec tests could be run only after the plugin is built as
43its pre build hook will download required Fuel custom puppet module for corosync
44and puppetlabs/stdlib module.
45
46### Beginning with pcs_fencing
47
48Instructions for beginning with pcs_fencing will be added later.
49
50Implementation
51--------------
52
53### pcs_fencing
54
55pcs_fencing is a combination of Puppet manifest and ruby code to delivery
56configuration and extra functionality through custom types, providers, parser
57functions and facts from Fuel library of puppet manifests.
58Note that it requires a custom module for corosync and includes a custom
59provider for fencing topology.
60
61Limitations
62-----------
63
64Limitations will be added as they are discovered.
65
66Development
67-----------
68
69Developer documentation for the entire Fuel project.
70
71* https://wiki.openstack.org/wiki/Fuel#Where_can_documentation_be_found
72
73Contributors
74------------
75
76Will be added later
77
78Versioning
79----------
80
81This module is being versioned as well as Fuel Fencing plugin.
82
83Release Notes
84-------------
85
86This module has no a separate release notes. See the release notes for
87Fuel Fencing plugin.
diff --git a/deployment_scripts/puppet/modules/pcs_fencing/Rakefile b/deployment_scripts/puppet/modules/pcs_fencing/Rakefile
new file mode 100644
index 0000000..67e846c
--- /dev/null
+++ b/deployment_scripts/puppet/modules/pcs_fencing/Rakefile
@@ -0,0 +1,7 @@
1require 'rubygems'
2require 'puppetlabs_spec_helper/rake_tasks'
3require 'puppet-lint/tasks/puppet-lint'
4
5PuppetLint.configuration.fail_on_warnings = true
6PuppetLint.configuration.send('disable_80chars')
7PuppetLint.configuration.ignore_paths = ["spec/**/*.pp", "pkg/**/*.pp"]
diff --git a/deployment_scripts/puppet/modules/pcs_fencing/examples/pcs_fencing.yaml b/deployment_scripts/puppet/modules/pcs_fencing/examples/pcs_fencing.yaml
new file mode 100644
index 0000000..ea34149
--- /dev/null
+++ b/deployment_scripts/puppet/modules/pcs_fencing/examples/pcs_fencing.yaml
@@ -0,0 +1,117 @@
1#fence_policy: reboot
2fence_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
18fence_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
diff --git a/deployment_scripts/puppet/modules/pcs_fencing/examples/pcs_fencing_virsh.yaml b/deployment_scripts/puppet/modules/pcs_fencing/examples/pcs_fencing_virsh.yaml
new file mode 100644
index 0000000..a2a3f19
--- /dev/null
+++ b/deployment_scripts/puppet/modules/pcs_fencing/examples/pcs_fencing_virsh.yaml
@@ -0,0 +1,43 @@
1#fence_policy: reboot
2fence_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
12fence_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'
diff --git a/deployment_scripts/puppet/modules/pcs_fencing/lib/facter/fencing_config.rb b/deployment_scripts/puppet/modules/pcs_fencing/lib/facter/fencing_config.rb
new file mode 100644
index 0000000..93a8632
--- /dev/null
+++ b/deployment_scripts/puppet/modules/pcs_fencing/lib/facter/fencing_config.rb
@@ -0,0 +1,15 @@
1require 'facter'
2
3fencing_settings_path = ['/etc/pcs_fencing.yaml']
4
5fencing_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
15end
diff --git a/deployment_scripts/puppet/modules/pcs_fencing/lib/facter/naily.rb b/deployment_scripts/puppet/modules/pcs_fencing/lib/facter/naily.rb
new file mode 100644
index 0000000..06755d1
--- /dev/null
+++ b/deployment_scripts/puppet/modules/pcs_fencing/lib/facter/naily.rb
@@ -0,0 +1,16 @@
1require 'facter'
2
3# This file is created and managed by Astute
4astute_settings_path = ['/etc/fuel/astute.yaml', '/etc/astute.yaml']
5
6astute_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
16end
diff --git a/deployment_scripts/puppet/modules/pcs_fencing/lib/facter/pacemaker_hostname.rb b/deployment_scripts/puppet/modules/pcs_fencing/lib/facter/pacemaker_hostname.rb
new file mode 100644
index 0000000..83cd20e
--- /dev/null
+++ b/deployment_scripts/puppet/modules/pcs_fencing/lib/facter/pacemaker_hostname.rb
@@ -0,0 +1,9 @@
1# Fact: pacemaker_hostname
2#
3# Purpose: Return name of the node used by Pacemaker
4#
5Facter.add(:pacemaker_hostname) do
6 setcode do
7 rv = Facter::Util::Resolution.exec('crm_node -n')
8 end
9end
diff --git a/deployment_scripts/puppet/modules/pcs_fencing/lib/puppet/parser/functions/filter_hash.rb b/deployment_scripts/puppet/modules/pcs_fencing/lib/puppet/parser/functions/filter_hash.rb
new file mode 100644
index 0000000..bbf0529
--- /dev/null
+++ b/deployment_scripts/puppet/modules/pcs_fencing/lib/puppet/parser/functions/filter_hash.rb
@@ -0,0 +1,13 @@
1module 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
13end
diff --git a/deployment_scripts/puppet/modules/pcs_fencing/lib/puppet/parser/functions/filter_nodes.rb b/deployment_scripts/puppet/modules/pcs_fencing/lib/puppet/parser/functions/filter_nodes.rb
new file mode 100644
index 0000000..89a58da
--- /dev/null
+++ b/deployment_scripts/puppet/modules/pcs_fencing/lib/puppet/parser/functions/filter_nodes.rb
@@ -0,0 +1,9 @@
1module 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
9end \ No newline at end of file
diff --git a/deployment_scripts/puppet/modules/pcs_fencing/lib/puppet/parser/functions/parseyaml.rb b/deployment_scripts/puppet/modules/pcs_fencing/lib/puppet/parser/functions/parseyaml.rb
new file mode 100644
index 0000000..53d54fa
--- /dev/null
+++ b/deployment_scripts/puppet/modules/pcs_fencing/lib/puppet/parser/functions/parseyaml.rb
@@ -0,0 +1,24 @@
1#
2# parseyaml.rb
3#
4
5module Puppet::Parser::Functions
6 newfunction(:parseyaml, :type => :rvalue, :doc => <<-EOS
7This function accepts YAML as a string and converts it into the correct
8Puppet 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
22end
23
24# vim: set ts=2 sw=2 et :
diff --git a/deployment_scripts/puppet/modules/pcs_fencing/lib/puppet/provider/corosync.rb b/deployment_scripts/puppet/modules/pcs_fencing/lib/puppet/provider/corosync.rb
new file mode 100644
index 0000000..014e87b
--- /dev/null
+++ b/deployment_scripts/puppet/modules/pcs_fencing/lib/puppet/provider/corosync.rb
@@ -0,0 +1,144 @@
1require 'pp'
2require 'open3'
3require 'rexml/document'
4
5class 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
144end
diff --git a/deployment_scripts/puppet/modules/pcs_fencing/lib/puppet/provider/cs_fencetopo/crm.rb b/deployment_scripts/puppet/modules/pcs_fencing/lib/puppet/provider/cs_fencetopo/crm.rb
new file mode 100644
index 0000000..c4d7f8d
--- /dev/null
+++ b/deployment_scripts/puppet/modules/pcs_fencing/lib/puppet/provider/cs_fencetopo/crm.rb
@@ -0,0 +1,134 @@
1require 'pathname'
2require 'open3'
3require Pathname.new(__FILE__).dirname.dirname.expand_path + 'corosync'
4require 'rexml/document'
5
6Puppet::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
134end
diff --git a/deployment_scripts/puppet/modules/pcs_fencing/lib/puppet/type/cs_fencetopo.rb b/deployment_scripts/puppet/modules/pcs_fencing/lib/puppet/type/cs_fencetopo.rb
new file mode 100644
index 0000000..1565882
--- /dev/null
+++ b/deployment_scripts/puppet/modules/pcs_fencing/lib/puppet/type/cs_fencetopo.rb
@@ -0,0 +1,55 @@
1module 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
55end
diff --git a/deployment_scripts/puppet/modules/pcs_fencing/manifests/fencing.pp b/deployment_scripts/puppet/modules/pcs_fencing/manifests/fencing.pp
new file mode 100644
index 0000000..f3c0375
--- /dev/null
+++ b/deployment_scripts/puppet/modules/pcs_fencing/manifests/fencing.pp
@@ -0,0 +1,44 @@
1# == Define: pcs_fencing::fencing
2#
3# Configure STONITH resources for corosync/pacemaker.
4#
5define 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}
diff --git a/deployment_scripts/puppet/modules/pcs_fencing/manifests/fencing_primitives.pp b/deployment_scripts/puppet/modules/pcs_fencing/manifests/fencing_primitives.pp
new file mode 100644
index 0000000..7679b6a
--- /dev/null
+++ b/deployment_scripts/puppet/modules/pcs_fencing/manifests/fencing_primitives.pp
@@ -0,0 +1,41 @@
1# Creates fencing primitives and topology for given nodes.
2# Assumes all nodes have the same OS installed
3#
4class 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}
diff --git a/deployment_scripts/puppet/modules/pcs_fencing/metadata.json b/deployment_scripts/puppet/modules/pcs_fencing/metadata.json
new file mode 100644
index 0000000..d050044
--- /dev/null
+++ b/deployment_scripts/puppet/modules/pcs_fencing/metadata.json
@@ -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}
diff --git a/deployment_scripts/puppet/modules/pcs_fencing/spec/classes/fencing_primitives_spec.rb b/deployment_scripts/puppet/modules/pcs_fencing/spec/classes/fencing_primitives_spec.rb
new file mode 100644
index 0000000..c0a31b7
--- /dev/null
+++ b/deployment_scripts/puppet/modules/pcs_fencing/spec/classes/fencing_primitives_spec.rb
@@ -0,0 +1,66 @@
1require 'spec_helper'
2
3describe '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
66end
diff --git a/deployment_scripts/puppet/modules/pcs_fencing/spec/defines/fencing_spec.rb b/deployment_scripts/puppet/modules/pcs_fencing/spec/defines/fencing_spec.rb
new file mode 100644
index 0000000..3bff555
--- /dev/null
+++ b/deployment_scripts/puppet/modules/pcs_fencing/spec/defines/fencing_spec.rb
@@ -0,0 +1,87 @@
1require 'spec_helper'
2
3describe '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
87end
diff --git a/deployment_scripts/puppet/modules/pcs_fencing/spec/fixtures/cib/cib.xml b/deployment_scripts/puppet/modules/pcs_fencing/spec/fixtures/cib/cib.xml
new file mode 100644
index 0000000..60a9f1f
--- /dev/null
+++ b/deployment_scripts/puppet/modules/pcs_fencing/spec/fixtures/cib/cib.xml
@@ -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>
diff --git a/deployment_scripts/puppet/modules/pcs_fencing/spec/fixtures/cib/cib_no_topo.xml b/deployment_scripts/puppet/modules/pcs_fencing/spec/fixtures/cib/cib_no_topo.xml
new file mode 100644
index 0000000..aab8cc4
--- /dev/null
+++ b/deployment_scripts/puppet/modules/pcs_fencing/spec/fixtures/cib/cib_no_topo.xml
@@ -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>
diff --git a/deployment_scripts/puppet/modules/pcs_fencing/spec/spec_helper.rb b/deployment_scripts/puppet/modules/pcs_fencing/spec/spec_helper.rb
new file mode 100644
index 0000000..3d92005
--- /dev/null
+++ b/deployment_scripts/puppet/modules/pcs_fencing/spec/spec_helper.rb
@@ -0,0 +1 @@
require 'puppetlabs_spec_helper/module_spec_helper' \ No newline at end of file
diff --git a/deployment_scripts/puppet/modules/pcs_fencing/spec/unit/puppet/provider/cs_fencetopo/crm_spec.rb b/deployment_scripts/puppet/modules/pcs_fencing/spec/unit/puppet/provider/cs_fencetopo/crm_spec.rb
new file mode 100644
index 0000000..6097f44
--- /dev/null
+++ b/deployment_scripts/puppet/modules/pcs_fencing/spec/unit/puppet/provider/cs_fencetopo/crm_spec.rb
@@ -0,0 +1,150 @@
1require 'spec_helper'
2
3describe 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
149end
150
diff --git a/deployment_scripts/puppet/modules/pcs_fencing/spec/unit/puppet/type/cs_fencetopo_spec.rb b/deployment_scripts/puppet/modules/pcs_fencing/spec/unit/puppet/type/cs_fencetopo_spec.rb
new file mode 100644
index 0000000..9d29091
--- /dev/null
+++ b/deployment_scripts/puppet/modules/pcs_fencing/spec/unit/puppet/type/cs_fencetopo_spec.rb
@@ -0,0 +1,92 @@
1require 'spec_helper'
2
3describe 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
92end
diff --git a/deployment_scripts/puppet/modules/pcs_fencing/test/site.pp b/deployment_scripts/puppet/modules/pcs_fencing/test/site.pp
new file mode 100644
index 0000000..3d45524
--- /dev/null
+++ b/deployment_scripts/puppet/modules/pcs_fencing/test/site.pp
@@ -0,0 +1,24 @@
1node 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}
diff --git a/environment_config.yaml b/environment_config.yaml
new file mode 100644
index 0000000..fd34816
--- /dev/null
+++ b/environment_config.yaml
@@ -0,0 +1,7 @@
1attributes:
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"
diff --git a/metadata.yaml b/metadata.yaml
new file mode 100644
index 0000000..150ae04
--- /dev/null
+++ b/metadata.yaml
@@ -0,0 +1,25 @@
1# Plugin name
2name: ha_fencing
3title: HA fencing for Pacemaker cluster
4# Plugin version
5version: 6.0.0
6# Description
7description: Enables STONITH of the failed nodes in Corosync & Pacemaker cluster
8# Required fuel version
9fuel_version: ['6.0']
10
11# The plugin is compatible with releases in the list
12releases:
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
25package_version: '1.0.0'
diff --git a/pre_build_hook b/pre_build_hook
new file mode 100755
index 0000000..4de9622
--- /dev/null
+++ b/pre_build_hook
@@ -0,0 +1,26 @@
1#!/bin/bash
2
3set -eux
4
5ROOT="$(dirname `readlink -f $0`)"
6MODULES="${ROOT}"/deployment_scripts/puppet/modules/
7TMP_DIR="${ROOT}"/tmp
8mkdir -p "${MODULES}"
9mkdir -p "${TMP_DIR}"
10#Puppetlabs/stdlib 4.5.0
11REPO_PATH='https://github.com/puppetlabs/puppetlabs-stdlib/tarball/80f09623b63cf6946b5913b629911e2c49b5d1dd'
12
13wget -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
19REPO_PATH='https://github.com/stackforge/fuel-library/tarball/a3043477337b4a0a8fd166dc83d6cd5d504f5da8'
20MODULES="${ROOT}"/deployment_scripts/puppet/modules/
21
22wget -qO- "${REPO_PATH}" | \
23 tar -C "${MODULES}" --strip-components=3 -zxvf - \
24 stackforge-fuel-library-a304347/deployment/puppet/corosync
25
26
diff --git a/repositories/centos/.gitkeep b/repositories/centos/.gitkeep
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/repositories/centos/.gitkeep
diff --git a/repositories/ubuntu/.gitkeep b/repositories/ubuntu/.gitkeep
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/repositories/ubuntu/.gitkeep
diff --git a/tasks.yaml b/tasks.yaml
new file mode 100644
index 0000000..7fa6f2e
--- /dev/null
+++ b/tasks.yaml
@@ -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