コード例 #1
0
ファイル: shellmanager.py プロジェクト: ozzie00/hue
  def _get_delegation_tokens(self, username, delegation_token_dir):
    """
    If operating against Kerberized Hadoop, we'll need to have obtained delegation tokens for
    the user we want to run the subprocess as. We have to do it here rather than in the subprocess
    because the subprocess does not have Kerberos credentials in that case.
    """
    delegation_token_files = []
    all_clusters = []
    all_clusters += all_mrclusters().values()
    all_clusters += get_all_hdfs().values()

    LOG.debug("Clusters to potentially acquire tokens for: %s" % (repr(all_clusters),))

    for cluster in all_clusters:
      if cluster.security_enabled:
        current_user = cluster.user
        try:
          cluster.setuser(username)
          token = cluster.get_delegation_token(KERBEROS.HUE_PRINCIPAL.get())
          token_file_no, path = tempfile.mkstemp(dir=delegation_token_dir)
          os.write(token_file_no, token)
          os.close(token_file_no)
          delegation_token_files.append(path)
        finally:
          cluster.setuser(current_user)

    return delegation_token_files
コード例 #2
0
ファイル: shellmanager.py プロジェクト: 2013Commons/HUE-SHARK
  def _get_delegation_tokens(self, username, delegation_token_dir):
    """
    If operating against Kerberized Hadoop, we'll need to have obtained delegation tokens for
    the user we want to run the subprocess as. We have to do it here rather than in the subprocess
    because the subprocess does not have Kerberos credentials in that case.
    """
    delegation_token_files = []
    all_clusters = []
    all_clusters += all_mrclusters().values()
    all_clusters += get_all_hdfs().values()

    LOG.debug("Clusters to potentially acquire tokens for: %s" % (repr(all_clusters),))

    for cluster in all_clusters:
      if cluster.security_enabled:
        current_user = cluster.user
        try:
          cluster.setuser(username)
          token = cluster.get_delegation_token(KERBEROS.HUE_PRINCIPAL.get())
          token_file_no, path = tempfile.mkstemp(dir=delegation_token_dir)
          os.write(token_file_no, token)
          os.close(token_file_no)
          delegation_token_files.append(path)
        finally:
          cluster.setuser(current_user)

    return delegation_token_files
コード例 #3
0
 def kill_children(self):
     for pid, child in self.children.items():
         try:
             os.write(child.kill_pipe, 'k')
             child.active = False
             # all maintenance of children's membership happens in runloop()
             # as children die and os.wait() gets results
         except OSError, e:
             if e.errno != errno.EPIPE:
                 raise
コード例 #4
0
 def kill_children(self):
     for pid, child in self.children.items():
         try:
             os.write(child.kill_pipe, 'k')
             child.active = False
             # all maintenance of children's membership happens in runloop()
             # as children die and os.wait() gets results
         except OSError, e:
             if e.errno != errno.EPIPE:
                 raise
コード例 #5
0
ファイル: status.py プロジェクト: 2013Commons/HUE-SHARK
 def collect_child_status(self, child):
     self.child_events[child.pid] = event.Event()
     try:
         try:
             # tell the child to POST its status to us, we handle it in the
             # wsgi application below
             eventlet.hubs.trampoline(child.kill_pipe, write=True)
             os.write(child.kill_pipe, 's')
             t = eventlet.Timeout(1)
             results = self.child_events[child.pid].wait()
             t.cancel()
         except (OSError, IOError), e:
             results = {'error': "%s %s" % (type(e), e)}
         except eventlet.Timeout:
             results = {'error':'Timed out'}
コード例 #6
0
ファイル: status.py プロジェクト: maduhu/HDP-hue
 def collect_child_status(self, child):
     self.child_events[child.pid] = event.Event()
     try:
         try:
             # tell the child to POST its status to us, we handle it in the
             # wsgi application below
             eventlet.hubs.trampoline(child.kill_pipe, write=True)
             os.write(child.kill_pipe, 's')
             t = eventlet.Timeout(1)
             results = self.child_events[child.pid].wait()
             t.cancel()
         except (OSError, IOError), e:
             results = {'error': "%s %s" % (type(e), e)}
         except eventlet.Timeout:
             results = {'error': 'Timed out'}
コード例 #7
0
ファイル: shellmanager.py プロジェクト: ozzie00/hue
class Shell(object):
  """
  A class to encapsulate I/O with a shell subprocess.
  """
  def __init__(self, shell_command, subprocess_env, shell_id, username, delegation_token_dir):
    try:
      user_info = pwd.getpwnam(username)
    except KeyError:
      LOG.error("Unix user account didn't exist at subprocess creation. Was it deleted?")
      raise

    parent, child = pty.openpty()

    try:
      tty.setraw(parent)
    except tty.error:
      LOG.debug("Could not set parent fd to raw mode, user will see duplicated input.")

    subprocess_env[constants.HOME] = user_info.pw_dir
    command_to_use = [_SETUID_PROG, str(user_info.pw_uid), str(user_info.pw_gid)]
    command_to_use.extend(shell_command)

    delegation_token_files = self._get_delegation_tokens(username, delegation_token_dir)
    if delegation_token_files:
      merged_token_file_path = self._merge_delegation_tokens(delegation_token_files, delegation_token_dir)
      for path in delegation_token_files:
        try:
          os.unlink(path)
        except:
          LOG.warning("Could not remove delegation token file %s" % path)
      delegation_token_files = [merged_token_file_path]
      subprocess_env[constants.HADOOP_TOKEN_FILE_LOCATION] = merged_token_file_path

    try:
      LOG.debug("Starting subprocess with command '%s' and environment '%s'" %
                                                             (command_to_use, subprocess_env,))
      p = subprocess.Popen(command_to_use, stdin=child, stdout=child, stderr=child,
                                                                 env=subprocess_env, close_fds=True)
    except (OSError, ValueError):
      os.close(parent)
      os.close(child)
      raise

    msg_format =  "%s - shell_id:%s pid:%d - args:%s"
    msg_args = (username, shell_id, p.pid, ' '.join(command_to_use))
    msg = msg_format % msg_args
    SHELL_OUTPUT_LOGGER.info(msg)
    SHELL_INPUT_LOGGER.info(msg)

    # State that shouldn't be touched by any other classes.
    self._output_buffer_length = 0
    self._commands = []
    self._fd = parent
    self._child_fd = child
    self.subprocess = p
    self.pid = p.pid
    self._write_buffer = cStringIO.StringIO()
    self._read_buffer = cStringIO.StringIO()
    self._delegation_token_files = delegation_token_files

    # State that's accessed by other classes.
    self.shell_id = shell_id
    self.username = username
    # Timestamp that is updated on shell creation and on every output request. Used so that we know
    # when to kill the shell.
    self.time_received = time.time()
    self.last_output_sent = False
    self.remove_at_next_iteration = False
    self.destroyed = False

  def _merge_delegation_tokens(self, delegation_token_files, delegation_token_dir):
    """
    Use the Credentials Merger utility to combine the delegation token files into one delegation token file.
    Returns the NamedTemporaryFile that contains the combined delegation tokens.
    """
    merged_token_file_no, merged_token_file_path = tempfile.mkstemp(dir=delegation_token_dir)
    os.close(merged_token_file_no)
    merge_tool_args = [hadoop.conf.HDFS_CLUSTERS['default'].HADOOP_BIN.get(), 'jar']
    merge_tool_args += [hadoop.conf.CREDENTIALS_MERGER_JAR.get(), merged_token_file_path]
    merge_tool_args += delegation_token_files
    LOG.debug("Merging credentials files with command: '%s'" % (' '.join(merge_tool_args)))
    merge_process = subprocess.Popen(merge_tool_args, stderr=subprocess.PIPE, shell=False, close_fds=True)
    while merge_process.poll() is None:
      time.sleep(1)
    retcode = merge_process.wait()
    if retcode != 0:
      LOG.error("Failed to merge credentials :'%s'..." % (merge_process.stderr.readline(),))
      raise MergeToolException(_("bin/hadoop return non-zero %(retcode)d while trying to merge credentials.") % dict(retcode=(retcode,)))
    return merged_token_file_path

  def _get_delegation_tokens(self, username, delegation_token_dir):
    """
    If operating against Kerberized Hadoop, we'll need to have obtained delegation tokens for
    the user we want to run the subprocess as. We have to do it here rather than in the subprocess
    because the subprocess does not have Kerberos credentials in that case.
    """
    delegation_token_files = []
    all_clusters = []
    all_clusters += all_mrclusters().values()
    all_clusters += get_all_hdfs().values()

    LOG.debug("Clusters to potentially acquire tokens for: %s" % (repr(all_clusters),))

    for cluster in all_clusters:
      if cluster.security_enabled:
        current_user = cluster.user
        try:
          cluster.setuser(username)
          token = cluster.get_delegation_token(KERBEROS.HUE_PRINCIPAL.get())
          token_file_no, path = tempfile.mkstemp(dir=delegation_token_dir)
          os.write(token_file_no, token)
          os.close(token_file_no)
          delegation_token_files.append(path)
        finally:
          cluster.setuser(current_user)

    return delegation_token_files

  def mark_for_cleanup(self):
    """
    Flag this shell to be picked up at the next iteration of handle_periodic.
    """
    self.remove_at_next_iteration = True

  def get_previous_output(self):
    """
    Called when a Hue session is restored. Returns a tuple of ( all previous output, next offset).
    """
    val = self._read_buffer.getvalue()
    return ( val, len(val))

  def get_previous_commands(self):
    """
    Return the list of previously entered commands. This is used for bash_history semantics
    when restoring Shells.
    """
    return self._commands

  def get_cached_output(self, offset):
    """
    The offset is not the latest one, so some output has already been generated and is
    stored in the read buffer. So let's fetch it from there.
    Returns (output, has_more, new_offset) or None.
    """
    self._read_buffer.seek(offset)
    next_output = self._read_buffer.read()
    if not next_output:
      return None
    more_available = len(next_output) >= shell.conf.SHELL_OS_READ_AMOUNT.get()
    return (next_output, more_available, self._output_buffer_length)

  def process_command(self, command):
    """
    Write the command to the end of the wite buffer, and spawn a greenlet to write it
    into the subprocess when the subprocess becomes writable.

    Returns a dictionary with {return_code: bool}.
    """
    # TODO(bc): Track the buffer size to avoid calling getvalue() every time
    if len(self._write_buffer.getvalue()) >= shell.conf.SHELL_WRITE_BUFFER_LIMIT.get():
      return { constants.BUFFER_EXCEEDED : True }
    else:
      self._append_to_write_buffer(command)
      eventlet.spawn_n(self._write_child_when_able)
      return { constants.SUCCESS : True }

  def _append_to_write_buffer(self, command):
    """
    Append the received command to the write buffer. This buffer is used
    when the child becomes readable to send commands to the child subprocess.
    """
    self._write_buffer.seek(len(self._write_buffer.getvalue()))
    self._write_buffer.write("%s" % (command,))
    # We seek back to the beginning so that when the child becomes writable we
    # feed the commands to the child in the order they were received.
    self._commands.append(command)
    while len(self._commands) > 25:
      self._commands.pop(0)

  def _read_from_write_buffer(self):
    """
    Read and return the contents of the write buffer.
    """
    self._write_buffer.seek(0)
    contents = self._write_buffer.read()
    return contents

  def _write_child_when_able(self):
    """
    Select on the child's input file descriptor becoming writable, and then write commands to it.
    If not successful in writing all the commands, spawn a new greenlet to retry.
    """
    LOG.debug("write_child_when_able")
    buffer_contents = self._read_from_write_buffer()
    if not buffer_contents:
      return

    try:
      r, w, x = select.select([],[self._fd],[])
    except Exception, e:
      # The next 9 lines are taken from Facebook's Tornado project, which is open-sourced under
      # the Apache license.
      # Depending on python version and poll implementation,
      # different exception types may be thrown and there are
      # two ways EINTR might be signaled:
      # * e.errno == errno.EINTR
      # * e.args is like (errno.EINTR, 'Interrupted system call')
      if (getattr(e, 'errno') == errno.EINTR or
          (isinstance(getattr(e, 'args'), tuple) and
           len(e.args) == 2 and e.args[0] == errno.EINTR)):
        LOG.warning("Interrupted system call", exc_info=1)
        eventlet.spawn_n(self._write_child_when_able)
      else:
        LOG.error("Unexpected error on select")
        self.mark_for_cleanup()
      return

    if not w:
      return

    try:
      bytes_written = os.write(self._fd, buffer_contents)
      self._advance_write_buffer(bytes_written)
    except OSError, e:
      if e.errno == errno.EINTR:
        eventlet.spawn_n(self._write_child_when_able)
      elif e.errno != errno.EAGAIN:
        error_str = "%s - shell_id:%s pid:%d - Error writing to subprocess:%s" %\
                                  (self.username, self.shell_id, self.pid, e,)
        LOG.error(error_str)
        SHELL_INPUT_LOGGER.error(error_str)
        self.mark_for_cleanup()