cryptic-hoffa 0.0.16

Sign up to get free protection for your applications and to get access to all the features.
Files changed (31) hide show
  1. data/.gitignore +17 -0
  2. data/.ruby-version +1 -0
  3. data/Gemfile +7 -0
  4. data/LICENSE.txt +1 -0
  5. data/RBPKG +5 -0
  6. data/README.md +22 -0
  7. data/Rakefile +1 -0
  8. data/bin/dev-glu-tunnel.sh +5 -0
  9. data/bin/highway-from-sql +76 -0
  10. data/bin/hoffa +195 -0
  11. data/bin/prod-glu-tunnel.sh +5 -0
  12. data/binstubs/.gitkeep +0 -0
  13. data/cassandra_migrations/20131024000000_create_keyspace_Cryptic_down.cql.erb +1 -0
  14. data/cassandra_migrations/20131024000000_create_keyspace_Cryptic_up.cql.erb +2 -0
  15. data/cassandra_migrations/20131025000000_create_table_overlay_actions_down.cql.erb +3 -0
  16. data/cassandra_migrations/20131025000000_create_table_overlay_actions_up.cql.erb +15 -0
  17. data/cassandra_migrations/20131114488001_create_client_survey_user_site_tables_down.cql.erb +9 -0
  18. data/cassandra_migrations/20131114488001_create_client_survey_user_site_tables_up.cql.erb +94 -0
  19. data/cassandra_migrations/20131206944013_create_table_cryptic_place_marker_down.cql.erb +3 -0
  20. data/cassandra_migrations/20131206944013_create_table_cryptic_place_marker_up.cql.erb +10 -0
  21. data/cassandra_migrations/20140115150807_add_place_marker_to_place_markers_down.cql +3 -0
  22. data/cassandra_migrations/20140115150807_add_place_marker_to_place_markers_up.cql +3 -0
  23. data/cassandra_migrations/20140116281758_repartition_place_markers_table_down.cql +10 -0
  24. data/cassandra_migrations/20140116281758_repartition_place_markers_table_up.cql +10 -0
  25. data/cryptic-hoffa.gemspec +34 -0
  26. data/database.yml +13 -0
  27. data/jenkins.recipe +67 -0
  28. data/lib/cryptic/hoffa.rb +183 -0
  29. data/lib/cryptic/hoffa/version.rb +6 -0
  30. data/vendor/.gitkeep +0 -0
  31. metadata +240 -0
data/.gitignore ADDED
@@ -0,0 +1,17 @@
1
+ *.gem
2
+ *.rbc
3
+ .bundle
4
+ .config
5
+ .yardoc
6
+ Gemfile.lock
7
+ InstalledFiles
8
+ _yardoc
9
+ coverage
10
+ doc/
11
+ lib/bundler/man
12
+ pkg
13
+ rdoc
14
+ spec/reports
15
+ test/tmp
16
+ test/version_tmp
17
+ tmp
data/.ruby-version ADDED
@@ -0,0 +1 @@
1
+ 1.9.3-p327
data/Gemfile ADDED
@@ -0,0 +1,7 @@
1
+ source 'https://rubygems.org'
2
+ source 'http://gems.onlive.com'
3
+
4
+ # Specify your gem's dependencies in cryptic-hoffa.gemspec
5
+ gemspec
6
+
7
+ gem "cryptic-highway", ">= 0.0.8"
data/LICENSE.txt ADDED
@@ -0,0 +1 @@
1
+ Copyright (c) 2013 OL2, Inc. All Rights Reserved.
data/RBPKG ADDED
@@ -0,0 +1,5 @@
1
+ name: ol_cryptic_hoffa
2
+ version: %gitver%
3
+ dest: /opt/cryptic_hoffa
4
+ rvm: ruby-1.9.3-p327
5
+ binstubs_dir: binstubs
data/README.md ADDED
@@ -0,0 +1,22 @@
1
+ # Cryptic::Hoffa
2
+
3
+ Cryptic Highway is OnLive's Cassandra series reader/writer code. The
4
+ cryptic-highway gem is the "public" part, in that it could be made
5
+ open-source without a problem.
6
+
7
+ Other parts of our code are very OnLive-specific, such as reading
8
+ messages from OMQ and our specific user sites. This code lives in the
9
+ cryptic-hoffa gem.
10
+
11
+ This gem currently includes two programs:
12
+
13
+ * hoffa - an OMQ reader that writes into Cassandra
14
+ * highway-from-sql - an ActiveRecord-based SQL reader that writes into Cassandra
15
+
16
+ ## Installation
17
+
18
+ "gem install cryptic-hoffa", if you have gems.onlive.com set up as a gem server.
19
+
20
+ ## Usage
21
+
22
+ (To be added)
data/Rakefile ADDED
@@ -0,0 +1 @@
1
+ require "bundler/gem_tasks"
@@ -0,0 +1,5 @@
1
+ #! /bin/sh
2
+ # Copyright (C) 2013 OL2, inc. All Rights Reserved.
3
+
4
+ echo Starting ssh to manage the tunnel to site $1
5
+ ssh shell01.aav.onlive.net -L 61618:glu01.$1:61618 -L 60080:rpcweb.$1:62999
@@ -0,0 +1,76 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ # Copyright (C) 2013-2014 OL2, Inc. All Rights Reserved.
4
+
5
+ require "trollop"
6
+ require "yaml"
7
+ require "active_record"
8
+ require "erubis"
9
+
10
+ require "cryptic/highway"
11
+ require "cryptic/hoffa"
12
+
13
+ opts = Trollop::options do
14
+ banner <<BANNER
15
+ Highway-from-sql is a tool to move database information from SQL DBs into Cassandra.
16
+ Its database.yml file must include a "sql_db" database connection in standard ActiveRecord format
17
+
18
+ Specific options:
19
+ BANNER
20
+ opt :db_file, "Database YAML file", :default => "database.yml", :type => String
21
+ opt :site, "User site", :required => true, :type => String
22
+ opt :cass_host, "Cassandra host(s)", :type => :strings, :default => [ "localhost" ]
23
+ opt :cass_key, "Name for Cass keyspace", :type => String, :default => "cryptic"
24
+ end
25
+
26
+ SITE = opts[:site]
27
+
28
+ raise "Wrong args!" if ARGV.size > 0
29
+
30
+ Cryptic.redirect_user_sites! [opts[:site]]
31
+ site_port = Cryptic.redirected_database_port_for_user_site opts[:site]
32
+
33
+ eruby = Erubis::Eruby.new File.read(opts[:db_file])
34
+ eruby_string = eruby.evaluate :site => SITE, :site_port => site_port
35
+ db_yml = YAML.load eruby_string
36
+
37
+ STDERR.puts "Connecting with DB options: #{db_yml["sql_db"].inspect}"
38
+ ActiveRecord::Base.establish_connection db_yml["sql_db"]
39
+
40
+ class ClientSurveyUSBDevice < ActiveRecord::Base
41
+ self.table_name = "client_survey_usb_device"
42
+ end
43
+
44
+ ramp = Cryptic::Highway::Ramp.new("client_survey_usb_device") do
45
+ hosts opts[:cass_host]
46
+ keyspace opts[:cass_key]
47
+ site opts[:site]
48
+ marker_name "client_survey_usb_device-#{opts[:site]}"
49
+ to_update do |place_marker|
50
+ STDERR.puts "Updating forward from #{place_marker.inspect}!"
51
+ sql_query = <<-QUERY
52
+ SELECT client_survey_timestamp, u.* FROM client_survey_usb_device u
53
+ JOIN client_survey cs ON cs.client_survey_id = u.client_survey_id
54
+ WHERE client_survey_timestamp >= '2014-01-14 00:00:00'
55
+ #{ place_marker.nil? ? "" : "AND client_survey_timestamp >= '#{place_marker}'" }
56
+ LIMIT #{ db_yml["block_size"] || 100 }
57
+ QUERY
58
+ STDERR.puts "Query: #{sql_query}"
59
+ objs = ClientSurveyUSBDevice.find_by_sql sql_query
60
+ STDERR.puts "Query returned #{objs.size} records!"
61
+
62
+ data = objs.map do |o|
63
+ item = o.attributes
64
+ item["timestamp"] = item.delete "client_survey_timestamp"
65
+ item
66
+ end
67
+ ramp.insert_rows data
68
+
69
+ STDERR.puts "New last marker: #{objs.last["client_survey_timestamp"].to_s.inspect}"
70
+ objs.last["client_survey_timestamp"].to_s
71
+ end
72
+ end
73
+
74
+ ramp.update_until_done
75
+
76
+ puts "Highway: Finished. Exiting."
data/bin/hoffa ADDED
@@ -0,0 +1,195 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require "trollop"
4
+ require "cryptic/highway"
5
+ require "cryptic/highway/utils"
6
+ require "cryptic/hoffa"
7
+ require "rubyglu"
8
+
9
+ require "logger"
10
+ ERR_LOG = Logger.new STDERR
11
+ ERR_LOG.level = Logger::INFO
12
+ OUT_LOG = Logger.new STDOUT
13
+ OUT_LOG.level = Logger::INFO
14
+
15
+ # Make a fake logger object that just passes all operations to both log objects
16
+ BOTH_LOGS = Cryptic::Highway::Utils::MultiDispatcher.new(ERR_LOG, OUT_LOG)
17
+
18
+ BOTH_LOGS.info "Started hoffa with arguments: #{ARGV.inspect}"
19
+
20
+ opts = Trollop::options do
21
+ banner <<-BANNER
22
+ Read JSON messages from OMQ (GLU), insert them into Cassandra.
23
+ BANNER
24
+ opt :omq_host, "OMQ broker hostname", :type => String
25
+ opt :omq_host_site, "OMQ site of specified host", :type => String
26
+ opt :omq_port, "OMQ broker port", :type => Integer
27
+ opt :omq_debug, "OMQ debug mode"
28
+ opt :cass_host, "Cassandra host", :type => :strings, :default => [ "localhost" ]
29
+ #opt :cass_port, "Cassandra port", :type => Integer, :default => 9042
30
+
31
+ opt :cass_table, "Name for Cass table/CF", :type => String, :required => true
32
+ opt :cass_key, "Name for Cass keyspace", :type => String, :default => "Cryptic"
33
+
34
+ opt :duplication,"Number of times to duplicate each site.", :type => Integer, :default => 1
35
+
36
+ opt :user_sites, "OnLive User site(s) for reading data, comma-separated.", :type => String
37
+ end
38
+
39
+ if opts[:user_sites] && (opts[:omq_host] || opts[:omq_port])
40
+ ERR_LOG.error <<-ERR_STRING
41
+ Can't specify user site(s) with individual host/port for OMQ!
42
+ Sites: #{opts[:user_sites].inspect}
43
+ Host: #{opts[:omq_host]}
44
+ Port: #{opts[:omq_port]}"
45
+ ERR_STRING
46
+ raise "Can't specify user site(s) with individual host/port for OMQ!"
47
+ end
48
+
49
+ # Doing a kill -9 on this should kill the parent and all children, but nobody else.
50
+ Process.setpgrp
51
+
52
+ if opts[:user_sites]
53
+ if sites == "query"
54
+ sites = Cryptic.user_sites
55
+ else
56
+ sites = opts[:user_sites].split ","
57
+ end
58
+ omq_brokers = sites.map { |s| ["omq://omq.#{s}.onlive.net:61618", s] }
59
+ else
60
+ # Use omq_host and omq_port, defaulting to localhost:61618
61
+ omq_brokers = [["omq://#{opts[:omq_host] || "localhost"}:#{opts[:omq_port] || 61618}", opts[:omq_host_site]]]
62
+ end
63
+
64
+ BOTH_LOGS.warn "(Re)starting Hoffa."
65
+
66
+ broker_list = omq_brokers.flat_map { |broker| (1..opts[:duplication]).map { |i| broker + [ i ] } }
67
+
68
+ opts[:slots] = ARGV.length > 0 ? ARGV : [ "*" ]
69
+ puts "Connecting to OMQ broker(s): #{broker_list.inspect}"
70
+
71
+ processes = []
72
+
73
+ # Create a new process, return the pid.
74
+ def new_process(omq_broker, user_site, dup_num, opts = {})
75
+ fork do
76
+ $0 = "Hoffa worker (ruby), Site: #{user_site || "none"} Dup Num: #{dup_num || "none"}"
77
+ Process.setrlimit :RSS, 10_000_000 # This should be way higher than needed
78
+
79
+ consecutive_failures = 0
80
+ loop do
81
+ # Re-instantiate Cassandra connection in each process and after OMQ errors.
82
+ # This works around problems with down nodes not always being marked up
83
+ # again after Cassandra recovery. We want to prevent cases where we keep
84
+ # looping and keep failing... Also, for the same reason, don't share
85
+ # the connection between processes.
86
+ ramp = Cryptic::Highway::Ramp.new(opts[:cass_table]) do
87
+ hosts opts[:cass_host]
88
+ keyspace opts[:cass_key]
89
+ site user_site
90
+ end
91
+
92
+ begin
93
+ GLU::services do
94
+ debug_mode if opts[:omq_debug]
95
+ use_broker omq_broker
96
+ use_name "ruby_omq_tail"
97
+ no_clear
98
+
99
+ consumer opts[:slots] do |message|
100
+ if message.type == 'Log'
101
+ puts "#{message[:timestamp]} LOG-#{message[:level]}(#{message[:componentName]}.#{message[:componentVersion]}) " +
102
+ "#{message[:sourceFileName]}@#{message[:sourceHostName]}: #{message[:message]}"
103
+ elsif not message.name.nil?
104
+ data = message.message_parameters
105
+ sentTo = message.headers["sentTo"]
106
+
107
+ puts "Data to insert: #{sentTo} / #{data.inspect}" # In case of error on insert, put it in the logfile
108
+
109
+ actions = data['actions']
110
+ base = {
111
+ "userSessionKey" => data["userSessionKey"],
112
+ "appSessionId" => data["appSessionId"],
113
+ "topic" => sentTo,
114
+ "sequenceNum" => message.headers["sequenceNum"],
115
+ }
116
+ rows = actions.map { |action| action.merge base }
117
+
118
+ ramp.insert_rows rows
119
+ consecutive_failures = 0 # Reset number of consecutive failures
120
+ else
121
+ BOTH_LOGS.warn "Nil name on message? #{message.inspect}"
122
+ end
123
+ end
124
+ end
125
+ rescue
126
+ # One more failure since the last success
127
+ consecutive_failures += 1
128
+
129
+ is_in_maint = Cryptic.sites_in_maintenance & [ user_site ]
130
+ maint_msg = is_in_maint ? "(In maint)" : "(Not in maint)"
131
+
132
+ OUT_LOG.error "Error caused GLU exit: #{$!.class} / #{$!.message}!"
133
+ ERR_LOG.error <<-ERROR_MESSAGE
134
+ #{maint_msg} Error caused GLU exit: #{$!.class} / #{$!.message}
135
+ #{$!.backtrace.join(" \n")}
136
+ ERROR_MESSAGE
137
+ puts "#{maint_msg} Error caused GLU exit: #{$!.class}: #{$!.message}"
138
+
139
+ if consecutive_failures >= 10
140
+ # That's nearly a minute of consecutive failure, or maybe
141
+ # even longer. Kill the process, in case something is
142
+ # royally and globally screwed. It may just be that
143
+ # Cassandra or OMQ is down, in which case this restarting is
144
+ # fine too.
145
+ exit! # Always use "exit!" at end of a forked Ruby process
146
+ end
147
+
148
+ BOTH_LOGS.warn "#{maint_msg} Looping after 5 second delay (60 in maint)..."
149
+ puts "#{maint_msg} Looping after 5 second delay (60 in maint)..."
150
+ sleep is_in_maint ? 60.0 : 5.0
151
+ end
152
+ end # loop do
153
+ end # fork
154
+ end
155
+
156
+ broker_list.each do |omq_broker, user_site, dup_num|
157
+ pid = new_process omq_broker, user_site, dup_num, opts
158
+ processes << { pid: pid, omq_broker: omq_broker, site: user_site, dup_num: dup_num }
159
+ end
160
+
161
+ $0 = "Hoffa master (ruby), Sites: #{opts[:user_sites].inspect}"
162
+
163
+ Signal.trap("TERM") do
164
+ processes.each { |process| `kill -TERM #{process[:pid]}` }
165
+ shutdown()
166
+ end
167
+
168
+ loop do
169
+
170
+ # Default: reap children every 5 seconds, check for data every 1 minute.
171
+ # That means 12 5-second iterations.
172
+ 12.times do
173
+ tries = 0
174
+ pid, status = Process.waitpid2 0, Process::WNOHANG # Did any child die?
175
+
176
+ # Reap child processes until nobody dies, or up to 10
177
+ while pid != nil && tries < 10
178
+ # Find the descriptor for the dead child process
179
+ proc = processes.detect { |p| p[:pid] == pid }
180
+
181
+ processes -= [ proc ]
182
+ pid = new_process proc[:omq_broker], proc[:site], opts
183
+ processes << { pid: pid, omq_broker: proc[:omq_broker], site: proc[:site] }
184
+ tries += 1
185
+ pid, status = Process.waitpid2 0, Process::WNOHANG # Did any child die?
186
+ end
187
+
188
+ sleep 5 # Reap children and check data no more often than this
189
+ end
190
+
191
+ # TODO: check if process is writing, somehow, and restart if not.
192
+ end
193
+
194
+ ERR_LOG.error "Finished! Shouldn't get here."
195
+ raise "Finished! Shouldn't get here."
@@ -0,0 +1,5 @@
1
+ #! /bin/sh
2
+ # Copyright (C) 2013 OL2, inc. All Rights Reserved.
3
+
4
+ echo Starting ssh to manage the tunnel to site $1
5
+ ssh shell01.mca.onlive.net -L 61618:omq.$1:61618 -L 60080:rpcweb.$1:62999
data/binstubs/.gitkeep ADDED
File without changes
@@ -0,0 +1 @@
1
+ DROP KEYSPACE "cryptic";
@@ -0,0 +1,2 @@
1
+ CREATE KEYSPACE "cryptic" WITH REPLICATION =
2
+ { 'class' : 'SimpleStrategy', 'replication_factor' : <%= ENV['CASS_REPLICATION'] || 1 %> };
@@ -0,0 +1,3 @@
1
+ use "cryptic";
2
+
3
+ DROP TABLE "overlay_actions";
@@ -0,0 +1,15 @@
1
+ -- Create table for purchase-flow analytics and other actions in the overlay.
2
+
3
+ use "cryptic";
4
+
5
+ CREATE TABLE overlay_actions (
6
+ "partition_key" varchar, // Partition by site and time bucket
7
+ "topic" varchar, // OMQ topic
8
+ "name" text, // overlay action name
9
+ "timestamp" bigint, // nanosec since the epoch
10
+ "sequenceNum" int,
11
+ "appSessionId" varchar,
12
+ "userSessionKey" varchar,
13
+ "actionKey" varchar,
14
+ "tags" map<text,text>,
15
+ PRIMARY KEY ("partition_key", "topic", "actionKey"));
@@ -0,0 +1,9 @@
1
+ -- Create tables for user-site client_survey data
2
+
3
+ use "cryptic";
4
+
5
+ DROP TABLE client_survey;
6
+ DROP TABLE client_survey_usb_device;
7
+ DROP TABLE client_survey_monitor;
8
+ DROP TABLE network_test_result;
9
+ DROP TABLE spi_profile_log;
@@ -0,0 +1,94 @@
1
+ -- Create tables for user-site client_survey data
2
+
3
+ use "cryptic";
4
+
5
+ CREATE TABLE client_survey (
6
+ "partition_key" varchar, // Partition by site and time bucket
7
+ "client_survey_id" varchar,
8
+ "client_survey_timestamp" bigint,
9
+ "user_id" varchar,
10
+ "user_session_key" varchar,
11
+ "aero" boolean,
12
+ "audio_driver" blob,
13
+ "client_version" varchar,
14
+ "decode_time" float,
15
+ "input_keyboard" boolean,
16
+ "input_mouse" boolean,
17
+ "operating_system" varchar,
18
+ "operating_system_build" int,
19
+ "operating_system_build_str" varchar,
20
+ "pixel_shader_version" float,
21
+ "processor_cores" int,
22
+ "processor_current_frequency" int,
23
+ "processor_details" varchar,
24
+ "processor_features" varchar,
25
+ "processor_frequency" int,
26
+ "processor_name" varchar,
27
+ "processor_vendor" varchar,
28
+ "system_page_used" int,
29
+ "system_ram" int,
30
+ "system_ram_available" int,
31
+ "video_card" varchar,
32
+ "video_card_driver" varchar,
33
+ "video_memory" varchar,
34
+ "video_modes" varchar,
35
+ "client_survey_auto_id" bigint,
36
+ PRIMARY KEY ("partition_key","client_survey_id"));
37
+
38
+ CREATE TABLE client_survey_monitor (
39
+ "partition_key" varchar, // Partition by site and time bucket
40
+ "client_survey_monitor_id" bigint,
41
+ "client_survey_id" varchar,
42
+ "timestamp" bigint,
43
+ "edid" blob,
44
+ "monitor_name" blob,
45
+ "manufacture_date" blob,
46
+ "horizontal_cm" int,
47
+ "vertical_cm" int,
48
+ "name" blob,
49
+ "primary" boolean,
50
+ "refresh_rate" int,
51
+ "resolution" blob,
52
+ PRIMARY KEY ("partition_key","client_survey_monitor_id"));
53
+
54
+ CREATE TABLE client_survey_usb_device (
55
+ "partition_key" varchar, // Partition by site and time bucket
56
+ "client_survey_usb_device_id" bigint,
57
+ "timestamp" bigint,
58
+ "client_survey_id" varchar,
59
+ "client_survey_usb_device_type_id" int,
60
+ "name" blob, // overlay action name
61
+ PRIMARY KEY ("partition_key","client_survey_usb_device_id"));
62
+
63
+ CREATE TABLE network_test_result (
64
+ "partition_key" varchar, // Partition by site and time bucket
65
+ "network_test_result_id" bigint,
66
+ "test_timestamp" timeuuid,
67
+ "user_session_key" varchar,
68
+ "user_id" varchar,
69
+ "bandwidth" bigint,
70
+ "connection" varchar,
71
+ "decode_time" double,
72
+ "jitter" double,
73
+ "latency" double,
74
+ "machine_power_save_possible" boolean,
75
+ "packet_loss" double,
76
+ "error" blob,
77
+ PRIMARY KEY ("partition_key","network_test_result_id"));
78
+
79
+ CREATE TABLE spi_profile_log (
80
+ "partition_key" varchar, // Partition by site and time bucket
81
+ "id" bigint,
82
+ "timestamp" bigint,
83
+ "hw_id" varchar,
84
+ "hw_id_type" varchar,
85
+ "device_type" varchar,
86
+ "slice_id" varchar,
87
+ "session_key" varchar,
88
+ "user_id" varchar,
89
+ "connection_type" varchar,
90
+ "tested_rate_kbps" int,
91
+ "desired_rate_kbps" int,
92
+ "decode_time_ms" bigint,
93
+ "computer_speed" varchar,
94
+ PRIMARY KEY ("partition_key","id"));
@@ -0,0 +1,3 @@
1
+ use "cryptic";
2
+
3
+ DROP TABLE "place_markers";
@@ -0,0 +1,10 @@
1
+ -- Create tables for place markers for Cryptic Highway and other Cryptic scripts and processes
2
+
3
+ use "cryptic";
4
+
5
+ CREATE TABLE "place_markers" (
6
+ "partition_key" varchar, // Partition by site and time bucket
7
+ "marker_type" varchar,
8
+ "timestamp" bigint,
9
+ "notes" varchar,
10
+ PRIMARY KEY ("partition_key","marker_type","timestamp"));
@@ -0,0 +1,3 @@
1
+ use cryptic;
2
+
3
+ ALTER TABLE place_markers DROP place_marker;
@@ -0,0 +1,3 @@
1
+ use cryptic;
2
+
3
+ ALTER TABLE place_markers ADD place_marker blob;
@@ -0,0 +1,10 @@
1
+ use "cryptic";
2
+
3
+ DROP TABLE place_markers;
4
+
5
+ CREATE TABLE "place_markers" (
6
+ "partition_key" varchar, // Partition by site and time bucket
7
+ "marker_type" varchar,
8
+ "timestamp" bigint,
9
+ "notes" varchar,
10
+ PRIMARY KEY ("partition_key","marker_type","timestamp"));
@@ -0,0 +1,10 @@
1
+ use "cryptic";
2
+
3
+ DROP TABLE place_markers;
4
+
5
+ CREATE TABLE "place_markers" (
6
+ "place_marker" blob,
7
+ "marker_type" varchar,
8
+ "timestamp" bigint,
9
+ "notes" varchar,
10
+ PRIMARY KEY ("marker_type"));
@@ -0,0 +1,34 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'cryptic/hoffa/version'
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = "cryptic-hoffa"
8
+ spec.version = Cryptic::Hoffa::VERSION
9
+ spec.authors = ["Noah Gibbs"]
10
+ spec.email = ["noah@onlive.com"]
11
+ spec.description = %q{Gateway OMQ data to Cassandra}
12
+ spec.summary = %q{Gateway OMQ data to Cassandra}
13
+ spec.homepage = ""
14
+ spec.license = "Proprietary"
15
+
16
+ spec.files = (File.exist?("MANIFEST") ? File.read("MANIFEST") : `git ls-files`).split($/)
17
+ spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
18
+ spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
19
+ spec.require_paths = ["lib"]
20
+
21
+ spec.add_development_dependency "bundler", "~> 1.3"
22
+ spec.add_development_dependency "rake"
23
+ spec.add_development_dependency "cassandra_migrate"
24
+
25
+ spec.add_runtime_dependency "trollop"
26
+ spec.add_runtime_dependency "cryptic-highway"
27
+ spec.add_runtime_dependency "rubyglu"
28
+ spec.add_runtime_dependency "multi_json"
29
+ spec.add_runtime_dependency "erubis"
30
+
31
+ # For Highway-from-SQL and querying user sites
32
+ spec.add_runtime_dependency "activerecord"
33
+ spec.add_runtime_dependency "mysql2"
34
+ end
data/database.yml ADDED
@@ -0,0 +1,13 @@
1
+ ---
2
+ sql_db:
3
+ adapter: mysql2
4
+ database: analytics
5
+ host: 127.0.0.1
6
+ port: <%= @site_port %>
7
+ username: analytics
8
+ password: 5parklin3
9
+ secure_auth: false
10
+ socket: /tmp/mysql.sock
11
+ pool: 5
12
+ timeout: 5000
13
+ block_size: 1500
data/jenkins.recipe ADDED
@@ -0,0 +1,67 @@
1
+ #!/bin/bash
2
+ # Jenkins recipe to build rpm via rbpkgbuild
3
+
4
+ ### SETUP ENVIRONMENT ###
5
+ : ${RVM_SH_PATH:=/etc/profile.d/rvm.sh}
6
+ . $RVM_SH_PATH
7
+ progname=$(basename $0)
8
+
9
+ # Should this use the specific ruby version specified?
10
+ cd $WORKSPACE
11
+ rvm use 1.9.3-p392
12
+
13
+ log() {
14
+ echo "$progname: ""$@"
15
+ }
16
+
17
+ err() {
18
+ log "$@" >&2
19
+ }
20
+
21
+ ### UPDATE TAGS ###
22
+ git fetch --tags
23
+
24
+ ### INSTALL GEMS ###
25
+ rm -rf .bundle
26
+ rm -rf Gemfile.lock
27
+ rm -rf vendor/bundle
28
+ cp -r bin bindir # bin gets replaced with stubs by rbpkgbuild
29
+ echo "Do bundle install --path=vendor/bundle"
30
+ bundle install --path=vendor/bundle
31
+
32
+ ### RUN TESTS ###
33
+
34
+
35
+ ### CREATE MANIFEST ###
36
+ echo "Creating MANIFEST for deployment"
37
+ git ls-files > MANIFEST
38
+
39
+ ### CREATE RPM ###
40
+ log "Creating rpm with rbpkgbuild"
41
+ /usr/local/bin/rbpkgbuild
42
+ if [ $? -ne 0 ]; then
43
+ err "Error creating rpms"
44
+ exit 2
45
+ else
46
+ log "rpmpkgbuild completed without error."
47
+ fi
48
+
49
+ ### PUBLISH RPM ###
50
+ # Define RPM destinations
51
+ EL5REPO='mm-apt.mmm.onlive.net::incoming-rpm/integration/'
52
+ EL6REPO='mm-apt.mmm.onlive.net::incoming-rpm/integration6/'
53
+
54
+ # Push rpms to repo
55
+ ## depending on build server, el5 or el6 rpms are generated
56
+ log "Publishing rpms to mm-apt via rsync"
57
+ rsync *el6*.rpm $EL6REPO || rsync *el5*.rpm $EL5REPO
58
+ if [ $? -ne 0 ]; then
59
+ err "Error pushing rpm to repo"
60
+ exit 3
61
+ else
62
+ log "Publishing completed without error."
63
+ fi
64
+
65
+ ### END ###
66
+ log "Congratulations! We made it to the end of the jenkins recipe."
67
+ exit 0
@@ -0,0 +1,183 @@
1
+ # Copyright (C) 2013-2015 OL2, Inc. All Rights Reserved.
2
+
3
+ require "cryptic/hoffa/version"
4
+ require "mysql2"
5
+
6
+ # This allows redirecting user sites and querying them.
7
+ # You can pass the following options into the
8
+ # various methods:
9
+ #
10
+ # :master_site - the site ID (aav, mca, etc.) of the master site
11
+ # :grok_master - the URL of the Grok master host
12
+ #
13
+
14
+ module Cryptic
15
+ extend self
16
+
17
+ BASE_PORT=28808
18
+ DEFAULT_OPTIONS = {
19
+ :host => "127.0.0.1",
20
+ :port => 3306,
21
+ :username => "analytics",
22
+ :password => "5parklin3",
23
+ :secure_auth => false,
24
+ :database => "analytics",
25
+ }
26
+
27
+ def user_sites(master_options = {})
28
+ return @user_sites.keys if @user_sites
29
+ conn = slave_connection master_options
30
+ # @user_sites maps site name to grok-master DB ID in the etl_control table. This ensures consistent local port mappings.
31
+ @user_sites = {}
32
+ conn.query("SELECT id, site FROM etl_control WHERE level = 'user' AND status = 'up'").each { |row| @user_sites[row["site"]] = row["id"] }
33
+ @user_sites.keys
34
+ end
35
+
36
+ def master_site(options = {})
37
+ return ENV['CRYPTIC_MASTER_SITE'] if ENV['CRYPTIC_MASTER_SITE']
38
+ return options[:master_site] if options[:master_site]
39
+
40
+ reference_site = options[:grok_master] || `hostname -f`.chomp
41
+ onlive_user_site = reference_site.split(".")[1]
42
+ if onlive_user_site && (onlive_user_site =~ /^[A-Za-z]{3}$/ || onlive_user_site == "prod")
43
+ nil # Use local
44
+ else
45
+ "aav"
46
+ end
47
+
48
+ end
49
+
50
+ def master_host(options = {})
51
+ if options[:grok_master]
52
+ options[:grok_master]
53
+ else
54
+ site = master_site(options)
55
+
56
+ site ? "grok-master.#{site}.onlive.net" : "grok-master"
57
+ end
58
+ end
59
+
60
+ def slave_host(options = {})
61
+ if options[:grok_slave]
62
+ options[:grok_slave]
63
+ else
64
+ site = master_site(options) # There's not a separate "slave site" - just master & slave in the same master site.
65
+
66
+ site ? "grokdbro.#{site}.onlive.net" : "grokdbro"
67
+ end
68
+ end
69
+
70
+ def master_connection(options = {})
71
+ @master_conn ||= Mysql2::Client.new(DEFAULT_OPTIONS.merge(:host => master_host(options)).merge(options))
72
+ end
73
+
74
+ def slave_connection(options = {})
75
+ @master_conn ||= Mysql2::Client.new(DEFAULT_OPTIONS.merge(:host => slave_host(options)).merge(options))
76
+ end
77
+
78
+ def dont_redirect_user_sites!(master_options = {})
79
+ @already_redirected_user_sites = true
80
+ end
81
+
82
+ def redirect_user_sites!(sites = nil, master_options = {})
83
+ return if @already_redirected_user_sites
84
+ @already_redirected_user_sites = true
85
+
86
+ sites = user_sites(master_options) unless sites
87
+
88
+ user = `whoami`.chomp
89
+ sites.each do |site|
90
+ local_port = redirected_database_port_for_user_site(site, master_options)
91
+ process = `pgrep -f #{local_port}`
92
+
93
+ # TODO: use slaves where available (currently not for user sites)
94
+ hostname = "grok.#{site}"
95
+
96
+ ms = master_site(master_options)
97
+ master_domain = ms ? "#{ms}.onlive.net" : ""
98
+
99
+ cmd = "ssh -Nf -L#{local_port}:#{hostname}:3306 -o \"StrictHostKeyChecking no\" #{user}@shell.#{master_domain}"
100
+ if process && !process.strip.empty? # Redirection already active
101
+ process.strip.split("\n").each do |p|
102
+ pid = p.to_i
103
+ cur_cmd = `ps -p #{pid}`.split("\n")[1].split(/\s+/, 4)[3]
104
+
105
+ # If this same command was issued then we're already redirecting the site to the right port number.
106
+ # But the double-quotes go away in the ps output.
107
+ if cur_cmd != cmd.gsub('"', "")
108
+ Process.kill("INT", pid)
109
+ STDERR.puts "Remapping port #{local_port} for site #{site}. Command: #{cmd} / Old Command: #{cur_cmd}"
110
+ system(cmd) || raise("Failed port-map for site #{site}, port #{local_port}!")
111
+ end
112
+ end
113
+ else
114
+ STDERR.puts "Mapping port #{local_port} for site #{site}. Command: #{cmd}"
115
+ system(cmd) || raise("Failed port-map for site #{site}, port #{local_port}!")
116
+ end
117
+ end
118
+ end
119
+
120
+ def redirected_database_port_for_user_site(site, master_options = {})
121
+ user_sites(master_options) unless @user_sites
122
+ index = @user_sites[site]
123
+ raise "No index for site #{site.inspect}!" unless index
124
+ BASE_PORT + index
125
+ end
126
+
127
+ def user_site_connection(site = nil, master_options = {})
128
+ @user_site_connections ||= {}
129
+ return @user_site_connections[site] if @user_site_connections[site]
130
+
131
+ local_port = redirected_database_port_for_user_site(site)
132
+
133
+ @user_site_connections[site] = Mysql2::Client.new DEFAULT_OPTIONS.merge(:port => local_port)
134
+ @user_site_connections[site]
135
+ end
136
+
137
+ # Default timeout for queries: 10 minutes
138
+ SELECT_TIMEOUT = 600
139
+
140
+ def query_on_user_sites(query, sites = nil, master_options = {}, &block)
141
+ redirect_user_sites!(sites, master_options)
142
+
143
+ sites = user_sites(master_options) unless sites
144
+ conns = sites.map { |site| user_site_connection(site, master_options) }
145
+
146
+ results = {}
147
+ threads = []
148
+ sites.zip(conns).each do |site, conn|
149
+ t = nil
150
+ begin
151
+ t = Thread.new do
152
+ # Global Interpreter Lock means we don't need to synchronize this
153
+ results[site] = conn.query(query)
154
+ block.call(results[site]) if block
155
+ end
156
+ rescue
157
+ STDERR.puts "Errors on query for site #{site}:\n#{query}\n\n"
158
+ STDERR.puts "Backtrace:\n#{$!.backtrace.join("\n")}\n"
159
+ end
160
+ threads.push(t)
161
+ end
162
+ threads.each(&:join)
163
+
164
+ results
165
+ end
166
+
167
+ # TODO: figure out how to do this outside prod
168
+ def sites_in_maintenance
169
+ begin
170
+ stat = `curl http://mastermon.mca.onlive.net/glustats/glustats.json 2>/dev/null`
171
+ glustat = JSON.parse(stat)
172
+ sites = glustat.keys
173
+ sites = sites.select do |site|
174
+ glustat[site].is_a?(Hash) && glustat[site]["drain_state"] == "drain"
175
+ end
176
+ return sites
177
+ rescue
178
+ STDERR.puts "Error querying sites in maint: #{$!.message}\n#{$!.backtrace.join "\n"}"
179
+ return []
180
+ end
181
+ end
182
+
183
+ end
@@ -0,0 +1,6 @@
1
+ # Copyright (C) 2013-2015 OL2, Inc. All Rights Reserved.
2
+ module Cryptic
3
+ module Hoffa
4
+ VERSION = "0.0.16"
5
+ end
6
+ end
data/vendor/.gitkeep ADDED
File without changes
metadata ADDED
@@ -0,0 +1,240 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: cryptic-hoffa
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.16
5
+ prerelease:
6
+ platform: ruby
7
+ authors:
8
+ - Noah Gibbs
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2015-02-04 00:00:00.000000000 Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: bundler
16
+ requirement: !ruby/object:Gem::Requirement
17
+ none: false
18
+ requirements:
19
+ - - ~>
20
+ - !ruby/object:Gem::Version
21
+ version: '1.3'
22
+ type: :development
23
+ prerelease: false
24
+ version_requirements: !ruby/object:Gem::Requirement
25
+ none: false
26
+ requirements:
27
+ - - ~>
28
+ - !ruby/object:Gem::Version
29
+ version: '1.3'
30
+ - !ruby/object:Gem::Dependency
31
+ name: rake
32
+ requirement: !ruby/object:Gem::Requirement
33
+ none: false
34
+ requirements:
35
+ - - ! '>='
36
+ - !ruby/object:Gem::Version
37
+ version: '0'
38
+ type: :development
39
+ prerelease: false
40
+ version_requirements: !ruby/object:Gem::Requirement
41
+ none: false
42
+ requirements:
43
+ - - ! '>='
44
+ - !ruby/object:Gem::Version
45
+ version: '0'
46
+ - !ruby/object:Gem::Dependency
47
+ name: cassandra_migrate
48
+ requirement: !ruby/object:Gem::Requirement
49
+ none: false
50
+ requirements:
51
+ - - ! '>='
52
+ - !ruby/object:Gem::Version
53
+ version: '0'
54
+ type: :development
55
+ prerelease: false
56
+ version_requirements: !ruby/object:Gem::Requirement
57
+ none: false
58
+ requirements:
59
+ - - ! '>='
60
+ - !ruby/object:Gem::Version
61
+ version: '0'
62
+ - !ruby/object:Gem::Dependency
63
+ name: trollop
64
+ requirement: !ruby/object:Gem::Requirement
65
+ none: false
66
+ requirements:
67
+ - - ! '>='
68
+ - !ruby/object:Gem::Version
69
+ version: '0'
70
+ type: :runtime
71
+ prerelease: false
72
+ version_requirements: !ruby/object:Gem::Requirement
73
+ none: false
74
+ requirements:
75
+ - - ! '>='
76
+ - !ruby/object:Gem::Version
77
+ version: '0'
78
+ - !ruby/object:Gem::Dependency
79
+ name: cryptic-highway
80
+ requirement: !ruby/object:Gem::Requirement
81
+ none: false
82
+ requirements:
83
+ - - ! '>='
84
+ - !ruby/object:Gem::Version
85
+ version: '0'
86
+ type: :runtime
87
+ prerelease: false
88
+ version_requirements: !ruby/object:Gem::Requirement
89
+ none: false
90
+ requirements:
91
+ - - ! '>='
92
+ - !ruby/object:Gem::Version
93
+ version: '0'
94
+ - !ruby/object:Gem::Dependency
95
+ name: rubyglu
96
+ requirement: !ruby/object:Gem::Requirement
97
+ none: false
98
+ requirements:
99
+ - - ! '>='
100
+ - !ruby/object:Gem::Version
101
+ version: '0'
102
+ type: :runtime
103
+ prerelease: false
104
+ version_requirements: !ruby/object:Gem::Requirement
105
+ none: false
106
+ requirements:
107
+ - - ! '>='
108
+ - !ruby/object:Gem::Version
109
+ version: '0'
110
+ - !ruby/object:Gem::Dependency
111
+ name: multi_json
112
+ requirement: !ruby/object:Gem::Requirement
113
+ none: false
114
+ requirements:
115
+ - - ! '>='
116
+ - !ruby/object:Gem::Version
117
+ version: '0'
118
+ type: :runtime
119
+ prerelease: false
120
+ version_requirements: !ruby/object:Gem::Requirement
121
+ none: false
122
+ requirements:
123
+ - - ! '>='
124
+ - !ruby/object:Gem::Version
125
+ version: '0'
126
+ - !ruby/object:Gem::Dependency
127
+ name: erubis
128
+ requirement: !ruby/object:Gem::Requirement
129
+ none: false
130
+ requirements:
131
+ - - ! '>='
132
+ - !ruby/object:Gem::Version
133
+ version: '0'
134
+ type: :runtime
135
+ prerelease: false
136
+ version_requirements: !ruby/object:Gem::Requirement
137
+ none: false
138
+ requirements:
139
+ - - ! '>='
140
+ - !ruby/object:Gem::Version
141
+ version: '0'
142
+ - !ruby/object:Gem::Dependency
143
+ name: activerecord
144
+ requirement: !ruby/object:Gem::Requirement
145
+ none: false
146
+ requirements:
147
+ - - ! '>='
148
+ - !ruby/object:Gem::Version
149
+ version: '0'
150
+ type: :runtime
151
+ prerelease: false
152
+ version_requirements: !ruby/object:Gem::Requirement
153
+ none: false
154
+ requirements:
155
+ - - ! '>='
156
+ - !ruby/object:Gem::Version
157
+ version: '0'
158
+ - !ruby/object:Gem::Dependency
159
+ name: mysql2
160
+ requirement: !ruby/object:Gem::Requirement
161
+ none: false
162
+ requirements:
163
+ - - ! '>='
164
+ - !ruby/object:Gem::Version
165
+ version: '0'
166
+ type: :runtime
167
+ prerelease: false
168
+ version_requirements: !ruby/object:Gem::Requirement
169
+ none: false
170
+ requirements:
171
+ - - ! '>='
172
+ - !ruby/object:Gem::Version
173
+ version: '0'
174
+ description: Gateway OMQ data to Cassandra
175
+ email:
176
+ - noah@onlive.com
177
+ executables:
178
+ - dev-glu-tunnel.sh
179
+ - highway-from-sql
180
+ - hoffa
181
+ - prod-glu-tunnel.sh
182
+ extensions: []
183
+ extra_rdoc_files: []
184
+ files:
185
+ - .gitignore
186
+ - .ruby-version
187
+ - Gemfile
188
+ - LICENSE.txt
189
+ - RBPKG
190
+ - README.md
191
+ - Rakefile
192
+ - bin/dev-glu-tunnel.sh
193
+ - bin/highway-from-sql
194
+ - bin/hoffa
195
+ - bin/prod-glu-tunnel.sh
196
+ - binstubs/.gitkeep
197
+ - cassandra_migrations/20131024000000_create_keyspace_Cryptic_down.cql.erb
198
+ - cassandra_migrations/20131024000000_create_keyspace_Cryptic_up.cql.erb
199
+ - cassandra_migrations/20131025000000_create_table_overlay_actions_down.cql.erb
200
+ - cassandra_migrations/20131025000000_create_table_overlay_actions_up.cql.erb
201
+ - cassandra_migrations/20131114488001_create_client_survey_user_site_tables_down.cql.erb
202
+ - cassandra_migrations/20131114488001_create_client_survey_user_site_tables_up.cql.erb
203
+ - cassandra_migrations/20131206944013_create_table_cryptic_place_marker_down.cql.erb
204
+ - cassandra_migrations/20131206944013_create_table_cryptic_place_marker_up.cql.erb
205
+ - cassandra_migrations/20140115150807_add_place_marker_to_place_markers_down.cql
206
+ - cassandra_migrations/20140115150807_add_place_marker_to_place_markers_up.cql
207
+ - cassandra_migrations/20140116281758_repartition_place_markers_table_down.cql
208
+ - cassandra_migrations/20140116281758_repartition_place_markers_table_up.cql
209
+ - cryptic-hoffa.gemspec
210
+ - database.yml
211
+ - jenkins.recipe
212
+ - lib/cryptic/hoffa.rb
213
+ - lib/cryptic/hoffa/version.rb
214
+ - vendor/.gitkeep
215
+ homepage: ''
216
+ licenses:
217
+ - Proprietary
218
+ post_install_message:
219
+ rdoc_options: []
220
+ require_paths:
221
+ - lib
222
+ required_ruby_version: !ruby/object:Gem::Requirement
223
+ none: false
224
+ requirements:
225
+ - - ! '>='
226
+ - !ruby/object:Gem::Version
227
+ version: '0'
228
+ required_rubygems_version: !ruby/object:Gem::Requirement
229
+ none: false
230
+ requirements:
231
+ - - ! '>='
232
+ - !ruby/object:Gem::Version
233
+ version: '0'
234
+ requirements: []
235
+ rubyforge_project:
236
+ rubygems_version: 1.8.23
237
+ signing_key:
238
+ specification_version: 3
239
+ summary: Gateway OMQ data to Cassandra
240
+ test_files: []