class Net::Ping::TNS

The Ping::TNS class encapsulates the information and behavior of tns ping.

Constants

VERSION

The version of the net-tnsping library.

Attributes

database[RW]

Database name.

database_source_name[RW]

Database source name.

db[RW]

Database name.

driver[RW]

The name of the oracle driver to use for connections. Defaults to OCI8.

dsn[RW]

Database source name.

host[RW]

The name of the host the database sits on.

hosts[R]

A list of hosts for the given database name.

ora_home[RW]

The value of your ORACLE_HOME or ORA_HOME environment variable.

oracle_home[RW]

The value of your ORACLE_HOME or ORA_HOME environment variable.

oracle_home=[RW]

The value of your ORACLE_HOME or ORA_HOME environment variable.

port[R]

The port used when attempting a connection. The default is 1521.

ports[R]

A list of ports for the given database name

tns_admin[RW]

The toplevel tns admin path.

tns_file[RW]

The full path to the tnsnames.ora file.

Public Class Methods

new(database, driver='OCI8', host=nil, port=1521, timeout=5) { |self| ... } click to toggle source

Creates and returns a new Ping::TNS object. If the db specified cannot be found in the tnsnames.ora file, then a Ping::TNS::Error is raised.

# File lib/net/tnsping.rb, line 49
def initialize(database, driver='OCI8', host=nil, port=1521, timeout=5)
  @database  = database
  @dsn       = "dbi:#{driver}:" << database
  @host      = host
  @timeout   = timeout
  @port      = port
  @driver    = driver
  @ports     = []  # There can be more than one host/port
  @hosts     = []  # for each dsn.  Try them in order.
  @sid       = nil

  @tns_admin = ENV['TNS_ADMIN']
  @ora_home  = ENV['ORACLE_HOME'] || ENV['ORA_HOME']

  if @tns_admin
    @tns_file = File.join(@tns_admin, 'tnsnames.ora')
  elsif @ora_home
    @tns_file = File.join(@ora_home, 'network', 'admin', 'tnsnames.ora')
  else
    home = ENV['HOME'] || ENV['USERPROFILE']
    @tns_file = File.join(home, 'tnsnames.ora')
  end

  yield self if block_given?

  # If the host is not specified, look for it in the tnsnames.ora file
  if host.nil?
    err_msg = "tnsnames.ora file could not be found"
    raise Error, err_msg unless File.exists?(@tns_file)
    parse_tns_file
  else
    @hosts.push(host)
    @ports.push(port)
  end
end

Public Instance Methods

ping?() click to toggle source

Performs a TCP ping on the listener. The host and port are determined from your tnsnames.ora file. If more than one host and/or port are found in the tnsnames.ora file, then each will be tried. So long as at least one of them connects successfully, true is returned.

If you specify a host and port in the constructor, then the attempt will only be made against that host on the given port.

Remember, this only pings the listener. If you want to ping the listener and the database, use the ping_all? method.

Calls superclass method
# File lib/net/tnsping.rb, line 108
def ping?
  if @hosts.empty?
    raise Error, "No hosts found"
  end

  # Use 1521 if no ports were found in the tnsnames.ora file.
  if @ports.empty?
    @ports.push(@port)
  end

  # If the host is provided, only ping that host
  if @host
    0.upto(@ports.length-1){ |n|
      @port = @ports[n]
      return super
    }
  else
    0.upto(@ports.length-1){ |n|
      @port = @ports[n]
      @host = @hosts[n]
      return super
    }
  end
end
Also aliased as: ping_listener?
ping_all?() click to toggle source

Simple wrapper for ping_listener? + ping_database?

# File lib/net/tnsping.rb, line 176
def ping_all?
  return false unless self.ping_listener?
  return false unless self.ping_database?
  true
end
ping_database?(dsn=@dsn, timeout=@timeout, user=@sid, passwd=Time.now.to_s) click to toggle source

Attempts to make a connection using a bogus login and password via the DBI class. If an ORA-01017 Oracle error is returned, that means the database is up and running and true is returned.

Note that each of the arguments for this method use the defaults passed to the constructor (or have a default otherwise set). You generally should not pass any arguments to this method. In the event that this method fails, false is returned and the error can be viewed via Ping::TNS#exception.

# File lib/net/tnsping.rb, line 147
def ping_database?(dsn=@dsn, timeout=@timeout, user=@sid, passwd=Time.now.to_s)
  re   = /ORA-01017/
  dbh  = nil
  user ||= Time.now.to_s
  rv = false

  begin
    Timeout.timeout(timeout){
      dbh = DBI.connect(dsn,user,passwd)
    }
  rescue DBI::DatabaseError => e
    if re.match(e.to_s)
      rv = true
    else
      @exception = e
    end
  rescue Timeout::Error, StandardError => e
    @exception = e
  ensure
    if dbh
      dbh.disconnect if dbh.connected?
    end
  end

  rv
end
port=(num) click to toggle source

Sets the port that the Ping::TNS#ping_listener? method will use. If this is set, then a ping will only be attempted on this port regardless of what is in the tnsnames.ora file.

# File lib/net/tnsping.rb, line 89
def port=(num)
  @port = num
  @ports = [num]
end

Private Instance Methods

parse_tns_file(file=@tns_file, db=@database) click to toggle source

parse_tns_file

Search for the dsn entry within the tnsnames.ora file and get the host and port information. Private method.

# File lib/net/tnsping.rb, line 189
def parse_tns_file(file=@tns_file, db=@database)
  re_blank      = /^$/
  re_comment    = /^#/
  re_tns_sentry = /^#{db}.*?=/i                # specific entry
  re_tns_gentry = /^\w.*?=/                    # generic entry
  re_tns_pair   = /\w+\s*\=\s*[\w\.-]+/        # used to parse key=val
  re_keys       = /\bhost\b|\bport\b|\bsid\b/i

  data_string = ""
  found       = false

  IO.foreach(file){ |line|
    next if re_blank.match(line)
    next if re_comment.match(line)
    line.chomp!

    # Skip over lines until an entry for the db is found.
    match = re_tns_sentry.match(line)

    if match
      found = true
      data_string << match.post_match # slurp the rest of the line
      next
    end

    # Once found, slurp the lines into a variable until the next
    # db entry is encountered.
    if found
      break if re_tns_gentry.match(line)
      line.strip!
      data_string << line
    end
  }

  unless found
    raise Error, "unable to find '#{db}' in #{file}"
  end

  # Break each 'key = value' line into its parts
  data_string.scan(re_tns_pair).each{ |pair|
    key, value = pair.split("=")
    key.strip!
    value.strip!

    next unless re_keys.match(key)

    case key.downcase
      when 'host'
        @hosts.push(value)
      when 'port'
        @ports.push(value.to_i)
      when 'sid'
        @sid = value
    end
  }
end
ping_listener?()
Alias for: ping?