onesnooper-server 0.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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
|