docs: def _executor_internal(self, host):
          ''' executes any module one or more times '''
  
          host_variables = self.inventory.get_variables(host)
          if self.transport in [ 'paramiko', 'ssh' ]:
              port = host_variables.get('ansible_ssh_port', self.remote_port)
              if port is None:
                  port = C.DEFAULT_REMOTE_PORT
          else:
              # fireball, local, etc
              port = self.remote_port
  
          inject = {}
          inject.update(host_variables)
          inject.update(self.module_vars)
          inject.update(self.setup_cache[host])
          inject['hostvars'] = HostVars(self.setup_cache, self.inventory)
          inject['group_names'] = host_variables.get('group_names', [])
          inject['groups'] = self.inventory.groups_list()
  
          # allow with_foo to work in playbooks...
          items = None
          items_plugin = self.module_vars.get('items_lookup_plugin', None)
          if items_plugin is not None and items_plugin in utils.plugins.lookup_loader:
              items_terms = self.module_vars.get('items_lookup_terms', '')
              items_terms = utils.template_ds(self.basedir, items_terms, inject)
              items = utils.plugins.lookup_loader.get(items_plugin, runner=self, basedir=self.basedir).run(items_terms, inject=inject)
              if type(items) != list:
                  raise errors.AnsibleError("lookup plugins have to return a list: %r" % items)
  
              if len(items) and self.module_name in [ 'apt', 'yum' ]:
                  # hack for apt and soon yum, with_items maps back into a single module call
                  inject['item'] = ",".join(items)
                  items = None
  
          # logic to decide how to run things depends on whether with_items is used
  
          if items is None:
              return self._executor_internal_inner(host, self.module_name, self.module_args, inject, port)
          elif len(items) > 0:
              # executing using with_items, so make multiple calls
              # TODO: refactor
              aggregrate = {}
              all_comm_ok = True
              all_changed = False
              all_failed = False
              results = []
              for x in items:
                  inject['item'] = x
                  result = self._executor_internal_inner(host, self.module_name, self.module_args, inject, port)
                  results.append(result.result)
                  if result.comm_ok == False:
                      all_comm_ok = False
                      all_failed = True
                      break
                  for x in results:
                      if x.get('changed') == True:
                          all_changed = True
                      if (x.get('failed') == True) or (('rc' in x) and (x['rc'] != 0)):
                          all_failed = True
                          break
              msg = 'All items completed'
              if all_failed:
                  msg = "One or more items failed."
              rd_result = dict(failed=all_failed, changed=all_changed, results=results, msg=msg)
              if not all_failed:
                  del rd_result['failed']
              return ReturnData(host=host, comm_ok=all_comm_ok, result=rd_result)
          else:
              self.callbacks.on_skipped(host, None)
              return ReturnData(host=host, comm_ok=True, result=dict(skipped=True))