heat_template_version: wallaby

description: >
  OpenStack containerized Nova Vncproxy service

parameters:
  ContainerNovaVncProxyImage:
    description: image
    type: string
  ContainerNovaConfigImage:
    description: The container image to use for the nova config_volume
    type: string
  NovaVncproxyLoggingSource:
    type: json
    default:
      tag: openstack.nova.vncproxy
      file: /var/log/containers/nova/nova-novncproxy.log
  EndpointMap:
    default: {}
    description: Mapping of service endpoint -> protocol. Typically set
                 via parameter_defaults in the resource registry.
    type: json
  AdminPassword:
    description: The password for the keystone admin account, used for monitoring, querying neutron etc.
    type: string
    hidden: true
  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
  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
  UseTLSTransportForVnc:
    type: boolean
    default: true
    description: If set to true and if EnableInternalTLS is enabled, it will
                 enable TLS transaport for libvirt VNC and configure the
                 relevant keys for libvirt.
  InternalTLSVncProxyCAFile:
    default: '/etc/ipa/ca.crt'
    type: string
    description: Specifies the CA cert to use for VNC TLS.
  CertificateKeySize:
    type: string
    default: '2048'
    description: Specifies the private key size used when creating the
                 certificate.
  NovaVNCCertificateKeySize:
    type: string
    default: ''
    description: Override the private key size used when creating the
                 certificate for this service
  LibvirtVNCClientCertificateKeySize:
    type: string
    default: ''
    description: Override the private key size used when creating the
                 certificate for this service
  LibvirtVncCACert:
    type: string
    default: ''
    description: This specifies the CA certificate to use for VNC TLS.
                 This file will be symlinked to the default CA path,
                 which is /etc/pki/CA/certs/vnc.crt.
                 This parameter should be used if the default (which comes from
                 the InternalTLSVncProxyCAFile parameter) is not desired. The current
                 default reflects TripleO's default CA, which is FreeIPA.
                 It will only be used if internal TLS is enabled.
  NovaVNCProxySSLCiphers:
    type: string
    default: ''
    description: OpenSSL cipher preference string that specifies what ciphers
                 to allow for TLS connections from clients.  See the man page
                 for the OpenSSL 'ciphers' command for details of the cipher
                 preference string format and allowed values.
  NovaVNCProxySSLMinimumVersion:
    type: string
    default: 'default'
    description: Minimum allowed SSL/TLS protocol version.  Valid values are
                 'default', 'tlsv1_1', 'tlsv1_2', and 'tlsv1_3'.  A value of
                 'default' will use the underlying system OpenSSL defaults.
    constraints:
    - allowed_values: ['default', 'tlsv1_1', 'tlsv1_2', 'tlsv1_3']
  StackUpdateType:
    type: string
    description: >
      Type of update, to differentiate between UPGRADE and UPDATE cases
      when StackAction is UPDATE (both are the same stack action).
    constraints:
    - allowed_values: ['', 'UPGRADE']
    default: ''


conditions:

  use_tls_for_vnc:
    and:
    - equals:
      - {get_param: EnableInternalTLS}
      - true
    - equals:
      - {get_param: UseTLSTransportForVnc}
      - true

  libvirt_vnc_specific_ca_unset:
    equals:
      - {get_param: LibvirtVncCACert}
      - ''

  proxy_ssl_ciphers_unset:
    equals:
      - {get_param: NovaVNCProxySSLCiphers}
      - ''

  allow_noauth:
    # Allow noauth VNC connections during P->Q upgrade. Remove in Rocky.
    equals: [{get_param: StackUpdateType}, 'UPGRADE']

  key_size_novavnc_override_unset: {equals: [{get_param: NovaVNCCertificateKeySize}, '']}
  key_size_libvirtvnc_override_unset: {equals: [{get_param: LibvirtVNCClientCertificateKeySize}, '']}

resources:

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

  MySQLClient:
    type: ../../deployment/database/mysql-client.yaml

  NovaLogging:
    type: OS::TripleO::Services::Logging::NovaCommon
    properties:
      ContainerNovaImage: {get_param: ContainerNovaVncProxyImage}
      NovaServiceName: 'vncproxy'

  NovaBase:
    type: ./nova-base-puppet.yaml
    properties:
      ServiceData: {get_param: ServiceData}
      ServiceNetMap: {get_param: ServiceNetMap}
      EndpointMap: {get_param: EndpointMap}
      RoleName: {get_param: RoleName}
      RoleParameters: {get_param: RoleParameters}

  NovaDBClient:
    type: ./nova-db-client-puppet.yaml
    properties:
      ServiceData: {get_param: ServiceData}
      ServiceNetMap: {get_param: ServiceNetMap}
      EndpointMap: {get_param: EndpointMap}
      RoleName: {get_param: RoleName}
      RoleParameters: {get_param: RoleParameters}


outputs:
  role_data:
    description: Role data for the Nova Vncproxy service.
    value:
      service_name: nova_vnc_proxy
      firewall_rules:
        '137 nova_vnc_proxy':
          dport:
            - 6080
            - 13080
      config_settings:
        map_merge:
          - get_attr: [NovaBase, role_data, config_settings]
          - get_attr: [NovaDBClient, role_data, config_settings]
          - get_attr: [NovaLogging, config_settings]
          - nova::vncproxy::enabled: true
            nova::vncproxy::common::vncproxy_protocol: {get_param: [EndpointMap, NovaVNCProxyCellPublic, protocol]}
            nova::vncproxy::common::vncproxy_host: {get_param: [EndpointMap, NovaVNCProxyCellPublic, host_nobrackets]}
            nova::vncproxy::common::vncproxy_port: {get_param: [EndpointMap, NovaVNCProxyCellPublic, port]}
            # 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
            nova::vncproxy::host:
              str_replace:
                template:
                  "%{hiera('$NETWORK')}"
                params:
                  $NETWORK: {get_param: [ServiceNetMap, NovaLibvirtNetwork]}
          -
            if:
              - use_tls_for_vnc
              -
                nova::vncproxy::allow_vencrypt: true
                nova::vncproxy::allow_noauth: {if: [allow_noauth, true, false]}
                nova::vncproxy::vencrypt_key: /etc/pki/tls/private/libvirt-vnc-client-cert.key
                nova::vncproxy::vencrypt_cert: /etc/pki/tls/certs/libvirt-vnc-client-cert.crt
                nova::vncproxy::vencrypt_ca: /etc/pki/CA/certs/vnc.crt
                nova::ssl_only: true
                nova::console_ssl_ciphers:
                  if:
                    - proxy_ssl_ciphers_unset
                    - null
                    - get_param: NovaVNCProxySSLCiphers
                nova::console_ssl_minimum_version: {get_param: NovaVNCProxySSLMinimumVersion}
                nova::cert: /etc/pki/tls/certs/novnc-proxy.crt
                nova::key: /etc/pki/tls/private/novnc-proxy.key
              - {}
      service_config_settings:
        rabbitmq: {get_attr: [NovaBase, role_data, service_config_settings], rabbitmq}
        mysql:
          map_merge:
            - get_attr: [NovaDBClient, role_data, service_config_settings, mysql]
        rsyslog:
          tripleo_logging_sources_nova_vnc_proxy:
            - {get_param: NovaVncproxyLoggingSource}
      # BEGIN DOCKER SETTINGS
      puppet_config:
        config_volume: nova
        puppet_tags: nova_config
        step_config:
          list_join:
            - "\n"
            - - include tripleo::profile::base::nova::vncproxy
              - {get_attr: [MySQLClient, role_data, step_config]}
        config_image: {get_param: ContainerNovaConfigImage}
      kolla_config:
        /var/lib/kolla/config_files/nova_vnc_proxy.json:
          command:
            list_join:
            - ' '
            - - /usr/bin/nova-novncproxy --web /usr/share/novnc/
              - get_attr: [NovaLogging, cmd_extra_args]
          config_files:
            - source: "/var/lib/kolla/config_files/src/*"
              dest: "/"
              merge: true
              preserve_properties: true
            - source: "/var/lib/kolla/config_files/src-tls/*"
              dest: "/"
              merge: true
              preserve_properties: true
              optional: true
          permissions:
            - path: /var/log/nova
              owner: nova:nova
              recurse: true
            - path: /etc/pki/tls/certs/novnc-proxy.crt
              owner: root:root
              perm: '0644'
            - path: /etc/pki/tls/private/novnc-proxy.key
              owner: root:nova
              perm: '0640'
      docker_config:
        step_4:
          nova_vnc_proxy:
            image: {get_param: ContainerNovaVncProxyImage}
            net: host
            privileged: false
            restart: always
            healthcheck:
              test: /openstack/healthcheck
            volumes:
              list_concat:
                - {get_attr: [ContainersCommon, volumes]}
                - {get_attr: [NovaLogging, volumes]}
                -
                  - /var/lib/kolla/config_files/nova_vnc_proxy.json:/var/lib/kolla/config_files/config.json:ro
                  - /var/lib/config-data/puppet-generated/nova:/var/lib/kolla/config_files/src:ro
                -
                  if:
                    - use_tls_for_vnc
                    -
                      - str_replace:
                          template: "CACERT:/etc/pki/CA/certs/vnc.crt:ro"
                          params:
                            CACERT:
                              if:
                                - libvirt_vnc_specific_ca_unset
                                - get_param: InternalTLSVncProxyCAFile
                                - get_param: LibvirtVncCACert
                      - /etc/pki/tls/certs/libvirt-vnc-client-cert.crt:/etc/pki/tls/certs/libvirt-vnc-client-cert.crt:ro
                      - /etc/pki/tls/private/libvirt-vnc-client-cert.key:/etc/pki/tls/private/libvirt-vnc-client-cert.key:ro
                      - /etc/pki/tls/certs/novnc-proxy.crt:/var/lib/kolla/config_files/src-tls/etc/pki/tls/certs/novnc-proxy.crt:ro
                      - /etc/pki/tls/private/novnc-proxy.key:/var/lib/kolla/config_files/src-tls/etc/pki/tls/private/novnc-proxy.key:ro
                    - null
            environment:
              KOLLA_CONFIG_STRATEGY: COPY_ALWAYS
      metadata_settings:
        if:
          - use_tls_for_vnc
          -
            - service: libvirt-vnc
              network: {get_param: [ServiceNetMap, NovaLibvirtNetwork]}
              type: node
            - service: novnc-proxy
              network: {get_param: [ServiceNetMap, NovaLibvirtNetwork]}
              type: node
          - null
      deploy_steps_tasks:
        list_concat:
          - - name: validate nova-vnc-proxy container state
              podman_container_info:
                name: nova_vnc_proxy
              register: nova_vnc_proxy_infos
              failed_when:
                - nova_vnc_proxy_infos.containers.0.Healthcheck.Status is defined
                - "'healthy' not in nova_vnc_proxy_infos.containers.0.Healthcheck.Status"
              retries: 10
              delay: 30
              tags:
                - opendev-validation
                - opendev-validation-nova
              when:
                - container_cli == 'podman'
                - not container_healthcheck_disabled
                - step|int == 5
          - if:
            - use_tls_for_vnc
            -
              - name: Certificate generation
                when: step|int == 1
                block:
                  - include_role:
                      name: linux-system-roles.certificate
                    vars:
                      certificate_requests:
                        - name: libvirt-vnc-client-cert
                          dns:
                            str_replace:
                              template: "{{fqdn_NETWORK}}"
                              params:
                                NETWORK: {get_param: [ServiceNetMap, NovaLibvirtNetwork]}
                          principal:
                            str_replace:
                              template: "libvirt-vnc/{{fqdn_NETWORK}}@{{idm_realm}}"
                              params:
                                NETWORK: {get_param: [ServiceNetMap, NovaLibvirtNetwork]}
                          key_size:
                            if:
                              - key_size_libvirtvnc_override_unset
                              - {get_param: CertificateKeySize}
                              - {get_param: LibvirtVNCClientCertificateKeySize}
                          ca: ipa
                        - name: novnc-proxy
                          dns:
                            str_replace:
                              template: "{{fqdn_$NETWORK}}"
                              params:
                                $NETWORK: {get_param: [ServiceNetMap, NovaLibvirtNetwork]}
                          principal:
                            str_replace:
                              template: "novnc-proxy/{{fqdn_$NETWORK}}@{{idm_realm}}"
                              params:
                                $NETWORK: {get_param: [ServiceNetMap, NovaLibvirtNetwork]}
                          run_after: |
                            container_name=$({{container_cli}} ps --format=\{\{.Names\}\} | grep nova_vnc_proxy)
                            service_crt="/etc/pki/tls/certs/novnc-proxy.crt"
                            service_key="/etc/pki/tls/private/novnc-proxy.key"
                            # Copy the new cert from the mount-point to the real path
                            {{container_cli}} exec -u root "$container_name" cp "/var/lib/kolla/config_files/src-tls$service_crt" "$service_crt"
                            # Copy the new key from the mount-point to the real path
                            {{container_cli}} exec -u root "$container_name" cp "/var/lib/kolla/config_files/src-tls$service_key" "$service_key"

                            # Set permissions
                            {{container_cli}} exec -u root "$container_name" chmod 0644 $service_crt
                            {{container_cli}} exec -u root "$container_name" chmod 0640 $service_key
                            {{container_cli}} exec -u root "$container_name" chgrp nova $service_key

                            # No need to trigger a reload for novnc proxy since the cert is not cached
                          key_size:
                            if:
                              - key_size_libvirtvnc_override_unset
                              - {get_param: CertificateKeySize}
                              - {get_param: NovaVNCCertificateKeySize}
                          ca: ipa
            - []
      host_prep_tasks:
        list_concat:
          -  {get_attr: [NovaLogging, host_prep_tasks]}
          - - name: ensure qemu group is present on the host
              group:
                name: qemu
                gid: 107
                state: present
            - name: ensure qemu user is present on the host
              user:
                name: qemu
                uid: 107
                group: qemu
                state: present
                shell: /sbin/nologin
                comment: qemu user
      post_upgrade_tasks:
        - when:
            - step|int == 5
          tags:
            - never
            - system_upgrade_stop_services
          environment:
            OS_USERNAME: admin
            OS_USER_DOMAIN_NAME: "Default"
            OS_PROJECT_DOMAIN_NAME: "Default"
            OS_PROJECT_NAME: admin
            OS_PASSWORD: { get_param: AdminPassword }
            OS_AUTH_URL: { get_param: [EndpointMap, KeystoneV3Public, uri] }
            OS_IDENTITY_API_VERSION: 3
            OS_AUTH_TYPE: password
          block:
            - name: Get nova-consoleauth service ID
              command: openstack compute service list --service nova-consoleauth --column ID  --column Host --format yaml
              register: nova_compute_service_result
              delegate_to: localhost
              check_mode: no
              run_once: yes
              changed_when: false
            - name: Deleting nova-consoleauth
              command: "openstack compute service delete {{ item.ID }}"
              loop: "{{ nova_compute_service_result.stdout | from_yaml }}"
              run_once: yes
              delegate_to: localhost
              ignore_errors: yes
      external_upgrade_tasks:
        - when:
            - step|int == 1
          tags:
            - never
            - system_upgrade_transfer_data
            - system_upgrade_stop_services
          block:
            - name: Stop nova vnc proxy container
              import_role:
                name: tripleo_container_stop
              vars:
                tripleo_containers_to_stop:
                  - nova_vnc_proxy
                tripleo_delegate_to: "{{ groups['nova_vnc_proxy'] | default([]) }}"