class oci::generic(
  $machine_hostname         = undef,
  $machine_role             = undef,
  $openstack_release        = undef,
  $admin_email_address      = 'admin@example.com',
  $mail_relay_host          = 'mail.example.com',
  $mail_relay_enable        = false,

  $use_http_proxy           = true,
  $http_proxy_addr          = 'example.com:3128',

#  $machine_ip               = undef,
  $ssh_listen_ips           = ['127.0.0.0', $facts['networking']['fqdn']],
  $etc_hosts                = undef,
  $oci_repository_key       = undef,
  $time_server_host         = undef,
  $pass_root_ssh_pub        = undef,
  $pass_root_ssh_priv       = undef,
  $authorized_keys_hash     = undef,
#  $time_server_use_pool     = false,
  $generate_root_ssh_keys   = false,

  # List of network interface, to be setup for lldpd
  $nics                     = ['eth0', 'eth1'],

  $oci_facts                = undef,

  #####################
  ### PKI materials ###
  #####################
  # This host cert
  $ssl_cert_snakeoil_key    = undef,
  $ssl_cert_snakeoil_pem    = undef,

  # This is the host client key and cert.
  # Currently, this is only used for libvirt auth
  # for live migrations over TLS, and for
  # vnc-proxy (in controlleur) to VNC in compute auth.
  $ssl_cert_client_key      = undef,
  $ssl_cert_client_crt      = undef,
  $ssl_cert_client_pem      = undef,

  # OCI cluster internal CA certs:
  $oci_pki_oci_ca           = undef,
  $oci_pki_root_ca          = undef,
  # OpenStack API keys
  $oci_pki_api_crt          = undef,
  $oci_pki_api_csr          = undef,
  $oci_pki_api_key          = undef,
  $oci_pki_api_pem          = undef,
  $oci_pki_oci_ca_chain     = undef,
  # Swift proxy keys
  $oci_pki_swiftproxy_key   = undef,
  $oci_pki_swiftproxy_pem   = undef,

  # If we're doing hyperconverged, we're calling oci::cephosd
  # instead of oci::compute. In this case, /etc/pki cannot be
  # setup because the libvirt-qemu user doesn't exist. Which
  # is why we have the below variable.
  $skip_etc_pki_setup       = false,
){
  # Generic sysctl options
  ::oci::sysctl { 'oci-rox': }

  # Write the oci_facts.txt file, as maintained in src/inc/slave_actions.php,
  # so it's both maintained at provisionning time and in puppet.
  file { '/etc/facter/facts.d/oci_facts.yaml':
    ensure                  => present,
    owner                   => 'root',
    group                   => 'root',
    mode                    => '0644',
    selinux_ignore_defaults => true,
    content                 => base64('decode', $oci_facts),
  }

  if $use_http_proxy {
    file { '/etc/oci/http_proxy_addr':
      ensure                  => present,
      owner                   => 'root',
      group                   => 'root',
      mode                    => '0644',
      selinux_ignore_defaults => true,
      content                 => $http_proxy_addr,
    }
  }else{
    file { '/etc/oci/http_proxy_addr':
      ensure => absent,
    }
  }

  ###################################
  # Write the key materials on disk #
  ###################################

  file { '/usr/share/ca-certificates/oci':
    ensure                  => directory,
    owner                   => 'root',
    mode                    => '0755',
    selinux_ignore_defaults => true,
  }->
  # Root ca used to sign the OCI ca
  file { '/usr/share/ca-certificates/oci/OCI_1_selfsigned-root-ca.crt':
    ensure                  => present,
    owner                   => 'root',
    group                   => 'root',
    mode                    => '0644',
    selinux_ignore_defaults => true,
    content                 => base64('decode', $oci_pki_root_ca),
  }->
  # OCI ca used to sign everything
  file { '/usr/share/ca-certificates/oci/OCI_2_oci-ca.crt':
    ensure                  => present,
    owner                   => 'root',
    group                   => 'root',
    mode                    => '0644',
    selinux_ignore_defaults => true,
    content                 => base64('decode', $oci_pki_oci_ca),
  }->
  file_line { 'ca-certificates-with-oci-1':
    path   => '/etc/ca-certificates.conf',
    match  => 'oci/OCI_1_selfsigned-root-ca.crt',
    line   => 'oci/OCI_1_selfsigned-root-ca.crt',
  }->
  file_line { 'ca-certificates-with-oci-2':
    path   => '/etc/ca-certificates.conf',
    match  => 'oci/OCI_2_oci-ca.crt',
    line   => 'oci/OCI_2_oci-ca.crt',
  }->
  exec { 'update-ca-certificates':
    command     => 'update-ca-certificates',
    logoutput   => 'on_failure',
    refreshonly => true,
    subscribe   => File['/usr/share/ca-certificates/oci/OCI_1_selfsigned-root-ca.crt', '/usr/share/ca-certificates/oci/OCI_2_oci-ca.crt'],
    path        => ['/usr/sbin', '/usr/bin', '/bin', '/sbin/', ],
  }

  file { '/etc/ssl/private/ssl-cert-snakeoil.key':
    ensure                  => present,
    owner                   => 'root',
    group                   => 'ssl-cert',
    mode                    => '0640',
    selinux_ignore_defaults => true,
    content                 => base64('decode', $ssl_cert_snakeoil_key),
  }

  file { '/etc/ssl/certs/ssl-cert-snakeoil.pem':
    ensure                  => present,
    owner                   => 'root',
    group                   => 'root',
    mode                    => '0644',
    selinux_ignore_defaults => true,
    content                 => base64('decode', $ssl_cert_snakeoil_pem),
  }

  file { '/etc/ssl/certs/oci-pki-oci-ca.pem':
    ensure                  => present,
    owner                   => 'root',
    group                   => 'root',
    mode                    => '0644',
    selinux_ignore_defaults => true,
    content                 => base64('decode', $oci_pki_oci_ca),
  }

  file { '/etc/ssl/certs/oci-pki-root-ca.pem':
    ensure                  => present,
    owner                   => 'root',
    group                   => 'root',
    mode                    => '0644',
    selinux_ignore_defaults => true,
    content                 => base64('decode', $oci_pki_root_ca),
  }

  file { '/etc/ssl/certs/oci-pki-api.crt':
    ensure                  => present,
    owner                   => 'root',
    group                   => 'root',
    mode                    => '0644',
    selinux_ignore_defaults => true,
    content                 => base64('decode', $oci_pki_api_crt),
  }

  file {'/etc/ssl/certs/oci-pki-api.csr':
    ensure                  => present,
    owner                   => 'root',
    group                   => 'root',
    mode                    => '0644',
    selinux_ignore_defaults => true,
    content                 => base64('decode', $oci_pki_api_csr),
  }

  file {'/etc/ssl/private/oci-pki-api.pem':
    ensure                  => present,
    owner                   => 'root',
    group                   => 'ssl-cert',
    mode                    => '0640',
    selinux_ignore_defaults => true,
    content                 => base64('decode', $oci_pki_api_pem),
  }
  # That's wrong on old nodes.
  file {'/etc/ssl/certs/oci-pki-api.pem':
    ensure => absent,
  }

  file {'/etc/ssl/certs/oci-pki-oci-ca-chain.pem':
    ensure                  => present,
    owner                   => 'root',
    group                   => 'root',
    mode                    => '0644',
    selinux_ignore_defaults => true,
    content                 => base64('decode', $oci_pki_oci_ca_chain),
  }

  if $machine_role == 'controller' {
    file {'/etc/ssl/private/oci-pki-api.key':
      ensure                  => present,
      owner                   => 'root',
      group                   => 'ssl-cert',
      mode                    => '0640',
      selinux_ignore_defaults => true,
      content                 => base64('decode', $oci_pki_api_key),
    }
  }else{
    file {'/etc/ssl/private/oci-pki-api.key':
      ensure                  => absent,
    }
  }
  # Fix eventual wrong old node...
  file {'/etc/ssl/certs/oci-pki-api.key':
    ensure                  => absent,
  }


  if ($machine_role == 'swiftproxy') or ($machine_role == 'radosgw') {
    file {'/etc/ssl/private/oci-pki-swiftproxy.key':
      ensure                  => present,
      owner                   => 'root',
      group                   => 'ssl-cert',
      mode                    => '0640',
      selinux_ignore_defaults => true,
      content                 => base64('decode', $oci_pki_swiftproxy_key),
    }
    file {'/etc/ssl/private/oci-pki-swiftproxy.pem':
      ensure                  => present,
      owner                   => 'root',
      group                   => 'root',
      mode                    => '0644',
      selinux_ignore_defaults => true,
      content                 => base64('decode', $oci_pki_swiftproxy_pem),
    }
  }else{
    file {'/etc/ssl/private/oci-pki-swiftproxy.key':
      ensure                  => absent,
    }
    file {'/etc/ssl/private/oci-pki-swiftproxy.pem':
      ensure                  => absent,
    }
  }
  # Fix an eventual wrong old node...
  file {'/etc/ssl/certs/oci-pki-swiftproxy.key':
    ensure                  => absent,
  }
  file {'/etc/ssl/certs/oci-pki-swiftproxy.pem':
    ensure                  => absent,
  }

  # Setup client and server certificates for compute node
  # live migrations over TLS.
  if ($machine_role == 'compute' and $skip_etc_pki_setup == false){
    file { '/etc/pki':
      ensure                  => directory,
      owner                   => 'root',
      mode                    => '0755',
      selinux_ignore_defaults => true,
    }->
    # Install the root CA
    file { '/etc/pki/CA':
      ensure                  => directory,
      owner                   => 'root',
      mode                    => '0755',
      selinux_ignore_defaults => true,
    }->
    file {'/etc/pki/CA/cacert.pem':
      ensure                  => present,
      owner                   => 'root',
      group                   => 'root',
      mode                    => '0644',
      selinux_ignore_defaults => true,
      content                 => base64('decode', $oci_pki_oci_ca_chain),
    }->
    file { '/etc/pki/qemu':
      ensure                  => directory,
      owner                   => 'root',
      mode                    => '0755',
      selinux_ignore_defaults => true,
    }->
    file {'/etc/pki/qemu/ca-cert.pem':
      ensure                  => present,
      owner                   => 'root',
      group                   => 'root',
      mode                    => '0644',
      selinux_ignore_defaults => true,
      content                 => base64('decode', $oci_pki_oci_ca_chain),
    }->
    file {'/etc/pki/qemu/server-cert.pem':
      ensure                  => present,
      owner                   => 'root',
      group                   => 'root',
      mode                    => '0644',
      selinux_ignore_defaults => true,
      content                 => base64('decode', $ssl_cert_snakeoil_pem),
    }->
    file {'/etc/pki/qemu/client-cert.pem':
      ensure                  => present,
      owner                   => 'root',
      group                   => 'root',
      mode                    => '0644',
      selinux_ignore_defaults => true,
      content                 => base64('decode', $ssl_cert_snakeoil_pem),
    }->
    file {'/etc/pki/qemu/server-key.pem':
      ensure                  => present,
      owner                   => 'root',
      group                   => 'root',
      mode                    => '0644',
      selinux_ignore_defaults => true,
      content                 => base64('decode', $ssl_cert_snakeoil_key),
    }->
    file {'/etc/pki/qemu/client-key.pem':
      ensure                  => present,
      owner                   => 'root',
      group                   => 'root',
      mode                    => '0644',
      selinux_ignore_defaults => true,
      content                 => base64('decode', $ssl_cert_snakeoil_key),
    }->
    # libvirt-vnc PKI
    file { '/etc/pki/libvirt-vnc':
      ensure                  => directory,
      owner                   => 'root',
      mode                    => '0755',
      selinux_ignore_defaults => true,
    }->
    file {'/etc/pki/libvirt-vnc/ca-cert.pem':
      ensure                  => present,
      owner                   => 'root',
      group                   => 'root',
      mode                    => '0644',
      selinux_ignore_defaults => true,
      content                 => base64('decode', $oci_pki_oci_ca_chain),
    }->
    file {'/etc/pki/libvirt-vnc/server-cert.pem':
      ensure                  => present,
      owner                   => 'root',
      group                   => 'root',
      mode                    => '0644',
      selinux_ignore_defaults => true,
      content                 => base64('decode', $ssl_cert_snakeoil_pem),
    }->
    file {'/etc/pki/libvirt-vnc/server-key.pem':
      ensure                  => present,
      owner                   => 'root',
      group                   => 'root',
      mode                    => '0644',
      selinux_ignore_defaults => true,
      content                 => base64('decode', $ssl_cert_snakeoil_key),
    }->
    # Install the server key materials
    file { '/etc/pki/libvirt':
      ensure                  => directory,
      owner                   => 'root',
      group                   => 'libvirt-qemu',
      mode                    => '0755',
      selinux_ignore_defaults => true,
    }->
    file {'/etc/pki/libvirt/servercert.pem':
      ensure                  => present,
      owner                   => 'root',
      group                   => 'libvirt-qemu',
      mode                    => '0400',
      selinux_ignore_defaults => true,
      content                 => base64('decode', $ssl_cert_snakeoil_pem),
    }->
    file { '/etc/pki/libvirt/private':
      ensure                  => directory,
      owner                   => 'root',
      group                   => 'libvirt-qemu',
      mode                    => '0750',
      selinux_ignore_defaults => true,
    }->
    file {'/etc/pki/libvirt/private/serverkey.pem':
      ensure                  => present,
      owner                   => 'root',
      group                   => 'libvirt-qemu',
      mode                    => '0400',
      selinux_ignore_defaults => true,
      content                 => base64('decode', $ssl_cert_snakeoil_key),
    }->
    # Install the client materials
    file {'/etc/pki/libvirt/clientcert.pem':
      ensure                  => present,
      owner                   => 'root',
      group                   => 'libvirt-qemu',
      mode                    => '0400',
      selinux_ignore_defaults => true,
      content                 => base64('decode', $ssl_cert_client_crt),
    }->
    file {'/etc/pki/libvirt/private/clientkey.pem':
      ensure                  => present,
      owner                   => 'root',
      group                   => 'libvirt-qemu',
      mode                    => '0400',
      selinux_ignore_defaults => true,
      content                 => base64('decode', $ssl_cert_client_key),
    }
  }

  if $machine_role == 'controller' {
    file { '/etc/pki':
      ensure                  => directory,
      owner                   => 'root',
      mode                    => '0755',
      selinux_ignore_defaults => true,
    }->
    # Install the root CA
    file { '/etc/pki/CA':
      ensure                  => directory,
      owner                   => 'root',
      mode                    => '0755',
      selinux_ignore_defaults => true,
    }->
    file {'/etc/pki/CA/cacert.pem':
      ensure                  => present,
      owner                   => 'root',
      group                   => 'root',
      mode                    => '0644',
      selinux_ignore_defaults => true,
      content                 => base64('decode', $oci_pki_oci_ca_chain),
    }->
    # nova-novncproxy PKI
    file { '/etc/pki/nova-novncproxy':
      ensure                  => directory,
      owner                   => 'root',
      mode                    => '0755',
      selinux_ignore_defaults => true,
    }->
    file {'/etc/pki/nova-novncproxy/ca-cert.pem':
      ensure                  => present,
      owner                   => 'root',
      group                   => 'root',
      mode                    => '0644',
      selinux_ignore_defaults => true,
      content                 => base64('decode', $oci_pki_oci_ca_chain),
    }->
    file {'/etc/pki/nova-novncproxy/client-cert.pem':
      ensure                  => present,
      owner                   => 'root',
      group                   => 'root',
      mode                    => '0644',
      selinux_ignore_defaults => true,
      content                 => base64('decode', $ssl_cert_snakeoil_pem),
    }->
    file {'/etc/pki/nova-novncproxy/client-key.pem':
      ensure                  => present,
      owner                   => 'root',
      group                   => 'root',
      mode                    => '0644',
      selinux_ignore_defaults => true,
      content                 => base64('decode', $ssl_cert_snakeoil_key),
    }
  }

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

  file { '/etc/rsyslog.d/00-timestamp.conf':
    ensure => present,
    source => 'puppet:///modules/oci/rsyslog/00-timestamp.conf',
    path => '/etc/rsyslog.d/00-timestamp.conf',
    group => 'root',
    owner => 'root',
    mode => '0644',
    require => [Package['rsyslog']],
    notify  => Service['rsyslog'],
  }

  #########
  # lldpd #
  #########
  $ifacelist = join($nics, ',')
  package { 'lldpd':
    ensure => present,
  }->
  file {'/etc/lldpd.d/only-eth.conf':
    ensure                  => present,
    owner                   => 'root',
    group                   => 'root',
    mode                    => '0644',
    selinux_ignore_defaults => true,
    # This makes sure we're filtering and using only physical interfaces.
    content                 => "configure system interface pattern ${ifacelist}
",
    notify                  => Service['lldpd'],
  }->
  service { 'lldpd':
    ensure    => running,
    enable    => true,
    hasstatus => true,
  }

  #######################
  # Populate /etc/hosts #
  #######################
  class { '::oci::etchosts':
    etc_hosts_file => $etc_hosts,
  }

  ####################################################
  # Maintain sources.list.d/osbpo.debian.net.sources #
  ####################################################
  $debian_release = $facts['os']['distro']['codename']
  file { "/etc/oci/oci-repository-key.asc":
    ensure                  => file,
    owner                   => "root",
    content                 => base64('decode', $oci_repository_key),
    selinux_ignore_defaults => true,
    mode                    => '0644',
  }
  -> file { "/etc/apt/sources.list.d/osbpo.debian.net.sources":
    ensure                  => file,
    owner                   => "root",
    content                 => "Types: deb deb-src
URIs: http://osbpo.debian.net/debian
Suites: ${debian_release}-${openstack_release}-backports ${debian_release}-${openstack_release}-backports-nochange
Components: main
Signed-By: /etc/oci/oci-repository-key.asc
",
    selinux_ignore_defaults => true,
    mode                    => '0644',
    notify                  => Exec['apt-update-after-osbpo-debian-net'],
  }
  -> file { "/etc/apt/sources.list.d/bullseye-victoria.list":
    ensure => absent,
    notify => Exec['apt-update-after-osbpo-debian-net'],
  }
  -> file { "/etc/apt/sources.list.d/bullseye-wallaby.list":
    ensure => absent,
    notify => Exec['apt-update-after-osbpo-debian-net'],
  }
  -> file { "/etc/apt/sources.list.d/bullseye-xena.list":
    ensure => absent,
    notify => Exec['apt-update-after-osbpo-debian-net'],
  }
  -> file { "/etc/apt/sources.list.d/bullseye-yoga.list":
    ensure => absent,
    notify => Exec['apt-update-after-osbpo-debian-net'],
  }
  -> file { "/etc/apt/sources.list.d/bullseye-zed.list":
    ensure => absent,
    notify => Exec['apt-update-after-osbpo-debian-net'],
  }
  -> file { "/etc/apt/sources.list.d/bookworm-zed.list":
    ensure => absent,
    notify => Exec['apt-update-after-osbpo-debian-net'],
  }
  -> file { "/etc/apt/sources.list.d/bookworm-antelope.list":
    ensure => absent,
    notify => Exec['apt-update-after-osbpo-debian-net'],
  }
  -> file { "/etc/apt/sources.list.d/bookworm-bobcat.list":
    ensure => absent,
    notify => Exec['apt-update-after-osbpo-debian-net'],
  }
  -> file { "/etc/apt/sources.list.d/bookworm-caracal.list":
    ensure => absent,
    notify => Exec['apt-update-after-osbpo-debian-net'],
  }
  -> file { "/etc/apt/sources.list.d/bookworm-dalmatian.list":
    ensure => absent,
    notify => Exec['apt-update-after-osbpo-debian-net'],
  }
  -> file { "/etc/apt/sources.list.d/bookworm-epoxy.list":
    ensure => absent,
    notify => Exec['apt-update-after-osbpo-debian-net'],
  }
  -> file { "/etc/apt/sources.list.d/trixie-epoxy.list":
    ensure => absent,
    notify => Exec['apt-update-after-osbpo-debian-net'],
  }
  -> file { "/etc/apt/sources.list.d/trixie-flamingo.list":
    ensure => absent,
    notify => Exec['apt-update-after-osbpo-debian-net'],
  }
  -> exec { "apt-update-after-osbpo-debian-net":
    command     => "/bin/true # comment to satisfy puppet syntax requirements
apt-get update
",
    logoutput   => 'on_failure',
    tries       => 3,
    try_sleep   => 1,
    refreshonly => true,
  }

  ######################
  # ssh root key stuff #
  ######################
  if $generate_root_ssh_keys{
    ::oci::oci_ssh_keypair { 'root-keypair':
      path    => '/root',
      type    => 'ssh-rsa',
      user    => 'root',
      group   => 'root',
      pubkey  => $pass_root_ssh_pub,
      privkey => base64('decode', $pass_root_ssh_priv),
    }
  }

  if $generate_root_ssh_keys{
    if $authorized_keys_hash {
      $authorized_keys_hash.map |String $key_hostname, String $key_pubkey| {
        ssh_authorized_key { "oci-auto-authorized-keys-hash-${key_hostname}":
          ensure  => present,
          key     => $key_pubkey,
          type    => 'ssh-rsa',
          user    => 'root',
        }
      }
    }
  }

  ################
  # Setup chrony #
  ################
  class {
    '::oci::chrony': time_server_host => $time_server_host,
  }

  # Make sure we have anacron, because we may install some
  # cron on nodes.
  package { 'anacron':
    ensure => present,
  }

  #############################
  # Switch to iptables-legacy #
  #############################
  # Fix-up iptables-legacy as selected alternative
  if $facts['os']['lsb'] != undef{
    $mycodename = $facts['os']['lsb']['distcodename']
  }else{
    $mycodename = $facts['os']['distro']['codename']
  }

  if $mycodename == 'buster' or $mycodename == 'bullseye' or $mycodename == 'bookworm' or $mycodename == 'trixie' {
    alternatives { 'iptables':
      path => '/usr/sbin/iptables-legacy',
    }
    alternatives { 'ip6tables':
      path => '/usr/sbin/ip6tables-legacy',
    }
  }

  ######################
  # SSHd configuration #
  ######################
  # Fix-up the /etc/ssh/sshd_config to be safer, ie
  # listen only on localhost & management network,
  # avoiding the risk that ssh binds on public IP,
  # also make sure we're using the signed ssh host keys.
  #
  # Appart from ListenAddress and HostCertificate,
  # this is the default configuration from Debian.
  class { 'ssh::server':
    options => {
      'Port'                            => [22],
      'ListenAddress'                   => $ssh_listen_ips,
      'ChallengeResponseAuthentication' => 'no',
      'UsePAM'                          => 'yes',
      'X11Forwarding'                   => 'yes',
      'PrintMotd'                       => 'no',
      'AcceptEnv'                       => 'LANG LC_*',
      'Subsystem'                       => 'sftp /usr/lib/openssh/sftp-server',
      'HostCertificate'                 => $facts['oci_ssh_host_key_certs_list'],
      #['/etc/ssh/ssh_host_ecdsa_key-cert.pub', '/etc/ssh/ssh_host_ed25519_key-cert.pub', '/etc/ssh/ssh_host_rsa_key-cert.pub']
    },
    require                             => Sysctl::Value['net.ipv4.ip_nonlocal_bind'],
  }

  ###########
  # Postfix #
  ###########
  # Install an MTA, so that we can recieve error mails.
  if $mail_relay_enable {
    class { 'postfix':
      inet_interfaces     => 'localhost',
      inet_protocols      => 'ipv4',
      relayhost           => $mail_relay_host,
      root_mail_recipient => $admin_email_address,
      mta                 => true,
    }
  } else {
    class { 'postfix':
      inet_interfaces     => 'localhost',
      inet_protocols      => 'ipv4',
      relayhost           => $mail_relay_host,
      root_mail_recipient => $admin_email_address,
    }
  }

  # Ensure ssh starts after FRR, which is mandatory
  # if using a bgp-2-the-host setup (otherwise ssh
  # starts before the IP is bound to the loopback)
# Note: since we use net.ipv4.ip_nonlocal_bind,
# this is counter-productive, and just make sshd
# starts too late.
#  file { "/etc/systemd/system/ssh.service.d":
#    ensure                  => directory,
#    owner                   => 'root',
#    mode                    => '0755',
#    selinux_ignore_defaults => true,
#  }->
#  file { "/etc/systemd/system/ssh.service.d/after-network-is-online.conf":
#    ensure                  => present,
#    owner                   => root,
#    content                 => '[Unit]
#After=network-online.target auditd.service',
#    selinux_ignore_defaults => true,
#    mode                    => '0644',
#  }
}
