onesnooper-server 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.gitignore +34 -0
- data/Gemfile +3 -0
- data/LICENSE +202 -0
- data/README.md +115 -0
- data/Rakefile +26 -0
- data/bin/onesnooper-server +45 -0
- data/config/onesnooper-server.yml +41 -0
- data/examples/monitoring.data +66 -0
- data/examples/send-dummy-data +39 -0
- data/lib/onesnooper-server.rb +32 -0
- data/lib/onesnooper_server/datagram.rb +19 -0
- data/lib/onesnooper_server/datagrams.rb +7 -0
- data/lib/onesnooper_server/datagrams/failure_datagram.rb +9 -0
- data/lib/onesnooper_server/datagrams/invalid_datagram.rb +9 -0
- data/lib/onesnooper_server/datagrams/success_datagram.rb +60 -0
- data/lib/onesnooper_server/log.rb +74 -0
- data/lib/onesnooper_server/payload_parser.rb +143 -0
- data/lib/onesnooper_server/request_handler.rb +85 -0
- data/lib/onesnooper_server/settings.rb +12 -0
- data/lib/onesnooper_server/sql_store.rb +65 -0
- data/lib/onesnooper_server/store.rb +23 -0
- data/lib/onesnooper_server/stores.rb +7 -0
- data/lib/onesnooper_server/stores/invalid_store.rb +10 -0
- data/lib/onesnooper_server/stores/mongodb_store.rb +23 -0
- data/lib/onesnooper_server/stores/mysql_store.rb +17 -0
- data/lib/onesnooper_server/stores/sqlite_store.rb +10 -0
- data/lib/onesnooper_server/udp_handler.rb +50 -0
- data/lib/onesnooper_server/version.rb +4 -0
- data/migrations/001_create_db.rb +60 -0
- data/onesnooper-server.gemspec +41 -0
- data/spec/helpers/.keep +0 -0
- data/spec/onesnooper_server_spec.rb +7 -0
- data/spec/spec_helper.rb +13 -0
- metadata +265 -0
@@ -0,0 +1,66 @@
|
|
1
|
+
ARCH=x86_64
|
2
|
+
MODELNAME="Intel(R) Xeon(R) CPU E5649 @ 2.53GHz"
|
3
|
+
DS_LOCATION_USED_MB=149998
|
4
|
+
DS_LOCATION_TOTAL_MB=1092886
|
5
|
+
DS_LOCATION_FREE_MB=887373
|
6
|
+
DS = [
|
7
|
+
ID = 0,
|
8
|
+
USED_MB = 149998,
|
9
|
+
TOTAL_MB = 1092886,
|
10
|
+
FREE_MB = 887373
|
11
|
+
]
|
12
|
+
DS = [
|
13
|
+
ID = 120,
|
14
|
+
USED_MB = 149998,
|
15
|
+
TOTAL_MB = 1092886,
|
16
|
+
FREE_MB = 887373
|
17
|
+
]
|
18
|
+
HOSTNAME=host1.localhost
|
19
|
+
VM_POLL=YES
|
20
|
+
VM=[
|
21
|
+
ID=12036,
|
22
|
+
DEPLOY_ID=one-12036,
|
23
|
+
POLL="NETRX=247208960 NETTX=1786718208 USEDCPU=60.3 USEDMEMORY=16777216 NAME=one-12036 STATE=a" ]
|
24
|
+
VM=[
|
25
|
+
ID=12037,
|
26
|
+
DEPLOY_ID=one-12037,
|
27
|
+
POLL="NETRX=231688192 NETTX=1806806016 USEDCPU=60.1 USEDMEMORY=16777216 NAME=one-12037 STATE=a" ]
|
28
|
+
VM=[
|
29
|
+
ID=12039,
|
30
|
+
DEPLOY_ID=one-12039,
|
31
|
+
POLL="NETRX=155998208 NETTX=2421689344 USEDCPU=0.1 USEDMEMORY=1048576 NAME=one-12039 STATE=a" ]
|
32
|
+
VM=[
|
33
|
+
ID=12139,
|
34
|
+
DEPLOY_ID=one-12139,
|
35
|
+
POLL="NETRX=4646912 NETTX=433727488 USEDCPU=0.2 USEDMEMORY=1048576 NAME=one-12139 STATE=a" ]
|
36
|
+
VM=[
|
37
|
+
ID=12031,
|
38
|
+
DEPLOY_ID=one-12031,
|
39
|
+
POLL="NETRX=7949312 NETTX=1946089472 USEDCPU=0.1 USEDMEMORY=1048576 NAME=one-12031 STATE=a" ]
|
40
|
+
VM=[
|
41
|
+
ID=12032,
|
42
|
+
DEPLOY_ID=one-12032,
|
43
|
+
POLL="NETRX=9022464 NETTX=2323913728 USEDCPU=0.2 USEDMEMORY=3145728 NAME=one-12032 STATE=a" ]
|
44
|
+
VM=[
|
45
|
+
ID=12034,
|
46
|
+
DEPLOY_ID=one-12034,
|
47
|
+
POLL="NETRX=332258304 NETTX=4284767232 USEDCPU=50.5 USEDMEMORY=16777216 NAME=one-12034 STATE=a" ]
|
48
|
+
VM=[
|
49
|
+
ID=-1,
|
50
|
+
DEPLOY_ID=Domain-0,
|
51
|
+
POLL="NETRX=0 NETTX=0 USEDCPU=3.7 USEDMEMORY=5233724 NAME=Domain-0 STATE=a" ]
|
52
|
+
VM=[
|
53
|
+
ID=12035,
|
54
|
+
DEPLOY_ID=one-12035,
|
55
|
+
POLL="NETRX=209708032 NETTX=1790325760 USEDCPU=59.5 USEDMEMORY=16777216 NAME=one-12035 STATE=a" ]
|
56
|
+
VERSION="4.10.1"
|
57
|
+
HYPERVISOR=xen
|
58
|
+
TOTALCPU=2400
|
59
|
+
CPUSPEED=2533
|
60
|
+
TOTALMEMORY=100654080
|
61
|
+
FREEMEMORY=20727808
|
62
|
+
USEDMEMORY=79926272
|
63
|
+
USEDCPU=244
|
64
|
+
FREECPU=2156
|
65
|
+
NETTX=16400482
|
66
|
+
NETRX=1170391
|
@@ -0,0 +1,39 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
# -------------------------------------------------------------------------- #
|
4
|
+
# Licensed under the Apache License, Version 2.0 (the "License"); you may #
|
5
|
+
# not use this file except in compliance with the License. You may obtain #
|
6
|
+
# a copy of the License at #
|
7
|
+
# #
|
8
|
+
# http://www.apache.org/licenses/LICENSE-2.0 #
|
9
|
+
# #
|
10
|
+
# Unless required by applicable law or agreed to in writing, software #
|
11
|
+
# distributed under the License is distributed on an "AS IS" BASIS, #
|
12
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. #
|
13
|
+
# See the License for the specific language governing permissions and #
|
14
|
+
# limitations under the License. #
|
15
|
+
#--------------------------------------------------------------------------- #
|
16
|
+
|
17
|
+
# external dependencies
|
18
|
+
require 'rubygems'
|
19
|
+
|
20
|
+
require 'socket'
|
21
|
+
require 'base64'
|
22
|
+
require 'resolv'
|
23
|
+
require 'ipaddr'
|
24
|
+
|
25
|
+
# add local dirs to load path if necessary
|
26
|
+
lib = File.expand_path(File.join('..', '..', 'lib'), __FILE__)
|
27
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
28
|
+
|
29
|
+
# add example dirs
|
30
|
+
examples = File.expand_path(File.join('..'), __FILE__)
|
31
|
+
|
32
|
+
# prepare data
|
33
|
+
udp_socket = UDPSocket.new
|
34
|
+
result = "SUCCESS"
|
35
|
+
host_id = 527
|
36
|
+
data64 = Base64::encode64(File.read(File.join(examples, 'monitoring.data'))).strip.delete("\n")
|
37
|
+
|
38
|
+
# send data
|
39
|
+
udp_socket.send("MONITOR #{result} #{host_id} #{data64}\n", 0, 'localhost', 9000)
|
@@ -0,0 +1,32 @@
|
|
1
|
+
# Internals of the onesnooper-server application. Classes
|
2
|
+
# in this namespace should not be used by external libraries.
|
3
|
+
module OnesnooperServer; end
|
4
|
+
|
5
|
+
# internal ruby dependencies
|
6
|
+
require 'date'
|
7
|
+
require 'ipaddr'
|
8
|
+
require 'base64'
|
9
|
+
|
10
|
+
# active support stuff
|
11
|
+
require 'active_support'
|
12
|
+
require 'active_support/core_ext'
|
13
|
+
require 'active_support/json'
|
14
|
+
require 'active_support/inflector'
|
15
|
+
require 'active_support/notifications'
|
16
|
+
|
17
|
+
# external dependencies
|
18
|
+
require 'eventmachine'
|
19
|
+
require 'settingslogic'
|
20
|
+
|
21
|
+
# internal components
|
22
|
+
require 'onesnooper_server/version'
|
23
|
+
require 'onesnooper_server/settings'
|
24
|
+
require 'onesnooper_server/log'
|
25
|
+
require 'onesnooper_server/store'
|
26
|
+
require 'onesnooper_server/sql_store'
|
27
|
+
require 'onesnooper_server/stores'
|
28
|
+
require 'onesnooper_server/datagram'
|
29
|
+
require 'onesnooper_server/datagrams'
|
30
|
+
require 'onesnooper_server/payload_parser'
|
31
|
+
require 'onesnooper_server/request_handler'
|
32
|
+
require 'onesnooper_server/udp_handler'
|
@@ -0,0 +1,19 @@
|
|
1
|
+
# Base class for all datagram processing classes. Defines
|
2
|
+
# required stub methods. No functionality is implemented here.
|
3
|
+
class OnesnooperServer::Datagram
|
4
|
+
|
5
|
+
# Initializes class instances.
|
6
|
+
#
|
7
|
+
# @param params [Hash] hash-like with params
|
8
|
+
def initialize(params = {})
|
9
|
+
@params = params
|
10
|
+
end
|
11
|
+
|
12
|
+
# Runs datagram processing for the chosen datagram type.
|
13
|
+
#
|
14
|
+
# @param deferred_callback [::EventMachine::DefaultDeferrable] response callback
|
15
|
+
def run(deferred_callback)
|
16
|
+
fail "This method needs to be implemented in subclasses"
|
17
|
+
end
|
18
|
+
|
19
|
+
end
|
@@ -0,0 +1,7 @@
|
|
1
|
+
# Wrapper module for all available datagram processing
|
2
|
+
# classes. Each class has to implement method stubs
|
3
|
+
# outlined in `OnesnooperServer::Datagram`.
|
4
|
+
module OnesnooperServer::Datagrams; end
|
5
|
+
|
6
|
+
# Load all available datagram types
|
7
|
+
Dir.glob(File.join(File.dirname(__FILE__), 'datagrams', "*.rb")) { |datagram_file| require datagram_file.chomp('.rb') }
|
@@ -0,0 +1,9 @@
|
|
1
|
+
# Handles processing of datagrams with FAILED monitoring
|
2
|
+
# messages. Doesn't perform any backend operations.
|
3
|
+
class OnesnooperServer::Datagrams::FailureDatagram < ::OnesnooperServer::Datagram
|
4
|
+
|
5
|
+
def run(deferred_callback)
|
6
|
+
::EventMachine.defer { deferred_callback.fail "Failed monitoring result will not be recorded" }
|
7
|
+
end
|
8
|
+
|
9
|
+
end
|
@@ -0,0 +1,9 @@
|
|
1
|
+
# Handles processing of datagrams with invalid
|
2
|
+
# messages. Doesn't perform any backend operations.
|
3
|
+
class OnesnooperServer::Datagrams::InvalidDatagram < ::OnesnooperServer::Datagram
|
4
|
+
|
5
|
+
def run(deferred_callback)
|
6
|
+
::EventMachine.defer { deferred_callback.fail "Invalid monitoring result will not be recorded" }
|
7
|
+
end
|
8
|
+
|
9
|
+
end
|
@@ -0,0 +1,60 @@
|
|
1
|
+
# Handles processing of datagrams with SUCESS monitoring
|
2
|
+
# messages. Performs 'save' operation on the backend.
|
3
|
+
class OnesnooperServer::Datagrams::SuccessDatagram < ::OnesnooperServer::Datagram
|
4
|
+
|
5
|
+
def run(deferred_callback)
|
6
|
+
::EventMachine.defer do
|
7
|
+
if parse_payload! && store_all!
|
8
|
+
deferred_callback.succeed "Successful monitoring result was " \
|
9
|
+
"recorded in #{store_info.join(', ')}"
|
10
|
+
else
|
11
|
+
deferred_callback.fail "Processing partially or completely failed, see logs"
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
private
|
17
|
+
|
18
|
+
# Decodes and parses datagram payload into a
|
19
|
+
# hash-like structure. Modification is done
|
20
|
+
# in-place.
|
21
|
+
def parse_payload!
|
22
|
+
@params[:payload] = ::OnesnooperServer::PayloadParser.parse(@params[:payload])
|
23
|
+
end
|
24
|
+
|
25
|
+
# Stores decoded and parsed payload in all
|
26
|
+
# enabled data stores. Errors are logged
|
27
|
+
# but not otherwise reported.
|
28
|
+
#
|
29
|
+
# @return [Boolean] success in all stores
|
30
|
+
def store_all!
|
31
|
+
if @params[:payload].blank?
|
32
|
+
::OnesnooperServer::Log.warn "[#{self.class.name}] Skipping empty payload " \
|
33
|
+
"from ONE ID:#{@params[:host_id]}"
|
34
|
+
return false
|
35
|
+
end
|
36
|
+
|
37
|
+
all_good = true
|
38
|
+
@params[:stores].each do |store|
|
39
|
+
begin
|
40
|
+
::OnesnooperServer::Log.debug "[#{self.class.name}] Saving data in #{store.class.name}"
|
41
|
+
store.save!(DateTime.now, @params[:payload])
|
42
|
+
rescue => ex
|
43
|
+
::OnesnooperServer::Log.error "[#{self.class.name}] Error while saving " \
|
44
|
+
"in #{store.class.name}: #{ex.message}"
|
45
|
+
all_good = false
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
all_good
|
50
|
+
end
|
51
|
+
|
52
|
+
# Returns human-readable information about enabled
|
53
|
+
# backend stores.
|
54
|
+
#
|
55
|
+
# @return [Array] list of textual store information
|
56
|
+
def store_info
|
57
|
+
@params[:stores].collect { |store| store.class.name }
|
58
|
+
end
|
59
|
+
|
60
|
+
end
|
@@ -0,0 +1,74 @@
|
|
1
|
+
require 'logger'
|
2
|
+
|
3
|
+
# Wrapper for log subscribers combining the functionality
|
4
|
+
# of `Logger` and `ActiveSupport::Notifications`. Allows
|
5
|
+
# the use of Singleton-like logging facilities.
|
6
|
+
class OnesnooperServer::Log
|
7
|
+
|
8
|
+
include ::Logger::Severity
|
9
|
+
|
10
|
+
attr_reader :logger, :log_prefix
|
11
|
+
|
12
|
+
# Default subscription handle for notifications
|
13
|
+
SUBSCRIPTION_HANDLE = "onesnooper-server.log"
|
14
|
+
|
15
|
+
# Creates a new logger
|
16
|
+
#
|
17
|
+
# @param log_dev [IO,String] The log device. This is a filename (String) or IO object (typically +STDOUT+, +STDERR+, or an open file).
|
18
|
+
# @param log_prefix [String] String placed in front of every logged message
|
19
|
+
def initialize(log_dev, log_prefix = '[onesnooper-server]')
|
20
|
+
if log_dev.kind_of? ::Logger
|
21
|
+
@logger = log_dev
|
22
|
+
else
|
23
|
+
@logger = ::Logger.new(log_dev)
|
24
|
+
end
|
25
|
+
|
26
|
+
@log_prefix = log_prefix.blank? ? '' : log_prefix.strip
|
27
|
+
|
28
|
+
# subscribe to log messages and send to logger
|
29
|
+
@log_subscriber = ActiveSupport::Notifications.subscribe(self.class::SUBSCRIPTION_HANDLE) do |name, start, finish, id, payload|
|
30
|
+
@logger.log(payload[:level], "#{@log_prefix} #{payload[:message]}") if @logger
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
def close
|
35
|
+
ActiveSupport::Notifications.unsubscribe(@log_subscriber)
|
36
|
+
end
|
37
|
+
|
38
|
+
# @param severity [::Logger::Severity] severity
|
39
|
+
def level=(severity)
|
40
|
+
@logger.level = severity
|
41
|
+
end
|
42
|
+
|
43
|
+
# @return [::Logger::Severity]
|
44
|
+
def level
|
45
|
+
@logger.level
|
46
|
+
end
|
47
|
+
|
48
|
+
# @see info
|
49
|
+
def self.debug(message)
|
50
|
+
ActiveSupport::Notifications.instrument(self::SUBSCRIPTION_HANDLE, :level => ::Logger::DEBUG, :message => message)
|
51
|
+
end
|
52
|
+
|
53
|
+
# Log an +INFO+ message
|
54
|
+
# @param message [String] message the message to log; does not need to be a String
|
55
|
+
def self.info(message)
|
56
|
+
ActiveSupport::Notifications.instrument(self::SUBSCRIPTION_HANDLE, :level => ::Logger::INFO, :message => message)
|
57
|
+
end
|
58
|
+
|
59
|
+
# @see info
|
60
|
+
def self.warn(message)
|
61
|
+
ActiveSupport::Notifications.instrument(self::SUBSCRIPTION_HANDLE, :level => ::Logger::WARN, :message => message)
|
62
|
+
end
|
63
|
+
|
64
|
+
# @see info
|
65
|
+
def self.error(message)
|
66
|
+
ActiveSupport::Notifications.instrument(self::SUBSCRIPTION_HANDLE, :level => ::Logger::ERROR, :message => message)
|
67
|
+
end
|
68
|
+
|
69
|
+
# @see info
|
70
|
+
def self.fatal(message)
|
71
|
+
ActiveSupport::Notifications.instrument(self::SUBSCRIPTION_HANDLE, :level => ::Logger::FATAL, :message => message)
|
72
|
+
end
|
73
|
+
|
74
|
+
end
|
@@ -0,0 +1,143 @@
|
|
1
|
+
# Central parsing class for decoding and analyzing
|
2
|
+
# the content of incoming monitoring messages. Implements
|
3
|
+
# one publicly available method `parse(payload)`. Internally,
|
4
|
+
# the payload is decoded from Base64 and parsed into a
|
5
|
+
# hash-like structure.
|
6
|
+
class OnesnooperServer::PayloadParser
|
7
|
+
|
8
|
+
TRIM_CLEANUP = /\s*/
|
9
|
+
KEY = /[[[:upper:]]|[[:digit:]]|_]+/
|
10
|
+
VALUE = /[[[:alnum:]]|_|\-|\.|:]+/
|
11
|
+
QUOTED_VALUE = /.+/
|
12
|
+
HASH_VALUE = /[^\]]+/
|
13
|
+
EQUALS = /#{TRIM_CLEANUP}=#{TRIM_CLEANUP}/
|
14
|
+
|
15
|
+
KEY_HASH_VALUE_REGEXP = /#{TRIM_CLEANUP}^#{TRIM_CLEANUP}(?<key>#{KEY})#{EQUALS}\[(?<value>#{HASH_VALUE})\]#{TRIM_CLEANUP}$#{TRIM_CLEANUP}/m
|
16
|
+
KEY_QUOTED_VALUE_REGEXP = /#{TRIM_CLEANUP}^#{TRIM_CLEANUP}(?<key>#{KEY})#{EQUALS}"(?<value>#{QUOTED_VALUE})"#{TRIM_CLEANUP}$#{TRIM_CLEANUP}/
|
17
|
+
KEY_RAW_VALUE_REGEXP = /#{TRIM_CLEANUP}^#{TRIM_CLEANUP}(?<key>#{KEY})#{EQUALS}(?<value>#{VALUE})#{TRIM_CLEANUP}$#{TRIM_CLEANUP}/
|
18
|
+
|
19
|
+
# Parses given payload into a hash-like structure. Payload
|
20
|
+
# is decoded from Base64 and then analyzed and parsed.
|
21
|
+
#
|
22
|
+
# @param payload [String] Base64-encoded payload with ONE monitoring data
|
23
|
+
# @return [Hash] hash-like structure with parsed payload
|
24
|
+
def self.parse(payload)
|
25
|
+
return {} if payload.blank?
|
26
|
+
analyze(decode(payload))
|
27
|
+
end
|
28
|
+
|
29
|
+
private
|
30
|
+
|
31
|
+
# Decodes given Base64-encoded string.
|
32
|
+
#
|
33
|
+
# @param payload [String] Base64-encoded string
|
34
|
+
# @return [String] decoded string
|
35
|
+
def self.decode(payload)
|
36
|
+
::OnesnooperServer::Log.debug "[#{self.name}] Decoding #{payload.inspect}"
|
37
|
+
begin
|
38
|
+
Base64.strict_decode64(payload)
|
39
|
+
rescue => ex
|
40
|
+
::OnesnooperServer::Log.error "[#{self.name}] Decoding Base64 failed with: #{ex.message}"
|
41
|
+
return ''
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
# Analyzes payload content and returns a corresponding
|
46
|
+
# hash-like structure.
|
47
|
+
#
|
48
|
+
# @param payload [String] plain text payload in ONE format
|
49
|
+
# @param subpayload [Boolean] recursive call for parsing complex values
|
50
|
+
# @return [Hash] a hash-like structure with analyzed payload content
|
51
|
+
def self.analyze(payload, subpayload = false)
|
52
|
+
::OnesnooperServer::Log.debug "[#{self.name}] Scanning decoded #{subpayload ? 'sub-' : '' }payload #{payload.inspect}"
|
53
|
+
return {} if payload.blank?
|
54
|
+
|
55
|
+
scanned_payload = {}
|
56
|
+
scannable_payload = StringScanner.new(payload)
|
57
|
+
begin
|
58
|
+
if scanned = scannable_payload.scan(KEY_HASH_VALUE_REGEXP)
|
59
|
+
scanned.strip!
|
60
|
+
::OnesnooperServer::Log.debug "[#{self.name}] Scanned #{scanned.inspect}"
|
61
|
+
analyze_simple_pair(scanned, scanned_payload, KEY_HASH_VALUE_REGEXP, true)
|
62
|
+
elsif scanned = scannable_payload.scan(KEY_QUOTED_VALUE_REGEXP)
|
63
|
+
scanned.strip!
|
64
|
+
scanned.gsub! "\n", ''
|
65
|
+
::OnesnooperServer::Log.debug "[#{self.name}] Scanned #{scanned.inspect}"
|
66
|
+
analyze_simple_pair(scanned, scanned_payload, KEY_QUOTED_VALUE_REGEXP)
|
67
|
+
elsif scanned = scannable_payload.scan(KEY_RAW_VALUE_REGEXP)
|
68
|
+
scanned.strip!
|
69
|
+
scanned.gsub! "\n", ''
|
70
|
+
::OnesnooperServer::Log.debug "[#{self.name}] Scanned #{scanned.inspect}"
|
71
|
+
analyze_simple_pair(scanned, scanned_payload, KEY_RAW_VALUE_REGEXP)
|
72
|
+
else
|
73
|
+
::OnesnooperServer::Log.error "[#{self.name}] Failed scanning #{subpayload ? 'sub-' : '' }payload " \
|
74
|
+
"#{payload.inspect} at #{scannable_payload.pos}"
|
75
|
+
break
|
76
|
+
end
|
77
|
+
end until scannable_payload.eos?
|
78
|
+
|
79
|
+
scanned_payload
|
80
|
+
end
|
81
|
+
|
82
|
+
# Parses complex value strings into a
|
83
|
+
# hash-like structure.
|
84
|
+
#
|
85
|
+
# @param complex_value [String] input string
|
86
|
+
# @return [Hash] result
|
87
|
+
def self.analyze_complex_value(complex_value)
|
88
|
+
complex_parsed = analyze(complex_value.gsub(',', "\n").strip.gsub("\n\n", "\n"), true)
|
89
|
+
unless complex_parsed['POLL'].blank?
|
90
|
+
::OnesnooperServer::Log.debug "[#{self.name}] Found complex POLL values, triggering analysis"
|
91
|
+
complex_parsed['POLL'] = analyze(complex_parsed['POLL'].gsub(/\s+/, "\n"), true)
|
92
|
+
end
|
93
|
+
|
94
|
+
complex_parsed
|
95
|
+
end
|
96
|
+
|
97
|
+
# Parses simple key value strings into the given
|
98
|
+
# hash-like structure.
|
99
|
+
#
|
100
|
+
# @param key_value [String] input string
|
101
|
+
# @param parsed [Hash] output hash-like structure
|
102
|
+
# @param regexp [Regexp] regular expression for parsing
|
103
|
+
# @param suspected_complex [Boolean] suspect complex value
|
104
|
+
# @return [Boolean] success or failure
|
105
|
+
def self.analyze_simple_pair(key_value, parsed, regexp, suspected_complex = false)
|
106
|
+
matched = key_value.match(regexp)
|
107
|
+
if matched
|
108
|
+
::OnesnooperServer::Log.debug "[#{self.name}] Matched #{key_value.inspect} " \
|
109
|
+
"as #{matched[:key].inspect} and #{matched[:value].inspect}"
|
110
|
+
if suspected_complex
|
111
|
+
parsed[matched[:key]] ||= []
|
112
|
+
parsed[matched[:key]] << analyze_complex_value(matched[:value])
|
113
|
+
else
|
114
|
+
parsed[matched[:key]] = typecast_if_num(matched[:value])
|
115
|
+
end
|
116
|
+
else
|
117
|
+
::OnesnooperServer::Log.error "[#{self.name}] Couldn't match " \
|
118
|
+
"#{key_value.inspect} as key & simple value"
|
119
|
+
end
|
120
|
+
|
121
|
+
true
|
122
|
+
end
|
123
|
+
|
124
|
+
# Attempts to type-cast values to `Integer` or `Float` if this casting
|
125
|
+
# makes sense for the given value. Otherwise the original value
|
126
|
+
# is returned.
|
127
|
+
#
|
128
|
+
# @param potential_num [String] value to type-cast if applicable
|
129
|
+
# @return [String, Integer, Float] type-casted value if applicable
|
130
|
+
def self.typecast_if_num(potential_num)
|
131
|
+
return potential_num unless potential_num.kind_of? String
|
132
|
+
|
133
|
+
case potential_num
|
134
|
+
when potential_num.to_i.to_s
|
135
|
+
potential_num.to_i
|
136
|
+
when potential_num.to_f.to_s
|
137
|
+
potential_num.to_f
|
138
|
+
else
|
139
|
+
potential_num
|
140
|
+
end
|
141
|
+
end
|
142
|
+
|
143
|
+
end
|