コード例 #1
0
 def __init__(self, host, transport, service=None, mechanism=None):
     self.host = host
     self.transport = transport
     self.service = service
     self.mechanism = mechanism
     TSaslClientTransport.__init__(self, self.SaslClientFactory,
                                   self.mechanism, transport)
コード例 #2
0
def _get_transport(sock, host, use_ldap, ldap_user, ldap_password,
                   use_kerberos, kerberos_service_name):
    if not use_ldap and not use_kerberos:
        return TBufferedTransport(sock)
    try:
        import saslwrapper as sasl
    except ImportError:
        import sasl
    from thrift_sasl import TSaslClientTransport

    def sasl_factory():
        sasl_client = sasl.Client()
        sasl_client.setAttr("host", host)
        if use_ldap:
            sasl_client.setAttr("username", ldap_user)
            sasl_client.setAttr("password", ldap_password)
        else:
            sasl_client.setAttr("service", kerberos_service_name)
        sasl_client.init()
        return sasl_client

    if use_kerberos:
        return TSaslClientTransport(sasl_factory, "GSSAPI", sock)
    else:
        return TSaslClientTransport(sasl_factory, "PLAIN", sock)
コード例 #3
0
ファイル: thrift_util.py プロジェクト: yx91490/impala-1
def create_transport(host,
                     port,
                     service,
                     transport_type="buffered",
                     user=None,
                     password=None,
                     use_ssl=False,
                     ssl_cert=None):
    """
  Create a new Thrift Transport based on the requested type.
  Supported transport types:
  - buffered, returns simple buffered transport
  - plain_sasl, return a SASL transport with the PLAIN mechanism
  - kerberos, return a SASL transport with the GSSAPI mechanism

  If use_ssl is True, the connection will use SSL, optionally using the file at ssl_cert
  as the CA cert.
  """
    port = int(port)
    if use_ssl:
        from thrift.transport import TSSLSocket
        if ssl_cert is None:
            sock = TSSLSocket.TSSLSocket(host, port, validate=False)
        else:
            sock = TSSLSocket.TSSLSocket(host,
                                         port,
                                         validate=True,
                                         ca_certs=ssl_cert)
        # Set allowed SSL / TLS protocols to a permissive set to connect to any Impala server.
        import ssl
        sock.SSL_VERSION = ssl.PROTOCOL_SSLv23
    else:
        sock = TSocket(host, port)
    if transport_type.lower() == "buffered":
        return TBufferedTransport(sock)

    # Set defaults for LDAP connections
    if transport_type.lower() == "plain_sasl":
        if user is None: user = getpass.getuser()
        if password is None: password = ""

    # Initializes a sasl client
    def sasl_factory():
        sasl_client = sasl.Client()
        sasl_client.setAttr("host", host)
        sasl_client.setAttr("service", service)
        if transport_type.lower() == "plain_sasl":
            sasl_client.setAttr("username", user)
            sasl_client.setAttr("password", password)
        sasl_client.init()
        return sasl_client

    if transport_type.lower() == "plain_sasl":
        return TSaslClientTransport(sasl_factory, "PLAIN", sock)
    else:
        # GSSASPI is the underlying mechanism used by kerberos to authenticate.
        return TSaslClientTransport(sasl_factory, "GSSAPI", sock)
コード例 #4
0
ファイル: impala_client.py プロジェクト: yew1eb/impala
  def _get_socket_and_transport(self):
    """Create a Transport.

       A non-kerberized impalad just needs a simple buffered transport. For
       the kerberized version, a sasl transport is created.

       If SSL is enabled, a TSSLSocket underlies the transport stack; otherwise a TSocket
       is used.
       This function returns the socket and the transport object.
    """
    if self.use_ssl:
      # TSSLSocket needs the ssl module, which may not be standard on all Operating
      # Systems. Only attempt to import TSSLSocket if the user wants an SSL connection.
      from TSSLSocketWithWildcardSAN import TSSLSocketWithWildcardSAN

    # sasl does not accept unicode strings, explicitly encode the string into ascii.
    # The kerberos_host_fqdn option exposes the SASL client's hostname attribute to
    # the user. impala-shell checks to ensure this host matches the host in the kerberos
    # principal. So when a load balancer is configured to be used, its hostname is expected by
    # impala-shell. Setting this option to the load balancer hostname allows impala-shell to
    # connect directly to an impalad.
    if self.kerberos_host_fqdn is not None:
      sasl_host = self.kerberos_host_fqdn.split(':')[0].encode('ascii', 'ignore')
    else:
      sasl_host = self.impalad_host

    # Always use the hostname and port passed in to -i / --impalad as the host for the purpose of
    # creating the actual socket.
    sock_host = self.impalad_host
    sock_port = self.impalad_port
    if self.use_ssl:
      if self.ca_cert is None:
        # No CA cert means don't try to verify the certificate
        sock = TSSLSocketWithWildcardSAN(sock_host, sock_port, validate=False)
      else:
        sock = TSSLSocketWithWildcardSAN(sock_host, sock_port, validate=True, ca_certs=self.ca_cert)
    else:
      sock = TSocket(sock_host, sock_port)
    if not (self.use_ldap or self.use_kerberos):
      return sock, TBufferedTransport(sock)

    # Initializes a sasl client
    def sasl_factory():
      sasl_client = sasl.Client()
      sasl_client.setAttr("host", sasl_host)
      if self.use_ldap:
        sasl_client.setAttr("username", self.user)
        sasl_client.setAttr("password", self.ldap_password)
      else:
        sasl_client.setAttr("service", self.kerberos_service_name)
      sasl_client.init()
      return sasl_client
    # GSSASPI is the underlying mechanism used by kerberos to authenticate.
    if self.use_kerberos:
      return sock, TSaslClientTransport(sasl_factory, "GSSAPI", sock)
    else:
      return sock, TSaslClientTransport(sasl_factory, "PLAIN", sock)
コード例 #5
0
ファイル: hive_hooks.py プロジェクト: zhaohc10/airflow2
    def get_metastore_client(self):
        """
        Returns a Hive thrift client.
        """
        from thrift.transport import TSocket, TTransport
        from thrift.protocol import TBinaryProtocol
        from hive_service import ThriftHive
        ms = self.metastore_conn
        auth_mechanism = ms.extra_dejson.get('authMechanism', 'NOSASL')
        if configuration.get('core', 'security') == 'kerberos':
            auth_mechanism = ms.extra_dejson.get('authMechanism', 'GSSAPI')
            kerberos_service_name = ms.extra_dejson.get('kerberos_service_name', 'hive')

        socket = TSocket.TSocket(ms.host, ms.port)
        if configuration.get('core', 'security') == 'kerberos' and auth_mechanism == 'GSSAPI':
            try:
                import saslwrapper as sasl
            except ImportError:
                import sasl

            def sasl_factory():
                sasl_client = sasl.Client()
                sasl_client.setAttr("host", ms.host)
                sasl_client("service", kerberos_service_name)
                sasl_client.init()

            from thrift_sasl import TSaslClientTransport
            transport = TSaslClientTransport(sasl_factory, "GSSAPI", socket)
        else:
            transport = TTransport.TBufferedTransport(socket)

        protocol = TBinaryProtocol.TBinaryProtocol(transport)

        return ThriftHive.Client(protocol)
コード例 #6
0
def connect_to_thrift(conf):
  """
  Connect to a thrift endpoint as determined by the 'conf' parameter.
  Note that this does *not* open the transport.

  Returns a tuple of (service, protocol, transport)
  """
  sock = TSocket(conf.host, conf.port)
  if conf.timeout_seconds:
    # Thrift trivia: You can do this after the fact with
    # _grab_transport_from_wrapper(self.wrapped.transport).setTimeout(seconds*1000)
    sock.setTimeout(conf.timeout_seconds*1000.0)
  if conf.use_sasl:
    def sasl_factory():
      saslc = sasl.Client()
      saslc.setAttr("host", "hadoop")
      saslc.setAttr("service", str(conf.kerberos_principal))
      saslc.init()
      return saslc

    transport = TSaslClientTransport(sasl_factory, "GSSAPI", sock)
  else:
    transport = TBufferedTransport(sock)

  protocol = TBinaryProtocol(transport)
  service = conf.klass(protocol)
  return service, protocol, transport
コード例 #7
0
def get_transport(socket,
                  host,
                  kerberos_service_name,
                  auth_mechanism='NOSASL',
                  user=None,
                  password=None):
    """
    Creates a new Thrift Transport using the specified auth_mechanism.
    Supported auth_mechanisms are:
    - None or 'NOSASL' - returns simple buffered transport (default)
    - 'PLAIN'  - returns a SASL transport with the PLAIN mechanism
    - 'GSSAPI' - returns a SASL transport with the GSSAPI mechanism
    """
    log.debug(
        'get_transport: socket=%s host=%s kerberos_service_name=%s '
        'auth_mechanism=%s user=%s password=fuggetaboutit', socket, host,
        kerberos_service_name, auth_mechanism, user)

    auth_mechanism = auth_mechanism or 'NOSASL'
    if auth_mechanism == 'NOSASL':
        return TBufferedTransport(socket)

    # Set defaults for PLAIN SASL / LDAP connections.
    if auth_mechanism in ['LDAP', 'PLAIN']:
        if user is None:
            user = getpass.getuser()
            log.debug('get_transport: user=%s', user)
        if password is None:
            if auth_mechanism == 'LDAP':
                password = ''
            else:
                # PLAIN always requires a password for HS2.
                password = '******'
            log.debug('get_transport: password=%s', password)

    # Initializes a sasl client
    from thrift_sasl import TSaslClientTransport
    try:
        import sasl  # pylint: disable=import-error

        def sasl_factory():
            sasl_client = sasl.Client()
            sasl_client.setAttr('host', host)
            sasl_client.setAttr('service', kerberos_service_name)
            if auth_mechanism.upper() in ['PLAIN', 'LDAP']:
                sasl_client.setAttr('username', user)
                sasl_client.setAttr('password', password)
            sasl_client.init()
            return sasl_client
    except ImportError:
        log.warn("Unable to import 'sasl'. Fallback to 'puresasl'.")
        from dask_hivemetastore.sasl_compat import PureSASLClient

        def sasl_factory():
            return PureSASLClient(host,
                                  username=user,
                                  password=password,
                                  service=kerberos_service_name)

    return TSaslClientTransport(sasl_factory, auth_mechanism, socket)
コード例 #8
0
    def _get_transport(self):
        """Create a Transport.

       A non-kerberized impalad just needs a simple buffered transport. For
       the kerberized version, a sasl transport is created.

       If SSL is enabled, a TSSLSocket underlies the transport stack; otherwise a TSocket
       is used.
    """
        if self.use_ssl:
            # TSSLSocket needs the ssl module, which may not be standard on all Operating
            # Systems. Only attempt to import TSSLSocket if the user wants an SSL connection.
            from thrift.transport import TSSLSocket

        # sasl does not accept unicode strings, explicitly encode the string into ascii.
        host, port = self.impalad[0].encode('ascii',
                                            'ignore'), int(self.impalad[1])
        if self.use_ssl:
            if self.ca_cert is None:
                # No CA cert means don't try to verify the certificate
                sock = TSSLSocket.TSSLSocket(host, port, validate=False)
            else:
                sock = TSSLSocket.TSSLSocket(host,
                                             port,
                                             validate=True,
                                             ca_certs=self.ca_cert)
        else:
            sock = TSocket(host, port)
        if not (self.use_ldap or self.use_kerberos):
            return TBufferedTransport(sock)
        # Initializes a sasl client
        def sasl_factory():
            sasl_client = sasl.Client()
            sasl_client.setAttr("host", host)
            if self.use_ldap:
                sasl_client.setAttr("username", self.user)
                sasl_client.setAttr("password", self.ldap_password)
            else:
                sasl_client.setAttr("service", self.kerberos_service_name)
            sasl_client.init()
            return sasl_client

        # GSSASPI is the underlying mechanism used by kerberos to authenticate.
        if self.use_kerberos:
            return TSaslClientTransport(sasl_factory, "GSSAPI", sock)
        else:
            return TSaslClientTransport(sasl_factory, "PLAIN", sock)
コード例 #9
0
ファイル: hive.py プロジェクト: leahecole/airflow
    def get_metastore_client(self) -> Any:
        """Returns a Hive thrift client."""
        import hmsclient
        from thrift.protocol import TBinaryProtocol
        from thrift.transport import TSocket, TTransport

        host = self._find_valid_host()
        conn = self.conn

        if not host:
            raise AirflowException("Failed to locate the valid server.")

        if 'authMechanism' in conn.extra_dejson:
            warnings.warn(
                "The 'authMechanism' option is deprecated. Please use 'auth_mechanism'.",
                DeprecationWarning,
                stacklevel=2,
            )
            conn.extra_dejson['auth_mechanism'] = conn.extra_dejson[
                'authMechanism']
            del conn.extra_dejson['authMechanism']

        auth_mechanism = conn.extra_dejson.get('auth_mechanism', 'NOSASL')

        if conf.get('core', 'security') == 'kerberos':
            auth_mechanism = conn.extra_dejson.get('auth_mechanism', 'GSSAPI')
            kerberos_service_name = conn.extra_dejson.get(
                'kerberos_service_name', 'hive')

        conn_socket = TSocket.TSocket(host, conn.port)

        if conf.get('core',
                    'security') == 'kerberos' and auth_mechanism == 'GSSAPI':
            try:
                import saslwrapper as sasl
            except ImportError:
                import sasl

            def sasl_factory() -> sasl.Client:
                sasl_client = sasl.Client()
                sasl_client.setAttr("host", host)
                sasl_client.setAttr("service", kerberos_service_name)
                sasl_client.init()
                return sasl_client

            from thrift_sasl import TSaslClientTransport

            transport = TSaslClientTransport(sasl_factory, "GSSAPI",
                                             conn_socket)
        else:
            transport = TTransport.TBufferedTransport(conn_socket)

        protocol = TBinaryProtocol.TBinaryProtocol(transport)

        return hmsclient.HMSClient(iprot=protocol)
コード例 #10
0
    def _refresh_thrift_client(self):
        """Refresh the Thrift socket, transport, and client."""
        socket = TSocket(host=self.host,
                         port=self.port,
                         socket_timeout=self.timeout)

        self.transport = self._transport_class(socket)
        if self.use_kerberos:
            self.transport = TSaslClientTransport(self.transport, self.host,
                                                  self.sasl_service_name)
            sasl_auth = 'GSSAPI'

            def sasl_factory():
                sasl_client = sasl.Client()
                sasl_client.setAttr('host', self.host)
                sasl_client.setAttr('service', self.sasl_service_name)
                sasl_client.init()
                return sasl_client

            self.transport = TSaslClientTransport(sasl_factory, sasl_auth,
                                                  socket)
        protocol = self._protocol_class(self.transport, decode_response=False)
        self.client = TClient(Hbase, protocol)
コード例 #11
0
def get_transport(socket,
                  host,
                  kerberos_service_name,
                  auth_mechanism='NOSASL',
                  user=None,
                  password=None):
    """
    Creates a new Thrift Transport using the specified auth_mechanism.
    Supported auth_mechanisms are:
    - None or 'NOSASL' - returns simple buffered transport (default)
    - 'PLAIN'  - returns a SASL transport with the PLAIN mechanism
    - 'GSSAPI' - returns a SASL transport with the GSSAPI mechanism
    """
    log.debug(
        'get_transport: socket=%s host=%s kerberos_service_name=%s '
        'auth_mechanism=%s user=%s password=fuggetaboutit', socket, host,
        kerberos_service_name, auth_mechanism, user)

    if auth_mechanism == 'NOSASL':
        return TBufferedTransport(socket)

    # Set defaults for PLAIN SASL / LDAP connections.
    if auth_mechanism in ['LDAP', 'PLAIN']:
        if user is None:
            user = getpass.getuser()
            log.debug('get_transport: user=%s', user)
        if password is None:
            if auth_mechanism == 'LDAP':
                password = ''
            else:
                # PLAIN always requires a password for HS2.
                password = '******'
            log.debug('get_transport: password=%s', password)

    # Initializes a sasl client
    import sasl
    from thrift_sasl import TSaslClientTransport

    def sasl_factory():
        sasl_client = sasl.Client()
        sasl_client.setAttr('host', host)
        sasl_client.setAttr('service', kerberos_service_name)
        if auth_mechanism.upper() in ['PLAIN', 'LDAP']:
            sasl_client.setAttr('username', user)
            sasl_client.setAttr('password', password)
        sasl_client.init()
        return sasl_client

    return TSaslClientTransport(sasl_factory, auth_mechanism, socket)
コード例 #12
0
ファイル: hive.py プロジェクト: zorseti/airflow
    def get_metastore_client(self):
        """
        Returns a Hive thrift client.
        """
        import hmsclient
        from thrift.transport import TSocket, TTransport
        from thrift.protocol import TBinaryProtocol

        conn = self._find_valid_server()

        if not conn:
            raise AirflowException("Failed to locate the valid server.")

        auth_mechanism = conn.extra_dejson.get('authMechanism', 'NOSASL')

        if conf.get('core', 'security') == 'kerberos':
            auth_mechanism = conn.extra_dejson.get('authMechanism', 'GSSAPI')
            kerberos_service_name = conn.extra_dejson.get(
                'kerberos_service_name', 'hive')

        conn_socket = TSocket.TSocket(conn.host, conn.port)

        if conf.get('core', 'security') == 'kerberos' \
                and auth_mechanism == 'GSSAPI':
            try:
                import saslwrapper as sasl
            except ImportError:
                import sasl

            def sasl_factory():
                sasl_client = sasl.Client()
                sasl_client.setAttr("host", conn.host)
                sasl_client.setAttr("service", kerberos_service_name)
                sasl_client.init()
                return sasl_client

            from thrift_sasl import TSaslClientTransport
            transport = TSaslClientTransport(sasl_factory, "GSSAPI",
                                             conn_socket)
        else:
            transport = TTransport.TBufferedTransport(conn_socket)

        protocol = TBinaryProtocol.TBinaryProtocol(transport)

        return hmsclient.HMSClient(iprot=protocol)
コード例 #13
0
ファイル: _thrift_api.py プロジェクト: danielewood/impyla
def get_transport(socket,
                  host,
                  kerberos_service_name,
                  auth_mechanism='NOSASL',
                  user=None,
                  password=None):
    """
    Creates a new Thrift Transport using the specified auth_mechanism.
    Supported auth_mechanisms are:
    - None or 'NOSASL' - returns simple buffered transport (default)
    - 'PLAIN'  - returns a SASL transport with the PLAIN mechanism
    - 'GSSAPI' - returns a SASL transport with the GSSAPI mechanism
    """
    log.debug(
        'get_transport: socket=%s host=%s kerberos_service_name=%s '
        'auth_mechanism=%s user=%s password=fuggetaboutit', socket, host,
        kerberos_service_name, auth_mechanism, user)

    if auth_mechanism == 'NOSASL':
        return TBufferedTransport(socket)

    # Set defaults for PLAIN SASL / LDAP connections.
    if auth_mechanism in ['LDAP', 'PLAIN']:
        if user is None:
            user = getpass.getuser()
            log.debug('get_transport: user=%s', user)
        if password is None:
            if auth_mechanism == 'LDAP':
                password = ''
            else:
                # PLAIN always requires a password for HS2.
                password = '******'
            log.debug('get_transport: password=%s', password)
        auth_mechanism = 'PLAIN'  # sasl doesn't know mechanism LDAP
    # Initializes a sasl client
    from thrift_sasl import TSaslClientTransport
    from impala.sasl_compat import PureSASLClient

    def sasl_factory():
        return PureSASLClient(host,
                              username=user,
                              password=password,
                              service=kerberos_service_name)

    return TSaslClientTransport(sasl_factory, auth_mechanism, socket)
コード例 #14
0
  def __get_transport(self):
    """Create a Transport.

       A non-kerberized impalad just needs a simple buffered transport. For
       the kerberized version, a sasl transport is created.
    """
    sock = TSocket(self.impalad[0], int(self.impalad[1]))
    if not self.use_kerberos:
      return TBufferedTransport(sock)
    # Initializes a sasl client
    def sasl_factory():
      sasl_client = sasl.Client()
      sasl_client.setAttr("host", self.impalad[0])
      sasl_client.setAttr("service", self.kerberos_service_name)
      sasl_client.init()
      return sasl_client
    # GSSASPI is the underlying mechanism used by kerberos to authenticate.
    return TSaslClientTransport(sasl_factory, "GSSAPI", sock)
コード例 #15
0
 def __init__(self, host, transport, service=None, mechanism=None):
     self.host = host
     self.transport = transport
     self.service = service
     self.mechanism = mechanism
     TSaslClientTransport.__init__(self, self.SaslClientFactory, self.mechanism, transport)
コード例 #16
0
class Connection(object):
    """Connection to an HBase Thrift server.

    The `host` and `port` arguments specify the host name and TCP port
    of the HBase Thrift server to connect to. If omitted or ``None``,
    a connection to the default port on ``localhost`` is made. If
    specifed, the `timeout` argument specifies the socket timeout in
    milliseconds.

    If `autoconnect` is `True` (the default) the connection is made
    directly, otherwise :py:meth:`Connection.open` must be called
    explicitly before first use.

    The optional `table_prefix` and `table_prefix_separator` arguments
    specify a prefix and a separator string to be prepended to all table
    names, e.g. when :py:meth:`Connection.table` is invoked. For
    example, if `table_prefix` is ``myproject``, all tables tables will
    have names like ``myproject_XYZ``.

    The optional `compat` argument sets the compatibility level for
    this connection. Older HBase versions have slightly different Thrift
    interfaces, and using the wrong protocol can lead to crashes caused
    by communication errors, so make sure to use the correct one. This
    value can be either the string ``0.90``, ``0.92``, ``0.94``, or
    ``0.96`` (the default).

    The optional `transport` argument specifies the Thrift transport
    mode to use. Supported values for this argument are ``buffered``
    (the default) and ``framed``. Make sure to choose the right one,
    since otherwise you might see non-obvious connection errors or
    program hangs when making a connection. HBase versions before 0.94
    always use the buffered transport. Starting with HBase 0.94, the
    Thrift server optionally uses a framed transport, depending on the
    argument passed to the ``hbase-daemon.sh start thrift`` command.
    The default ``-threadpool`` mode uses the buffered transport; the
    ``-hsha``, ``-nonblocking``, and ``-threadedselector`` modes use the
    framed transport.

    The optional `protocol` argument specifies the Thrift transport
    protocol to use. Supported values for this argument are ``binary``
    (the default) and ``compact``. Make sure to choose the right one,
    since otherwise you might see non-obvious connection errors or
    program hangs when making a connection. ``TCompactProtocol`` is
    a more compact binary format that is  typically more efficient to
    process as well. ``TBinaryProtocol`` is the default protocol that
    Happybase uses.

    .. versionadded:: 0.9
       `protocol` argument

    .. versionadded:: 0.5
       `timeout` argument

    .. versionadded:: 0.4
       `table_prefix_separator` argument

    .. versionadded:: 0.4
       support for framed Thrift transports

    :param str host: The host to connect to
    :param int port: The port to connect to
    :param int timeout: The socket timeout in milliseconds (optional)
    :param bool autoconnect: Whether the connection should be opened directly
    :param str table_prefix: Prefix used to construct table names (optional)
    :param str table_prefix_separator: Separator used for `table_prefix`
    :param str compat: Compatibility mode (optional)
    :param str transport: Thrift transport mode (optional)
    """
    def __init__(self,
                 host=DEFAULT_HOST,
                 port=DEFAULT_PORT,
                 timeout=None,
                 autoconnect=True,
                 table_prefix=None,
                 table_prefix_separator=b'_',
                 compat=DEFAULT_COMPAT,
                 transport=DEFAULT_TRANSPORT,
                 protocol=DEFAULT_PROTOCOL,
                 use_kerberos=False,
                 sasl_service_name='hbase'):

        if transport not in THRIFT_TRANSPORTS:
            raise ValueError("'transport' must be one of %s" %
                             ", ".join(THRIFT_TRANSPORTS.keys()))

        if table_prefix is not None:
            if not isinstance(table_prefix, STRING_OR_BINARY):
                raise TypeError("'table_prefix' must be a string")
            table_prefix = ensure_bytes(table_prefix)

        if not isinstance(table_prefix_separator, STRING_OR_BINARY):
            raise TypeError("'table_prefix_separator' must be a string")
        table_prefix_separator = ensure_bytes(table_prefix_separator)

        if compat not in COMPAT_MODES:
            raise ValueError("'compat' must be one of %s" %
                             ", ".join(COMPAT_MODES))

        if protocol not in THRIFT_PROTOCOLS:
            raise ValueError("'protocol' must be one of %s" %
                             ", ".join(THRIFT_PROTOCOLS))

        # Allow host and port to be None, which may be easier for
        # applications wrapping a Connection instance.
        self.host = host or DEFAULT_HOST
        self.port = port or DEFAULT_PORT
        self.timeout = timeout
        self.table_prefix = table_prefix
        self.table_prefix_separator = table_prefix_separator
        self.compat = compat
        self.use_kerberos = use_kerberos
        self.sasl_service_name = sasl_service_name

        self._transport_class = THRIFT_TRANSPORTS[transport]
        self._protocol_class = THRIFT_PROTOCOLS[protocol]
        self._refresh_thrift_client()

        if autoconnect:
            self.open()

        self._initialized = True

    def _refresh_thrift_client(self):
        """Refresh the Thrift socket, transport, and client."""
        socket = TSocket(host=self.host,
                         port=self.port,
                         socket_timeout=self.timeout)

        self.transport = self._transport_class(socket)
        if self.use_kerberos:
            self.transport = TSaslClientTransport(self.transport, self.host,
                                                  self.sasl_service_name)
            sasl_auth = 'GSSAPI'

            def sasl_factory():
                sasl_client = sasl.Client()
                sasl_client.setAttr('host', self.host)
                sasl_client.setAttr('service', self.sasl_service_name)
                sasl_client.init()
                return sasl_client

            self.transport = TSaslClientTransport(sasl_factory, sasl_auth,
                                                  socket)
        protocol = self._protocol_class(self.transport, decode_response=False)
        self.client = TClient(Hbase, protocol)

    def _table_name(self, name):
        """Construct a table name by optionally adding a table name prefix."""
        name = ensure_bytes(name)
        if self.table_prefix is None:
            return name
        return self.table_prefix + self.table_prefix_separator + name

    def open(self):
        """Open the underlying transport to the HBase instance.

        This method opens the underlying Thrift transport (TCP connection).
        """
        if self.transport.is_open():
            return

        logger.debug("Opening Thrift transport to %s:%d", self.host, self.port)
        self.transport.open()

    def close(self):
        """Close the underyling transport to the HBase instance.

        This method closes the underlying Thrift transport (TCP connection).
        """
        if not self.transport.is_open():
            return

        if logger is not None:
            # If called from __del__(), module variables may no longer
            # exist.
            logger.debug("Closing Thrift transport to %s:%d", self.host,
                         self.port)

        self.transport.close()

    def __del__(self):
        try:
            self._initialized
        except AttributeError:
            # Failure from constructor
            return
        else:
            self.close()

    def table(self, name, use_prefix=True):
        """Return a table object.

        Returns a :py:class:`happybase.Table` instance for the table
        named `name`. This does not result in a round-trip to the
        server, and the table is not checked for existence.

        The optional `use_prefix` argument specifies whether the table
        prefix (if any) is prepended to the specified `name`. Set this
        to `False` if you want to use a table that resides in another
        ‘prefix namespace’, e.g. a table from a ‘friendly’ application
        co-hosted on the same HBase instance. See the `table_prefix`
        argument to the :py:class:`Connection` constructor for more
        information.

        :param str name: the name of the table
        :param bool use_prefix: whether to use the table prefix (if any)
        :return: Table instance
        :rtype: :py:class:`Table`
        """
        name = ensure_bytes(name)
        if use_prefix:
            name = self._table_name(name)
        return Table(name, self)

    #
    # Table administration and maintenance
    #

    def tables(self):
        """Return a list of table names available in this HBase instance.

        If a `table_prefix` was set for this :py:class:`Connection`, only
        tables that have the specified prefix will be listed.

        :return: The table names
        :rtype: List of strings
        """
        names = self.client.getTableNames()

        # Filter using prefix, and strip prefix from names
        if self.table_prefix is not None:
            prefix = self._table_name(b'')
            offset = len(prefix)
            names = [n[offset:] for n in names if n.startswith(prefix)]

        return names

    def create_table(self, name, families):
        """Create a table.

        :param str name: The table name
        :param dict families: The name and options for each column family

        The `families` argument is a dictionary mapping column family
        names to a dictionary containing the options for this column
        family, e.g.

        ::

            families = {
                'cf1': dict(max_versions=10),
                'cf2': dict(max_versions=1, block_cache_enabled=False),
                'cf3': dict(),  # use defaults
            }
            connection.create_table('mytable', families)

        These options correspond to the ColumnDescriptor structure in
        the Thrift API, but note that the names should be provided in
        Python style, not in camel case notation, e.g. `time_to_live`,
        not `timeToLive`. The following options are supported:

        * ``max_versions`` (`int`)
        * ``compression`` (`str`)
        * ``in_memory`` (`bool`)
        * ``bloom_filter_type`` (`str`)
        * ``bloom_filter_vector_size`` (`int`)
        * ``bloom_filter_nb_hashes`` (`int`)
        * ``block_cache_enabled`` (`bool`)
        * ``time_to_live`` (`int`)
        """
        name = self._table_name(name)
        if not isinstance(families, dict):
            raise TypeError("'families' arg must be a dictionary")

        if not families:
            raise ValueError(
                "Cannot create table %r (no column families specified)" % name)

        column_descriptors = []
        for cf_name, options in six.iteritems(families):
            if options is None:
                options = dict()

            kwargs = dict()
            for option_name, value in six.iteritems(options):
                kwargs[pep8_to_camel_case(option_name)] = value

            if not cf_name.endswith(':'):
                cf_name += ':'
            kwargs['name'] = cf_name

            column_descriptors.append(ColumnDescriptor(**kwargs))

        self.client.createTable(name, column_descriptors)

    def delete_table(self, name, disable=False):
        """Delete the specified table.

        .. versionadded:: 0.5
           `disable` argument

        In HBase, a table always needs to be disabled before it can be
        deleted. If the `disable` argument is `True`, this method first
        disables the table if it wasn't already and then deletes it.

        :param str name: The table name
        :param bool disable: Whether to first disable the table if needed
        """
        if disable and self.is_table_enabled(name):
            self.disable_table(name)

        name = self._table_name(name)
        self.client.deleteTable(name)

    def enable_table(self, name):
        """Enable the specified table.

        :param str name: The table name
        """
        name = self._table_name(name)
        self.client.enableTable(name)

    def disable_table(self, name):
        """Disable the specified table.

        :param str name: The table name
        """
        name = self._table_name(name)
        self.client.disableTable(name)

    def is_table_enabled(self, name):
        """Return whether the specified table is enabled.

        :param str name: The table name

        :return: whether the table is enabled
        :rtype: bool
        """
        name = self._table_name(name)
        return self.client.isTableEnabled(name)

    def compact_table(self, name, major=False):
        """Compact the specified table.

        :param str name: The table name
        :param bool major: Whether to perform a major compaction.
        """
        name = self._table_name(name)
        if major:
            self.client.majorCompact(name)
        else:
            self.client.compact(name)