docs: def run_wfunc(self):
          '''
          Execute a wrapper function
  
          Returns tuple of (json_data, '')
          '''
          # Ensure that opts/grains are up to date
          # Execute routine
          data_cache = False
          data = None
          cdir = os.path.join(self.opts['cachedir'], 'minions', self.id)
          if not os.path.isdir(cdir):
              os.makedirs(cdir)
          datap = os.path.join(cdir, 'ssh_data.p')
          refresh = False
          if not os.path.isfile(datap):
              refresh = True
          else:
              passed_time = (time.time() - os.stat(datap).st_mtime) / 60
              if passed_time > self.opts.get('cache_life', 60):
                  refresh = True
  
          if self.opts.get('refresh_cache'):
              refresh = True
          conf_grains = {}
          # Save conf file grains before they get clobbered
          if 'ssh_grains' in self.opts:
              conf_grains = self.opts['ssh_grains']
          if not data_cache:
              refresh = True
          if refresh:
              # Make the datap
              # TODO: Auto expire the datap
              pre_wrapper = salt.client.ssh.wrapper.FunctionWrapper(
                  self.opts,
                  self.id,
                  fsclient=self.fsclient,
                  minion_opts=self.minion_opts,
                  **self.target)
              opts_pkg = pre_wrapper['test.opts_pkg']()  # pylint: disable=E1102
              if '_error' in opts_pkg:
                  #Refresh failed
                  retcode = opts_pkg['retcode']
                  ret = salt.utils.json.dumps({'local': opts_pkg})
                  return ret, retcode
  
              opts_pkg['file_roots'] = self.opts['file_roots']
              opts_pkg['pillar_roots'] = self.opts['pillar_roots']
              opts_pkg['ext_pillar'] = self.opts['ext_pillar']
              opts_pkg['extension_modules'] = self.opts['extension_modules']
              opts_pkg['module_dirs'] = self.opts['module_dirs']
              opts_pkg['_ssh_version'] = self.opts['_ssh_version']
              opts_pkg['__master_opts__'] = self.context['master_opts']
              if 'known_hosts_file' in self.opts:
                  opts_pkg['known_hosts_file'] = self.opts['known_hosts_file']
              if '_caller_cachedir' in self.opts:
                  opts_pkg['_caller_cachedir'] = self.opts['_caller_cachedir']
              else:
                  opts_pkg['_caller_cachedir'] = self.opts['cachedir']
              # Use the ID defined in the roster file
              opts_pkg['id'] = self.id
  
              retcode = 0
  
              # Restore master grains
              for grain in conf_grains:
                  opts_pkg['grains'][grain] = conf_grains[grain]
              # Enable roster grains support
              if 'grains' in self.target:
                  for grain in self.target['grains']:
                      opts_pkg['grains'][grain] = self.target['grains'][grain]
  
              popts = {}
              popts.update(opts_pkg)
  
              # Master centric operations such as mine.get must have master option loaded.
              # The pillar must then be compiled by passing master opts found in opts_pkg['__master_opts__']
              # which causes the pillar renderer to loose track of salt master options
              #
              # Depending on popts merge order, it will overwrite some options found in opts_pkg['__master_opts__']
              master_centric_funcs = [
                "pillar.items",
                "mine.get"
              ]
  
              # Pillar compilation is a master centric operation.
              # Master options take precedence during Pillar compilation
              popts.update(opts_pkg['__master_opts__'])
  
              pillar = salt.pillar.Pillar(
                      popts,
                      opts_pkg['grains'],
                      opts_pkg['id'],
                      opts_pkg.get('saltenv', 'base')
                      )
              pillar_data = pillar.compile_pillar()
  
              # Once pillar has been compiled, restore priority of minion opts
              if self.fun not in master_centric_funcs:
                  log.debug('%s is a minion function', self.fun)
                  popts.update(opts_pkg)
              else:
                  log.debug('%s is a master function', self.fun)
  
              # TODO: cache minion opts in datap in master.py
              data = {'opts': opts_pkg,
                      'grains': opts_pkg['grains'],
                      'pillar': pillar_data}
              if data_cache:
                  with salt.utils.files.fopen(datap, 'w+b') as fp_:
                      fp_.write(
                              self.serial.dumps(data)
                              )
          if not data and data_cache:
              with salt.utils.files.fopen(datap, 'rb') as fp_:
                  data = self.serial.load(fp_)
          opts = data.get('opts', {})
          opts['grains'] = data.get('grains')
  
          # Restore master grains
          for grain in conf_grains:
              opts['grains'][grain] = conf_grains[grain]
          # Enable roster grains support
          if 'grains' in self.target:
              for grain in self.target['grains']:
                  opts['grains'][grain] = self.target['grains'][grain]
  
          opts['pillar'] = data.get('pillar')
          wrapper = salt.client.ssh.wrapper.FunctionWrapper(
              opts,
              self.id,
              fsclient=self.fsclient,
              minion_opts=self.minion_opts,
              **self.target)
          self.wfuncs = salt.loader.ssh_wrapper(opts, wrapper, self.context)
          wrapper.wfuncs = self.wfuncs
  
          # We're running in the mine, need to fetch the arguments from the
          # roster, pillar, master config (in that order)
          if self.mine:
              mine_args = None
              mine_fun_data = None
              mine_fun = self.fun
  
              if self.mine_functions and self.fun in self.mine_functions:
                  mine_fun_data = self.mine_functions[self.fun]
              elif opts['pillar'] and self.fun in opts['pillar'].get('mine_functions', {}):
                  mine_fun_data = opts['pillar']['mine_functions'][self.fun]
              elif self.fun in self.context['master_opts'].get('mine_functions', {}):
                  mine_fun_data = self.context['master_opts']['mine_functions'][self.fun]
  
              if isinstance(mine_fun_data, dict):
                  mine_fun = mine_fun_data.pop('mine_function', mine_fun)
                  mine_args = mine_fun_data
              elif isinstance(mine_fun_data, list):
                  for item in mine_fun_data[:]:
                      if isinstance(item, dict) and 'mine_function' in item:
                          mine_fun = item['mine_function']
                          mine_fun_data.pop(mine_fun_data.index(item))
                  mine_args = mine_fun_data
              else:
                  mine_args = mine_fun_data
  
              # If we found mine_args, replace our command's args
              if isinstance(mine_args, dict):
                  self.args = []
                  self.kwargs = mine_args
              elif isinstance(mine_args, list):
                  self.args = mine_args
                  self.kwargs = {}
  
          try:
              if self.mine:
                  result = wrapper[mine_fun](*self.args, **self.kwargs)
              else:
                  result = self.wfuncs[self.fun](*self.args, **self.kwargs)
          except TypeError as exc:
              result = 'TypeError encountered executing {0}: {1}'.format(self.fun, exc)
              log.error(result, exc_info_on_loglevel=logging.DEBUG)
              retcode = 1
          except Exception as exc:
              result = 'An Exception occurred while executing {0}: {1}'.format(self.fun, exc)
              log.error(result, exc_info_on_loglevel=logging.DEBUG)
              retcode = 1
          # Mimic the json data-structure that "salt-call --local" will
          # emit (as seen in ssh_py_shim.py)
          if isinstance(result, dict) and 'local' in result:
              ret = salt.utils.json.dumps({'local': result['local']})
          else:
              ret = salt.utils.json.dumps({'local': {'return': result}})
          return ret, retcode