From 67bd698aa9c1a53ce390f4bdae1f314e52910470 Mon Sep 17 00:00:00 2001
From: Michele Baldessari <michele@acksyn.org>
Date: Fri, 29 Sep 2017 22:36:25 +0200
Subject: [PATCH] WIP Implement stonith levels resource

WIP needed for Instance HA POC

Change-Id: I9cc5064e776a3771e30d853d9453026d2731c98d
---
 lib/puppet/provider/pcmk_stonith_level/default.rb | 50 ++++++++++++
 lib/puppet/type/pcmk_stonith_level.rb             | 95 +++++++++++++++++++++++
 manifests/stonith/level.pp                        | 92 ++++++++++++++++++++++
 3 files changed, 237 insertions(+)
 create mode 100644 lib/puppet/provider/pcmk_stonith_level/default.rb
 create mode 100644 lib/puppet/type/pcmk_stonith_level.rb
 create mode 100644 manifests/stonith/level.pp

diff --git a/lib/puppet/provider/pcmk_stonith_level/default.rb b/lib/puppet/provider/pcmk_stonith_level/default.rb
new file mode 100644
index 000000000000..d81dc725ec5e
--- /dev/null
+++ b/lib/puppet/provider/pcmk_stonith_level/default.rb
@@ -0,0 +1,50 @@
+require_relative '../pcmk_common'
+
+Puppet::Type.type(:pcmk_stonith_level).provide(:default) do
+  desc 'A base resource definition for a pacemaker stonith level definition'
+
+  ### overloaded methods
+  def create
+    level = @resource[:level]
+    target = @resource[:target]
+    stonith_resources = @resource[:stonith_resources]
+    res = stonith_resources.join(',')
+    cmd = "stonith level add #{level} #{target} #{res}"
+
+    pcs('create', "#{name}-#{target}-#{res}", cmd, @resource[:tries],
+        @resource[:try_sleep], @resource[:verify_on_create], @resource[:post_success_sleep])
+  end
+
+  def destroy
+    # Any corresponding location rules will be deleted by
+    # pcs automatically, if present
+    target = @resource[:target]
+    level = @resource[:level]
+    cmd = "stonith level remove #{level} #{target}"
+    pcs('delete', @resource[:name], cmd, @resource[:tries],
+        @resource[:try_sleep], @resource[:verify_on_create], @resource[:post_success_sleep])
+  end
+
+  def exists?
+    Puppet.debug("Exists: stonith level exists")
+    # stonith level output is a bit cumbersome to parse:
+    # Target: overcloud-galera-0
+    #   Level 1 - stonith-fence_ipmilan-006809859383,stonith-fence_compute-fence-nova
+    # Target: overcloud-novacompute-0
+    #   Level 1 - stonith-fence_ipmilan-006809859383,stonith-fence_compute-fence-nova
+    #   Level 2 - stonith-fence_ipmilan-006809859383,stonith-fence_compute-fence-nova
+    # Target: overcloud-rabbit-0
+    #   Level 2 - stonith-fence_ipmilan-006809859383,stonith-fence_compute-fence-nova
+    target = @resource[:target]
+    level = @resource[:level]
+    stonith_resources = @resource[:stonith_resources]
+    res = stonith_resources.join(',')
+    # The below cmd return the "Level X - ...." strings after the Target: string until the next
+    # Target: string (or until the bottom of the file if it is the last Target in the output
+    cmd = "stonith level | sed -n '/^Target: #{target}$/,/^Target:/{/^Target: #{target}$/b;/^Target:/b;p}'"
+    cmd += "| grep -e 'Level[[:space:]]*#{level}[[:space:]]*-[[:space:]]*#{res}'"
+    ret = pcs('show', @resource[:name], cmd, @resource[:tries],
+              @resource[:try_sleep], @resource[:verify_on_create], @resource[:post_success_sleep])
+    return ret == false ? false : true
+  end
+end
diff --git a/lib/puppet/type/pcmk_stonith_level.rb b/lib/puppet/type/pcmk_stonith_level.rb
new file mode 100644
index 000000000000..3640da965ab9
--- /dev/null
+++ b/lib/puppet/type/pcmk_stonith_level.rb
@@ -0,0 +1,95 @@
+require 'puppet/parameter/boolean'
+
+Puppet::Type.newtype(:pcmk_stonith_level) do
+  @doc = "Base resource definition for a pacemaker stonith level resource"
+
+  ensurable
+
+  newparam(:level) do
+    desc "The stonith level"
+    munge do |value|
+      if value.is_a?(String)
+        unless value =~ /^[\d]+$/
+          raise ArgumentError, "The stonith level must be an integer"
+        end
+        value = Integer(value)
+      end
+      raise ArgumentError, "Level must be an integer >= 1" if value < 1
+      value
+    end
+  end
+
+  newparam(:target) do
+    desc "The pacemaker stonith target to apply the level to"
+  end
+
+  newparam(:stonith_resources) do
+    desc "The array containing the list of stonith devices"
+    # FIXME: check for an array of strings
+  end
+
+  ## borrowed from exec.rb
+  newparam(:tries) do
+    desc "The number of times to attempt to create a pcs resource.
+      Defaults to '1'."
+
+    munge do |value|
+      if value.is_a?(String)
+        unless value =~ /^[\d]+$/
+          raise ArgumentError, "Tries must be an integer"
+        end
+        value = Integer(value)
+      end
+      raise ArgumentError, "Tries must be an integer >= 1" if value < 1
+      value
+    end
+
+    defaultto 1
+  end
+
+  newparam(:try_sleep) do
+    desc "The time to sleep in seconds between 'tries'."
+
+    munge do |value|
+      if value.is_a?(String)
+        unless value =~ /^[-\d.]+$/
+          raise ArgumentError, "try_sleep must be a number"
+        end
+        value = Float(value)
+      end
+      raise ArgumentError, "try_sleep cannot be a negative number" if value < 0
+      value
+    end
+
+    defaultto 0
+  end
+
+  newparam(:verify_on_create, :boolean => true, :parent => Puppet::Parameter::Boolean) do
+     desc "Whether to verify pcs resource creation with an additional
+     call to 'pcs resource show' rather than just relying on the exit
+     status of 'pcs resource create'.  When true, $try_sleep
+     determines how long to wait to verify and $post_success_sleep is
+     ignored.  Defaults to `false`."
+
+     defaultto false
+   end
+
+  newparam(:post_success_sleep) do
+    desc "The time to sleep after successful pcs action.  The reason to set
+          this is to avoid immediate back-to-back 'pcs resource create' calls
+          when creating multiple resources.  Defaults to '0'."
+
+    munge do |value|
+      if value.is_a?(String)
+        unless value =~ /^[-\d.]+$/
+          raise ArgumentError, "post_success_sleep must be a number"
+        end
+        value = Float(value)
+      end
+      raise ArgumentError, "post_success_sleep cannot be a negative number" if value < 0
+      value
+    end
+
+    defaultto 0
+  end
+end
diff --git a/manifests/stonith/level.pp b/manifests/stonith/level.pp
new file mode 100644
index 000000000000..4dc0477724bf
--- /dev/null
+++ b/manifests/stonith/level.pp
@@ -0,0 +1,92 @@
+# == Define: pacemaker::stonith::level
+#
+# A resource type to create pacemaker stonith levels
+#
+# === Parameters
+#
+# [*ensure*]
+#   (optional) Whether to make sure resource is created or removed
+#   Defaults to present
+#
+# [*level*]
+#   The stonith level
+#   Defaults to undef
+#
+# [*target*]
+#   The stonith level target
+#   Defaults to undef
+#
+# [*stonith_resources*]
+#   Array containing the list of stonith devices
+#   Defaults to undef
+#
+# [*post_success_sleep*]
+#   (optional) How long to wait acfter successful action
+#   Defaults to 0
+#
+# [*tries*]
+#   (optional) How many times to attempt to perform the action
+#   Defaults to 1
+#
+# [*try_sleep*]
+#   (optional) How long to wait between tries
+#   Defaults to 0
+#
+# [*verify_on_create*]
+#   (optional) Whether to verify creation of resource
+#   Defaults to false
+#
+# === Dependencies
+#
+#  None
+#
+# === Authors
+#
+#  Michele Baldessari <michele@acksyn.org>
+#
+# === Copyright
+#
+# Copyright (C) 2017 Red Hat Inc.
+#
+# Licensed under the Apache License, Version 2.0 (the "License"); you may
+# not use this file except in compliance with the License. You may obtain
+# a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations
+# under the License.
+#
+define pacemaker::stonith::level(
+  $ensure             = 'present',
+  $level              = undef,
+  $target             = undef,
+  $stonith_resources  = undef,
+  $post_success_sleep = 0,
+  $tries              = 1,
+  $try_sleep          = 0,
+  $verify_on_create   = false,
+  ) {
+  if $level == undef {
+    fail('Must provide level')
+  }
+  if $target == undef {
+    fail('Must provide target')
+  }
+  if $stonith_resource == undef {
+    fail('Must provide stonith_resource')
+  }
+  $res = join($stonith_resources, '_')
+  pcmk_stonith_level{ "${level}-${target}-${res}":
+    level              => $level,
+    target             => $target,
+    stonith_resources  => $stonith_resource,
+    post_success_sleep => $post_success_sleep,
+    tries              => $tries,
+    try_sleep          => $try_sleep,
+    verify_on_create   => $verify_on_create,
+  }
+}
-- 
2.13.5

