Sindbad~EG File Manager

Current Path : /proc/12558/cwd/opt/microsoft/omsagent/plugin/
Upload File :
Current File : //proc/12558/cwd/opt/microsoft/omsagent/plugin/mysql_workload_lib.rb

class MysqlWorkload_Lib
  require_relative 'oms_common'

  def initialize(host, port, username, password, database, encoding, mock_interface=nil)
    if mock_interface.nil?
      require 'mysql2'
    end
    @host = host
    @port = port
    @username = username
    @password = password
    @database = database
    @encoding = encoding
    @mock_interface = mock_interface
    @query_global_status = "SHOW GLOBAL STATUS"
    @query_variables = "SHOW VARIABLES"
    # total size of all databases in bytes (ServerDiskUseInBytes)
    @query_sizeof_all_databases = 'select "Total" as "Database", SUM(a.data_length) + SUM(a.index_length) as "Size (Bytes)" from information_schema.schemata b left join information_schema.tables a on b.schema_name = a.table_schema'
    # Execute a query to get a result set like:
    #   +--------------------+--------+-------------------------------+
    #   | Database           | Size (Bytes) |
    #  +--------------------+--------+--------------------------------+
    #   | Total             | 89998192 |
    #   +--------------------+--------+-------------------------------+

    if (@database == nil)

      # Number of tables in each database and the table size in bytes
      @query_get_database = 'select b.schema_name as "DatabaseName",COUNT(a.table_name) as "NumberOfTables", SUM(a.data_length) + SUM(a.index_length) as "SizeInBytes" from information_schema.schemata b left join information_schema.tables a on b.schema_name = a.table_schema group by b.schema_name'
      # Execute a query to get a result set like:
      #   +--------------------+--------+-------------------------------+
      #   | DatabaseName           | NumberOfTables | SizeInBytes |
      #  +--------------------+--------+--------------------------------+
      #   | information_schema |       28 |              8192 |
      #   +--------------------+--------+-------------------------------+
      # Note that Size may be NULL if database has no tables

    else
      # Number of tables in given database and the table size in bytes
      @query_get_database = 'select b.schema_name as "DatabaseName",COUNT(a.table_name) as "NumberOfTables",
                                                                       SUM(a.data_length) + SUM(a.index_length) as "SizeInBytes"
                                                                         from information_schema.schemata b
                                                                         left join information_schema.tables a on b.schema_name = a.table_schema
                                                                         where b.schema_name =\'' + @database + '\' group by b.schema_name'
    end
  end

  def get_connection
    # Opens a connection to mysql server instance
    begin
      return Mysql2::Client.new({
        :host => @host,
        :port => @port,
        :username => @username,
        :password => @password,
        :database => @database,
        :encoding => @encoding,
        :reconnect => true
      })
    rescue Exception => e
      @log.warn("mysql_workload: #{e}")
    end
  end
  
  def close_connection
    # check if close connection was called prior the client connection is created
    if @mysql != nil
      mysql.close
    end
  end
  
  def query(input_query)
    if @mock_interface.nil?
      @mysql ||= get_connection
      begin
        return @mysql.query(input_query, :cast => false, :cache_rows => false)
      rescue Exception => e
        @log.warn("mysql_workload: #{e}")
      end
    else
      return @mock_interface.query(input_query)
    end

  end

  def get_mysql_hostname
    query("SHOW VARIABLES LIKE 'hostname'").each do |row|
      return row.fetch('Value')
    end
  end

  def transform_row(row)
    # creates a new hash table where keys from the input row are replaced with the Variable_name key name, 
    # and the value of the key Variable_name. Case sensitive comparision.
    # Example {VariableName => connectedthreads, Value => 20} will be replaced with {connectedthreads = > 20}
    result = Hash.new()
    result.store(row["Variable_name"], row["Value"] )
    return result
  end

  def get_value (input_array, key_to_find)
    # finds given key in the array of hash tables 
    #and returns it's value, -1 otherwise
    value = -1
    input_array.each do |row|
      if (row.has_key?(key_to_find))
        value = row.fetch(key_to_find)
        break
      end
    end
    return value
  end

  def get_server_database_type
    # default to InnoDB if default_storage_engine not supported
    engine_info_value = "InnoDB"    
    #retrieve engine info from default_storage_engine
    query("show variables like 'default_storage_engine'").each do |row|
    engine_info_value = row.fetch('Value')
    end
    return engine_info_value
  end

  def get_server_database_version
    dbversion_info_value = 0 # default
    #retrieve database version info using innodb_version
    query("show variables like 'version'").each do |row|
    dbversion_info_value = row.fetch('Value')
    end  
    return dbversion_info_value
  end

  def get_server_stats
    global_stats = Array.new
    env_var_stats = Array.new
    #final list of counters that will be sent to oms. Filtered based on mysql spec.
    final_records = Array.new

    # populate all mysql performance and analytical data throw SHOW GLOBAL STATUS or SHOW VARIABLES
    q_global_status = query(@query_global_status)
    q_global_status.each do |row|
      new_row = transform_row(row)
      global_stats.push(new_row)
    end
    q_variables = query(@query_variables)
    q_variables.each do |row|
      new_row = transform_row(row)
      env_var_stats.push(new_row)
    end

    # Add Number of Connections
    num_connections = Hash.new
    num_connections_value = get_value(global_stats, "Threads_connected")
    num_connections.store("Number of Connections", num_connections_value)
    final_records.push(num_connections)

    # Add Maximum Allowed Connections
    max_connections = Hash.new
    max_connections_value = get_value(env_var_stats, "max_connections")
    max_connections.store("Maximum Allowed Connections", max_connections_value)
    final_records.push(max_connections)

    # Add Aborted Connections
    failed_connections = Hash.new
    failedconnections_value = get_value(global_stats, "Aborted_connects")
    failed_connections.store("Aborted Connections", failedconnections_value)
    final_records.push(failed_connections)

    # Add Uptime
    uptime = Hash.new
    uptime_value = get_value(global_stats, "Uptime")
    uptime.store("Uptime", uptime_value)
    final_records.push(uptime)

    # add KeyCacheHitPct
    key_cache_hit_pct = Hash.new
    key_reads = get_value(global_stats,'Key_reads')
    key_read_requests = get_value(global_stats,'Key_read_requests')
    value = 0.0 #initialize
    if key_read_requests.to_f != 0
      value = (key_reads.to_f / key_read_requests.to_f) * 100
    end
    key_cache_hit_pct.store("Key Cache Hit Pct", value.round(2))
    final_records.push(key_cache_hit_pct)

    # add MySQL Worst Case RAM Usage
    server_max_ram= Hash.new
    value = 0.0 #reset
    key_buffer_size = get_value(env_var_stats, 'key_buffer_size')
    read_buffer_size  = get_value(env_var_stats, 'read_buffer_size')
    sort_buffer_size = get_value(env_var_stats, 'sort_buffer_size')
    max_connections = get_value(env_var_stats, 'max_connections')
    value = key_buffer_size.to_f + (read_buffer_size.to_f + sort_buffer_size.to_f)*max_connections.to_f
    server_max_ram.store("MySQL Worst Case RAM Usage", value.round(2))
    final_records.push(server_max_ram)

    #Add MySQL Server Disk Usage In Bytes
    q_size_all_db = query(@query_sizeof_all_databases) # Total size
    q_size_all_db.each do |row|
      s_database = Hash.new()
      s_database.store("MySQL Server Disk Usage In Bytes",row["Size (Bytes)"])
      final_records.push(s_database)
    end

    #add SlowQueryPct
    slow_query_pct = Hash.new
    queries = get_value(global_stats, "Queries")
    if queries == -1
      # On MySQL 5.0 queries is not supported. instead use queries = questions + com_stmt_close + com_stmt_reset + com_stmt_prepare
      # On MySQL 5.1+, just use variable "queries"
      questions = get_value(global_stats, "Questions")
      com_stmt_close = get_value(global_stats, "Com_stmt_close")
      com_stmt_reset = get_Value(global_stats, "Com_stmt_reset")
      com_stmt_prepare = get_value(global_stats, "Com_stmt_prepare")
      queries = questions.to_i + com_stmt_close.to_i + com_stmt_reset.to_i + com_stmt_prepare.to_i
    end
    slow_query_pct.store("Slow Query Pct", queries )
    final_records.push(slow_query_pct)

    #add Key Cache Write Pct
    key_cache_write_pct = Hash.new()
    key_writes = get_value(global_stats,"Key_writes")
    key_writes_requests = get_value(global_stats,"Key_writes_requests")
    value = 0.0
    if(key_writes_requests.to_f != 0)
      value = (key_writes.to_f)/(key_writes_requests.to_f) * 100
    end
    key_cache_write_pct.store("Key Cache Write Pct", value.round(2))
    final_records.push(key_cache_write_pct)

    #add Query CacheHitPct
    query_cache_hit_pct = Hash.new()
    qcache_hits = get_value(global_stats,"Qcache_hits")
    com_select = get_value(global_stats,"Com_select")
    value = 0.0
    if (qcache_hits.to_f + com_select.to_f) != 0
      value = qcache_hits.to_f/(qcache_hits.to_f + com_select.to_f)*100
    end
    query_cache_hit_pct.store("Query Cache Hit Pct", value.round(2))
    final_records.push(query_cache_hit_pct)

    #add Query Cache Low memory Prunes
    query_cache_prunes_pct = Hash.new()
    qcache_prunes = get_value(global_stats,"Qcache_lowmem_prunes")
    value = 0.0
    if queries.to_f != 0
      value = (qcache_prunes.to_f/queries.to_f) * 100
    end
    query_cache_prunes_pct.store("Query Cache Low memory Prunes", value.round(2))
    final_records.push(query_cache_prunes_pct)

    #add TableCacheHitPct
    table_hit_pct = Hash.new()
    open_tables = get_value(global_stats,"Open_tables")
    opened_tables = get_value(global_stats,"Opened_tables")
    value = 0.0
    if opened_tables.to_f != 0
      value = open_tables.to_f/opened_tables.to_f * 100
    end
    table_hit_pct.store("Table Cache Hit Pct", value.round(2))
    final_records.push(table_hit_pct)

    #add TableLockContentionPct
    table_lock_pct = Hash.new()
    table_lock_waited = get_value(global_stats,"Table_locks_waited")
    table_lock_immediate = get_value(global_stats,"Table_locks_immediate")
    value = 0.0
    if (table_lock_waited.to_f + table_lock_immediate.to_f) != 0
      value = table_lock_waited.to_f/(table_lock_waited.to_f + table_lock_immediate.to_f) * 100
    end
    table_lock_pct.store("Table Lock Contention Pct", value.round(2))
    final_records.push(table_lock_pct)

    #add InnoDB Buffer Pool Hit Percent
    idb_hit_pct = Hash.new()
    innodb_buffer_pool_reads = get_value(global_stats,"Innodb_buffer_pool_reads")
    innodb_buffer_pool_read_requests = get_value(global_stats,"Innodb_buffer_pool_read_requests")
    value = 0.0
    if (innodb_buffer_pool_reads.to_f + innodb_buffer_pool_read_requests.to_f) != 0
      value = innodb_buffer_pool_reads.to_f/(innodb_buffer_pool_reads.to_f + innodb_buffer_pool_read_requests.to_f) * 100
    end
    idb_hit_pct.store("InnoDB Buffer Pool Hit Percent", value.round(2))
    final_records.push(idb_hit_pct)

    #add InnoDB Buffer Pool Percent Use
    idb_use_pct = Hash.new()
    innodb_buffer_pool_pages_data = get_value(global_stats,"Innodb_buffer_pool_pages_data")
    innodb_buffer_pool_pages_total = get_value(global_stats,"Innodb_buffer_pool_pages_total")
    value = 0.0
    if( innodb_buffer_pool_pages_total.to_f != 0)
      value = innodb_buffer_pool_pages_data.to_f/innodb_buffer_pool_pages_total.to_f * 100
    end
    idb_use_pct.store("InnoDB Buffer Pool Percent Use", value.round(2) )
    final_records.push(idb_use_pct)

    #add FullTableScanPct
    full_table_pct = Hash.new()
    handler_read_rnd = get_value(global_stats,"Handler_read_rnd")
    handler_read_first = get_value(global_stats,"Handler_read_first")
    handler_read_key = get_value(global_stats,"Handler_read_key")
    handler_read_next = get_value(global_stats,"Handler_read_next")
    handler_read_prev = get_value(global_stats,"Handler_read_prev")
    handler_read_rnd_next = get_value(global_stats,"Handler_read_rnd_next")
    full_scan_reads = handler_read_rnd.to_f + handler_read_rnd_next.to_f
    all_row_access  = handler_read_rnd.to_f + handler_read_first.to_f + handler_read_key.to_f + handler_read_next.to_f + handler_read_prev.to_f + handler_read_rnd_next.to_f
    value = 0.0 #reset
    if all_row_access != 0
      value = full_scan_reads/all_row_access * 100
    end
    full_table_pct.store("Full Table Scan Pct", value.round(2) )
    final_records.push(full_table_pct)

    # add SlaveStatus
    # add SlaveLag
    # add ServerCPU

    return final_records
  end

  # retrieve all databases or given database info of the mysql instance as a hashtable
  def get_server_databases (input_query)
    database_stats = Array.new

    #retrieve engine info from default_storage_engine
    #engine_info_value = get_server_database_type

    #retrieve database version info frm innodb_version
    #dbversion_info_value = get_server_database_version

    #get database table information
    # Example, execute a query to get a result set like:
    #   +--------------------+--------+-------------------------------+
    #   | DatabaseName           | NumberOfTables | SizeInBytes |
    #  +--------------------+--------+--------------------------------+
    #   | information_schema |       28 |              8192 |
    #   +--------------------+--------+-------------------------------+
    stmt = query(input_query)
    stmt.each do |row|
      #   row.store("Engine", engine_info_value)
      #   row.store("Version", dbversion_info_value)
      #   Add SizeAllocated
      #   Add SizeUnits
      #   Add LastBackup
      database_stats.push(row)
    end
    return database_stats
  end

  def get_mysql_instance
    # retrieve mysql instance stats later to be used in heartbeat
    result = Array.new
    row = Hash.new
    host_name = get_mysql_hostname

    row.store("ProductName", "MySQL Server")
    prod_id = host_name + ":" + @port.to_s
    row.store("ProductIdentifyingNumber", prod_id)
    row.store("ProductVendor", "Oracle")
    version_value = get_server_database_version
    row.store("ProductVersion", version_value.to_s)
    q_variables = query(@query_variables)
    server_id = get_value(q_variables,"server_id")
    row.store("SystemID", server_id.to_s)
    compile_os = get_value(q_variables,"version_compile_os")
    row.store("CollectionID", compile_os.to_s)
    row.store("Name", nil)
    socket_info = get_value(q_variables,"socket")
    row.store("SocketFile", socket_info.to_s)
    row.store("Port", @port.to_s)
    data_dir= get_value(q_variables,"datadir")
    row.store("DataDirectory", data_dir.to_s)
    log_error = get_value(q_variables,"log_error")
    row.store("ErrorLogFile", log_error.to_s)
    result.push(row)
    return result
  end

  # Convert data to oms format
  def enumerate (time)
    #final list of counters to send to oms filtered based on the counters supporeted by mysql plugin (per mysql spec)
    final_records = Array.new
    timestamp = OMS::Common.format_time(time)

    # get mysql global stats and variables
    mysqlserver_counters = Array.new
    server_stats = get_server_stats
    server_stats.each do |row|
      row.each {
        |key, value|
        mysql_server_counter = Hash.new
        mysql_server_counter.store("CounterName", key)
        mysql_server_counter.store("Value", value)
        mysqlserver_counters.push(mysql_server_counter)
      }
    end

    host_name = get_mysql_hostname
    # Only one mysql instance is supported
    mysql_instance = Hash.new
    mysql_instance["Timestamp"] = timestamp
    mysql_instance["Host"] = host_name
    mysql_instance["ObjectName"]="MySQL Server"
    mysql_instance_name = host_name + ":" + @port.to_s
    mysql_instance["InstanceName"] = mysql_instance_name
    mysql_instance["Collections"] = mysqlserver_counters
    #add the instance to the list
    final_records.push(mysql_instance)

    #get mysql database tables info
    result_dbs = get_server_databases(@query_get_database)

    result_dbs.each do |row|
      mysql_db_counters = Array.new
      db_instance = Hash.new
      db_instance["Timestamp"] = timestamp
      db_instance["Host"] = host_name
      db_instance["ObjectName"]="MySQL Database"
      db_instance["InstanceName"] = row["DatabaseName"]
      row.delete("DatabaseName") # leave only table name and size columns and remove database name
      row.each {
        |key, value|
        db_counter = Hash.new
        db_counter.store("CounterName", key)
        if value.nil?
          db_counter.store("Value", 0) # if the given database has no tables, set the size to 0 instead of nil
        else
          db_counter.store("Value", value)
        end
        mysql_db_counters.push(db_counter)
      }

      db_instance["Collections"] = mysql_db_counters
      #add the db instance to the list
      final_records.push(db_instance)
    end

    if final_records.length > 0
      wrapper = {
        "DataType"=>"LINUX_PERF_BLOB",
        "IPName"=>"LogManagement",
        "DataItems"=>final_records
      }
      return wrapper
    end
  end
end

Sindbad File Manager Version 1.0, Coded By Sindbad EG ~ The Terrorists