class oci::swiftproxy(
  $openstack_release          = undef,
  $machine_hostname           = undef,
  $machine_ip                 = undef,
  $first_master               = undef,
  $first_master_ip            = undef,
  $vip_hostname               = undef,
  $vip_ipaddr                 = undef,
  $vip_netmask                = undef,
  $self_signed_api_cert       = true,
  $network_ipaddr             = undef,
  $network_cidr               = undef,
  $all_masters                = undef,
  $all_masters_ip             = undef,
  $all_swiftstore_ip          = undef,
  $all_swiftstore             = undef,
  $all_swiftproxy             = undef,
  $all_swiftproxy_ip          = undef,
  $swiftproxy_hostname        = 'none',
  $swiftregion_id             = undef,
  $statsd_hostname            = undef,
  $monitoring_graphite_host   = undef,
  $monitoring_graphite_port   = undef,
  $pass_swift_authtoken       = undef,
  $pass_swift_hashpathsuffix  = undef,
  $pass_swift_hashpathprefix  = undef,
  $swift_encryption_key_id    = undef,
  $max_containers_per_account = 0,
  $max_containers_whitelist   = undef,
  $use_ssl                    = true,
  $swift_disable_encryption   = undef,
  $pass_haproxy_stats         = undef,
  $object_expirer_enable      = false,
  # These are if we're setting-up account + containers on the proxy hosts
  $swift_object_replicator_concurrency = undef,
  $swift_rsync_connection_limit        = undef,
  $block_devices                       = undef,

  $messaging_nodes_for_notifs = false,
  $all_rabbits                = [],
  $all_rabbits_ips            = [],

  $swift_store_account        = false,
  $swift_store_container      = false,
  $swift_store_object         = false,

  $servers_per_port           = 1,
  $network_chunk_size         = 65535,
  $disk_chunk_size            = 524288,

  $swift_storage_allowed_networks = undef,
){

  if $facts['os']['lsb'] != undef{
    $mycodename = $facts['os']['lsb']['distcodename']
  }else{
    $mycodename = $facts['os']['distro']['codename']
  }

  if $mycodename == 'stretch' or $mycodename == 'buster' {
    # This used to work with haproxy 1.8,
    # let's see if we can get rid of it
    $haproxy_replace_path = 'reqrep'
    $haproxy_replace_head = 'rspirep'
    $use_haproxy_21_format = false
  }else{
    # This works with haproxy >= 2.1
    $haproxy_replace_path  = 'http-request replace-path'
    $haproxy_replace_head  = 'http-response replace-header'
    $use_haproxy_21_format = true
  }

  # Generic performances tweak (will default to throughput-performance)
  # class { '::oci::tuned': }

  if $use_ssl {
    $proto = 'https'
    $api_port = 443
  } else {
    $proto = 'http'
    $api_port = 80
  }

  $base_url = "${proto}://${vip_hostname}"
  $keystone_auth_uri  = "${base_url}/identity"
  $keystone_admin_uri = "${base_url}/identity"
  $memcached_servers  = ["${machine_ip}:11211"]

  if $self_signed_api_cert {
    $api_endpoint_ca_file = '/etc/ssl/certs/oci-pki-oci-ca-chain.pem'
  }else{
    $api_endpoint_ca_file = ''
  }

  class { '::oci::puppet_oci_ca_cert':
    self_signed_api_cert => $self_signed_api_cert,
  }

  if $openstack_release == 'rocky' or $openstack_release == 'stein'{
    package { 'python-keystonemiddleware':
      ensure => present,
      notify => Service['swift-proxy'],
    }
  }else{
    package { 'python3-keystonemiddleware':
      ensure => present,
      notify => Service['swift-proxy'],
    }
  }

  package { 'rsyslog':
    ensure => present,
  }
  service { 'rsyslog':
    ensure  => running,
    enable  => true,
    require => Package['rsyslog'],
  }

  file { '/var/log/swift':
    ensure => directory,
    mode   => '0750',
  }

  file { "/etc/systemd/system/local-fs.target.d":
    ensure                  => directory,
    owner                   => 'root',
    mode                    => '0755',
    selinux_ignore_defaults => true,
  }

  $str = "# Clear OnFailure= (set it to nothing)
# so the server continues to boot on mount failures
[Unit]
OnFailure=
"

  file { "/etc/systemd/system/local-fs.target.d/nofail.conf":
    ensure                  => present,
    owner                   => 'root',
    mode                    => '0644',
    content                 => $str,
    selinux_ignore_defaults => true,
  }

  file { '/etc/rsyslog.d/10-swift.conf':
    ensure  => present,
    source  => "puppet:///modules/${module_name}/rsyslog-swift.conf",
    require => [Package['rsyslog'], File['/var/log/swift']],
    notify  => Service['rsyslog'],
  }

  File<| title == '/var/log/swift' |> {
    owner => 'swift',
    group => 'adm'
  }

  # Get memcached
  class { '::memcached':
    listen_ip => "0.0.0.0",
    udp_port  => 0,
    max_memory => '20%',
  }

  firewall { "050. Allow memcached on $machine_ip TCP":
    proto       => tcp,
    action      => accept,
    source      => "$machine_ip",
    dport       => '11211',
  }->
  firewall { "050. Allow memcached on 127.0.0.1 TCP":
    proto       => tcp,
    action      => accept,
    source      => "127.0.0.1",
    dport       => '11211',
  }->
  firewall { "050. Drop memcached on public TCP":
    proto       => tcp,
    action      => drop,
    dport       => '11211',
  }
  firewall { "050. Allow memcached on $machine_ip UDP":
    proto       => udp,
    action      => accept,
    source      => "$machine_ip",
    dport       => '11211',
  }->
  firewall { "050. Allow memcached on 127.0.0.1 UDP":
    proto       => udp,
    action      => accept,
    source      => "127.0.0.1",
    dport       => '11211',
  }->
  firewall { "050. Drop memcached on public UDP":
    proto       => udp,
    action      => drop,
    dport       => '11211',
  }

  $swift_storage_allowed_networks.each |Integer $index, String $value| {
    $val1 = $index*2+100
    $val2 = $index*2+101
    firewall { "${val1} Allow ${value} to access to swift container and account servers":
      proto       => tcp,
      action      => accept,
      source      => "${value}",
      dport       => '6001-6002',
    }->
    firewall { "${val2} Allow ${value} to access to swift object servers":
      proto       => tcp,
      action      => accept,
      source      => "${value}",
      dport       => '6200-6229',
    }
  }

  firewall { '801 Jump to LOGDROP for container and account servers':
    proto       => tcp,
    jump        => 'LOGDROP',
    dport       => '6001-6002',
  }

  firewall { '801 Jump to LOGDROP for object servers':
    proto       => tcp,
    jump        => 'LOGDROP',
    dport       => '6200-6229',
  }

  firewallchain { 'LOGDROP:filter:IPv4':
    ensure  => present,
  }

  firewall { '901 LOG rule for dropped packets':
    chain       => 'LOGDROP',
    proto       => tcp,
    jump        => 'LOG',
    log_level   => '6',
    log_prefix  => 'swift dropped packet',
    limit       => '1/sec',
  }
  firewall { "902 Deny all access to container and account server":
    chain       => 'LOGDROP',
    proto       => tcp,
    action      => drop,
    dport       => '6001-6002',
  }
  firewall { "903 Deny all access to object server":
    chain       => 'LOGDROP',
    proto       => tcp,
    action      => drop,
    dport       => '6200-6229',
  }

  if $swiftproxy_hostname == "none" {
    debug("OCI will *NOT* setup a swiftproxy direct access.")
  } else {
    debug("OCI now setting-up a swiftproxy direct access.")
    if $use_ssl {
      file { "/etc/haproxy/ssl":
        ensure                  => directory,
        owner                   => 'root',
        mode                    => '0755',
        selinux_ignore_defaults => true,
        require       => Package['haproxy'],
      }->
      file { "/etc/haproxy/ssl/private":
        ensure                  => directory,
        owner                   => 'root',
        mode                    => '0755',
        selinux_ignore_defaults => true,
      }->
      file { "/etc/haproxy/ssl/private/oci-pki-swiftproxy.pem":
        ensure                  => present,
        owner                   => "haproxy",
        source                  => "/etc/ssl/private/oci-pki-swiftproxy.pem",
        selinux_ignore_defaults => true,
        mode                    => '0600',
        notify                  => Service['haproxy'],
      }
    }

    class { 'haproxy':
      global_options   => {
        'log'     => '/dev/log local0',
        'maxconn' => '40960',
        'user'    => 'haproxy',
        'group'   => 'haproxy',
        'daemon'  => '',
        'nbproc'  => '4',
        'ssl-default-bind-ciphers'   => 'ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-SHA384:ECDHE-RSA-AES256-SHA384:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA256',
        'ssl-default-bind-options'   => 'no-sslv3 no-tlsv10 no-tlsv11 no-tls-tickets',
        'ssl-default-server-ciphers' => 'ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-SHA384:ECDHE-RSA-AES256-SHA384:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA256',
        'ssl-default-server-options' => 'no-sslv3 no-tlsv10 no-tlsv11 no-tls-tickets',
      },
      defaults_options => {
        'mode'    => 'http',
        'maxconn' => '40960',
        'timeout' => [
          'http-request 10s',
          'queue 11m',
          'connect 10s',
          'client 11m',
          'server 11m',
          'check 10s',
        ],
        'stats'     => 'enable',
        'option'  => [
          'httplog',
          'logasap',
         ],
      },
      merge_options => true,
    }->
    haproxy::listen { 'hapstats':
      section_name => 'statshttp',
      bind         => { "${machine_ip}:8088" => 'user root'},
      mode         => 'http',
      options      => {
          'stats' => [ 'uri /', "auth admin:${pass_haproxy_stats}", 'refresh 15', 'admin if TRUE', 'realm Haproxy Statistics', ],
        },
    }


    # Global frontend for all services with dispatch depending on the URL
    haproxy::frontend { 'openstackfe':
      mode      => 'http',
      bind      => { "0.0.0.0:${api_port}" => ['ssl', 'crt', '/etc/haproxy/ssl/private/oci-pki-swiftproxy.pem', 'crt', '/etc/haproxy/ssl/private/'] },
      options   => [
        { 'acl'         => 'url_swift path_beg -i /object'},
        { 'use_backend' => 'swiftbe if url_swift'},
        { 'use_backend' => 'swiftbe_always'},
        # Set HSTS (63072000 seconds = 2 years)
        { 'http-response' => 'set-header Strict-Transport-Security max-age=63072000'},
        { 'option'      => [
            'httplog',
            'logasap',
          ],
        }
      ]
    }

    # This backend + backend-member is for the /object version, as
    # advertized in our endpoints.
    if $use_haproxy_21_format {
      $tweak_path_object = '^/object/?(.*) /\1'
    }else{
      $tweak_path_object = '^([^\ ]*\ /)object[/]?(.*)     \1\2'
    }
    haproxy::backend { 'swiftbe':
      options => [
         { 'option'              => 'httpchk GET /healthcheck' },
         { 'option'              => 'forwardfor' },
         { 'mode'                => 'http' },
         { 'balance'             => 'source' },
         { $haproxy_replace_path => $tweak_path_object},
      ],
    }

    haproxy::balancermember { 'swiftbm':
      listening_service => 'swiftbe',
      ipaddresses       => [ $machine_ip ],
      server_names      => [ $machine_hostname ],
      ports             => 8080,
      options           => 'check',
    }

    $other_swift_proxies = $all_swiftproxy - $machine_hostname
    $other_swift_proxies_ip = $all_swiftproxy_ip - $machine_ip
    haproxy::balancermember { 'swiftbmbackup':
      listening_service => 'swiftbe',
      ipaddresses       => $other_swift_proxies_ip,
      server_names      => $other_swift_proxies,
      ports             => 8080,
      options           => "check backup",
    }

    # This backend + backend-member is for without the /object version, as
    # per what will S3 clients use.
    haproxy::backend { 'swiftbe_always':
      options => [
         { 'option' => 'httpchk GET /healthcheck' },
         { 'option' => 'forwardfor' },
         { 'mode' => 'http' },
         { 'balance' => 'source' },
      ],
    }

    haproxy::balancermember { 'swiftbm_always':
      listening_service => 'swiftbe_always',
      ipaddresses       => [ $machine_ip ],
      server_names      => [ $machine_hostname ],
      ports             => 8080,
      options           => 'check',
    }
  }


  class { '::swift':
    swift_hash_path_suffix => $pass_swift_hashpathsuffix,
    swift_hash_path_prefix => $pass_swift_hashpathprefix,
  }

  class { '::swift::proxy::s3api':
    max_upload_part_num => 10000,
  }
  class { 'swift::proxy::s3token':
    auth_uri => "${keystone_auth_uri}/v3",
  }

  # Because there's no ca_file option in castellan, we must
  # allow swiftproxy to run without encryption  in case we're
  # running on a PoC without a real certificate for the API
  if($swift_disable_encryption =='yes' or $swift_encryption_key_id == ''){
    $disable_encryption = true
  }
  $pipeline_start = [ 'catch_errors', 'healthcheck', 'proxy-logging', 'cache', 'container_sync', 'bulk', 'ratelimit', 'authtoken', 's3api', 's3token', 'keystone', 'copy', 'container-quotas', 'account-quotas', 'slo', 'dlo', 'versioned_writes' ]
  if $swift_encryption_key_id == "" {
    $pipeline_kms = $pipeline_start
  } else {
    $pipeline_kms = concat($pipeline_start, [ 'kms_keymaster', 'encryption' ])
  }
  $pipeline = concat($pipeline_kms, [ 'proxy-logging', 'proxy-server' ])

  if $openstack_release == 'rocky' or $openstack_release == 'stein'{
    package { 'barbicanclient':
      name   => 'python-barbicanclient',
      ensure => installed,
    }->
    package { 'castellan':
      name   => 'python3-castellan',
      ensure => installed,
    }->
    class { '::swift::proxy':
      proxy_local_net_ip         => $machine_ip,
      port                       => '8080',
      account_autocreate         => true,
      pipeline                   => $pipeline,
      node_timeout               => 30,
  #    read_affinity              => "r${swiftregion_id}=100",
  #    write_affinity             => "r${swiftregion_id}",
      max_containers_per_account => $max_containers_per_account,
      max_containers_whitelist   => $max_containers_whitelist,
      workers                    => 20,
      client_timeout             => 340,
    }
  }else{
    package { 'barbicanclient':
      name   => 'python3-barbicanclient',
      ensure => installed,
    }->
    package { 'castellan':
      name   => 'python3-castellan',
      ensure => installed,
    }->
    class { '::swift::proxy':
      proxy_local_net_ip         => $machine_ip,
      port                       => '8080',
      account_autocreate         => true,
      pipeline                   => $pipeline,
      node_timeout               => 30,
  #    read_affinity              => "r${swiftregion_id}=100",
  #    write_affinity             => "r${swiftregion_id}",
      max_containers_per_account => $max_containers_per_account,
      max_containers_whitelist   => $max_containers_whitelist,
      workers                    => 20,
      client_timeout             => 340,
    }
  }
  swift_config {
    'swift-constraints/container_listing_limit': value => '100000';
  }
  swift_proxy_config {
    'app:proxy-server/conn_timeout': value => '2';
    'DEFAULT/max_clients':           value => '2048';
  }
  include ::swift::proxy::catch_errors
  include ::swift::proxy::healthcheck
  include ::swift::proxy::kms_keymaster
  class { '::swift::proxy::encryption':
    disable_encryption => $disable_encryption,
  }
  class { '::swift::proxy::cache':
    memcache_servers => $memcached_servers,
  }
  include ::swift::proxy::encryption
  include ::swift::proxy::proxy_logging
  include ::swift::proxy::container_sync
  include ::swift::proxy::bulk
  include ::swift::proxy::ratelimit
  include ::swift::proxy::keystone
  include ::swift::proxy::copy
  include ::swift::proxy::container_quotas
  include ::swift::proxy::account_quotas
  class { '::swift::proxy::slo': 
    max_manifest_segments => '10000',
  }
  include ::swift::proxy::dlo

  class { '::swift::proxy::versioned_writes':
    allow_versioned_writes => true,
  }

  if ($openstack_release == 'rocky' or $openstack_release == 'stein' or $openstack_release == 'train' or $openstack_release == 'ussuri') {
    class { '::swift::proxy::authtoken':
      auth_uri                => "${keystone_auth_uri}/v3",
      auth_url                => "${keystone_auth_uri}/v3",
      password                => $pass_swift_authtoken,
      delay_auth_decision     => 'True',
      cache                   => 'swift.cache',
      include_service_catalog => 'False',
    }
  }else{
    class { '::swift::proxy::authtoken':
      www_authenticate_uri    => "${keystone_auth_uri}/v3",
      auth_url                => "${keystone_auth_uri}/v3",
      password                => $pass_swift_authtoken,
      delay_auth_decision     => 'True',
      cache                   => 'swift.cache',
      include_service_catalog => 'False',
    }
  }

  if $statsd_hostname == ''{
    swift_proxy_config {
      'filter:authtoken/cafile': value => $api_endpoint_ca_file;
    }
  } else {
    swift_proxy_config {
      'filter:authtoken/cafile':          value => $api_endpoint_ca_file;
      'DEFAULT/log_statsd_host':          value => $statsd_hostname;
      'DEFAULT/log_statsd_metric_prefix': value => $machine_hostname;
    }
  }

  if $statsd_hostname == '' and $monitoring_graphite_host == ''{
    notice("Please defile statsd_hostname and monitoring_graphite_host to enable Collectd")
  }else{
    # Collected
    class { '::collectd':
      purge           => true,
      recurse         => true,
      purge_config    => true,
      minimum_version => '5.4',
    }
    class { 'collectd::plugin::statsd':
      host            => '127.0.0.1',
      port            => 8125,
      timersum        => 'true',
      timercount      => 'true',
    }
    collectd::plugin::write_graphite::carbon { "${monitoring_graphite_host}-carbon":
      graphitehost   => $monitoring_graphite_host,
      graphiteport   => 2003,
      protocol       => 'tcp'
    }
  }

  class { '::swift::keymaster':
    api_class     => 'barbican',
    key_id        => $swift_encryption_key_id,
    password      => $pass_swift_authtoken,
    auth_endpoint => "${keystone_auth_uri}/v3",
    project_name  => 'services',
  }

  # We setup *one* object-expirer per cluster
  if $object_expirer_enable {
    class { 'swift::objectexpirer':
      pipeline         => ['catch_errors', 'cache', 'proxy-server'],
      memcache_servers => $memcached_servers,
    }
  }else{
    package { 'swift-object-expirer':
      ensure => absent,
    }
  }
  if $swift_store_account or $swift_store_container or $swift_store_object{
    if $statsd_hostname == ''{
      $statsd_enabled = false
    } else {
      $statsd_enabled = true
    }

    class { '::swift::storage':
      storage_local_net_ip => $machine_ip,
    }

    if $swift_store_container {
      include swift::storage::container

      # This must be manually called, as otherwise, $memcache_servers is wrong
      # Currently desactivated, because it overwrites all of the file, but to
      # be done at some point...
#      class { '::swift::containerreconciler':
#        memcache_servers => $memcached_servers,
#      }
    }
    if $swift_store_account {
      include swift::storage::account
    }
    if $swift_store_object {
      include swift::storage::object
    }

    if $swift_store_account {
      swift::storage::server { '6002':
        type                 => 'account',
        devices              => '/srv/node',
        config_file_path     => 'account-server.conf',
        storage_local_net_ip => "${machine_ip}",
        pipeline             => ['healthcheck', 'recon', 'account-server'],
        max_connections          => $swift_rsync_connection_limit,
        statsd_enabled           => $statsd_enabled,
        log_statsd_host          => $statsd_hostname,
        log_statsd_metric_prefix => $machine_hostname,
      }
    }else{
      package { 'swift-account':
        ensure => absent,
      }
    }

    if $swift_store_container {
      swift::storage::server { '6001':
        type                 => 'container',
        devices              => '/srv/node',
        config_file_path     => 'container-server.conf',
        storage_local_net_ip => "${machine_ip}",
        pipeline             => ['healthcheck', 'recon', 'container-server'],
        max_connections      => $swift_rsync_connection_limit,
        statsd_enabled           => $statsd_enabled,
        log_statsd_host          => $statsd_hostname,
        log_statsd_metric_prefix => $machine_hostname,
      }
    }else{
      package { 'swift-container':
        ensure => absent,
      }
    }

    if $swift_store_object {
      swift::storage::server { '6200':
        type                   => 'object',
        devices                => '/srv/node',
        config_file_path       => 'object-server.conf',
        storage_local_net_ip   => "${machine_ip}",
        pipeline               => ['healthcheck', 'recon', 'object-server'],
        max_connections        => $swift_rsync_connection_limit,
        servers_per_port       => $servers_per_port,
        replicator_concurrency => $swift_object_replicator_concurrency,
        statsd_enabled           => $statsd_enabled,
        log_statsd_host          => $statsd_hostname,
        log_statsd_metric_prefix => $machine_hostname,
        network_chunk_size       => $network_chunk_size,
        disk_chunk_size          => $disk_chunk_size,
      }
    }else{
      package { 'swift-object':
        ensure => absent,
      }
    }

    $block_devices.each |Integer $index, String $value| {
      swift::storage::disk { "${value}":
        mount_type => 'uuid',
        require    => Class['swift'],
      }->
      exec { "fix-unix-right-of-${value}":
        path    => "/bin",
        command => "chown swift:swift /srv/node/${value}",
        unless  => "cat /proc/mounts | grep -E ^/dev/${value}",
      }
    }

    package { 'swift-drive-audit':
      ensure => present,
    }

    if $swift_store_account {
      $rings1 = [ 'account' ]
    }else{
      $rings1 = [ ]
    }
    if $swift_store_object {
      $rings2 = concat($rings1, ['object'])
    }else{
      $rings2 = $rings1
    }
    if $swift_store_container {
      $rings3 = concat($rings2, ['container'])
    }else{
      $rings3 = $rings2
    }
    swift::storage::filter::recon { $rings3: }
    swift::storage::filter::healthcheck { $rings3: }
  }else{
    package { 'swift-drive-audit':
      ensure => absent,
    }
    package { 'swift-account':
      ensure => absent,
    }
    package { 'swift-container':
      ensure => absent,
    }
    package { 'swift-object':
      ensure => absent,
    }
  }
}
