Puppet Class: corosync

Inherits:
::corosync::params
Defined in:
manifests/init.pp

Summary

Configures the Pacemaker+Corosync stack to provide high-availability.

Overview

This class will set up corosync for use by the Puppet Enterprise console to facilitate an active/standby configuration for high availability. It is assumed that this module has been initially ran on a Puppet master with the capabilities of signing certificates to do the initial key generation.

Authors

Cody Herriges <cody@puppetlabs.com>

Copyright 2012, Puppet Labs, LLC.

Examples:

Simple configuration without secauth


class { 'corosync':
  enable_secauth    => false,
  bind_address      => '192.168.2.10',
  multicast_address => '239.1.1.2',
}

Parameters:

  • enable_secauth (Boolean) (defaults to: $corosync::params::enable_secauth)

    Controls corosync’s ability to authenticate and encrypt multicast messages.

  • authkey_source (Enum['file', 'string']) (defaults to: $corosync::params::authkey_source)

    Allows to use either a file or a string as a authkey.

  • authkey (Variant[Stdlib::Filesource,Stdlib::Base64]) (defaults to: $corosync::params::authkey)

    Specifies the path to the CA which is used to sign Corosync’s certificate if authkey_source is ‘file’ or a base64 encoded version of the actual authkey if ‘string’ is used instead.

  • crypto_hash (Corosync::CryptoHash) (defaults to: 'sha1')

    Hashing algorithm used by corosync for intra-cluster communication. Valid values are none, md5, sha1, sha256, sha384, and sha512

  • crypto_cipher (Corosync::CryptoCipher) (defaults to: 'aes256')

    Encryption cipher used by corosync for intra-cluster communication. Valid values are none, aes256, aes192, aes128, and 3des

  • threads (Optional[Integer]) (defaults to: undef)

    How many threads you are going to let corosync use to encode and decode multicast messages. If you turn off secauth then corosync will ignore threads.

  • bind_address (Corosync::IpStringIp) (defaults to: $corosync::params::bind_address)

    The ip address we are going to bind the corosync daemon too. Can be specified as an array to have multiple rings.

  • port (Optional[Variant[Stdlib::Port, Array[Stdlib::Port]]]) (defaults to: $corosync::params::port)

    The UDP port that corosync will use to do its multicast communication. Be aware that corosync used this defined port plus minus one. Can be specified as an array to have multiple rings.

  • multicast_address (Optional[Corosync::IpStringIp]) (defaults to: undef)

    An IP address that has been reserved for multicast traffic. This is the default way that Corosync accomplishes communication across the cluster. Use ‘broadcast’ to have broadcast instead Can be specified as an array to have multiple rings (multicast only).

  • unicast_addresses (Optional[Array]) (defaults to: undef)

    An array of IP addresses that make up the cluster’s members. These are use if you are able to use multicast on your network and instead opt for the udpu transport. You need a relatively recent version of Corosync to make this possible. You can also have an array of arrays to have multiple rings. In that case, each subarray matches a host IP addresses.

  • force_online (Boolean) (defaults to: $corosync::params::force_online)

    Boolean parameter specifying whether to force nodes that have been put in standby back online.

  • check_standby (Boolean) (defaults to: $corosync::params::check_standby)

    Boolean parameter specifying whether puppet should return an error log message if a node is in standby. Useful for monitoring node state.

  • log_timestamp (Boolean) (defaults to: $corosync::params::log_timestamp)

    Boolean parameter specifying whether a timestamp should be placed on all log messages.

  • log_file (Boolean) (defaults to: $corosync::params::log_file)

    Boolean parameter specifying whether Corosync should produce debug output in a logfile.

  • log_file_name (Optional[Stdlib::Absolutepath]) (defaults to: undef)

    Absolute path to the logfile Corosync should use when ‘$log_file` (see above) is true.

  • debug (Boolean) (defaults to: $corosync::params::debug)

    Boolean parameter specifying whether Corosync should produce debug output in its logs.

  • log_stderr (Boolean) (defaults to: $corosync::params::log_stderr)

    Boolean parameter specifying whether Corosync should log errors to stderr.

  • syslog_priority (Corosync::SyslogPriority) (defaults to: $corosync::params::syslog_priority)

    String parameter specifying the minimal log level for Corosync syslog messages. Allowed values: debug|info|notice|warning|err|emerg.

  • log_function_name (Boolean) (defaults to: $corosync::params::log_function_name)

    Boolean parameter specifying whether Corosync should log called function names to.

  • rrp_mode (Optional[Enum['none', 'active', 'passive']]) (defaults to: undef)

    Mode of redundant ring. May be none, active, or passive.

  • netmtu (Optional[Integer]) (defaults to: undef)

    This specifies the network maximum transmit unit.

  • ttl (Optional[Integer[0,255]]) (defaults to: undef)

    Time To Live.

  • vsftype (Optional[Enum['ykd', 'none']]) (defaults to: undef)

    Virtual synchrony filter type.

  • package_corosync (Boolean) (defaults to: $corosync::params::package_corosync)

    Define if package corosync should be managed.

  • package_crmsh (Boolean) (defaults to: $corosync::params::package_crmsh)

    Define if package crmsh should be managed. Default (Debian based): true Default (otherwise): false

  • package_pacemaker (Boolean) (defaults to: $corosync::params::package_pacemaker)

    Define if package pacemaker should be managed.

  • package_pcs (Boolean) (defaults to: $corosync::params::package_pcs)

    Define if package pcs should be managed. Default (Red Hat based): true Default (otherwise): false

  • package_fence_agents (Boolean) (defaults to: $corosync::params::package_fence_agents)

    Define if package fence-agents should be managed. Default (Red Hat based): true Default (otherwise): false

  • packageopts_corosync (Optional[Array[String[1]]]) (defaults to: $corosync::params::package_install_options)

    Additional install-options for the corosync package resource. Default (Debian Jessie): [‘-t’, ‘jessie-backports’] Default (otherwise): undef

  • packageopts_crmsh (Optional[Array[String[1]]]) (defaults to: $corosync::params::package_install_options)

    Additional install-options for the crmsh package resource. Default (Debian Jessie): [‘-t’, ‘jessie-backports’] Default (otherwise): undef

  • packageopts_pacemaker (Optional[Array[String[1]]]) (defaults to: $corosync::params::package_install_options)

    Additional install-options for the pacemaker package resource. Default (Debian Jessie): [‘-t’, ‘jessie-backports’] Default (otherwise): undef

  • packageopts_pcs (Optional[Array[String[1]]]) (defaults to: $corosync::params::package_install_options)

    Additional install-options for the pcs package resource. Default (Debian Jessie): [‘-t’, ‘jessie-backports’] Default (otherwise): undef

  • packageopts_fence_agents (Optional[Array[String[1]]]) (defaults to: $corosync::params::package_install_options)

    Additional install-options for the pcs package resource. Default (Debian Jessie): [‘-t’, ‘jessie-backports’] Default (otherwise): undef

  • version_corosync (String[1]) (defaults to: $corosync::params::version_corosync)

    Define what version of the corosync package should be installed. Default: ‘present’

  • version_crmsh (String[1]) (defaults to: $corosync::params::version_crmsh)

    Define what version of the crmsh package should be installed. Default: ‘present’

  • version_pacemaker (String[1]) (defaults to: $corosync::params::version_pacemaker)

    Define what version of the pacemaker package should be installed. Default: ‘present’

  • version_pcs (String[1]) (defaults to: $corosync::params::version_pcs)

    Define what version of the pcs package should be installed. Default: ‘present’

  • version_fence_agents (String[1]) (defaults to: $corosync::params::version_fence_agents)

    Define what version of the fence-agents-all package should be installed. Default: ‘present’

  • set_votequorum (Boolean) (defaults to: $corosync::params::set_votequorum)

    Set to true if corosync_votequorum should be used as quorum provider. Default (Red Hat based): true Default (Ubuntu >= 14.04): true Default (otherwise): false

  • votequorum_expected_votes (Optional[Integer]) (defaults to: undef)

    Overrides the automatic calculation of expected votes which is normally derived from the number of nodes.

  • quorum_members (Array) (defaults to: ['localhost'])

    Array of quorum member hostname. This is required if set_votequorum is set to true. You can also have an array of arrays to have multiple rings. In that case, each subarray matches a member IP addresses.

  • quorum_members_ids (Optional[Array]) (defaults to: undef)

    Array of quorum member IDs. Persistent IDs are required for the dynamic config of a corosync cluster and when_set_votequorum is set to true. Should be used only with the quorum_members parameter.

  • quorum_members_names (Optional[Array]) (defaults to: undef)

    Array of quorum member names. Persistent names are required when you define IP addresses in quorum_members.

  • token (Optional[Integer]) (defaults to: undef)

    Time (in ms) to wait for a token

  • token_retransmits_before_loss_const (Optional[Integer]) (defaults to: undef)

    How many token retransmits before forming a new configuration.

  • compatibility (Optional[String]) (defaults to: undef)

    Older versions of corosync allowed a config-file directive to indicate backward compatibility. This sets that.

  • enable_corosync_service (Boolean) (defaults to: $corosync::params::enable_corosync_service)

    Whether the module should enable the corosync service.

  • manage_corosync_service (Boolean) (defaults to: $corosync::params::manage_corosync_service)

    Whether the module should try to manage the corosync service. If set to false, the service will need to be specified in the catalog elsewhere.

  • enable_pacemaker_service (Boolean) (defaults to: $corosync::params::enable_pacemaker_service)

    Whether the module should enable the pacemaker service.

  • manage_pacemaker_service (Boolean) (defaults to: $corosync::params::manage_pacemaker_service)

    Whether the module should try to manage the pacemaker service. Default (Red Hat based >= 7): true Default (Ubuntu >= 14.04): true Default (otherwise): false

  • enable_pcsd_service (Boolean) (defaults to: $corosync::params::enable_pcsd_service)

    Whether the module should enable the pcsd service.

  • manage_pcsd_service (Boolean) (defaults to: false)

    Whether the module should try to manage the pcsd service in addition to the corosync service. pcsd service is the GUI and the remote configuration interface.

  • manage_pcsd_auth (Boolean) (defaults to: false)

    This only has an effect when $manage_pcsd_service is enabled. If set, an attempt will be made to authorize pcs on the cluster node determined by manage_pcsd_auth_node. Note that this determination can only be made when the entries in quorum_members match the trusted certnames of the nodes in the environment or the IP addresses of the primary adapters. $sensitive_hacluster_password is mandatory if this parameter is set.

  • manage_pcsd_auth_node (Enum['first','last']) (defaults to: 'first')

    When managing authorization for PCS this determines which node does the work. Note that only one node ‘should’ do the work and nodes are chosen by matching local facts to the contents of quorum_members. When manage_pcsd_auth is disabled this parameter has no effect.

  • sensitive_hacluster_password (Optional[Sensitive[String]]) (defaults to: undef)

    When PCS is configured on a RHEL system this directive is used to set the password for the hacluster user. If both $manage_pcsd_service and $manage_pcsd_auth are both set to true the cluster will use this credential to authorize all nodes.

  • sensitive_hacluster_hash (Optional[Sensitive[String]]) (defaults to: undef)

    This parameter expects a valid password hash of sensitive_hacluster_password. If provided, the hash provided the hash will be used to set the password for the hacluster user on each node.

  • manage_quorum_device (Boolean) (defaults to: false)

    Enable or disable the addition of a quorum device external to the cluster. This device is used avoid cluster splits typically in conjunction with fencing by providing an external network vote. Additionally, this allows symmentric clusters to continue operation in the event that 50% of their nodes have failed.

  • quorum_device_host (Optional[Stdlib::Fqdn]) (defaults to: undef)

    The fully qualified hostname of the quorum device. This parameter is mandatory when manage_quorum_device is true.

  • quorum_device_algorithm (Optional[Corosync::QuorumAlgorithm]) (defaults to: 'ffsplit')

    There are currently two algorithms the quorum device can utilize to determine how its vote should be allocated; Fifty-fifty split and last-man-standing. See the [corosync-qdevice man page](www.systutorials.com/docs/linux/man/8-corosync-qdevice/) for details.

  • package_quorum_device (Optional[String]) (defaults to: $corosync::params::package_quorum_device)

    The name of the package providing the quorum device functionality. This parameter is mandatory if manage_quorum_device is true.

  • sensitive_quorum_device_password (Optional[Sensitive[String]]) (defaults to: undef)

    The plain text password for the hacluster user on the quorum_device_host. This parameter is mandatory if manage_quorum_device is true.

  • cluster_name (Optional[String[1]]) (defaults to: undef)

    This specifies the name of cluster and it’s used for automatic generating of multicast address.

  • join (Optional[Integer]) (defaults to: undef)

    This timeout specifies in milliseconds how long to wait for join messages in the membership protocol.

  • consensus (Optional[Integer]) (defaults to: undef)

    This timeout specifies in milliseconds how long to wait for consensus to be achieved before starting a new round of membership configuration. The minimum value for consensus must be 1.2 * token. This value will be automatically calculated at 1.2 * token if the user doesn’t specify a consensus value.

  • ip_version (Optional[String[1]]) (defaults to: undef)

    This specifies version of IP to ask DNS resolver for. The value can be one of ipv4 (look only for an IPv4 address) , ipv6 (check only IPv6 address), ipv4-6 (look for all address families and use first IPv4 address found in the list if there is such address, otherwise use first IPv6 address) and ipv6-4 (look for all address families and use first IPv6 address found in the list if there is such address, otherwise use first IPv4 address).

    Default (if unspecified) is ipv6-4 for knet and udpu transports and ipv4 for udp.

  • clear_node_high_bit (Optional[Enum['yes', 'no']]) (defaults to: undef)

    This configuration option is optional and is only relevant when no nodeid is specified. Some openais clients require a signed 32 bit nodeid that is greater than zero however by default openais uses all 32 bits of the IPv4 address space when generating a nodeid. Set this option to yes to force the high bit to be zero and therefor ensure the nodeid is a positive signed 32 bit integer. WARNING: The clusters behavior is undefined if this option is enabled on only a subset of the cluster (for example during a rolling upgrade).

  • max_messages (Optional[Integer]) (defaults to: undef)

    This constant specifies the maximum number of messages that may be sent by one processor on receipt of the token. The max_messages parameter is limited to 256000 / netmtu to prevent overflow of the kernel transmit buffers.

  • test_corosync_config (Boolean) (defaults to: $corosync::params::test_corosync_config)

    Whether we should test new configuration files with ‘corosync -t`. (requires corosync 2.3.4)



339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
# File 'manifests/init.pp', line 339

class corosync (
  Boolean $enable_secauth                                            = $corosync::params::enable_secauth,
  Enum['file', 'string'] $authkey_source                             = $corosync::params::authkey_source,
  Variant[Stdlib::Filesource,Stdlib::Base64] $authkey                = $corosync::params::authkey,
  Corosync::CryptoHash $crypto_hash                                  = 'sha1',
  Corosync::CryptoCipher $crypto_cipher                              = 'aes256',
  Optional[Integer] $threads                                         = undef,
  Optional[Variant[Stdlib::Port, Array[Stdlib::Port]]] $port         = $corosync::params::port,
  Corosync::IpStringIp $bind_address                                 = $corosync::params::bind_address,
  Optional[Corosync::IpStringIp] $multicast_address                  = undef,
  Optional[Array] $unicast_addresses                                 = undef,
  Boolean $force_online                                              = $corosync::params::force_online,
  Boolean $check_standby                                             = $corosync::params::check_standby,
  Boolean $log_timestamp                                             = $corosync::params::log_timestamp,
  Boolean $log_file                                                  = $corosync::params::log_file,
  Optional[Stdlib::Absolutepath] $log_file_name                      = undef,
  Boolean $debug                                                     = $corosync::params::debug,
  Boolean $log_stderr                                                = $corosync::params::log_stderr,
  Corosync::SyslogPriority $syslog_priority                          = $corosync::params::syslog_priority,
  Boolean $log_function_name                                         = $corosync::params::log_function_name,
  Optional[Enum['none', 'active', 'passive']] $rrp_mode              = undef,
  Optional[Integer] $netmtu                                          = undef,
  Optional[Integer[0,255]] $ttl                                      = undef,
  Optional[Enum['ykd', 'none']] $vsftype                             = undef,
  Boolean $package_corosync                                          = $corosync::params::package_corosync,
  Boolean $package_crmsh                                             = $corosync::params::package_crmsh,
  Boolean $package_pacemaker                                         = $corosync::params::package_pacemaker,
  Boolean $package_pcs                                               = $corosync::params::package_pcs,
  Boolean $package_fence_agents                                      = $corosync::params::package_fence_agents,
  Optional[Array[String[1]]] $packageopts_corosync                   = $corosync::params::package_install_options,
  Optional[Array[String[1]]] $packageopts_pacemaker                  = $corosync::params::package_install_options,
  Optional[Array[String[1]]] $packageopts_crmsh                      = $corosync::params::package_install_options,
  Optional[Array[String[1]]] $packageopts_pcs                        = $corosync::params::package_install_options,
  Optional[Array[String[1]]] $packageopts_fence_agents               = $corosync::params::package_install_options,
  String[1] $version_corosync                                        = $corosync::params::version_corosync,
  String[1] $version_crmsh                                           = $corosync::params::version_crmsh,
  String[1] $version_pacemaker                                       = $corosync::params::version_pacemaker,
  String[1] $version_pcs                                             = $corosync::params::version_pcs,
  String[1] $version_fence_agents                                    = $corosync::params::version_fence_agents,
  Boolean $set_votequorum                                            = $corosync::params::set_votequorum,
  Optional[Integer] $votequorum_expected_votes                       = undef,
  Array $quorum_members                                              = ['localhost'],
  Optional[Array] $quorum_members_ids                                = undef,
  Optional[Array] $quorum_members_names                              = undef,
  Optional[Integer] $token                                           = undef,
  Optional[Integer] $token_retransmits_before_loss_const             = undef,
  Optional[String] $compatibility                                    = undef,
  Boolean $enable_corosync_service                                   = $corosync::params::enable_corosync_service,
  Boolean $manage_corosync_service                                   = $corosync::params::manage_corosync_service,
  Boolean $enable_pacemaker_service                                  = $corosync::params::enable_pacemaker_service,
  Boolean $manage_pacemaker_service                                  = $corosync::params::manage_pacemaker_service,
  Boolean $enable_pcsd_service                                       = $corosync::params::enable_pcsd_service,
  Boolean $manage_pcsd_service                                       = false,
  Boolean $manage_pcsd_auth                                          = false,
  Optional[Sensitive[String]] $sensitive_hacluster_password          = undef,
  Optional[Sensitive[String]] $sensitive_hacluster_hash              = undef,
  Enum['first','last'] $manage_pcsd_auth_node                        = 'first',
  Boolean $manage_quorum_device                                      = false,
  Optional[Stdlib::Fqdn] $quorum_device_host                         = undef,
  Optional[Corosync::QuorumAlgorithm] $quorum_device_algorithm       = 'ffsplit',
  Optional[String] $package_quorum_device                            = $corosync::params::package_quorum_device,
  Optional[Sensitive[String]] $sensitive_quorum_device_password      = undef,
  Optional[String[1]] $cluster_name                                  = undef,
  Optional[Integer] $join                                            = undef,
  Optional[Integer] $consensus                                       = undef,
  Optional[String[1]] $ip_version                                    = undef,
  Optional[Enum['yes', 'no']] $clear_node_high_bit                   = undef,
  Optional[Integer] $max_messages                                    = undef,
  Boolean $test_corosync_config                                      = $corosync::params::test_corosync_config,
) inherits ::corosync::params {
  if $set_votequorum and (empty($quorum_members) and empty($multicast_address) and !$cluster_name) {
    fail('set_votequorum is true, so you must set either quorum_members, or one of multicast_address or cluster_name.')
  }

  if $quorum_members_names and empty($quorum_members) {
    fail('quorum_members_names may not be used without the quorum_members.')
  }

  if $quorum_members_ids and empty($quorum_members) {
    fail('quorum_members_ids may not be used without the quorum_members.')
  }

  if $package_corosync {
    package { 'corosync':
      ensure          => $version_corosync,
      install_options => $packageopts_corosync,
    }
    $corosync_package_require = Package['corosync']
  } else {
    $corosync_package_require = undef
  }

  if $package_pacemaker {
    package { 'pacemaker':
      ensure          => $version_pacemaker,
      install_options => $packageopts_pacemaker,
    }
  }

  if $package_crmsh {
    package { 'crmsh':
      ensure          => $version_crmsh,
      install_options => $packageopts_crmsh,
    }
  }

  if $package_pcs {
    package { 'pcs':
      ensure          => $version_pcs,
      install_options => $packageopts_pcs,
    }

    # Set the password for the hacluster user if it was provided
    if $sensitive_hacluster_hash {
      group { 'haclient':
        ensure  => 'present',
        require => Package['pcs'],
      }

      user { 'hacluster':
        ensure   => 'present',
        gid      => 'haclient',
        password => $sensitive_hacluster_hash,
      }
    }
  }

  if $package_fence_agents {
    package { 'fence-agents-all':
      ensure          => $version_fence_agents,
      install_options => $packageopts_fence_agents,
    }
  }

  # You have to specify at least one of the following parameters:
  # $multicast_address or $unicast_address or $cluster_name
  if !$multicast_address and empty($unicast_addresses) and !$cluster_name {
    fail('You must provide a value for multicast_address, unicast_address or cluster_name.')
  }

  # Using the Puppet infrastructure's ca as the authkey, this means any node in
  # Puppet can join the cluster.  Totally not ideal, going to come up with
  # something better.
  if $enable_secauth {
    case $authkey_source {
      'file': {
        file { '/etc/corosync/authkey':
          ensure  => file,
          source  => $authkey,
          mode    => '0400',
          owner   => 'root',
          group   => 'root',
          notify  => Service['corosync'],
          require => $corosync_package_require,
        }
        File['/etc/corosync/authkey'] -> File['/etc/corosync/corosync.conf']
      }
      'string': {
        file { '/etc/corosync/authkey':
          ensure  => file,
          content => Binary($authkey, '%B'),
          mode    => '0400',
          owner   => 'root',
          group   => 'root',
          notify  => Service['corosync'],
          require => $corosync_package_require,
        }
        File['/etc/corosync/authkey'] -> File['/etc/corosync/corosync.conf']
      }
      default: {}
    }
  }

  if $manage_pcsd_service {
    service { 'pcsd':
      ensure  => running,
      enable  => $enable_pcsd_service,
      require => Package['pcs'],
    }

    # Validate the pcs auth / qdevice parameters when both are enabled
    if $manage_pcsd_auth and $manage_quorum_device {
      # Ensure the optional parameters have been provided
      if ! $quorum_device_host {
        fail('The quorum device host must be specified!')
      }

      if ! $sensitive_quorum_device_password {
        fail('The password for the hacluster user on the quorum device node is mandatory!')
      }

      if ! $cluster_name {
        fail('A cluster name must be specified when a quorm device is configured!')
      }

      # The quorum device cannot be a member of the cluster!
      if $quorum_device_host in $quorum_members {
        fail('Quorum device host cannot also be a member of the cluster!')
      }
    } elsif $manage_pcsd_auth {
      if ! $sensitive_hacluster_password or ! $sensitive_hacluster_hash {
        fail('The hacluster password and hash must be provided to authorize nodes via pcsd.')
      }
    }

    # Determine if this node should perform authorizations
    case $manage_pcsd_auth_node {
      'first': { $auth_node = $quorum_members[0] }
      'last': { $auth_node = $quorum_members[length($quorum_members)-1] }
      default: {}
    }

    # Calculate a full list of IP addresses
    unless(empty($facts['networking']['interfaces'])) {
      $interface_ip_list = $facts['networking']['interfaces'].map |$entry| {
        if 'ip' in $entry[1] {
          $entry[1]['ip']
        } else {
          'no_address'
        }
      }
    } else {
      $interface_ip_list = []
    }

    # If the local data matches auth_node (hostname or primary IP) we can
    # perform auth processing for subsequent components
    if $trusted['certname'] == $auth_node
    or $trusted['hostname'] == $auth_node
    or $auth_node == $facts['networking']['ip']
    or $auth_node in $interface_ip_list {
      $is_auth_node = true
    } else {
      $is_auth_node = false
    }

    $exec_path = '/sbin:/bin:/usr/sbin:/usr/bin'

    if $manage_pcsd_auth and $is_auth_node {
      # TODO - verify if this breaks out of the sensitivity
      $hacluster_password = $sensitive_hacluster_password.unwrap
      $auth_credential_string = "-u hacluster -p ${hacluster_password}"

      # As the auth can happen before corosync.conf exists we need to explicitly
      # list the members to join.
      # TODO - verify that this is safe when quorum_members is a list of IP
      # addresses
      $node_string = join($quorum_members, ' ')

      # Attempt to authorize all members. The command will return successfully
      # if they were already authenticated so it's safe to run every time this
      # is applied.
      # TODO - make it run only once
      exec { 'pcs_cluster_auth':
        command => "pcs cluster auth ${node_string} ${auth_credential_string}",
        path    => $exec_path,
        require => [
          Service['pcsd'],
          User['hacluster'],
        ],
      }
    }

    if $manage_quorum_device and $manage_pcsd_auth and $set_votequorum {
      package { $package_quorum_device:
        ensure => 'present',
      }
      service { 'corosync-qdevice':
        ensure    => running,
        enable    => true,
        require   => Package[$package_quorum_device],
        subscribe => Service['corosync'],
      }
    }

    if $manage_quorum_device and $manage_pcsd_auth and $is_auth_node and $set_votequorum {
      # If the cluster hasn't been configured yet, temporarily configure it so
      # the pcs_cluster_auth_qdevice command doesn't fail. This should generate
      # a temporary corosync.conf which will then be overwritten
      exec { 'pcs_cluster_temporary':
        command => "pcs cluster setup --force --name ${cluster_name} ${node_string}",
        path    => $exec_path,
        onlyif  => 'test ! -f /etc/corosync/corosync.conf',
        require => Exec['pcs_cluster_auth'],
      }
      # We need to do this so the temporary cluster doesn't delete our authkey
      if $enable_secauth {
        Exec['pcs_cluster_temporary'] -> File['/etc/corosync/authkey']
      }

      # Authorize the quorum device via PCS so we can execute the configuration
      $token_prefix = 'test 0 -ne $(grep'
      $token_suffix = '/var/lib/pcsd/tokens >/dev/null 2>&1; echo $?)'
      $qdevice_token_check = "${token_prefix} ${quorum_device_host} ${token_suffix}"

      $quorum_device_password = $sensitive_quorum_device_password.unwrap
      exec { 'pcs_cluster_auth_qdevice':
        command => "pcs cluster auth ${quorum_device_host} -u hacluster -p ${quorum_device_password}",
        path    => $exec_path,
        onlyif  => $qdevice_token_check,
        require => [
          Package[$package_quorum_device],
          Exec['pcs_cluster_auth'],
          Exec['pcs_cluster_temporary'],
        ],
      }

      # Add the quorum device to the cluster
      $quorum_host = "host=${quorum_device_host}"
      $quorum_algorithm = "algorithm=${quorum_device_algorithm}"
      $quorum_setup_cmd =
        "pcs quorum device add model net ${quorum_host} ${quorum_algorithm}"
      exec { 'pcs_cluster_add_qdevice':
        command => $quorum_setup_cmd,
        path    => $exec_path,
        onlyif  => [
          'test 0 -ne $(pcs quorum config | grep "host:" >/dev/null 2>&1; echo $?)',
        ],
        require => Exec['pcs_cluster_auth_qdevice'],
        before  => File['/etc/corosync/corosync.conf'],
        notify  => Service['corosync-qdevice'],
      }
    }
  }

  # Template uses:
  # - $unicast_addresses
  # - $multicast_address
  # - $cluster_name
  # - $log_timestamp
  # - $log_file
  # - $log_file_name
  # - $debug
  # - $bind_address
  # - $port
  # - $enable_secauth
  # - $crypto_hash
  # - $crypto_cipher
  # - $threads
  # - $token
  # - $join
  # - $consensus
  # - $ip_version
  # - $clear_node_high_bit
  # - $max_messages
  if $test_corosync_config {
    # corosync -t is only included since 2.3.4
    $config_validate_cmd = '/usr/bin/env COROSYNC_MAIN_CONFIG_FILE=% /usr/sbin/corosync -t'
  } else {
    $config_validate_cmd = undef
  }

  file { '/etc/corosync/corosync.conf':
    ensure       => file,
    mode         => '0644',
    owner        => 'root',
    group        => 'root',
    content      => template("${module_name}/corosync.conf.erb"),
    validate_cmd => $config_validate_cmd,
    require      => $corosync_package_require,
  }

  file { '/etc/corosync/service.d':
    ensure  => directory,
    mode    => '0755',
    owner   => 'root',
    group   => 'root',
    recurse => true,
    purge   => true,
    require => $corosync_package_require,
  }

  case $facts['os']['family'] {
    'Debian': {
      augeas { 'enable corosync':
        lens    => 'Shellvars.lns',
        incl    => '/etc/default/corosync',
        context => '/files/etc/default/corosync',
        changes => [
          'set START "yes"',
        ],
        require => $corosync_package_require,
        before  => Service['corosync'],
      }
    }
    default: {}
  }

  if $check_standby {
    # Throws a puppet error if node is on standby
    exec { 'check_standby node':
      command => 'echo "Node appears to be on standby" && false',
      path    => ['/bin', '/usr/bin', '/sbin', '/usr/sbin'],
      onlyif  => "crm node status|grep ${facts['networking']['hostname']}-standby|grep 'value=\"on\"'",
      require => Service['corosync'],
    }
  }

  if $force_online {
    exec { 'force_online node':
      command => 'crm node online',
      path    => ['/bin', '/usr/bin', '/sbin', '/usr/sbin'],
      onlyif  => "crm node status|grep ${facts['networking']['hostname']}-standby|grep 'value=\"on\"'",
      require => Service['corosync'],
    }
  }

  if $manage_pacemaker_service {
    service { 'pacemaker':
      ensure     => running,
      enable     => $enable_pacemaker_service,
      hasrestart => true,
      subscribe  => Service['corosync'],
    }
  }

  if $manage_corosync_service {
    service { 'corosync':
      ensure    => running,
      enable    => $enable_corosync_service,
      subscribe => File[['/etc/corosync/corosync.conf', '/etc/corosync/service.d']],
    }
  }
}