heat_template_version: wallaby

description: >
  OpenStack Neutron openvswitch service

parameters:
  ContainerOpenvswitchImage:
    description: image
    type: string
  ContainerNeutronConfigImage:
    description: The container image to use for the neutron config_volume
    type: string
  DockerOpenvswitchUlimit:
    default: ['nofile=16384']
    description: ulimit for Openvswitch Container
    type: comma_delimited_list
  NeutronOpenVswitchAgentLoggingSource:
    type: json
    default:
      tag: openstack.neutron.agent.openvswitch
      file: /var/log/containers/neutron/openvswitch-agent.log
  ServiceData:
    default: {}
    description: Dictionary packing service data
    type: json
  ServiceNetMap:
    default: {}
    description: Mapping of service_name -> network name. Typically set
                 via parameter_defaults in the resource registry. Use
                 parameter_merge_strategies to merge it with the defaults.
    type: json
  RoleName:
    default: ''
    description: Role name on which the service is applied
    type: string
  RoleParameters:
    default: {}
    description: Parameters specific to the role
    type: json
  EndpointMap:
    default: {}
    description: Mapping of service endpoint -> protocol. Typically set
                 via parameter_defaults in the resource registry.
    type: json
  DeployIdentifier:
    default: ''
    type: string
    description: >
      Setting this to a unique value will re-run any deployment tasks which
      perform configuration on a Heat stack-update.
  DockerPuppetMountHostPuppet:
    type: boolean
    default: true
    description: Whether containerized puppet executions use modules from the baremetal host. Defaults to true. Can be set to false to consume puppet modules from containers directly.
  PythonInterpreter:
    type: string
    description: The python interpreter to use for python and ansible actions
    default: "$(command -v python3 || command -v python)"
  NeutronEnableL2Pop:
    type: string
    description: Enable/disable the L2 population feature in the Neutron agents.
    default: "False"
  NeutronBridgeMappings:
    description: >
      The OVS logical->physical bridge mappings to use. See the Neutron
      documentation for details. Defaults to mapping br-ex - the external
      bridge on hosts - to a physical name 'datacentre' which can be used
      to create provider networks (and we use this for the default floating
      network) - if changing this either use different post-install network
      scripts or be sure to keep 'datacentre' as a mapping network name.
    type: comma_delimited_list
    default: "datacentre:br-ex"
    tags:
      - role_specific
  NeutronTunnelTypes:
    default: 'vxlan'
    description: The tunnel types for the Neutron tenant network.
    type: comma_delimited_list
  NeutronAgentExtensions:
    default: "qos"
    description: |
        Comma-separated list of extensions enabled for the Neutron agents.
    type: comma_delimited_list
  NeutronEnableDVR:
    default: ''
    description: Enable Neutron DVR.
    type: string
  NeutronEnableARPResponder:
    default: false
    description: |
      Enable ARP responder feature in the OVS Agent.
    type: boolean
  MonitoringSubscriptionNeutronOvs:
    default: 'overcloud-neutron-ovs-agent'
    type: string
  NeutronOVSFirewallDriver:
    default: ''
    description: |
      Configure the classname of the firewall driver to use for implementing
      security groups. Possible values depend on system configuration. Some
      examples are: noop, openvswitch, iptables_hybrid. The default value of an
      empty string will result in a default supported configuration.
    type: string
  OvsHwOffload:
    default: false
    description: |
      Enable OVS Hardware Offload. This feature supported from OVS 2.8.0
    type: boolean
    tags:
      - role_specific
  OvsDisableEMC:
    default: false
    description: |
      Disable OVS Exact Match Cache.
    type: boolean
    tags:
      - role_specific
  NeutronOVSTunnelCsum:
    default: false
    description: |
      Set or un-set the tunnel header checksum  on outgoing IP packet
      carrying GRE/VXLAN tunnel.
    type: boolean
  NeutronPermittedEthertypes:
    default: []
    description: |
      Set additional ethertypes to to be configured on neutron firewalls.
    type: comma_delimited_list
  NeutronOvsResourceProviderBandwidths:
    description: >
      Comma-separated list of <bridge>:<egress_bw>:<ingress_bw> tuples, showing
      the available bandwidth for the given bridge in the given direction. The
      direction is meant from VM perspective. Bandwidth is measured in kilobits
      per second (kbps). The bridge must appear in bridge_mappings as the value.
    type: comma_delimited_list
    default: ""
    tags:
     - role_specific
  NeutronEnableIgmpSnooping:
    description: Enable IGMP Snooping.
    type: boolean
    default: false

  NeutronOVSAgentLoggingRateLimit:
    default: 100
    description: |
      Maximum number of packets logging per second
    type: number
  NeutronOVSAgentLoggingBurstLimit:
    default: 25
    description: |
      Maximum number of packets per rate_limit
    type: number
  NeutronOVSAgentLoggingLocalOutputLogBase:
    default: ''
    description: |
      Output logfile path on agent side, default syslog file
    type: string

conditions:
  firewall_driver_set:
    not: {equals : [{get_param: NeutronOVSFirewallDriver}, '']}
  neutron_dvr_set:
    not: {equals : [{get_param: NeutronEnableDVR}, '']}
  ethertypes_set:
    not: {equals : [{get_param: NeutronPermittedEthertypes}, []]}
  network_log_local_output_log_base_set:
    not: {equals : [{get_param: NeutronOVSAgentLoggingLocalOutputLogBase}, '']}

resources:
  ContainersCommon:
    type: ../containers-common.yaml

  # Merging role-specific parameters (RoleParameters) with the default parameters.
  # RoleParameters will have the precedence over the default parameters.
  RoleParametersValue:
    type: OS::Heat::Value
    properties:
      type: json
      value:
        map_replace:
          - map_replace:
            - neutron::agents::ml2::ovs::bridge_mappings: NeutronBridgeMappings
              vswitch::ovs::enable_hw_offload: OvsHwOffload
              vswitch::ovs::disable_emc: OvsDisableEMC
              neutron::agents::ml2::ovs::resource_provider_bandwidths: NeutronOvsResourceProviderBandwidths
            - values: {get_param: [RoleParameters]}
          - values:
              NeutronBridgeMappings: {get_param: NeutronBridgeMappings}
              OvsHwOffload: {get_param: OvsHwOffload}
              OvsDisableEMC: {get_param: OvsDisableEMC}
              NeutronOvsResourceProviderBandwidths: {get_param: NeutronOvsResourceProviderBandwidths}

  NeutronBase:
    type: ./neutron-base.yaml
    properties:
      EndpointMap: {get_param: EndpointMap}
      ServiceData: {get_param: ServiceData}
      ServiceNetMap: {get_param: ServiceNetMap}
      RoleName: {get_param: RoleName}
      RoleParameters: {get_param: RoleParameters}

  NeutronLogging:
    type: OS::TripleO::Services::Logging::NeutronCommon
    properties:
      NeutronServiceName: openvswitch-agent

outputs:
  role_data:
    description: Role data for Neutron openvswitch service
    value:
      service_name: neutron_ovs_agent
      firewall_rules:
        '118 neutron vxlan networks':
          proto: 'udp'
          dport: 4789
        '136 neutron gre networks':
          proto: 'gre'
      monitoring_subscription: {get_param: MonitoringSubscriptionNeutronOvs}
      config_settings:
        map_merge:
          - get_attr: [NeutronBase, role_data, config_settings]
          - get_attr: [RoleParametersValue, value]
          - get_attr: [NeutronLogging, config_settings]
          - neutron::agents::ml2::ovs::l2_population: {get_param: NeutronEnableL2Pop}
            neutron::agents::ml2::ovs::arp_responder: {get_param: NeutronEnableARPResponder}
            neutron::agents::ml2::ovs::tunnel_types: {get_param: NeutronTunnelTypes}
            neutron::agents::ml2::ovs::extensions: {get_param: NeutronAgentExtensions}
            neutron::agents::ml2::ovs::tunnel_csum: {get_param: NeutronOVSTunnelCsum}
            neutron::agents::ml2::ovs::igmp_snooping_enable: {get_param: NeutronEnableIgmpSnooping}
            neutron::agents::ml2::ovs::resource_provider_default_hypervisor: "%{hiera('fqdn_canonical')}"
            # NOTE: bind IP is found in hiera replacing the network name with the
            # local node IP for the given network; replacement examples
            # (eg. for internal_api):
            # internal_api -> IP
            # internal_api_uri -> [IP]
            # internal_api_subnet - > IP/CIDR
            neutron::agents::ml2::ovs::local_ip:
              str_replace:
                 template:
                   "%{hiera('$NETWORK')}"
                 params:
                   $NETWORK: {get_param: [ServiceNetMap, NeutronTenantNetwork]}
            neutron::agents::ml2::ovs::enable_distributed_routing:
              if:
                - neutron_dvr_set
                - {get_param: NeutronEnableDVR}
            neutron::agents::ml2::ovs::firewall_driver:
              if:
                - firewall_driver_set
                - {get_param: NeutronOVSFirewallDriver}
            neutron::agents::ml2::ovs::permitted_ethertypes:
              if:
                - ethertypes_set
                - {get_param: NeutronPermittedEthertypes}
          - neutron::agents::ml2::ovs::network_log_rate_limit: {get_param: NeutronOVSAgentLoggingRateLimit}
          - neutron::agents::ml2::ovs::network_log_burst_limit: {get_param: NeutronOVSAgentLoggingBurstLimit}
          - if:
            - network_log_local_output_log_base_set
            - neutron::agents::ml2::ovs::network_log_local_output_log_base: {get_param: NeutronOVSAgentLoggingLocalOutputLogBase}

      service_config_settings:
        map_merge:
          - get_attr: [NeutronBase, role_data, service_config_settings]
          - rsyslog:
              tripleo_logging_sources_neutron_ovs_agent:
                - {get_param: NeutronOpenVswitchAgentLoggingSource}
          - collectd:
              tripleo.collectd.plugins.neutron_ovs_agent:
                - ovs_events
                - ovs_stats
              collectd::plugin::ovs_events::socket: '/run/openvswitch/db.sock'
              collectd::plugin::ovs_stats::socket: '/run/openvswitch/db.sock'
      puppet_config:
        config_volume: neutron
        puppet_tags: neutron_config,neutron_agent_ovs,neutron_plugin_ml2
        step_config: |
          include tripleo::profile::base::neutron::ovs
        config_image: {get_param: ContainerNeutronConfigImage}
        # We need to mount /run for puppet_config step. This is because
        # puppet-vswitch runs the commands "ovs-vsctl list open_vswitch ."
        # when running vswitch::ovs::enable_hw_offload: true
        # ovs-vsctl talks to the ovsdb-server (hosting conf.db)
        # on the unix domain socket - /run/openvswitch/db.sock
        volumes:
          - /lib/modules:/lib/modules:ro
          - /run/openvswitch:/run/openvswitch:shared,z
      kolla_config:
        /var/lib/kolla/config_files/neutron_ovs_agent.json:
          command: /neutron_ovs_agent_launcher.sh
          config_files:
            - source: "/var/lib/kolla/config_files/src/*"
              dest: "/"
              merge: true
              preserve_properties: true
          permissions:
            - path: /var/log/neutron
              owner: neutron:neutron
              recurse: true
      container_config_scripts:
        neutron_ovs_agent_launcher.sh:
          mode: "0755"
          content:
            str_replace:
              template: |
                #!/bin/bash
                set -xe
                PYTHON -m neutron.cmd.destroy_patch_ports \
                --config-file /etc/neutron/neutron.conf \
                --config-file /etc/neutron/plugins/ml2/openvswitch_agent.ini \
                --config-dir /etc/neutron/conf.d/common \
                --config-dir /etc/neutron/conf.d/neutron-openvswitch-agent \
                --log-file=/var/log/neutron/openvswitch-agent.log

                /usr/bin/neutron-openvswitch-agent \
                --config-file /etc/neutron/neutron.conf \
                --config-file /etc/neutron/plugins/ml2/openvswitch_agent.ini \
                --config-dir /etc/neutron/conf.d/common \
                --log-file=/var/log/neutron/openvswitch-agent.log
              params:
                PYTHON: {get_param: PythonInterpreter}
      docker_config:
        step_3:
          neutron_ovs_bridge:
            detach: false
            image: {get_param: ContainerNeutronConfigImage}
            net: host
            pid: host
            user: root
            privileged: true
            security_opt:
              - label=disable
            command:
              - puppet
              - apply
              - --modulepath
              - /etc/puppet/modules:/usr/share/openstack-puppet/modules
              - --tags
              - file,file_line,concat,augeas,neutron::plugins::ovs::bridge,vs_config
              - -v
              - -e
              - include neutron::agents::ml2::ovs
            volumes:
              list_concat:
                - {get_attr: [ContainersCommon, volumes]}
                - - /var/lib/kolla/config_files/neutron_ovs_agent.json:/var/lib/kolla/config_files/config.json:ro
                  - /var/lib/config-data/puppet-generated/neutron:/var/lib/kolla/config_files/src:ro
                  - /lib/modules:/lib/modules:ro
                  - /run/openvswitch:/run/openvswitch:shared,z
                  - if:
                      - {get_param: DockerPuppetMountHostPuppet}
                      - /usr/share/openstack-puppet/modules/:/usr/share/openstack-puppet/modules/:ro
            environment:
              KOLLA_CONFIG_STRATEGY: COPY_ALWAYS
              TRIPLEO_DEPLOY_IDENTIFIER: {get_param: DeployIdentifier}
        step_4:
          neutron_ovs_agent:
            start_order: 10
            image: {get_param: ContainerOpenvswitchImage}
            net: host
            pid: host
            privileged: true
            security_opt:
              - label=disable
            restart: always
            depends_on:
              - openvswitch.service
            healthcheck: {get_attr: [ContainersCommon, healthcheck_rpc_port]}
            ulimit: {get_param: DockerOpenvswitchUlimit}
            volumes:
              list_concat:
                - {get_attr: [ContainersCommon, volumes]}
                - {get_attr: [NeutronLogging, volumes]}
                - - /var/lib/kolla/config_files/neutron_ovs_agent.json:/var/lib/kolla/config_files/config.json:ro
                  - /var/lib/config-data/puppet-generated/neutron:/var/lib/kolla/config_files/src:ro
                  - /var/lib/container-config-scripts/neutron_ovs_agent_launcher.sh:/neutron_ovs_agent_launcher.sh:ro
                  - /lib/modules:/lib/modules:ro
                  - /run/openvswitch:/run/openvswitch:shared,z
            environment:
              KOLLA_CONFIG_STRATEGY: COPY_ALWAYS
      metadata_settings:
        get_attr: [NeutronBase, role_data, metadata_settings]
      host_prep_tasks:
        list_concat:
          - {get_attr: [NeutronLogging, host_prep_tasks]}
          - - block:
              - name: load openvswitch module
                import_role:
                  name: tripleo_module_load
                vars:
                  modules:
                    - name: openvswitch
              - name: Copy in cleanup script
                copy:
                  content: {get_file: ./neutron-cleanup}
                  dest: '/usr/libexec/neutron-cleanup'
                  force: yes
                  mode: '0755'
              - name: Copy in cleanup service
                copy:
                  content: {get_file: ./neutron-cleanup.service}
                  dest: '/usr/lib/systemd/system/neutron-cleanup.service'
                  force: yes
              - name: Enabling the cleanup service
                service:
                  name: neutron-cleanup
                  enabled: yes
              - name: enable virt_sandbox_use_netlink for healthcheck
                seboolean:
                  name: virt_sandbox_use_netlink
                  persistent: yes
                  state: yes
      update_tasks:
        # puppetlabs-firewall manages security rules via Puppet but make the rules
        # consistent by default. Since Neutron also creates some rules, we don't
        # want them to be consistent so we have to ensure that they're not stored
        # into sysconfig.
        # https://bugzilla.redhat.com/show_bug.cgi?id=1541528
        - name: Remove IPv4 iptables rules created by Neutron that are persistent
          lineinfile: dest=/etc/sysconfig/iptables
                      regexp=".*neutron-"
                      state=absent
          when: step|int == 5
        - name: Remove IPv6 iptables rules created by Neutron that are persistent
          lineinfile: dest=/etc/sysconfig/ip6tables
                      regexp=".*neutron-"
                      state=absent
          when: step|int == 5