Repository: sonic-net/sonic-mgmt

Issue:
Docker shell module failed in the latest sonic-mgmt for some tasks
<!--
If you are reporting a new issue, make sure that we do not have any duplicates
already open. You can ensure this by searching the issue list for this
repository. If there is a duplicate, please close your issue and add a comment
to the existing issue instead.

If you suspect your issue is a bug, please edit your issue description to
include the BUG REPORT INFORMATION shown below. If you fail to provide this
information within 7 days, we cannot debug your issue and will close it. We
will, however, reopen it if you later provide the information.

For more information about reporting issues, see
https://github.com/Azure/SONiC/wiki#report-issues

---------------------------------------------------
GENERAL SUPPORT INFORMATION
---------------------------------------------------

The GitHub issue tracker is for bug reports and feature requests.
General support can be found at the following locations:

- SONiC Support Forums - https://groups.google.com/forum/#!forum/sonicproject

---------------------------------------------------
BUG REPORT INFORMATION
---------------------------------------------------
Use the commands below to provide key information from your environment:
You do NOT have to include this information if this is a FEATURE REQUEST
-->

**Description**
According to https://github.com/Azure/sonic-mgmt/issues/1243, the fix https://github.com/Azure/sonic-mgmt/pull/1253 was merged.
But this fix turned out to be incomplete and cause an error similar to previous when running some tasks with docker shell e.g. copy, template.

Here are the logs:
```
The full traceback is:
Traceback (most recent call last):
  File "/usr/local/lib/python2.7/dist-packages/ansible/executor/task_executor.py", line 111, in run
    item_results = self._run_loop(items)
  File "/usr/local/lib/python2.7/dist-packages/ansible/executor/task_executor.py", line 371, in _run_loop
    res = self._execute(variables=task_vars)
  File "/usr/local/lib/python2.7/dist-packages/ansible/executor/task_executor.py", line 664, in _execute
    result = self._handler.run(task_vars=variables)
  File "/usr/local/lib/python2.7/dist-packages/ansible/plugins/action/copy.py", line 590, in run
    self._remove_tmp_path(self._connection._shell.tmpdir)
  File "/usr/local/lib/python2.7/dist-packages/ansible/plugins/action/__init__.py", line 398, in _remove_tmp_path
    cmd = self._connection._shell.remove(tmp_path, recurse=True)
  File "/var/user/jenkins/bfn-sonic-mgmt/ansible/shell_plugins/docker.py", line 44, in remove
    argv = self.get_option('ansible_python_interpreter').split()
  File "/usr/local/lib/python2.7/dist-packages/ansible/plugins/__init__.py", line 60, in get_option
    raise KeyError(to_native(e))
KeyError: 'Requested entry (plugin_type: shell plugin: docker setting: ansible_python_interpreter ) was not defined in configuration.'

fatal: [cab18-2-dut]: FAILED! => {
    "msg": "Unexpected failure during module execution.", 
    "stdout": ""
}
```

<!--
Briefly describe the problem you are having in a few paragraphs.
-->

**Steps to reproduce the issue:**
1. run ecn_wred CT

**Describe the results you received:**


**Describe the results you expected:**


**Additional information you deem important:**
<!--
software/ASIC/Hardware Flatform version and info
-->
    **Output of `show version`:**

    ```
    (paste your output here)
    ```

    **Attach debug file `sudo generate_dump`:


    ```
    (paste your output here)
    ```



Discussion:

Resolving patch:
diff --git a/ansible/shell_plugins/docker.py b/ansible/shell_plugins/docker.py
deleted file mode 100644
--- a/ansible/shell_plugins/docker.py
+++ /dev/null
@@ -1,116 +0,0 @@
-from __future__ import (absolute_import, division)
-__metaclass__ = type
-
-DOCUMENTATION = '''
-name: docker
-plugin_type: shell
-short_description: "docker shell plugin"
-version_added: historical
-description:
-  - This module allows you to execute commands directly in docker on the remote host
-extends_documentation_fragment:
-  - shell_common
-'''
-
-import os
-import re
-import pipes
-import ansible.constants as C
-import time
-import random
-import shlex
-import getopt
-from ansible.module_utils.six import text_type
-from ansible.plugins.shell.sh import ShellModule as sh
-from ansible.errors import AnsibleError, AnsibleConnectionFailure, AnsibleFileNotFound
-
-class ShellModule(sh):
-
-    def __init__(self, *args, **kwargs):
-        super(ShellModule, self).__init__(*args, **kwargs)
-        self.dtemps = []
-
-    def join_path(self, *args):
-        ## HACK! HACK! HACK!
-        ## We observe the interactions between ShellModule and ActionModule, and
-        ## find the temporary directories Ansible created on remote machine. So we
-        ## collect them and copied to docker container in build_module_command
-        if len(args) >= 2 and (args[0].startswith('/home/') or args[0].startswith('/root/'))  and args[1] == '':
-            self.dtemps.append(args[0])
-
-        return super(ShellModule, self).join_path(*args)
-
-    def remove(self, path, recurse=False):
-        argv = self.get_option('ansible_python_interpreter').split()
-        assert(argv[0] == 'docker')
-        assert(argv[1] == 'exec')
-        opts, args = getopt.getopt(argv[2:], 'i')
-        self.container_name = args[0]
-
-        remove_files_on_host_cmd = super(ShellModule, self).remove(path, recurse)
-
-        cmd = remove_files_on_host_cmd + "; docker exec -i "
-        cmd += self.container_name + " rm -f "
-        if recurse:
-            cmd += '-r '
-        cmd += " ".join(self.dtemps)
-
-        return cmd
-
-    def build_module_command(self, env_string, shebang, cmd, arg_path=None):
-        # assert(self.container_name)
-        argv = shlex.split(shebang.replace("#!", ""))
-        assert(argv[0] == 'docker')
-        assert(argv[1] == 'exec')
-        opts, args = getopt.getopt(argv[2:], 'i')
-        self.container_name = args[0]
-
-        # Inject environment variable before python in the shebang string
-        assert(args[1].endswith('python'))
-        args[1] = 'env {0} {1}'.format(env_string, args[1])
-        argv_env = argv[0:2] + [o for opt in opts for o in opt] + args
-        shebang_env = ' '.join(argv_env)
-
-        ## Note: Docker cp behavior
-        ##   DEST_PATH exists and is a directory
-        ##   SRC_PATH does end with /.
-        ##     the content of the source directory is copied into this directory
-        ## Ref: https://docs.docker.com/engine/reference/commandline/cp/
-        pre = ''.join('docker exec {1} mkdir -p {0}; docker cp {0}/. {1}:{0}; '
-            .format(dtemp, self.container_name) for dtemp in self.dtemps)
-
-        return pre + super(ShellModule, self).build_module_command('', shebang_env, cmd, arg_path)
-
-    def checksum(self, path, python_interp):
-        """
-        Return the command to calculate the checksum for the file in ansible controlled machine
-        Arguments:
-          path:
-            the file path
-          python_interp:
-            the path for the python interpreter
-        Example:
-          path:
-            /zebra.conf
-          python_interp:
-            docker exec -i debian python
-          cmd:
-            rc=flag; [ -r /zebra.conf ] || rc=2; [ -f /zebra.conf ] || rc=1; [ -d /zebra.conf ] && rc=3; python -V 2>/dev/null || rc=4; [ x"$rc" != "xflag" ] && echo "${rc}  "/zebra.conf && exit 0; (python -c '...' 2>/dev/null)  || (echo '0  '/zebra.conf)
-          returns:
-            docker exec -i debian  sh -c "rc=flag; [ -r /zebra.conf ] || rc=2; [ -f /zebra.conf ] || rc=1; [ -d /zebra.conf ] && rc=3; python -V 2>/dev/null || rc=4; [ x\"\$rc\" != \"xflag\" ] && echo \"\${rc}  \"/zebra.conf && exit 0; (python -c '...' 2>/dev/null) || (echo '0  '/zebra.conf)"
-        """
-        ## Super class implements this function by sh commands and python scripts
-        ## If python_interp is modified to 'docker CONTAINER python', it will only influence the python
-        ## script part in super class. Instead we should influence both
-        simple_interp = 'python'
-        assert(python_interp.startswith('docker exec '))
-        assert(python_interp.endswith(' ' + simple_interp))
-
-        docker_prefix = re.sub(simple_interp, '', python_interp)
-        cmd = super(ShellModule, self).checksum(path, simple_interp)
-        ## Escape the cmd:
-        ##   " --> \"
-        cmd_escaped = cmd.replace('"', '\\"')
-        ##   $ --> \$
-        cmd_escaped = cmd_escaped.replace('$', '\\$')
-        return '%s sh -c "%s"' % (docker_prefix, cmd_escaped)


Test patch:
diff --git a/ansible/roles/test/tasks/acltb_ranges_test.yml b/ansible/roles/test/tasks/acltb_ranges_test.yml
--- a/ansible/roles/test/tasks/acltb_ranges_test.yml
+++ b/ansible/roles/test/tasks/acltb_ranges_test.yml
@@ -52,6 +52,11 @@
 # Outer block to execute laganalizer in always block
 - block:
   # Perform the test: generate and apply acl jsons
+
+  - include_tasks: add_container_to_inventory.yml
+    vars:
+      container_name: swss
+
   - block:
     - name: Copy JSON configs into docker filesystem
       template: src={{ item }}.j2 dest=/tmp/{{ item }}.json
@@ -77,9 +82,7 @@
       with_items:
         - "{{ acltb_configs[::-1] }}"
 
-    vars:
-      ansible_shell_type: docker
-      ansible_python_interpreter: docker exec -i swss python
+    delegate_to: "{{ ansible_host }}_swss"
 
   always:
     - include_tasks: roles/test/files/tools/loganalyzer/loganalyzer_analyze.yml
diff --git a/ansible/roles/test/tasks/add_container_to_inventory.yml b/ansible/roles/test/tasks/add_container_to_inventory.yml
new file mode 100644
--- /dev/null
+++ b/ansible/roles/test/tasks/add_container_to_inventory.yml
@@ -0,0 +1,26 @@
+- name: Add/update the public key in the "{{ lookup('env','HOME') + '/.ssh/known_hosts' }}"
+  delegate_to: localhost
+  known_hosts:
+    name: "{{ ansible_host }}"
+    key: "{{ lookup('pipe', 'ssh-keyscan ' + ansible_host) }}"
+    state: "present"
+
+- name: Generate an OpenSSH keypair
+  delegate_to: localhost
+  openssh_keypair:
+    path: "{{ lookup('env','HOME') + '/.ssh/id_rsa' }}"
+  register: out
+
+- name: Set authorized key
+  authorized_key:
+    user: "{{ ansible_ssh_user }}"
+    state: present
+    key: "{{ out.public_key }}"
+
+- name: Add {{ container_name }} container to inventory
+  add_host:
+    name: "{{ ansible_host }}_{{ container_name }}"
+    ansible_connection: docker
+    ansible_host: "{{ container_name }}"
+    ansible_docker_extra_args: "-H=ssh://{{ ansible_ssh_user }}@{{ ansible_host }}"
+    ansible_user: root
diff --git a/ansible/roles/test/tasks/bgp_entry_flap.yml b/ansible/roles/test/tasks/bgp_entry_flap.yml
--- a/ansible/roles/test/tasks/bgp_entry_flap.yml
+++ b/ansible/roles/test/tasks/bgp_entry_flap.yml
@@ -10,12 +10,14 @@
     hwsku: "{{ acs_devices[name]['hwsku'] }}"
     cred: "{{ switch_login[acs_devices[name]['hwsku']] }}"
 
+- include_tasks: add_container_to_inventory.yml
+  vars:
+    container_name: sswsyncd
+
 - name: Get ASIC tables
   switch_tables: asic="{{asic}}" nexthop=yes nexthopgroup=yes
   become: yes
-  vars:
-    ansible_shell_type: docker
-    ansible_python_interpreter: docker exec -i sswsyncd python
+  delegate_to: "{{ ansible_host }}_sswsyncd"
 
 - block:
   - name: Assert the particular entry is in nexthopgroup table
@@ -44,9 +46,7 @@
   - name: Update list of current nexthop group(s)
     switch_tables: asic="{{asic}}" nexthop=yes nexthopgroup=yes
     become: yes
-    vars:
-      ansible_shell_type: docker
-      ansible_python_interpreter: docker exec -i sswsyncd python
+    delegate_to: "{{ ansible_host }}_sswsyncd"
 
   - name: Poll for updated tables until peer is not in nexthop groups
     switch_tables: asic="{{asic}}" nexthop=yes nexthopgroup=yes
@@ -56,9 +56,7 @@
     retries: 6
     delay: 10
     with_items: "{{ nexthopgroup }}"
-    vars:
-      ansible_shell_type: docker
-      ansible_python_interpreter: docker exec -i sswsyncd python
+    delegate_to: "{{ ansible_host }}_sswsyncd"
 
   - name: Restart BGP session from neighbor
     action: cisco template=bgp_neighbor_noshut.j2
@@ -73,9 +71,7 @@
   - name: Update list of current nexthop group(s)
     switch_tables: asic="{{asic}}" nexthop=yes nexthopgroup=yes
     become: yes
-    vars:
-      ansible_shell_type: docker
-      ansible_python_interpreter: docker exec -i sswsyncd python
+    delegate_to: "{{ ansible_host }}_sswsyncd"
 
   - name: Poll for updated tables until peer is in nexthop groups
     switch_tables: asic="{{asic}}" nexthop=yes nexthopgroup=yes
@@ -85,8 +81,6 @@
     retries: 6
     delay: 10
     with_items: "{{ nexthopgroup }}"
-    vars:
-      ansible_shell_type: docker
-      ansible_python_interpreter: docker exec -i sswsyncd python
+    delegate_to: "{{ ansible_host }}_sswsyncd"
 
   when: "'T2' in name"
diff --git a/ansible/roles/test/tasks/bgp_gr_helper/get_vm_info.yml b/ansible/roles/test/tasks/bgp_gr_helper/get_vm_info.yml
--- a/ansible/roles/test/tasks/bgp_gr_helper/get_vm_info.yml
+++ b/ansible/roles/test/tasks/bgp_gr_helper/get_vm_info.yml
@@ -30,11 +30,13 @@
   with_items: "{{ minigraph_bgp }}"
   when: "item.name == vm_name and item.addr|ipv6"
 
+- include_tasks: add_container_to_inventory.yml
+  vars:
+    container_name: lldp
+
 - name: Gather information from LLDP
   lldp:
-  vars:
-    ansible_shell_type: docker
-    ansible_python_interpreter: docker exec -i lldp python
+  delegate_to: "{{ ansible_host }}_lldp"
 
 - name: Get VM IP address.
   set_fact:
diff --git a/ansible/roles/test/tasks/copp.yml b/ansible/roles/test/tasks/copp.yml
--- a/ansible/roles/test/tasks/copp.yml
+++ b/ansible/roles/test/tasks/copp.yml
@@ -1,3 +1,10 @@
+- include_tasks: add_container_to_inventory.yml
+  vars:
+    container_name: "{{ item }}"
+  with_items:
+    - "lldp"
+    - "syncd"
+
 - block:
     - fail: msg="Please set ptf_host variable"
       when: ptf_host is not defined
@@ -5,9 +12,7 @@
     - name: Ensure LLDP Daemon stopped
       become: yes
       supervisorctl: state=stopped name={{ item }}
-      vars:
-        ansible_shell_type: docker
-        ansible_python_interpreter: docker exec -i lldp python
+      delegate_to: "{{ ansible_host }}_lldp"
       with_items:
         - lldp-syncd
         - lldpd
@@ -38,15 +43,11 @@
 
     - name: Update ptf_nn_agent configuration inside dut
       template: src=ptf_nn_agent.conf.dut.j2 dest=/etc/supervisor/conf.d/ptf_nn_agent.conf
-      vars:
-        ansible_shell_type: docker
-        ansible_python_interpreter: docker exec -i syncd python
+      delegate_to: "{{ ansible_host }}_syncd"
 
     - name: Restart ptf_nn_agent inside dut
       supervisorctl: state=restarted name=ptf_nn_agent
-      vars:
-        ansible_shell_type: docker
-        ansible_python_interpreter: docker exec -i syncd python
+      delegate_to: "{{ ansible_host }}_syncd"
 
     - name: copy the test to ptf container
       copy: src=roles/test/files/ptftests dest=/root
@@ -96,22 +97,16 @@
 
     - name: Update ptf_nn_agent configuration inside dut
       template: src=ptf_nn_agent.conf.dut.j2 dest=/etc/supervisor/conf.d/ptf_nn_agent.conf
-      vars:
-        ansible_shell_type: docker
-        ansible_python_interpreter: docker exec -i syncd python
+      delegate_to: "{{ ansible_host }}_syncd"
 
     - name: Restart ptf_nn_agent inside dut
       supervisorctl: state=restarted name=ptf_nn_agent
-      vars:
-        ansible_shell_type: docker
-        ansible_python_interpreter: docker exec -i syncd python
+      delegate_to: "{{ ansible_host }}_syncd"
 
     - name: Restore LLDP Daemon
       become: yes
       supervisorctl: state=started name={{ item }}
-      vars:
-        ansible_shell_type: docker
-        ansible_python_interpreter: docker exec -i lldp python
+      delegate_to: "{{ ansible_host }}_lldp"
       with_items:
         - lldpd
         - lldp-syncd
diff --git a/ansible/roles/test/tasks/dip_sip.yml b/ansible/roles/test/tasks/dip_sip.yml
--- a/ansible/roles/test/tasks/dip_sip.yml
+++ b/ansible/roles/test/tasks/dip_sip.yml
@@ -20,11 +20,13 @@
   script: roles/test/files/helpers/change_mac.sh
   delegate_to: "{{ ptf_host }}"
 
+- include_tasks: add_container_to_inventory.yml
+  vars:
+    container_name: lldp
+
 - name: "Gather information from LLDP"
   lldp:
-  vars:
-    ansible_shell_type: docker
-    ansible_python_interpreter: docker exec -i lldp python
+  delegate_to: "{{ ansible_host }}_lldp"
 
 - name: "Copy tests to PTF"
   copy: src=roles/test/files/ptftests dest=/root
diff --git a/ansible/roles/test/tasks/ecn_wred.yml b/ansible/roles/test/tasks/ecn_wred.yml
--- a/ansible/roles/test/tasks/ecn_wred.yml
+++ b/ansible/roles/test/tasks/ecn_wred.yml
@@ -20,14 +20,15 @@
 - set_fact:
     red_min_threshold={{ wred_value.stdout }}
 
-- block:
-    - name: Copy test files to DUT
-      copy: src={{ item }} dest={{ tmp_dir }}
-      with_fileglob:
-         - "{{ test_files_dir }}/*"
+- include_tasks: add_container_to_inventory.yml
   vars:
-    ansible_shell_type: docker
-    ansible_python_interpreter: docker exec -i database python
+    container_name: database
+
+- name: Copy test files to DUT database
+  copy: src={{ item }} dest={{ tmp_dir }}
+  delegate_to: "{{ ansible_host }}_database"
+  with_fileglob:
+    - "{{ test_files_dir }}/*"
 
 - block:
     # Test case #3(MA): Check configuration applied
@@ -45,4 +46,4 @@
       shell: ecnconfig -p AZURE_LOSSLESS -rmin {{ red_min_threshold }}
       become: yes
       register: ecn_restore
-      failed_when: ecn_restore.rc != 0
\ No newline at end of file
+      failed_when: ecn_restore.rc != 0
diff --git a/ansible/roles/test/tasks/interface_up_down.yml b/ansible/roles/test/tasks/interface_up_down.yml
--- a/ansible/roles/test/tasks/interface_up_down.yml
+++ b/ansible/roles/test/tasks/interface_up_down.yml
@@ -1,5 +1,9 @@
 # This playbook tests neighbor interface flap and ACS interface status work properly
 #
+- include_tasks: add_container_to_inventory.yml
+  vars:
+    container_name: lldp
+
 - name: Gathering minigraph facts about the device
   minigraph_facts: host={{ inventory_hostname }}
 
@@ -8,15 +12,11 @@
   service: name=lldpd
            state=started
            enabled=yes
-  vars:
-    ansible_shell_type: docker
-    ansible_python_interpreter: docker exec -i lldp python
+  delegate_to: "{{ ansible_host }}_lldp"
 
 - name: Gather information from lldp
   lldp:
-  vars:
-    ansible_shell_type: docker
-    ansible_python_interpreter: docker exec -i lldp python
+  delegate_to: "{{ ansible_host }}_lldp"
 
 - name: If underlay exists, use underlay to replace it.
   set_fact:
@@ -47,9 +47,7 @@
 
 - name: Gather information from lldp again after ports are enabled
   lldp:
-  vars:
-    ansible_shell_type: docker
-    ansible_python_interpreter: docker exec -i lldp python
+  delegate_to: "{{ ansible_host }}_lldp"
 
 - name: rearrange lldp received data structure for interface up down test
   interface_up_down_data_struct_facts: data="{{ lldp }}"
diff --git a/ansible/roles/test/tasks/lag_2.yml b/ansible/roles/test/tasks/lag_2.yml
--- a/ansible/roles/test/tasks/lag_2.yml
+++ b/ansible/roles/test/tasks/lag_2.yml
@@ -23,11 +23,13 @@
 - set_fact: test_rate=true
   when: test_rate is not defined
 
+- include_tasks: add_container_to_inventory.yml
+  vars:
+    container_name: lldp
+
 - name: Gathering peer VM information from lldp
   lldp:
-  vars:
-    ansible_shell_type: docker
-    ansible_python_interpreter: docker exec -i lldp python
+  delegate_to: "{{ ansible_host }}_lldp"
 
 - name: gathering minigraph of the device configuration
   minigraph_facts: host={{ inventory_hostname }}
diff --git a/ansible/roles/test/tasks/lagall.yml b/ansible/roles/test/tasks/lagall.yml
--- a/ansible/roles/test/tasks/lagall.yml
+++ b/ansible/roles/test/tasks/lagall.yml
@@ -42,11 +42,13 @@
     dut_lag_members: "{{ dut_lag_members }} + [ '{{ item.1['members'][0] }}' ] + [ '{{ item.1['members'][1] }}' ]"
   with_indexed_items: "{{ minigraph_portchannel_interfaces }}"
 
+- include_tasks: add_container_to_inventory.yml
+  vars:
+      container_name: lldp
+
 - name: Gather information from LLDP.
   lldp:
-  vars:
-      ansible_shell_type: docker
-      ansible_python_interpreter: docker exec -i lldp python
+  delegate_to: "{{ ansible_host }}_lldp"
 
 - debug:
     msg: "{{ dut_lag_members }}"
diff --git a/ansible/roles/test/tasks/qos_sai.yml b/ansible/roles/test/tasks/qos_sai.yml
--- a/ansible/roles/test/tasks/qos_sai.yml
+++ b/ansible/roles/test/tasks/qos_sai.yml
@@ -6,6 +6,14 @@
 
 - include_vars: vars/qos.yml
 
+- include_tasks: add_container_to_inventory.yml
+  vars:
+    container_name: "{{ item }}"
+  with_items:
+    - "lldp"
+    - "bgp"
+    - "syncd"
+
 - block:
     - name: Getting minigraph facts
       minigraph_facts: host={{inventory_hostname}}
@@ -21,9 +29,7 @@
     - name: Ensure LLDP Daemon stopped
       become: yes
       supervisorctl: state=stopped name={{item}}
-      vars:
-        ansible_shell_type: docker
-        ansible_python_interpreter: docker exec -i lldp python
+      delegate_to: "{{ ansible_host }}_lldp"
       with_items:
         - lldpd
         - lldp-syncd
@@ -35,9 +41,7 @@
                   line='bgpd=no'
       notify:
         - Restart Quagga Daemon
-      vars:
-        ansible_shell_type: docker
-        ansible_python_interpreter: docker exec -i bgp python
+      delegate_to: "{{ ansible_host }}_bgp"
 
     - meta: flush_handlers
 
@@ -47,9 +51,7 @@
 
         - name: Disable Mellanox packet aging
           shell: python /root/packets_aging.py disable
-      vars:
-        ansible_shell_type: docker
-        ansible_python_interpreter: docker exec -i syncd python
+      delegate_to: "{{ ansible_host }}_syncd"
       when: minigraph_hwsku is defined and minigraph_hwsku in mellanox_hwskus
 
     - name: copy ptf tests
@@ -68,7 +70,7 @@
             ['Arista-7050-QX-32S', 'Arista-7060CX-32S-C32', 'Celestica-DX010-C32', 'Arista-7260CX3-D108C8', 'Force10-S6100', 'Arista-7260CX3-Q64'])
 
     - name: Init PTF base test parameters
-      set_fact: 
+      set_fact:
         ptf_base_params:
         - router_mac={% if testbed_type not in ['t0', 't0-64', 't0-116'] %}'{{ansible_Ethernet0['macaddress']}}'{% else %}''{% endif %}
         - server='{{ansible_host}}'
@@ -564,9 +566,7 @@
     - name: Restore LLDP Daemon
       become: yes
       supervisorctl: state=started name={{item}}
-      vars:
-        ansible_shell_type: docker
-        ansible_python_interpreter: docker exec -i lldp python
+      delegate_to: "{{ ansible_host }}_lldp"
       with_items:
         - lldpd
         - lldp-syncd
@@ -578,9 +578,7 @@
                   line='bgpd=yes'
       notify:
         - Restart Quagga Daemon
-      vars:
-        ansible_shell_type: docker
-        ansible_python_interpreter: docker exec -i bgp python
+      delegate_to: "{{ ansible_host }}_bgp"
 
     - name: Restore original watermark polling status
       shell: counterpoll watermark {{watermark_status.stdout}}
@@ -594,9 +592,7 @@
 
     - name: Enable Mellanox packet aging
       shell: python /root/packets_aging.py enable
-      vars:
-        ansible_shell_type: docker
-        ansible_python_interpreter: docker exec -i syncd python
+      delegate_to: "{{ ansible_host }}_syncd"
       when: minigraph_hwsku is defined and minigraph_hwsku in mellanox_hwskus
 
     - meta: flush_handlers