Class: JSS::DBConnection

Inherits:
Object show all
Includes:
Singleton
Defined in:
lib/jss/db_connection.rb,
lib/jss.rb

Overview

A mysql connection to the JSS database.

This is a singleton class, only one can exist at a time, and it is created, but not connected, automatically when the module loads.

Use it via the JSS::DB_CNX constant (for connection metadata) and the JSS::DB_CNX.db attribute (which contains the actual mysql query interface) for making queries

Direct MySQL access is minimal and discouraged, since it bypasses the API, and can be very dangerous. However, it's necessary to overcome some limitations of the API or to access custom tables.

While a database connction isn't required for most things, warnings will be sent to stderr when functionality is limited due to a lack of a database connection i.e. when JSS::DB_CNX.connected? == false

To make a connection with credentials, just call the #connect method thus:

JSS::DB_CNX.connect :server => 'server.company.com', :user => "user", :pw => "pw"

Other options include:

:db_name => which database to connect to, defaults to 'jamfsoftware'
:port => tcp port for connection to server, defaults to the standard mysql port.
:connect_timeout => seconds to wait before giving up on connection, defaults to 120
:read_timeout => seconds to wait before giving up on recieving data, defaults to 120
:write_timeout => seconds to wait before giving up on sending data, defaults to 120
:timeout => sets all three timeouts to the same value, defaults to 120

Calling JSS::DB_CNX.connect again will re-use any values not provided. but will create a new connection.

Constant Summary collapse

DEFAULT_DB_NAME =

The name of the JSS database on the mysql server

'jamfsoftware'.freeze
DFT_TIMEOUT =

give the connection a 60 second timeout, for really slow net connections (likeā€¦ from airplanes)

60
DFT_SOCKET =
'/var/mysql/mysql.sock'.freeze
DFT_PORT =

the default MySQL port

3306
DFT_CHARSET =

The default encoding in the tables - JAMF wisely uses UTF-8

'utf8'.freeze
SQL_DATE_FORMAT =

the strftime format for reading/writing dates in the db

'%Y-%m-%d %H:%M:%S'.freeze

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initializeDBConnection

Returns a new instance of DBConnection.



106
107
108
109
110
# File 'lib/jss/db_connection.rb', line 106

def initialize
  require 'mysql'
  @mysql = Mysql.init
  @connected = false
end

Instance Attribute Details

#connect_timeoutObject (readonly)

Returns the value of attribute connect_timeout.



101
102
103
# File 'lib/jss/db_connection.rb', line 101

def connect_timeout
  @connect_timeout
end

#connectedObject (readonly) Also known as: connected?

Returns the value of attribute connected.



104
105
106
# File 'lib/jss/db_connection.rb', line 104

def connected
  @connected
end

#db_nameObject (readonly)

Returns the value of attribute db_name.



100
101
102
# File 'lib/jss/db_connection.rb', line 100

def db_name
  @db_name
end

#portObject (readonly)

Returns the value of attribute port.



97
98
99
# File 'lib/jss/db_connection.rb', line 97

def port
  @port
end

#read_timeoutObject (readonly)

Returns the value of attribute read_timeout.



102
103
104
# File 'lib/jss/db_connection.rb', line 102

def read_timeout
  @read_timeout
end

#serverObject (readonly)

Returns the value of attribute server.



96
97
98
# File 'lib/jss/db_connection.rb', line 96

def server
  @server
end

#socketObject (readonly)

Returns the value of attribute socket.



98
99
100
# File 'lib/jss/db_connection.rb', line 98

def socket
  @socket
end

#userObject (readonly)

Returns the value of attribute user.



99
100
101
# File 'lib/jss/db_connection.rb', line 99

def user
  @user
end

#write_timeoutObject (readonly)

Returns the value of attribute write_timeout.



103
104
105
# File 'lib/jss/db_connection.rb', line 103

def write_timeout
  @write_timeout
end

Instance Method Details

#connect(args = {}) ⇒ true

Connect to the JSS MySQL database.

Parameters:

  • args (Hash) (defaults to: {})

    the keyed arguments for connection.

Options Hash (args):

  • :server (String)

    Required, the hostname of the JSS API server

  • :port (Integer)

    the port number to connect with, defaults to the default Mysql TCP port

  • :socket (String, Pathname)

    when the server is 'localhost', the path to the connection socket.

  • :db_name (String)

    the name of the database to use, defaults to 'jamfsoftware'

  • :user (String)

    Required, the mysql user to connect as

  • :pw (String, Symbol)

    Required, the password for that user, or :prompt, or :stdin If :prompt, the user is promted on the commandline to enter the password for the :user. If :stdin#, the password is read from a line of std in represented by the digit at #, so :stdin3 reads the passwd from the third line of standard input. defaults to line 2, if no digit is supplied. see JSS.stdin

  • :connect_timeout (Integer)

    the number of seconds to wait for an initial response, defaults to 120

  • :read_timeout (Integer)

    the number of seconds before read-request times out, defaults to 120

  • :write_timeout (Integer)

    the number of seconds before write-request times out, defaults to 120

  • :timeout (Integer)

    used for any of the timeouts that aren't explicitly set.

Returns:

  • (true)

    the connection was successfully made.



143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
# File 'lib/jss/db_connection.rb', line 143

def connect(args = {})
  begin
    disconnect if @connected
  rescue Mysql::ClientError::ServerGoneError
    @connected = false
  end

  # server might come frome several places
  # if not given in the args, use #hostname to figure out
  # which
  @server = args[:server] ? args[:server] : hostname

  # settings from config if they aren't in the args
  args[:port] ||= JSS::CONFIG.db_server_port ? JSS::CONFIG.db_server_port : Mysql::MYSQL_TCP_PORT
  args[:socket] ||= JSS::CONFIG.db_server_socket ? JSS::CONFIG.db_server_socket : DFT_SOCKET
  args[:db_name] ||= JSS::CONFIG.db_name ? JSS::CONFIG.db_name : DEFAULT_DB_NAME
  args[:user] ||= JSS::CONFIG.db_username
  args[:connect_timeout] ||= JSS::CONFIG.db_connect_timeout
  args[:read_timeout] ||= JSS::CONFIG.db_read_timeout
  args[:write_timeout] ||= JSS::CONFIG.db_write_timeout
  args[:charset] ||= DFT_CHARSET

  ### if one timeout was given, use it for all three
  args[:connect_timeout] ||= args[:timeout] ? args[:timeout] : DFT_TIMEOUT
  args[:read_timeout] ||= args[:timeout] ? args[:timeout] : DFT_TIMEOUT
  args[:write_timeout] ||= args[:timeout] ? args[:timeout] : DFT_TIMEOUT

  @port = args[:port]
  @socket = args[:socket]
  @mysql_name = args[:db_name]
  @user = args[:user]
  @connect_timeout = args[:connect_timeout]
  @read_timeout = args[:read_timeout]
  @write_timeout = args[:write_timeout]

  # make sure we have a user, pw, server
  raise JSS::MissingDataError, 'No MySQL user specified, or defined in configuration.' unless args[:user]
  raise JSS::MissingDataError, "Missing :pw (or :prompt/:stdin) for user '#{@user}'" unless args[:pw]
  raise JSS::MissingDataError, 'No MySQL Server hostname specified, or listed in configuration.' unless @server

  @pw = if args[:pw] == :prompt
          JSS.prompt_for_password "Enter the password for the MySQL user #{@user}@#{@server}:"
        elsif args[:pw].is_a?(Symbol) && args[:pw].to_s.start_with?('stdin')
          args[:pw].to_s =~ /^stdin(\d+)$/
          line = Regexp.last_match(1)
          line ||= 2
          JSS.stdin line
        else
          args[:pw]
        end

  @mysql = Mysql.init

  @mysql.options Mysql::OPT_CONNECT_TIMEOUT, @connect_timeout
  @mysql.options Mysql::OPT_READ_TIMEOUT, @read_timeout
  @mysql.options Mysql::OPT_WRITE_TIMEOUT, @write_timeout
  @mysql.charset = args[:charset]
  @mysql.connect @server, @user, @pw, @mysql_name, @port, @socket

  @connected = true

  @server
rescue Mysql::ServerError::NotSupportedAuthMode => e
  raise Mysql::ServerError::AccessDeniedError, "Probable unknown MySQL user '#{@user}'. Original error was 'Mysql::ServerError::NotSupportedAuthMode: #{e}' which is sometimes raised when the user does not exist on the server."
end

#dbMysql

Returns The mysql database connection itself.

Returns:

  • (Mysql)

    The mysql database connection itself

Raises:



212
213
214
215
# File 'lib/jss/db_connection.rb', line 212

def db
  raise JSS::InvalidConnectionError, 'No database connection. Please use JSS::DB_CNX.connect' unless JSS::DB_CNX.connected?
  @mysql
end

#disconnectObject

close the connection to the database it'll have to be re-connected before using again



221
222
223
224
225
226
227
228
229
230
231
232
# File 'lib/jss/db_connection.rb', line 221

def disconnect
  @mysql.close! if @mysql.protocol
  @server = nil
  @port = nil
  @socket = nil
  @user = nil
  @connection_timeout = DFT_TIMEOUT
  @read_timeout = DFT_TIMEOUT
  @write_timeout = DFT_TIMEOUT
  @connected = false
  nil
end

#hostnameString

The server to which we are connected, or will try connecting to if none is specified with the call to #connect

Returns:

  • (String)

    the hostname of the server



271
272
273
274
275
276
277
278
279
# File 'lib/jss/db_connection.rb', line 271

def hostname
  # return it if already set
  return @server if @server
  # otherwise, from the config
  srvr = JSS::CONFIG.db_server_name
  # otherwise, assume its on the JSS server to which this client talks
  srvr ||= JSS::Client.jss_server
  srvr
end

#valid_server?(server, port = DFT_PORT) ⇒ Boolean

Test that a given hostname is a MySQL server

Parameters:

  • server (String)

    The hostname to test

Returns:

  • (Boolean)

    does the server host a MySQL server?



240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
# File 'lib/jss/db_connection.rb', line 240

def valid_server?(server, port = DFT_PORT)
  mysql = Mysql.init
  mysql.options Mysql::OPT_CONNECT_TIMEOUT, 60
  mysql.charset = DFT_CHARSET

  begin
    # this connection should get an access denied error if there is
    # a mysql server there. I'm assuming no one will use this username
    # and pw for anything real
    # Also with newer versions of mysql, a Mysql instance that has
    # never authenticated will raise Mysql::ServerError::NotSupportedAuthMode
    # rather than Mysql::ServerError::AccessDeniedError, until a
    # successful connection is made. After that, re-connecting will
    # raise AccessDeniedError when credentials are invalid.

    mysql.connect server, 'notArealUser', "definatelyNotA#{$PROCESS_ID}password", 'not_a_db', port

  rescue Mysql::ServerError::AccessDeniedError, Mysql::ServerError::NotSupportedAuthMode
    return true
  rescue
    return false
  end
  false
end