fix(neutron): run native netns cleanup instead
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-netns-cleanup-cron.sh.tpl b/charts/neutron/templates/bin/_neutron-netns-cleanup-cron.sh.tpl
new file mode 100644
index 0000000..4e881ea
--- /dev/null
+++ b/charts/neutron/templates/bin/_neutron-netns-cleanup-cron.sh.tpl
@@ -0,0 +1,25 @@
+#!/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 -xe
+
+# Run "neutron-netns-cleanup" every 5 minutes
+while sleep 300; do
+    neutron-netns-cleanup \
+        --config-file /etc/neutron/neutron.conf \
+        --config-file /etc/neutron/dhcp_agent.ini \
+        --config-file /etc/neutron/l3_agent.ini
+done
diff --git a/charts/neutron/templates/configmap-bin.yaml b/charts/neutron/templates/configmap-bin.yaml
index 5d87faa..c8e9750 100644
--- a/charts/neutron/templates/configmap-bin.yaml
+++ b/charts/neutron/templates/configmap-bin.yaml
@@ -89,8 +89,8 @@
 {{ tuple "bin/_neutron-server.sh.tpl" . | include "helm-toolkit.utils.template" | indent 4 }}
   neutron-ironic-agent.sh: |
 {{ tuple "bin/_neutron-ironic-agent.sh.tpl" . | include "helm-toolkit.utils.template" | indent 4 }}
-  neutron-netns-cleanup-cron.py: |
-{{ tuple "bin/_neutron-netns-cleanup-cron.py.tpl" . | include "helm-toolkit.utils.template" | indent 4 }}
+  neutron-netns-cleanup-cron.sh: |
+{{ tuple "bin/_neutron-netns-cleanup-cron.sh.tpl" . | include "helm-toolkit.utils.template" | indent 4 }}
   rabbit-init.sh: |
 {{- include "helm-toolkit.scripts.rabbit_init" . | indent 4 }}
   neutron-test-force-cleanup.sh: |
diff --git a/charts/neutron/templates/daemonset-netns-cleanup-cron.yaml b/charts/neutron/templates/daemonset-netns-cleanup-cron.yaml
index c512d17..4688cdf 100644
--- a/charts/neutron/templates/daemonset-netns-cleanup-cron.yaml
+++ b/charts/neutron/templates/daemonset-netns-cleanup-cron.yaml
@@ -68,14 +68,7 @@
 {{ tuple $envAll $envAll.Values.pod.resources.netns_cleanup_cron | include "helm-toolkit.snippets.kubernetes_resources" | indent 10 }}
 {{ dict "envAll" $envAll "application" "neutron_netns_cleanup_cron" "container" "neutron_netns_cleanup_cron" | include "helm-toolkit.snippets.kubernetes_container_security_context" | indent 10 }}
           command:
-            - python
-            - /tmp/neutron-netns-cleanup-cron.py
-            - --config-file
-            -  /etc/neutron/neutron.conf
-            - --config-file
-            - /etc/neutron/dhcp_agent.ini
-            - --config-file
-            - /etc/neutron/l3_agent.ini
+            - /tmp/neutron-netns-cleanup-cron.sh
           env:
 {{- with $env := dict "ksUserSecret" $envAll.Values.secrets.identity.admin "useCA" false }}
 {{- include "helm-toolkit.snippets.keystone_openrc_env_vars" $env | indent 12 }}
@@ -84,8 +77,8 @@
             - name: pod-tmp
               mountPath: /tmp
             - name: neutron-bin
-              mountPath: /tmp/neutron-netns-cleanup-cron.py
-              subPath: neutron-netns-cleanup-cron.py
+              mountPath: /tmp/neutron-netns-cleanup-cron.sh
+              subPath: neutron-netns-cleanup-cron.sh
               readOnly: true
             - name: neutron-etc
               mountPath: /etc/neutron/neutron.conf