module Cryptic

This allows redirecting user sites and querying them. You can pass the following options into the various methods:

:master_site - the site ID (aav, mca, etc.) of the master site :grok_master - the URL of the Grok master host

Copyright (C) 2013-2015 OL2, Inc. All Rights Reserved.

Constants

BASE_PORT
DEFAULT_OPTIONS
SELECT_TIMEOUT

Default timeout for queries: 10 minutes

Public Instance Methods

dont_redirect_user_sites!(master_options = {}) click to toggle source
# File lib/cryptic/hoffa.rb, line 78
def dont_redirect_user_sites!(master_options = {})
  @already_redirected_user_sites = true
end
master_connection(options = {}) click to toggle source
# File lib/cryptic/hoffa.rb, line 70
def master_connection(options = {})
  @master_conn ||= Mysql2::Client.new(DEFAULT_OPTIONS.merge(:host => master_host(options)).merge(options))
end
master_host(options = {}) click to toggle source
# File lib/cryptic/hoffa.rb, line 50
def master_host(options = {})
  if options[:grok_master]
    options[:grok_master]
  else
    site = master_site(options)

    site ? "grok-master.#{site}.onlive.net" : "grok-master"
  end
end
master_site(options = {}) click to toggle source
# File lib/cryptic/hoffa.rb, line 36
def master_site(options = {})
  return ENV['CRYPTIC_MASTER_SITE'] if ENV['CRYPTIC_MASTER_SITE']
  return options[:master_site] if options[:master_site]

  reference_site = options[:grok_master] || `hostname -f`.chomp
  onlive_user_site = reference_site.split(".")[1]
  if onlive_user_site && (onlive_user_site =~ /^[A-Za-z]{3}$/ || onlive_user_site == "prod")
    nil # Use local
  else
    "aav"
  end

end
query_on_user_sites(query, sites = nil, master_options = {}, &block) click to toggle source
# File lib/cryptic/hoffa.rb, line 140
def query_on_user_sites(query, sites = nil, master_options = {}, &block)
  redirect_user_sites!(sites, master_options)

  sites = user_sites(master_options) unless sites
  conns = sites.map { |site| user_site_connection(site, master_options) }

  results = {}
  threads = []
  sites.zip(conns).each do |site, conn|
    t = nil
    begin
      t = Thread.new do
        # Global Interpreter Lock means we don't need to synchronize this
        results[site] = conn.query(query)
        block.call(results[site]) if block
      end
    rescue
      STDERR.puts "Errors on query for site #{site}:\n#{query}\n\n"
      STDERR.puts "Backtrace:\n#{$!.backtrace.join("\n")}\n"
    end
    threads.push(t)
  end
  threads.each(&:join)

  results
end
redirect_user_sites!(sites = nil, master_options = {}) click to toggle source
# File lib/cryptic/hoffa.rb, line 82
def redirect_user_sites!(sites = nil, master_options = {})
  return if @already_redirected_user_sites
  @already_redirected_user_sites = true

  sites = user_sites(master_options) unless sites

  user = `whoami`.chomp
  sites.each do |site|
    local_port = redirected_database_port_for_user_site(site, master_options)
    process = `pgrep -f #{local_port}`

    # TODO: use slaves where available (currently not for user sites)
    hostname = "grok.#{site}"

    ms = master_site(master_options)
    master_domain = ms ? "#{ms}.onlive.net" : ""

    cmd = "ssh -Nf -L#{local_port}:#{hostname}:3306 -o \"StrictHostKeyChecking no\" #{user}@shell.#{master_domain}"
    if process && !process.strip.empty?  # Redirection already active
      process.strip.split("\n").each do |p|
        pid = p.to_i
        cur_cmd = `ps -p #{pid}`.split("\n")[1].split(/\s+/, 4)[3]

        # If this same command was issued then we're already redirecting the site to the right port number.
        # But the double-quotes go away in the ps output.
        if cur_cmd != cmd.gsub('"', "")
          Process.kill("INT", pid)
          STDERR.puts "Remapping port #{local_port} for site #{site}.  Command: #{cmd} / Old Command: #{cur_cmd}"
          system(cmd) || raise("Failed port-map for site #{site}, port #{local_port}!")
        end
      end
    else
      STDERR.puts "Mapping port #{local_port} for site #{site}.  Command: #{cmd}"
      system(cmd) || raise("Failed port-map for site #{site}, port #{local_port}!")
    end
  end
end
redirected_database_port_for_user_site(site, master_options = {}) click to toggle source
# File lib/cryptic/hoffa.rb, line 120
def redirected_database_port_for_user_site(site, master_options = {})
  user_sites(master_options) unless @user_sites
  index = @user_sites[site]
  raise "No index for site #{site.inspect}!" unless index
  BASE_PORT + index
end
sites_in_maintenance() click to toggle source

TODO: figure out how to do this outside prod

# File lib/cryptic/hoffa.rb, line 168
def sites_in_maintenance
  begin
    stat = `curl http://mastermon.mca.onlive.net/glustats/glustats.json 2>/dev/null`
    glustat = JSON.parse(stat)
    sites = glustat.keys
    sites = sites.select do |site|
      glustat[site].is_a?(Hash) && glustat[site]["drain_state"] == "drain"
    end
    return sites
  rescue
    STDERR.puts "Error querying sites in maint: #{$!.message}\n#{$!.backtrace.join "\n"}"
    return []
  end
end
slave_connection(options = {}) click to toggle source
# File lib/cryptic/hoffa.rb, line 74
def slave_connection(options = {})
  @master_conn ||= Mysql2::Client.new(DEFAULT_OPTIONS.merge(:host => slave_host(options)).merge(options))
end
slave_host(options = {}) click to toggle source
# File lib/cryptic/hoffa.rb, line 60
def slave_host(options = {})
  if options[:grok_slave]
    options[:grok_slave]
  else
    site = master_site(options)  # There's not a separate "slave site" - just master & slave in the same master site.

    site ? "grokdbro.#{site}.onlive.net" : "grokdbro"
  end
end
user_site_connection(site = nil, master_options = {}) click to toggle source
# File lib/cryptic/hoffa.rb, line 127
def user_site_connection(site = nil, master_options = {})
  @user_site_connections ||= {}
  return @user_site_connections[site] if @user_site_connections[site]

  local_port = redirected_database_port_for_user_site(site)

  @user_site_connections[site] = Mysql2::Client.new DEFAULT_OPTIONS.merge(:port => local_port)
  @user_site_connections[site]
end
user_sites(master_options = {}) click to toggle source
# File lib/cryptic/hoffa.rb, line 27
def user_sites(master_options = {})
  return @user_sites.keys if @user_sites
  conn = slave_connection master_options
  # @user_sites maps site name to grok-master DB ID in the etl_control table. This ensures consistent local port mappings.
  @user_sites = {}
  conn.query("SELECT id, site FROM etl_control WHERE level = 'user' AND status = 'up'").each { |row| @user_sites[row["site"]] = row["id"] }
  @user_sites.keys
end