heat_template_version: rocky

description: >
  OpenStack containerized OpenDaylight API service

parameters:
  DockerOpendaylightApiImage:
    description: image
    type: string
  DockerOpendaylightConfigImage:
    description: image
    type: string
  EndpointMap:
    default: {}
    description: Mapping of service endpoint -> protocol. Typically set
                 via parameter_defaults in the resource registry.
    type: json
  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.  This
                 mapping overrides those in ServiceNetMapDefaults.
    type: json
  DefaultPasswords:
    default: {}
    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
  EnableInternalTLS:
    type: boolean
    default: false
  InternalTLSCAFile:
    default: '/etc/ipa/ca.crt'
    type: string
    description: Specifies the default CA cert to use if TLS is used for
                 services in the internal network.
  ODLUpdateLevel:
    default: 1
    description: Specify the level of update
    type: number
    constraints:
      - allowed_values:
          - 1
          - 2

conditions:

  internal_tls_enabled: {equals: [{get_param: EnableInternalTLS}, true]}

resources:

  ContainersCommon:
    type: ./containers-common.yaml

  OpenDaylightBase:
    type: ../../puppet/services/opendaylight-api.yaml
    properties:
      EndpointMap: {get_param: EndpointMap}
      ServiceData: {get_param: ServiceData}
      ServiceNetMap: {get_param: ServiceNetMap}
      DefaultPasswords: {get_param: DefaultPasswords}
      RoleName: {get_param: RoleName}
      RoleParameters: {get_param: RoleParameters}

  OpenDaylightApiLogging:
    type: OS::TripleO::Services::Logging::OpenDaylightApi

outputs:
  role_data:
    description: Role data for the OpenDaylight API role.
    value:
      service_name: {get_attr: [OpenDaylightBase, role_data, service_name]}
      config_settings:
        map_merge:
          - get_attr: [OpenDaylightBase, role_data, config_settings]
          - if:
            - internal_tls_enabled
            - tripleo::certmonger::opendaylight::postsave_cmd: "true" # TODO: restart the odl container here
            - {}
      # BEGIN DOCKER SETTINGS
      puppet_config:
        config_volume: opendaylight
        volumes:
          list_concat:
          - if:
            - internal_tls_enabled
            - - /etc/pki/tls/certs/odl.crt:/etc/pki/tls/certs/odl.crt:ro
              - /etc/pki/tls/private/odl.key:/etc/pki/tls/private/odl.key:ro
              - list_join:
                  - ':'
                  - - {get_param: InternalTLSCAFile}
                    - {get_param: InternalTLSCAFile}
                    - 'ro'
            - null
        # 'file,concat,file_line,augeas' are included by default
        puppet_tags: odl_user,odl_keystore
        step_config:
          get_attr: [OpenDaylightBase, role_data, step_config]
        config_image: {get_param: DockerOpendaylightConfigImage}
      kolla_config:
        /var/lib/kolla/config_files/opendaylight_api.json:
          command: /opt/opendaylight/bin/karaf server
          config_files:
            - source: "/var/lib/kolla/config_files/src/*"
              dest: "/"
              merge: true
              preserve_properties: true
          permissions:
            - path: /opt/opendaylight
              owner: odl:odl
              recurse: true
      docker_config:
        step_1:
          opendaylight_api:
            start_order: 0
            image: &odl_api_image {get_param: DockerOpendaylightApiImage}
            privileged: false
            net: host
            detach: true
            user: odl
            restart: unless-stopped
            healthcheck:
              test: /openstack/healthcheck
            volumes:
              list_concat:
                - {get_attr: [ContainersCommon, volumes]}
                - {get_attr: [OpenDaylightApiLogging, volumes]}
                -
                  - /var/lib/kolla/config_files/opendaylight_api.json:/var/lib/kolla/config_files/config.json:ro
                  - /var/lib/config-data/puppet-generated/opendaylight/:/var/lib/kolla/config_files/src:ro
                  - /var/lib/opendaylight/journal:/opt/opendaylight/journal
                  - /var/lib/opendaylight/snapshots:/opt/opendaylight/snapshots
            environment:
              - KOLLA_CONFIG_STRATEGY=COPY_ALWAYS
      metadata_settings:
        get_attr: [OpenDaylightBase, role_data, metadata_settings]
      host_prep_tasks:
        list_concat:
          - {get_attr: [OpenDaylightApiLogging, host_prep_tasks]}
          -
            - name: Delete data and karaf folder
              file:
                path: "{{ item }}"
                state: absent
              with_items:
                - /var/lib/opendaylight/data # Delete folder if present from previous deployment
                - /var/lib/config-data/puppet-generated/opendaylight/opt/opendaylight/etc/opendaylight/karaf
            - name: create persistent directories
              file:
                path: "{{ item }}"
                state: directory
              with_items:
                - /var/lib/opendaylight/snapshots
                - /var/lib/opendaylight/journal
      upgrade_tasks:
        # Containerized deployment upgrade steps
        - name: ODL container L2 update and upgrade tasks
          block: &odl_container_upgrade_tasks
            - name: Set fact for container CLI
              set_fact:
                container_cli: {get_param: ContainerCli}
            - name: Check if ODL container is present
              shell: "{{ container_cli }} ps -a --format '{{ '{{' }}.Names{{ '}}' }}' | grep '^opendaylight_api$'"
              register: opendaylight_api_container_present
              failed_when: false
            # NOTE: using shell module because of
            # https://github.com/ansible/ansible/issues/27960
            ##TODO: (janki) make update policy compatible with podman
            - name: Update ODL container restart policy to unless-stopped
              shell: "docker update --restart=unless-stopped opendaylight_api"
              when:
                - opendaylight_api_container_present.rc == 0
                - container_cli == docker
            - name: stop previous ODL container
              docker_container:
                name: opendaylight_api
                state: stopped
              when:
                - step|int == 0
                - container_cli == docker
            ##TODO: (janki) Switch to podman ansible module once its available
            - name: stop previous ODL container using podman
              shell: "systemctl stop opendaylight_api"
              when:
                - step|int == 0
                - container_cli == podman
            - name: remove journal and snapshots
              file:
                path: /var/lib/opendaylight/{{item}}
                state: absent
              with_items:
                - snapshots
                - journal
              when: step|int == 0
            - name: Set ODL upgrade flag to True
              copy:
                dest: /var/lib/config-data/puppet-generated/opendaylight/opt/opendaylight/etc/opendaylight/datastore/initial/config/genius-mdsalutil-config.xml
                content: |
                  <config xmlns="urn:opendaylight:params:xml:ns:yang:mdsalutil">
                      <upgradeInProgress>true</upgradeInProgress>
                  </config>
                owner: 42462
                group: 42462
                mode: 0644
              when: step|int == 1
      post_upgrade_tasks: &odl_container_post_upgrade_tasks
        - name: Disable Upgrade in Config File
          copy:
            dest: /var/lib/config-data/puppet-generated/opendaylight/opt/opendaylight/etc/opendaylight/datastore/initial/config/genius-mdsalutil-config.xml
            content: |
              <config xmlns="urn:opendaylight:params:xml:ns:yang:mdsalutil">
                  <upgradeInProgress>false</upgradeInProgress>
              </config>
            owner: 42462
            group: 42462
            mode: 0644
          when: step|int == 0
          # 2 commands in 1 task because the sequence of commands needs to be ensured
          # and that no other task is executed in between.
        - name: Delete Upgrade Flag and Unset it via Rest
          shell:
            str_replace:
              template: >
                curl -k -v --silent --fail --show-error -u $ODL_USERNAME:$ODL_PASSWORD
                -H "Content-Type: application/json" -X DELETE
                $ODL_URI/restconf/config/genius-mdsalutil:config;
                curl -k -v --silent --fail --show-error -u $ODL_USERNAME:$ODL_PASSWORD
                -H "Content-Type: application/json" -X POST
                $ODL_URI/restconf/config/genius-mdsalutil:config
                -d "{ "upgradeInProgress": false }"
              params:
                $ODL_USERNAME: {get_attr: [OpenDaylightBase, role_data, config_settings, 'opendaylight::username']}
                $ODL_PASSWORD: {get_attr: [OpenDaylightBase, role_data, config_settings, 'opendaylight::password']}
                $ODL_URI: {get_param: [EndpointMap, OpenDaylightInternal, uri]}
          when: step|int == 0
          run_once: true
        - when: step|int == 1
          import_role:
            name: tripleo-docker-rm
          vars:
            containers_to_rm:
              - opendaylight_api
      update_tasks:
        - name: Get ODL update level
          block: &get_odl_update_level
            - name: store update level to update_level variable
              set_fact:
                odl_update_level: {get_param: ODLUpdateLevel}
        - name: Stop ODL container and remove cache
          block:
            - name: Set fact for container CLI
              set_fact:
                container_cli: {get_param: ContainerCli}
            - name: Check if ODL container is present
              shell: "{{ contianer_cli }} ps -a --format '{{ '{{' }}.Names{{ '}}' }}' | grep '^opendaylight_api$'"
              register: opendaylight_api_container_present
              failed_when: false
            # NOTE: using shell module because of
            # https://github.com/ansible/ansible/issues/27960
            ##TODO: (janki) make update policy compatible with podman
            - name: Update ODL container restart policy to unless-stopped
              shell: "docker update --restart=unless-stopped opendaylight_api"
              when: opendaylight_api_container_present.rc == 0
            - name: Stop previous ODL container
              docker_container:
                name: opendaylight_api
                state: stopped
              when: container_cli == docker
            ##TODO: (janki) Switch to podman ansible module once its available
            - name: stop previous ODL container using podman
              shell: "systemctl stop opendaylight_api"
              when: container_cli == podman
            - name: Delete data folder
              file:
                path: /var/lib/opendaylight/data
                state: absent
          when:
            - step|int == 0
            - odl_update_level == 1
        - name: Run L2 update tasks that are similar to upgrade_tasks when update level is 2
          block: *odl_container_upgrade_tasks
          when: odl_update_level == 2
      post_update_tasks:
        - block: *get_odl_update_level
        - block: *odl_container_post_upgrade_tasks
          when: odl_update_level == 2