class oci::network(
  $region_name              = 'RegionOne',
  $openstack_release        = undef,
  $cluster_name             = undef,
  $machine_hostname         = undef,
  $machine_ip               = undef,
  $bridge_mapping_list      = undef,
  $external_network_list    = undef,
  $first_master             = undef,
  $first_master_ip          = undef,
  $cluster_domain           = undef,
  $vmnet_ip                 = undef,

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

  $neutron_global_physnet_mtu = 1500,
  $neutron_path_mtu         = 1500,

  $all_masters              = undef,
  $all_masters_ip           = undef,
  $vip_hostname             = undef,
  $vip_ipaddr               = undef,
  $vip_netmask              = undef,
  $self_signed_api_cert     = true,
  $sql_vip_ip               = undef,
  $network_ipaddr           = undef,
  $network_cidr             = undef,
  $use_ssl                  = true,
  $pass_nova_authtoken      = undef,
  $pass_neutron_authtoken   = undef,
  $pass_neutron_messaging   = undef,
  $pass_neutron_vrrpauth    = undef,
  $pass_metadata_proxy_shared_secret = undef,
  $pass_keystone_adminuser  = undef,

  $disable_notifications    = false,

  $bgp_to_the_host          = false,
  $dhcp_domain              = '',
  $has_subrole_designate    = false,
  $neutron_dns_domain       = 'example.com.',
  $admin_email_address      = 'admin@example.com',

  $use_dynamic_routing      = false,

  $neutron_install_dragent  = false,

  $neutron_use_dvr          = true,
  # This one is unused, because we use the agregate of
  # netowrk + bridge instead. However, it's transmitted
  # by the ENC because it is a machine variable, so this
  # variable must be present.
  $neutron_external_network_name = undef,

  $kernel_from_backports    = false,
  $install_designate        = false,
  $brex_macaddr             = undef,

  $neutron_vxlan_vni_min     = '1000',
  $neutron_vxlan_vni_max     = '9999',
){

  # Tweak performances for network host
  # class { '::oci::tuned': profile => 'network-throughput' }

  if $messaging_nodes_for_notifs {
    $notif_rabbit_servers = $all_rabbits
  }else{
    $notif_rabbit_servers = $all_masters
  }

  if $use_ssl {
    $proto = 'https'
    $messaging_default_port = '5671'
    $messaging_notify_port = '5671'
    $api_port = 443
  } else {
    $proto = 'http'
    $messaging_default_port = '5672'
    $messaging_notify_port = '5672'
    $api_port = 80
  }
  $messaging_default_proto = 'rabbit'
  $messaging_notify_proto = 'rabbit'

  $base_url = "${proto}://${vip_hostname}"
  $keystone_auth_uri  = "${base_url}:${api_port}/identity"
  $keystone_admin_uri = "${base_url}:${api_port}/identity"

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

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

  if $disable_notifications {
    $notification_driver = 'noop'
  }else{
    $notification_driver = 'messagingv2'
  }

  ###############
  ### Neutron ###
  ###############
  if $disable_notifications {
    $neutron_notif_transport_url = ''
  } else {
    $neutron_notif_transport_url = os_transport_url({
                                     'transport' => $messaging_notify_proto,
                                     'hosts'     => fqdn_rotate($notif_rabbit_servers),
                                     'port'      => $messaging_notify_port,
                                     'username'  => 'neutron',
                                     'password'  => $pass_neutron_messaging,
                                   })
  }

  if $neutron_use_dvr {
    $router_distributed         = true
    $enable_distributed_routing = true
    $l3_agent_mode              = 'dvr_snat'
  }else{
    $router_distributed         = false
    $enable_distributed_routing = false
    $l3_agent_mode              = 'legacy'
  }


  if $openstack_release == 'rocky'{
    class { '::neutron':
      debug                      => true,
      default_transport_url      => os_transport_url({
        'transport' => $messaging_default_proto,
        'hosts'     => fqdn_rotate($all_masters),
        'port'      => $messaging_default_port,
        'username'  => 'neutron',
        'password'  => $pass_neutron_messaging,
      }),
      notification_transport_url => $neutron_notif_transport_url,
      dns_domain                 => $neutron_dns_domain,
      rabbit_use_ssl             => $use_ssl,
      rabbit_ha_queues           => true,
      kombu_ssl_ca_certs         => $oci_pki_root_ca_file,
      amqp_sasl_mechanisms       => 'PLAIN',
      allow_overlapping_ips      => true,
      core_plugin                => 'ml2',
      service_plugins            => ['router', 'metering', 'qos', 'trunk'],
      bind_host                  => $machine_ip,
      notification_driver        => $notification_driver,
      global_physnet_mtu         => $neutron_global_physnet_mtu,
      rpc_response_timeout       => '240',
    }
  }else{
    if $openstack_release == 'stein' or $openstack_release == 'train' or $openstack_release == 'ussuri' or $openstack_release == 'victoria' or $openstack_release == 'wallaby' or $openstack_release == 'xena' or $openstack_release == 'yoga'{
      class { '::neutron':
        default_transport_url      => os_transport_url({
          'transport' => $messaging_default_proto,
          'hosts'     => fqdn_rotate($all_masters),
          'port'      => $messaging_default_port,
          'username'  => 'neutron',
          'password'  => $pass_neutron_messaging,
        }),
        notification_transport_url => $neutron_notif_transport_url,
        dns_domain                 => $neutron_dns_domain,
        rabbit_use_ssl             => $use_ssl,
        rabbit_ha_queues           => true,
        kombu_ssl_ca_certs         => $oci_pki_root_ca_file,
        amqp_sasl_mechanisms       => 'PLAIN',
        allow_overlapping_ips      => true,
        core_plugin                => 'ml2',
        service_plugins            => ['router', 'metering', 'qos', 'trunk'],
        bind_host                  => $machine_ip,
        notification_driver        => $notification_driver,
        global_physnet_mtu         => $neutron_global_physnet_mtu,
        rpc_response_timeout       => '240',
      }
    # No allow_overlapping_ips in Zed and up
    }else{
      class { '::neutron':
        default_transport_url      => os_transport_url({
          'transport' => $messaging_default_proto,
          'hosts'     => fqdn_rotate($all_masters),
          'port'      => $messaging_default_port,
          'username'  => 'neutron',
          'password'  => $pass_neutron_messaging,
        }),
        notification_transport_url => $neutron_notif_transport_url,
        dns_domain                 => $neutron_dns_domain,
        rabbit_use_ssl             => $use_ssl,
        rabbit_ha_queues           => true,
        kombu_ssl_ca_certs         => $oci_pki_root_ca_file,
        amqp_sasl_mechanisms       => 'PLAIN',
        core_plugin                => 'ml2',
        service_plugins            => ['router', 'metering', 'qos', 'trunk'],
        bind_host                  => $machine_ip,
        notification_driver        => $notification_driver,
        global_physnet_mtu         => $neutron_global_physnet_mtu,
      }
    }
    class { '::neutron::logging':
      debug => true,
    }
  }
  neutron_config {
    'nova/cafile':         value => $api_endpoint_ca_file;
    'database/connection': value => '';
  }

  if $openstack_release == 'rocky' or $openstack_release == 'stein' or $openstack_release == 'train' or $openstack_release == 'ussuri'{
    class { '::neutron::server::notifications':
      auth_url => $keystone_admin_uri,
      password => $pass_nova_authtoken,
    }
  # Victoria and up
  }else{
    class { 'neutron::server::notifications::nova':
      auth_url    => $keystone_admin_uri,
      password    => $pass_nova_authtoken,
      region_name => $region_name,
    }
  }

  class { '::neutron::client': }
  class { '::neutron::keystone::authtoken':
    password             => $pass_neutron_authtoken,
    auth_url             => $keystone_admin_uri,
    www_authenticate_uri => $keystone_auth_uri,
    cafile               => $api_endpoint_ca_file,
  }
  class { '::neutron::agents::ml2::ovs':
    local_ip                   => $vmnet_ip,
    tunnel_types               => ['vxlan'],
    bridge_uplinks             => ['eth0'],
    bridge_mappings            => $bridge_mapping_list,
    extensions                 => '',
    l2_population              => true,
    arp_responder              => true,
    firewall_driver            => 'iptables_hybrid',
    drop_flows_on_start        => false,
    enable_distributed_routing => $enable_distributed_routing,
    manage_vswitch             => false,
  }
  class { '::neutron::agents::l3':
    interface_driver      => 'openvswitch',
    debug                 => true,
    agent_mode            => 'dvr_snat',
    ha_enabled            => false,
    extensions            => '',
    ha_vrrp_auth_type     => 'PASS',
    ha_vrrp_auth_password => $pass_neutron_vrrpauth,
    ha_vrrp_advert_int    => '3',
    manage_service        => false,
  }
  neutron_l3_agent_config {
    'DEFAULT/external_network_bridge': value => '';
  }

  if $has_subrole_designate {
    $extension_drivers = 'port_security,qos,dns,dns_domain_ports'
  }else{
    $extension_drivers = 'port_security,qos'
  }

  if($openstack_release == 'rocky' or $openstack_release == 'stein' or $openstack_release == 'train' or $openstack_release == 'ussuri'){
    class { '::neutron::plugins::ml2':
      type_drivers         => ['flat', 'vxlan', 'vlan', ],
      tenant_network_types => ['flat', 'vxlan', 'vlan', ],
      extension_drivers    => $extension_drivers,
      mechanism_drivers    => 'openvswitch,l2population',
      firewall_driver      => 'iptables_v2',
      flat_networks        => $external_network_list,
      network_vlan_ranges  => $external_network_list,
      vni_ranges           => "${neutron_vxlan_vni_min}:${neutron_vxlan_vni_max}",
      path_mtu             => $neutron_path_mtu,
    }
  }else{
    class { '::neutron::plugins::ml2':
      type_drivers         => ['flat', 'vxlan', 'vlan', ],
      tenant_network_types => ['flat', 'vxlan', 'vlan', ],
      extension_drivers    => $extension_drivers,
      mechanism_drivers    => 'openvswitch,l2population',
      flat_networks        => $external_network_list,
      network_vlan_ranges  => $external_network_list,
      vni_ranges           => "${neutron_vxlan_vni_min}:${neutron_vxlan_vni_max}",
      path_mtu             => $neutron_path_mtu,
    }
  }
  class { '::neutron::agents::dhcp':
    interface_driver         => 'openvswitch',
    debug                    => true,
    enable_isolated_metadata => true,
    enable_metadata_network  => true,
  }
#  class { '::neutron::agents::metering':
#    interface_driver => 'openvswitch',
#    debug            => true,
#  }
  package { 'l2gw networking':
    name => 'python3-networking-l2gw',
    ensure => installed,
  }

  class { '::neutron::agents::metadata':
    debug             => true,
    shared_secret     => $pass_metadata_proxy_shared_secret,
    metadata_workers  => 2,
    metadata_protocol => 'http',
    metadata_host     => 'localhost',
  }

  class { 'haproxy':
    global_options   => {
      'log'      => '/dev/log local0',
      'maxconn'  => '256',
      'user'     => 'haproxy',
      'group'    => 'haproxy',
      'daemon'   => '',
      'nbthread' => '8',
    },
    defaults_options => {
      'mode'    => 'http',
      'option'    => [
        'httplog',
      ],
    },
    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', ],
      },
  }

  $haproxy_options_for_metadata_proxy = [
    { 'use_backend' => 'metadatabe' },
  ]
  haproxy::frontend { 'openstackfe':
    mode      => 'http',
    bind      => { "127.0.0.1:8775" => [] },
    options   => $haproxy_options_for_metadata_proxy,
  }
  haproxy::backend { 'metadatabe':
    options => [
       { 'option' => 'forwardfor' },
       { 'option' => 'httpchk GET /healthcheck' },
       { 'mode' => 'http' },
       { 'balance' => 'roundrobin' },
    ],
  }
  if $openstack_release == 'rocky'{
    $metadatabm_options = 'check'
  }else{
    $metadatabm_options = 'check check-ssl ssl verify required ca-file /etc/ssl/certs/oci-pki-oci-ca-chain.pem'
  }
  haproxy::balancermember { 'metadatabm':
    listening_service => 'metadatabe',
    ipaddresses       => $all_masters_ip,
    server_names      => $all_masters,
    ports             => 8775,
    options           => $metadatabm_options
  }

  if $has_subrole_designate {
    class { '::neutron::designate':
      url                 => "${base_url}/dns/",
      username            => 'admin',
      project_domain_name => 'admin',
      password            => $pass_keystone_adminuser,
      auth_url            => "${base_url}/identity/v3/",
      ptr_zone_email      => $admin_email_address,
    }
  }

  if $neutron_install_dragent {
    class { '::neutron::agents::bgp_dragent':
      bgp_router_id => $machine_ip,
    }
  }else{
    package { 'neutron dynamic routing':
      name   => 'neutron-bgp-dragent',
      ensure => purged,
    }
    if $use_dynamic_routing {
      package { 'neutron-dynamic-routing-common':
        name   => 'neutron-dynamic-routing-common',
        ensure => present,
      }
    }else{
      package { 'python3-neutron-dynamic-routing':
        name   => 'python3-neutron-dynamic-routing',
        ensure => purged,
      }
    }
  }

  ############################
  ### OpenVSwitch defaults ###
  ############################
  file { '/etc/default/openvswitch-switch':
    content => '# Managed by Puppet
OVS_CTL_OPTS="--delete-bridges"',
  }
}
