ClusterLabs/hawk

View on GitHub
hawk/app/models/clone.rb

Summary

Maintainability
B
4 hrs
Test Coverage
# Copyright (c) 2009-2015 Tim Serong <tserong@suse.com>
# See COPYING for license.
require 'invoker'

class Clone < Resource
  attribute :id, String
  attribute :child, String

  validates :id,
    presence: { message: _("Clone ID is required") },
    format: { with: /\A[a-zA-Z0-9_-]+\z/, message: _("Invalid Clone ID") }

  validates :child,
    presence: { message: _("No Clone child specified") }

  validate do |record|
    # TODO(must): Ensure children are sanitized
  end

  def mapping
    self.class.mapping
  end

  class << self
    def all
      super.select do |record|
        record.is_a? self
      end
    end

    def instantiate(xml)
      record = allocate

      record.child = if xml.elements["primitive|group"]
        xml.elements["primitive|group"].attributes["id"]
      else
        nil
      end

      record.meta = if xml.elements["meta_attributes"]
        vals = xml.elements["meta_attributes"].elements.collect do |el|
          [
            el.attributes["name"],
            el.attributes["value"]
          ]
        end

        Hash[vals]
      else
        {}
      end

      record
    end

    def cib_type
      :clone
    end

    def mapping
      # TODO(must): Are other meta attributes for clone valid?
      @mapping ||= begin
        {
          "is-managed" => {
            type: "boolean",
            default: "true",
            longdesc: _("Is the cluster allowed to start and stop the resource?")
          },
          "maintenance" => {
            type: "boolean",
            default: "false",
            longdesc: _("Resources in maintenance mode are not monitored by the cluster.")
          },
          "priority" => {
            type: "integer",
            default: "0",
            longdesc: _("If not all resources can be active, the cluster will stop lower priority resources in order to keep higher priority ones active.")
          },
          "target-role" => {
            type: "enum",
            default: "Stopped",
            values: ["Started", "Stopped", "Master"],
            longdesc: _("What state should the cluster attempt to keep this resource in?")
          },
          "clone-max" => {
            type: "integer",
            default: current_cib.nodes.length.to_s,
            longdesc: _("How many copies of the resource to start. Defaults to the number of nodes in the cluster.")
          },
          "clone-node-max" => {
            type: "integer",
            default: "1",
            longdesc: _("How many copies of the resource can be started on a single node. Defaults to 1.")
          },
          "notify" => {
            type: "boolean",
            default: "false",
            longdesc: _("When stopping or starting a copy of the clone, tell all the other copies beforehand and when the action was successful.")
          },
          "globally-unique" => {
            type: "boolean",
            default: "true",
            longdesc: _("Does each copy of the clone perform a different function?")
          },
          "ordered" => {
            type: "boolean",
            default: "false",
            longdesc: _("Should the copies be started in series (instead of in parallel)?")
          },
          "interleave" => {
            type: "boolean",
            default: "false",
            longdesc: _("Changes the behavior of ordering constraints (between clones/masters) so that instances can start/stop as soon as their peer instance has (rather than waiting for every instance of the other clone has).")
          }
        }
      end
    end

    def help_text
      super.merge(
        "resource" => {
          type: "string",
          default: "",
          shortdesc: _("Child Resource"),
          longdesc: _("Child resource to use as clone.")
        }
      )
    end
  end

  protected

  def update
    # TODO(must): use crmsh for this
    unless self.class.exists?(self.id, self.class.cib_type_write)
      errors.add :base, _("The ID \"%{id}\" does not exist") % { id: self.id }
      return false
    end

    begin
      merge_nvpairs("meta_attributes", meta)
      Invoker.instance.cibadmin_replace xml.to_s
    rescue NotFoundError, SecurityError, RuntimeError => e
      errors.add :base, e.message
      return false
    end
  end

  def shell_syntax
    [].tap do |cmd|
      cmd.push "clone #{id} #{child}"

      unless meta.empty?
        cmd.push "meta"

        meta.each do |key, value|
          cmd.push [
            key,
            value.shellescape
          ].join("=")
        end
      end
    end.join(" ")
  end
end