chore(neutron): bump neutron version (#679)

diff --git a/charts/neutron/Chart.yaml b/charts/neutron/Chart.yaml
index b535e9f..587c56b 100644
--- a/charts/neutron/Chart.yaml
+++ b/charts/neutron/Chart.yaml
@@ -9,4 +9,4 @@
 sources:
 - https://opendev.org/openstack/neutron
 - https://opendev.org/openstack/openstack-helm
-version: 0.3.19
+version: 0.3.24
diff --git a/charts/neutron/charts/helm-toolkit/Chart.yaml b/charts/neutron/charts/helm-toolkit/Chart.yaml
index e6aec81..1ee9758 100644
--- a/charts/neutron/charts/helm-toolkit/Chart.yaml
+++ b/charts/neutron/charts/helm-toolkit/Chart.yaml
@@ -9,4 +9,4 @@
 sources:
 - https://opendev.org/openstack/openstack-helm-infra
 - https://opendev.org/openstack/openstack-helm
-version: 0.2.54
+version: 0.2.55
diff --git a/charts/neutron/charts/helm-toolkit/templates/manifests/_ingress.tpl b/charts/neutron/charts/helm-toolkit/templates/manifests/_ingress.tpl
index 4c476b2..972e429 100644
--- a/charts/neutron/charts/helm-toolkit/templates/manifests/_ingress.tpl
+++ b/charts/neutron/charts/helm-toolkit/templates/manifests/_ingress.tpl
@@ -67,10 +67,10 @@
       metadata:
         name: barbican
         annotations:
-          kubernetes.io/ingress.class: "nginx"
           nginx.ingress.kubernetes.io/rewrite-target: /
 
       spec:
+        ingressClassName: "nginx"
         rules:
           - host: barbican
             http:
@@ -108,10 +108,10 @@
       metadata:
         name: barbican-namespace-fqdn
         annotations:
-          kubernetes.io/ingress.class: "nginx"
           nginx.ingress.kubernetes.io/rewrite-target: /
 
       spec:
+        ingressClassName: "nginx"
         tls:
           - secretName: barbican-tls-public
             hosts:
@@ -133,10 +133,10 @@
       metadata:
         name: barbican-cluster-fqdn
         annotations:
-          kubernetes.io/ingress.class: "nginx-cluster"
           nginx.ingress.kubernetes.io/rewrite-target: /
 
       spec:
+        ingressClassName: "nginx-cluster"
         tls:
           - secretName: barbican-tls-public
             hosts:
@@ -202,10 +202,10 @@
       metadata:
         name: barbican
         annotations:
-          kubernetes.io/ingress.class: "nginx"
           nginx.ingress.kubernetes.io/rewrite-target: /
 
       spec:
+        ingressClassName: "nginx"
         tls:
           - secretName: barbican-tls-public
             hosts:
@@ -302,12 +302,12 @@
       metadata:
         name: barbican
         annotations:
-          kubernetes.io/ingress.class: "nginx"
           cert-manager.io/issuer: ca-issuer
           certmanager.k8s.io/issuer: ca-issuer
           nginx.ingress.kubernetes.io/backend-protocol: https
           nginx.ingress.kubernetes.io/secure-backends: "true"
       spec:
+        ingressClassName: "nginx"
         tls:
           - secretName: barbican-tls-public-certmanager
             hosts:
@@ -404,12 +404,12 @@
       metadata:
         name: barbican
         annotations:
-          kubernetes.io/ingress.class: "nginx"
           cert-manager.io/cluster-issuer: ca-issuer
           certmanager.k8s.io/cluster-issuer: ca-issuer
           nginx.ingress.kubernetes.io/backend-protocol: https
           nginx.ingress.kubernetes.io/secure-backends: "true"
       spec:
+        ingressClassName: "nginx"
         tls:
           - secretName: barbican-tls-public-certmanager
             hosts:
@@ -488,10 +488,10 @@
       metadata:
         name: grafana
         annotations:
-          kubernetes.io/ingress.class: "nginx"
           nginx.ingress.kubernetes.io/rewrite-target: /
 
       spec:
+        ingressClassName: "nginx"
         rules:
           - host: grafana
             http:
@@ -529,10 +529,10 @@
       metadata:
         name: grafana-namespace-fqdn
         annotations:
-          kubernetes.io/ingress.class: "nginx"
           nginx.ingress.kubernetes.io/rewrite-target: /
 
       spec:
+        ingressClassName: "nginx"
         tls:
           - secretName: grafana-tls-public
             hosts:
@@ -565,10 +565,10 @@
       metadata:
         name: grafana-cluster-fqdn
         annotations:
-          kubernetes.io/ingress.class: "nginx-cluster"
           nginx.ingress.kubernetes.io/rewrite-target: /
 
       spec:
+        ingressClassName: "nginx-cluster"
         tls:
           - secretName: grafana-tls-public
             hosts:
@@ -639,7 +639,6 @@
 metadata:
   name: {{ $ingressName }}
   annotations:
-    kubernetes.io/ingress.class: {{ index $envAll.Values.network $backendService "ingress" "classes" "namespace" | quote }}
 {{- if $certIssuer }}
     cert-manager.io/{{ $certIssuerType }}: {{ $certIssuer }}
     certmanager.k8s.io/{{ $certIssuerType }}: {{ $certIssuer }}
@@ -650,6 +649,7 @@
 {{- end }}
 {{ toYaml (index $envAll.Values.network $backendService "ingress" "annotations") | indent 4 }}
 spec:
+  ingressClassName: {{ index $envAll.Values.network $backendService "ingress" "classes" "namespace" | quote }}
 {{- $host := index $envAll.Values.endpoints ( $backendServiceType | replace "-" "_" ) "hosts" }}
 {{- if $certIssuer }}
 {{- $secretName := index $envAll.Values.secrets "tls" ( $backendServiceType | replace "-" "_" ) $backendService $endpoint }}
@@ -695,9 +695,9 @@
 metadata:
   name: {{ printf "%s-%s-%s" $ingressName $ingressController "fqdn" }}
   annotations:
-    kubernetes.io/ingress.class: {{ index $envAll.Values.network $backendService "ingress" "classes" $ingressController | quote }}
 {{ toYaml (index $envAll.Values.network $backendService "ingress" "annotations") | indent 4 }}
 spec:
+  ingressClassName: {{ index $envAll.Values.network $backendService "ingress" "classes" $ingressController | quote }}
 {{- $host := index $envAll.Values.endpoints ( $backendServiceType | replace "-" "_" ) "host_fqdn_override" }}
 {{- if hasKey $host $endpoint }}
 {{- $endpointHost := index $host $endpoint }}
diff --git a/charts/neutron/requirements.lock b/charts/neutron/requirements.lock
index 02a34df..1d7f269 100644
--- a/charts/neutron/requirements.lock
+++ b/charts/neutron/requirements.lock
@@ -1,6 +1,6 @@
 dependencies:
 - name: helm-toolkit
   repository: file://../../openstack-helm-infra/helm-toolkit
-  version: 0.2.54
-digest: sha256:337a0f1ffb3eae591150b305c22293d85fb8c18abec78f56672de4f3ada2faae
-generated: "2023-09-01T14:09:17.565455832Z"
+  version: 0.2.55
+digest: sha256:f25556fa4f0b285a96cbf853e72555c04e65772daf0ffa6b518321e5e249f5ca
+generated: "2023-10-27T21:53:49.227438894Z"
diff --git a/charts/neutron/templates/bin/_neutron-bgp-dragent.sh.tpl b/charts/neutron/templates/bin/_neutron-bgp-dragent.sh.tpl
new file mode 100644
index 0000000..c78bbc8
--- /dev/null
+++ b/charts/neutron/templates/bin/_neutron-bgp-dragent.sh.tpl
@@ -0,0 +1,21 @@
+#!/bin/bash
+
+{{/*
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+   http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/}}
+
+set -x
+exec neutron-bgp-dragent \
+      --config-file /etc/neutron/neutron.conf \
+      --config-file /etc/neutron/bgp_dragent.ini \
+      --debug
diff --git a/charts/neutron/templates/bin/_neutron-netns-cleanup-cron.py.tpl b/charts/neutron/templates/bin/_neutron-netns-cleanup-cron.py.tpl
deleted file mode 100644
index f4913ed..0000000
--- a/charts/neutron/templates/bin/_neutron-netns-cleanup-cron.py.tpl
+++ /dev/null
@@ -1,157 +0,0 @@
-#!/usr/bin/env python
-
-import sys
-import os
-import time
-import socket
-from neutron.common import config
-from oslo_config import cfg
-from oslo_concurrency import processutils
-from neutron.agent.linux import dhcp
-from neutron.agent.l3 import namespaces
-from neutron.agent.l3 import dvr_snat_ns
-from neutron.agent.l3 import dvr_fip_ns
-from neutron.cmd.netns_cleanup import setup_conf
-from neutron.cmd.netns_cleanup import unplug_device
-from neutron.cmd.netns_cleanup import eligible_for_deletion
-from neutron.conf.agent import common as agent_config
-from neutron.agent.linux import ip_lib
-from keystoneauth1.identity import v3
-from keystoneauth1 import session
-from neutronclient.neutron import client as neutron_client
-NS_PREFIXES = {'l3': [namespaces.NS_PREFIX, dvr_snat_ns.SNAT_NS_PREFIX,
-                      dvr_fip_ns.FIP_NS_PREFIX]}
-DHCP_NS_PREFIX = dhcp.NS_PREFIX
-
-def get_neutron_creds():
-    opts = {'auth_url': os.getenv('OS_AUTH_URL', 'https://keystone-api.openstack.svc.cluster.local:5000/v3'),
-        'password': os.getenv('OS_PASSWORD','nopassword'),
-        'project_domain_name': os.getenv('OS_PROJECT_DOMAIN_NAME', 'default'),
-        'project_name': os.getenv('OS_PROJECT_NAME', 'admin'),
-        'user_domain_name': os.getenv('OS_USER_DOMAIN_NAME', 'default'),
-        'username': os.getenv('OS_USERNAME', 'admin'),
-        'cafile' : os.getenv('OS_CACERT','/var/lib/neutron/openstack-helm/openstack-helm.crt'),
-        'insecure' : os.getenv('NEUTRON_CLEANUP_INSECURE', 'true'),
-        'debug': os.getenv('NEUTRON_CLEANUP_DEBUG', 'true'),
-        'wait': os.getenv('NEUTRON_CLEANUP_TIMEOUT', '600')}
-    return opts
-
-def ldestroy_namespace(conf, namespace):
-    try:
-        ip = ip_lib.IPWrapper(namespace=namespace)
-        if ip.netns.exists(namespace):
-            cmd = ['ip', 'netns', 'pids', namespace]
-            output = processutils.execute(*cmd, run_as_root=True, root_helper=conf.AGENT.root_helper)
-            for pid in output[0].splitlines():
-                utils.kill_process(pid, signal.SIGTERM, run_as_root=True, root_helper=conf.AGENT.root_helper)
-            for device in ip.get_devices():
-                unplug_device(device)
-        ip.garbage_collect_namespace()
-    except Exception as e:
-        sys.stderr.write("Error - unable to destroy namespace: {} : {}\n".format(namespace, e))
-
-def net_list(neutron_get):
-    hosts = dict()
-    net_list = neutron_get.list_networks()
-    if net_list['networks']:
-        for item in net_list['networks']:
-            net_id=item['id']
-            dhcp_agents = neutron_get.list_dhcp_agent_hosting_networks(net_id)['agents']
-            agents = list()
-            if dhcp_agents:
-                 for agent in dhcp_agents:
-                     agents.append(agent['host'].split('.')[0])
-            hosts[net_id] = agents
-    return hosts
-
-def sort_ns(all_ns, dhcp_prefix):
-    dhcp_ns = list()
-    not_dhcp_ns = list()
-    for ns in  all_ns:
-        if ns[:len(dhcp_prefix)] == dhcp_prefix:
-            dhcp_ns.append(ns)
-        else:
-            not_dhcp_ns.append(ns)
-    return dhcp_ns, not_dhcp_ns
-
-def del_bad_dhcp(dhcp_ns, dhcp_hosts, conf, dhcp_prefix, debug):
-    for ns in dhcp_ns:
-        cut_ns_name = ns[len(dhcp_prefix):]
-        if cut_ns_name in dhcp_hosts:
-            if hostname not in dhcp_hosts[cut_ns_name]:
-                ldestroy_namespace(conf, ns)
-                if debug:
-                    sys.stderr.write("DEBUG: {} host {} deleted {} because host wrong\n"
-                                     .format(sys.argv[0], hostname, ns))
-            else:
-                if debug:
-                    sys.stderr.write("DEBUG: {} host {} {} looks ok\n"
-                                     .format(sys.argv[0], hostname, ns))
-        else:
-            ldestroy_namespace(conf, ns)
-            if debug:
-                sys.stderr.write("DEBUG: {} host {} deleted {} because no related network found\n"
-                                 .format(sys.argv[0], hostname, ns))
-
-def del_bad_not_dhcp(not_dhcp_ns, conf, debug):
-    for ns in not_dhcp_ns:
-        if eligible_for_deletion(conf, ns, conf.force):
-            ldestroy_namespace(conf, ns)
-            if debug:
-                sys.stderr.write("DEBUG: {} host {} deleted {} because no IP addr\n"
-                                 .format(sys.argv[0], hostname, ns))
-
-if __name__ == "__main__":
-
-    conf = setup_conf()
-    cfg.CONF(sys.argv[1:])
-    opts = get_neutron_creds()
-    debug = False
-    verify= False
-    if opts.pop('debug') in ('true', '1', 'True'):
-        debug = True
-    insecure = opts.pop('insecure')
-    cafile = opts.pop('cafile')
-    if insecure in ('false', '0', 'False'):
-        verify = cafile
-    timeout = int(opts.pop('wait'))
-    conf()
-    config.setup_logging()
-    agent_config.setup_privsep()
-    auth = v3.Password(**opts)
-    hostname = socket.gethostname().split('.')[0]
-
-    while True:
-        try:
-            all_ns = ip_lib.list_network_namespaces()
-            sess = session.Session(auth=auth, verify=verify)
-            neutron_get = neutron_client.Client('2.0', session=sess)
-            dhcp_hosts = net_list(neutron_get)
-            if all_ns:
-                dhcp_ns, not_dhcp_ns = sort_ns(all_ns, DHCP_NS_PREFIX)
-                if dhcp_ns:
-                    del_bad_dhcp(dhcp_ns, dhcp_hosts, conf, DHCP_NS_PREFIX, debug)
-                else:
-                    if debug:
-                        sys.stderr.write("DEBUG: {} host {} no dhcp ns found\n"
-                                         .format(sys.argv[0], hostname))
-                if not_dhcp_ns:
-                    del_bad_not_dhcp(not_dhcp_ns, conf, debug)
-                else:
-                    if debug:
-                        sys.stderr.write("DEBUG: {} host {} no not_dhcp ns found\n"
-                                         .format(sys.argv[0], hostname))
-            else:
-                if debug:
-                    sys.stderr.write("DEBUG: {} host {} no ns found at all\n"
-                                     .format(sys.argv[0], hostname))
-        except Exception as ex:
-            sys.stderr.write(
-                "Cleaning network namespaces caught an exception %s"
-                % str(ex))
-            time.sleep(30)
-        except:
-            sys.stderr.write(
-                "Cleaning network namespaces caught an exception")
-            time.sleep(30)
-        time.sleep(timeout)
\ No newline at end of file
diff --git a/charts/neutron/templates/bin/_neutron-server.sh.tpl b/charts/neutron/templates/bin/_neutron-server.sh.tpl
index 0954ea4..8cbb688 100644
--- a/charts/neutron/templates/bin/_neutron-server.sh.tpl
+++ b/charts/neutron/templates/bin/_neutron-server.sh.tpl
@@ -23,13 +23,13 @@
 {{- if ( has "ovn" .Values.network.backend ) }}
         --config-file /tmp/pod-shared/ovn.ini \
 {{- end }}
-{{- if .Values.conf.plugins.taas.taas.enabled }} \
+{{- if .Values.conf.plugins.taas.taas.enabled }}
         --config-file /etc/neutron/taas_plugin.ini \
 {{- end }}
-{{- if ( has "sriov" .Values.network.backend ) }} \
+{{- if ( has "sriov" .Values.network.backend ) }}
         --config-file /etc/neutron/plugins/ml2/sriov_agent.ini \
 {{- end }}
-{{- if .Values.conf.plugins.l2gateway }} \
+{{- if .Values.conf.plugins.l2gateway }}
         --config-file /etc/neutron/l2gw_plugin.ini \
 {{- end }}
 {{- if ( has "tungstenfabric" .Values.network.backend ) }}
diff --git a/charts/neutron/templates/configmap-bin.yaml b/charts/neutron/templates/configmap-bin.yaml
index b509721..9a934e0 100644
--- a/charts/neutron/templates/configmap-bin.yaml
+++ b/charts/neutron/templates/configmap-bin.yaml
@@ -83,6 +83,8 @@
 {{ tuple "bin/_neutron-bagpipe-bgp.sh.tpl" . | include "helm-toolkit.utils.template" | indent 4 }}
   neutron-bagpipe-bgp-init.sh: |
 {{ tuple "bin/_neutron-bagpipe-bgp-init.sh.tpl" . | include "helm-toolkit.utils.template" | indent 4 }}
+  neutron-bgp-dragent.sh: |
+{{ tuple "bin/_neutron-bgp-dragent.sh.tpl" . | include "helm-toolkit.utils.template" | indent 4 }}
 {{- if .Values.manifests.certificates }}
   nginx.sh: |
 {{ tuple "bin/_nginx.sh.tpl" . | include "helm-toolkit.utils.template" | indent 4 }}
diff --git a/charts/neutron/templates/configmap-etc.yaml b/charts/neutron/templates/configmap-etc.yaml
index 0a3b718..8705836 100644
--- a/charts/neutron/templates/configmap-etc.yaml
+++ b/charts/neutron/templates/configmap-etc.yaml
@@ -308,6 +308,7 @@
   sriov_agent.ini: {{ include "helm-toolkit.utils.to_oslo_conf" $envAll.Values.conf.plugins.sriov_agent | b64enc }}
   l2gw_agent.ini: {{ default "\"\"" (include "helm-toolkit.utils.to_oslo_conf" .Values.conf.l2gateway_agent | b64enc) }}
   bagpipe_bgp.conf: {{ default "\"\"" (include "helm-toolkit.utils.to_oslo_conf" .Values.conf.bagpipe_bgp | b64enc) }}
+  bgp_dragent.ini: {{ default "\"\"" (include "helm-toolkit.utils.to_oslo_conf" .Values.conf.bgp_dragent | b64enc) }}
   tf_plugin.ini: {{ default "\"\"" (include "helm-toolkit.utils.to_oslo_conf" $envAll.Values.conf.plugins.tungstenfabric | b64enc) }}
   vnc_api_lib.ini: {{ default "\"\"" (include "helm-toolkit.utils.to_oslo_conf" $envAll.Values.conf.plugins.tf_vnc_api_lib | b64enc) }}
   dnsmasq.conf: "{{ $envAll.Values.conf.dnsmasq | b64enc }}"
diff --git a/charts/neutron/templates/daemonset-bgp-dragent.yaml b/charts/neutron/templates/daemonset-bgp-dragent.yaml
new file mode 100644
index 0000000..ca8a3d1
--- /dev/null
+++ b/charts/neutron/templates/daemonset-bgp-dragent.yaml
@@ -0,0 +1,167 @@
+{{/*
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+   http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/}}
+
+{{- define "bgp_dragentLivenessProbeTemplate" }}
+exec:
+  command:
+    - bash
+    - -c
+    - pidof -x /var/lib/openstack/bin/neutron-bgp-dragent
+{{- end }}
+
+
+{{- define "neutron.bgp_dragent.daemonset" }}
+{{- $daemonset := index . 0 }}
+{{- $configMapName := index . 1 }}
+{{- $serviceAccountName := index . 2 }}
+{{- $envAll := index . 3 }}
+{{- with $envAll }}
+
+{{- $mounts_neutron_bgp_dragent := .Values.pod.mounts.bgp_dragent.bgp_dragent }}
+{{- $mounts_neutron_bgp_dragent_init := .Values.pod.mounts.bgp_dragent.init_container }}
+
+---
+apiVersion: apps/v1
+kind: DaemonSet
+metadata:
+  name: bgp-dragent
+  annotations:
+    {{ tuple $envAll | include "helm-toolkit.snippets.release_uuid" }}
+  labels:
+{{ tuple $envAll "neutron" "bgp-dragent" | include "helm-toolkit.snippets.kubernetes_metadata_labels" | indent 4 }}
+spec:
+  selector:
+    matchLabels:
+{{ tuple $envAll "neutron" "bgp-dragent" | include "helm-toolkit.snippets.kubernetes_metadata_labels" | indent 6 }}
+{{ tuple $envAll "bgp_dragent" | include "helm-toolkit.snippets.kubernetes_upgrades_daemonset" | indent 2 }}
+  template:
+    metadata:
+      labels:
+{{ tuple $envAll "neutron" "bgp-dragent" | include "helm-toolkit.snippets.kubernetes_metadata_labels" | indent 8 }}
+      annotations:
+{{ tuple $envAll | include "helm-toolkit.snippets.release_uuid" | indent 8 }}
+        configmap-bin-hash: {{ tuple "configmap-bin.yaml" . | include "helm-toolkit.utils.hash" }}
+        configmap-etc-hash: {{ tuple "configmap-etc.yaml" . | include "helm-toolkit.utils.hash" }}
+    spec:
+{{ dict "envAll" $envAll "application" "neutron_bgp_dragent" | include "helm-toolkit.snippets.kubernetes_pod_security_context" | indent 6 }}
+      serviceAccountName: {{ $serviceAccountName }}
+{{ if $envAll.Values.pod.tolerations.neutron.enabled }}
+{{ tuple $envAll "neutron" | include "helm-toolkit.snippets.kubernetes_tolerations" | indent 6 }}
+{{ end }}
+      nodeSelector:
+        {{ .Values.labels.bgp_dragent.node_selector_key }}: {{ .Values.labels.bgp_dragent.node_selector_value }}
+      dnsPolicy: ClusterFirstWithHostNet
+      hostNetwork: true
+      {{- if or ( gt .Capabilities.KubeVersion.Major "1" ) ( ge .Capabilities.KubeVersion.Minor "10" ) }}
+      shareProcessNamespace: true
+      {{- else }}
+      hostPID: true
+      {{- end }}
+      containers:
+        - name: neutron-bgp-dragent
+{{ tuple $envAll "neutron_bgp_dragent" | include "helm-toolkit.snippets.image" | indent 10 }}
+{{ tuple $envAll $envAll.Values.pod.resources.agent.bgp_dragent | include "helm-toolkit.snippets.kubernetes_resources" | indent 10 }}
+{{ dict "envAll" $envAll "application" "neutron_bgp_dragent" "container" "neutron_bgp_dragent" | include "helm-toolkit.snippets.kubernetes_container_security_context" | indent 10 }}
+{{ dict "envAll" $envAll "component" "bgp_dragent" "container" "bgp_dragent" "type" "liveness" "probeTemplate" (include "bgp_dragentLivenessProbeTemplate" $envAll | fromYaml) | include "helm-toolkit.snippets.kubernetes_probe" | indent 10 }}
+          command:
+            - /tmp/neutron-bgp-dragent.sh
+          volumeMounts:
+            - name: pod-tmp
+              mountPath: /tmp
+            - name: pod-var-neutron
+              mountPath: {{ .Values.conf.neutron.DEFAULT.state_path }}
+            - name: neutron-bin
+              mountPath: /tmp/neutron-bgp-dragent.sh
+              subPath: neutron-bgp-dragent.sh
+              readOnly: true
+            - name: neutron-bin
+              mountPath: /tmp/health-probe.py
+              subPath: health-probe.py
+              readOnly: true
+            - name: neutron-etc
+              mountPath: /etc/neutron/neutron.conf
+              subPath: neutron.conf
+              readOnly: true
+            {{- if .Values.conf.neutron.DEFAULT.log_config_append }}
+            - name: neutron-etc
+              mountPath: {{ .Values.conf.neutron.DEFAULT.log_config_append }}
+              subPath: {{ base .Values.conf.neutron.DEFAULT.log_config_append }}
+              readOnly: true
+            {{- end }}
+            - name: neutron-etc
+              mountPath: /etc/neutron/bgp_dragent.ini
+              subPath: bgp_dragent.ini
+              readOnly: true
+            - name: neutron-etc
+              # NOTE (Portdirect): We mount here to override Kollas
+              # custom sudoers file when using Kolla images, this
+              # location will also work fine for other images.
+              mountPath: /etc/sudoers.d/kolla_neutron_sudoers
+              subPath: neutron_sudoers
+              readOnly: true
+            - name: neutron-etc
+              mountPath: /etc/neutron/rootwrap.conf
+              subPath: rootwrap.conf
+              readOnly: true
+            {{- range $key, $value := $envAll.Values.conf.rootwrap_filters }}
+            {{- if ( has "bgp_dragent" $value.pods ) }}
+            {{- $filePrefix := replace "_" "-"  $key }}
+            {{- $rootwrapFile := printf "/etc/neutron/rootwrap.d/%s.filters" $filePrefix }}
+            - name: neutron-etc
+              mountPath: {{ $rootwrapFile }}
+              subPath: {{ base $rootwrapFile }}
+              readOnly: true
+            {{- end }}
+            {{- end }}
+            - name: libmodules
+              mountPath: /lib/modules
+              readOnly: true
+            - name: run
+              mountPath: /run
+{{ if $mounts_neutron_bgp_dragent.volumeMounts }}{{ toYaml $mounts_neutron_bgp_dragent.volumeMounts | indent 12 }}{{ end }}
+      volumes:
+        - name: pod-tmp
+          emptyDir: {}
+        - name: pod-var-neutron
+          emptyDir: {}
+        - name: neutron-bin
+          configMap:
+            name: neutron-bin
+            defaultMode: 0555
+        - name: neutron-etc
+          secret:
+            secretName: {{ $configMapName }}
+            defaultMode: 0444
+        - name: libmodules
+          hostPath:
+            path: /lib/modules
+        - name: run
+          hostPath:
+            path: /run
+{{ if $mounts_neutron_bgp_dragent.volumes }}{{ toYaml $mounts_neutron_bgp_dragent.volumes | indent 8 }}{{ end }}
+{{- end }}
+{{- end }}
+
+{{- if .Values.manifests.daemonset_bgp_dragent }}
+{{- $envAll := . }}
+{{- $daemonset := "bgp-dragent" }}
+{{- $configMapName := "neutron-etc" }}
+{{- $serviceAccountName := "neutron-bgp-dragent" }}
+{{- $dependencyOpts := dict "envAll" $envAll "dependencyMixinParam" $envAll.Values.network.backend "dependencyKey" "bgp_dragent" -}}
+{{- $_ := include "helm-toolkit.utils.dependency_resolver" $dependencyOpts | toString | fromYaml }}
+{{ tuple $envAll "pod_dependency" $serviceAccountName | include "helm-toolkit.snippets.kubernetes_pod_rbac_serviceaccount" }}
+{{- $daemonset_yaml := list $daemonset $configMapName $serviceAccountName . | include "neutron.bgp_dragent.daemonset" | toString | fromYaml }}
+{{- $configmap_yaml := "neutron.configmap.etc" }}
+{{- list $daemonset $daemonset_yaml $configmap_yaml $configMapName . | include "helm-toolkit.utils.daemonset_overrides" }}
+{{- end }}
diff --git a/charts/neutron/values.yaml b/charts/neutron/values.yaml
index ba6d51f..747baa5 100644
--- a/charts/neutron/values.yaml
+++ b/charts/neutron/values.yaml
@@ -42,6 +42,7 @@
     neutron_sriov_agent: docker.io/openstackhelm/neutron:stein-18.04-sriov
     neutron_sriov_agent_init: docker.io/openstackhelm/neutron:stein-18.04-sriov
     neutron_bagpipe_bgp: docker.io/openstackhelm/neutron:stein-ubuntu_bionic
+    neutron_bgp_dragent: docker.io/openstackhelm/neutron:stein-ubuntu_bionic
     neutron_ironic_agent: docker.io/openstackhelm/neutron:stein-ubuntu_bionic
     neutron_netns_cleanup_cron: docker.io/openstackhelm/neutron:stein-ubuntu_bionic
     dep_check: quay.io/airshipit/kubernetes-entrypoint:v1.0.0
@@ -86,6 +87,9 @@
   bagpipe_bgp:
     node_selector_key: openstack-compute-node
     node_selector_value: enabled
+  bgp_dragent:
+    node_selector_key: openstack-compute-node
+    node_selector_value: enabled
   server:
     node_selector_key: openstack-control-plane
     node_selector_value: enabled
@@ -161,6 +165,7 @@
       ovn:
         server:
           pod: null
+      bgp_dragent: {}
       openvswitch:
         dhcp:
           pod:
@@ -444,6 +449,15 @@
           enabled: true
           params:
             initialDelaySeconds: 60
+    bgp_dragent:
+      bgp_dragent:
+        readiness:
+          enabled: false
+          params:
+        liveness:
+          enabled: true
+          params:
+            initialDelaySeconds: 60
     l2gw_agent:
       l2gw_agent:
         readiness:
@@ -493,6 +507,13 @@
         neutron_bagpipe_bgp:
           readOnlyRootFilesystem: true
           privileged: true
+    neutron_bgp_dragent:
+      pod:
+        runAsUser: 42424
+      container:
+        neutron_bgp_dragent:
+          readOnlyRootFilesystem: true
+          privileged: true
     neutron_l3_agent:
       pod:
         runAsUser: 42424
@@ -658,6 +679,11 @@
       bagpipe_bgp:
         volumeMounts:
         volumes:
+    bgp_dragent:
+      init_container: null
+      bgp_dragent:
+        volumeMounts:
+        volumes:
     neutron_ironic_agent:
       init_container: null
       neutron_ironic_agent:
@@ -805,6 +831,13 @@
         limits:
           memory: "1024Mi"
           cpu: "2000m"
+      bgp_dragent:
+        requests:
+          memory: "128Mi"
+          cpu: "100m"
+        limits:
+          memory: "1024Mi"
+          cpu: "2000m"
     server:
       requests:
         memory: "128Mi"
@@ -2003,6 +2036,7 @@
       backend: dogpile.cache.memcached
     ovs:
       ovsdb_connection: unix:/run/openvswitch/db.sock
+  bgp_dragent: {}
 
   rabbitmq:
     # NOTE(rk760n): adding rmq policy to mirror messages from notification queues and set expiration time for the ones
@@ -2484,6 +2518,7 @@
   daemonset_sriov_agent: true
   daemonset_l2gw_agent: false
   daemonset_bagpipe_bgp: false
+  daemonset_bgp_dragent: false
   daemonset_netns_cleanup_cron: true
   deployment_ironic_agent: false
   deployment_server: true
diff --git a/hack/sync-charts.sh b/hack/sync-charts.sh
index 16290c5..d97e6df 100755
--- a/hack/sync-charts.sh
+++ b/hack/sync-charts.sh
@@ -130,10 +130,22 @@
   | filterdiff -p1 -i 'ovn/*' \
   | patch -p2 -d ${ATMOSPHERE}/charts/ovn
 
-NEUTRON_VERSION=0.3.19
+NEUTRON_VERSION=0.3.24
 curl -sL https://tarballs.opendev.org/openstack/openstack-helm/neutron-${NEUTRON_VERSION}.tgz \
   | tar -xz -C ${ATMOSPHERE}/charts
-curl 'https://review.opendev.org/changes/openstack%2Fopenstack-helm~893733/revisions/2/patch?download' \
+curl 'https://review.opendev.org/changes/openstack%2Fopenstack-helm~899711/revisions/2/patch?download' \
+  | base64 --decode \
+  | filterdiff -p1 -x 'releasenotes/*' \
+  | filterdiff -p2 -x 'Chart.yaml' \
+  | filterdiff -p1 -i 'neutron/*' \
+  | patch -p2 -d ${ATMOSPHERE}/charts/neutron
+curl 'https://review.opendev.org/changes/openstack%2Fopenstack-helm~899684/revisions/4/patch?download' \
+  | base64 --decode \
+  | filterdiff -p1 -x 'releasenotes/*' \
+  | filterdiff -p2 -x 'Chart.yaml' \
+  | filterdiff -p1 -i 'neutron/*' \
+  | patch -p2 -d ${ATMOSPHERE}/charts/neutron
+curl 'https://review.opendev.org/changes/openstack%2Fopenstack-helm~899716/revisions/1/patch?download' \
   | base64 --decode \
   | filterdiff -p1 -x 'releasenotes/*' \
   | filterdiff -p2 -x 'Chart.yaml' \
diff --git a/roles/defaults/vars/main.yml b/roles/defaults/vars/main.yml
index c650ad0..e5abcab 100644
--- a/roles/defaults/vars/main.yml
+++ b/roles/defaults/vars/main.yml
@@ -113,6 +113,7 @@
   memcached: docker.io/library/memcached:1.6.17
   netoffload: ghcr.io/vexxhost/netoffload:v1.0.1
   neutron_bagpipe_bgp: quay.io/vexxhost/neutron@sha256:6309ca1db220338e3ea83cf637ee4c333c897a313435ef97968f75f5bbbab87d # image-source: quay.io/vexxhost/neutron:zed
+  neutron_bgp_dragent: quay.io/vexxhost/neutron@sha256:6309ca1db220338e3ea83cf637ee4c333c897a313435ef97968f75f5bbbab87d # image-source: quay.io/vexxhost/neutron:zed
   neutron_coredns: docker.io/coredns/coredns:1.9.3
   neutron_db_sync: quay.io/vexxhost/neutron@sha256:6309ca1db220338e3ea83cf637ee4c333c897a313435ef97968f75f5bbbab87d # image-source: quay.io/vexxhost/neutron:zed
   neutron_dhcp: quay.io/vexxhost/neutron@sha256:6309ca1db220338e3ea83cf637ee4c333c897a313435ef97968f75f5bbbab87d # image-source: quay.io/vexxhost/neutron:zed