zombie_check 0.1.0
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 +11 -0
- data/.rspec +2 -0
- data/.rubocop.yml +58 -0
- data/.rubocop_todo.yml +0 -0
- data/.travis.yml +4 -0
- data/Gemfile +8 -0
- data/README.md +45 -0
- data/Rakefile +7 -0
- data/bin/console +14 -0
- data/bin/setup +8 -0
- data/bin/zombie_check +27 -0
- data/hosts.txt +5 -0
- data/lib/zombie_check.rb +9 -0
- data/lib/zombie_check/core_ext/ennumerable.rb +26 -0
- data/lib/zombie_check/ping/checker.rb +69 -0
- data/lib/zombie_check/ping/checker_report.rb +41 -0
- data/lib/zombie_check/version.rb +4 -0
- data/zombie_check.gemspec +27 -0
- metadata +120 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 4920ff896447270b5f95b172f733995e82a7fc8f
|
4
|
+
data.tar.gz: 5c7b2c79e3ad4e1ac2b160a4e92abdfe512cadbe
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: b4429185406db8e6d1f8e0f7916192c9f814b7c82bff399b05fb75415f1e230fa4195f42ac7d18113164cf9d052b42546efeb4e17359e179fb7c2f5d5327f7de
|
7
|
+
data.tar.gz: 0ca9fb3648d8bc51dd0e91358960046071d2d20cd23d32544692ed8d99bd849ffc5398ee0ecdd94225ad9c096669246defb64928ad7dbc550449f08a4d34cfed
|
data/.gitignore
ADDED
data/.rspec
ADDED
data/.rubocop.yml
ADDED
@@ -0,0 +1,58 @@
|
|
1
|
+
Style/AlignHash:
|
2
|
+
EnforcedHashRocketStyle: table
|
3
|
+
EnforcedColonStyle: table
|
4
|
+
AutoCorrect: true
|
5
|
+
Style/IndentationConsistency:
|
6
|
+
EnforcedStyle: rails
|
7
|
+
Style/StringLiterals:
|
8
|
+
ConsistentQuotesInMultiline: true
|
9
|
+
Metrics/ClassLength:
|
10
|
+
CountComments: false
|
11
|
+
Max: 500
|
12
|
+
Metrics/ModuleLength:
|
13
|
+
CountComments: false
|
14
|
+
Max: 500
|
15
|
+
Lint/EndAlignment:
|
16
|
+
AutoCorrect: true
|
17
|
+
Lint/DefEndAlignment:
|
18
|
+
AutoCorrect: true
|
19
|
+
Rails:
|
20
|
+
Enabled: false
|
21
|
+
Style/AutoResourceCleanup:
|
22
|
+
Description: 'Suggests the usage of an auto resource cleanup version of a method (if available).'
|
23
|
+
Enabled: true
|
24
|
+
Metrics/MethodLength:
|
25
|
+
CountComments: false # count full line comments?
|
26
|
+
Max: 40
|
27
|
+
Style/StringLiterals:
|
28
|
+
EnforcedStyle: double_quotes
|
29
|
+
ConsistentQuotesInMultiline: true
|
30
|
+
Style/StringLiteralsInInterpolation:
|
31
|
+
EnforcedStyle: single_quotes
|
32
|
+
NumericLiterals:
|
33
|
+
Enabled: false
|
34
|
+
Metrics/LineLength:
|
35
|
+
Max: 160
|
36
|
+
Documentation:
|
37
|
+
Enabled: false
|
38
|
+
Lint/Debugger: # Easy deletion of all debugger breakpoints. false for debug
|
39
|
+
Enabled: true
|
40
|
+
AllCops:
|
41
|
+
Exclude:
|
42
|
+
- 'bin/**/*'
|
43
|
+
- 'vendor/**/*'
|
44
|
+
- 'log/**/*'
|
45
|
+
- 'tmp/**/*'
|
46
|
+
TargetRubyVersion: 2.3
|
47
|
+
Metrics/AbcSize:
|
48
|
+
Max: 35
|
49
|
+
inherit_from: .rubocop_todo.yml
|
50
|
+
Metrics/CyclomaticComplexity:
|
51
|
+
Max: 10
|
52
|
+
Rails/HasAndBelongsToMany:
|
53
|
+
Enabled: false
|
54
|
+
Style/GlobalVars:
|
55
|
+
Description: 'Do not introduce global variables.'
|
56
|
+
StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#instance-vars'
|
57
|
+
Reference: 'http://www.zenspider.com/Languages/Ruby/QuickRef.html'
|
58
|
+
Enabled: false
|
data/.rubocop_todo.yml
ADDED
File without changes
|
data/.travis.yml
ADDED
data/Gemfile
ADDED
data/README.md
ADDED
@@ -0,0 +1,45 @@
|
|
1
|
+
##Severs.com, тестовое задание
|
2
|
+
|
3
|
+
Нужно написать приложение:
|
4
|
+
|
5
|
+
Приложение имеет следующие вызовы:
|
6
|
+
1) Добавить IP-адрес к подсчету статистики (параметр - IP-адрес, нотацию выбери сам)
|
7
|
+
2) Удалить IP-адрес из подсчета статистики (параметр - IP-адрес, нотацию выбери сам)
|
8
|
+
3) Сообщить статистику доступности IP-адреса по ICMP [то есть посредством ping] (параметры - IP-адрес, начало интервала времени, конец интервала времени). Получив начало и конец интервала времени, должно вернуть JSON, содержащий следующие поля:
|
9
|
+
- среднее RTT (время отклика на пинг) за этот период
|
10
|
+
- минимальное RTT за этот период
|
11
|
+
- максимальное RTT за этот период
|
12
|
+
- медианное RTT за этот период
|
13
|
+
- среднеквадратичное отклонение замеров RTT за этот период
|
14
|
+
- процент потерянных пакетов ICMP (ping) до указанного адреса за этот период.
|
15
|
+
|
16
|
+
Если какую-то часть времени в этом периоде IP-адрес был вне расчета статистики (не был добавлен или был удален) - эту часть времени учитывать не нужно. Например, мы добавили ip-адрес 8.8.8.8 в 1 час, выключили в 2, включили в 3 и выключили в 4. Если я запросил статистику с 1 по 4 часа — надо объединить интервалы 1-2, 3-4 и отдать эту статистику по объединенному интервалу. Если IP-адрес не был в расчете статистики все время или был настолько мало времени, что мы не успели сделать хотя бы 1 замер - надо вернуть сообщение об ошибке.
|
17
|
+
|
18
|
+
Соответственно, удаленные машины, имеющие IP-адреса из этого списка могут вести себя очень странно - не отвечать на пинги, отвечать с ооочень большой задержкой - например, минуту (если захочется поиграться с этим случаем, его можно смоделировать на машинке, поставив, например, Charles http://www.charlesproxy.com/documentation/proxying/throttling/ туда).
|
19
|
+
|
20
|
+
Задание может быть выполнено на разном уровне сложности - можно замахнуться на то, чтобы сделать идеально и таки научиться обрабатывать ситуации с пингом в минуту, или пытаться оптимизировать производительность - а можно не делать ничего из этого. Ты можешь пойти любым путем в зависимости от твоего свободного времени.
|
21
|
+
|
22
|
+
Ниже доступ в панель servers.com — там ты можешь завести сколько тебе понадобится для выполнение виртуалок в клауде под Linux или Windows (на случай если тебе нужна среда запуска приложения или баз данных или еще чего-то, а на своем компьютере почему-то ты этого делать не хочешь).
|
23
|
+
|
24
|
+
URL: http://portal.servers.com
|
25
|
+
Login: foo@servers.com
|
26
|
+
Password: xxx
|
27
|
+
|
28
|
+
|
29
|
+
## Мои комментарии
|
30
|
+
|
31
|
+
Стартовать надо через sudo, ибо `net-ping` полключается напрямую к сокетам. Можно переколбасить на обычный ping, если критично.
|
32
|
+
|
33
|
+
В качестве интерфейса для хостов юзается фаил `hosts.txt` в корне. Но это можно переписать аргументами
|
34
|
+
|
35
|
+
Ранее не было проблем с запуском pry, да и sudo в реальсе не особо нужен. Словил прикольный момент,
|
36
|
+
который несколько смутил. Все шустро решилось, но все
|
37
|
+
же http://stackoverflow.com/questions/37458855/unix-sudo-pry-does-not-start
|
38
|
+
|
39
|
+
Charles умеет делать задержки для tcp или udp запросов, но вот ничего в настройках про icmp я так и не увидел. В любом случае буду признателен за тычек пальцем в нужную сторону.
|
40
|
+
|
41
|
+
Если убрать задержку в `zombie_check delay:0` (чтобы замутить попытку DOS через ping :) то потоки долго убиваются. Я пока что не нашел способа это ускорить. Как вариант приписать #join в 19й строке checker.rb, но тогда исчезает все веселье.
|
42
|
+
|
43
|
+
В данном виде, на бесконечном отрезке времени прога сожрет всю память. Я вкурсе, если это важно- поправлю.
|
44
|
+
|
45
|
+
Если нужны тесты- приделаю.
|
data/Rakefile
ADDED
data/bin/console
ADDED
@@ -0,0 +1,14 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
require 'bundler/setup'
|
4
|
+
require 'zombie_check'
|
5
|
+
|
6
|
+
# You can add fixtures and/or initialization code here to make experimenting
|
7
|
+
# with your gem easier. You can also use a different console, if you like.
|
8
|
+
|
9
|
+
# (If you use this, don't forget to add pry to your Gemfile!)
|
10
|
+
# require "pry"
|
11
|
+
# Pry.start
|
12
|
+
|
13
|
+
require 'irb'
|
14
|
+
IRB.start
|
data/bin/setup
ADDED
data/bin/zombie_check
ADDED
@@ -0,0 +1,27 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
require 'pathname'
|
3
|
+
require 'pry'
|
4
|
+
|
5
|
+
source_path = (Pathname.new(__FILE__).dirname + '../lib').expand_path
|
6
|
+
$LOAD_PATH << source_path
|
7
|
+
|
8
|
+
require 'zombie_check'
|
9
|
+
|
10
|
+
if ['-v', '--version'].include? ARGV[0]
|
11
|
+
puts "Version #{ZombieCheck::VERSION}"
|
12
|
+
exit 0
|
13
|
+
elsif ['-h', '--help'].include? ARGV[0]
|
14
|
+
puts 'You can specify options with syntax like option_name:value'
|
15
|
+
puts 'hosts_file - file with list of all hosts'
|
16
|
+
puts "delay - delay between pings in ms, default 1000 \n\n"
|
17
|
+
exit 0
|
18
|
+
end
|
19
|
+
|
20
|
+
options = {}.tap do |result|
|
21
|
+
ARGV.each do |argv|
|
22
|
+
split_args = argv.split ':'
|
23
|
+
result[split_args.first.to_sym] = split_args.last
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
ZombieCheck::Ping::Checker.new(options).start
|
data/lib/zombie_check.rb
ADDED
@@ -0,0 +1,26 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
module Enumerable
|
3
|
+
def sum
|
4
|
+
inject(0) { |accum, i| accum + i }
|
5
|
+
end
|
6
|
+
|
7
|
+
def mean
|
8
|
+
# rubocop bug, other way it dies https://github.com/bbatsov/rubocop/issues/3169
|
9
|
+
return -1 if send(:length) == 0
|
10
|
+
sum / length.to_f
|
11
|
+
end
|
12
|
+
|
13
|
+
def sigma
|
14
|
+
return -1 if length < 2
|
15
|
+
m = mean
|
16
|
+
sum = inject(0) { |accum, i| accum + (i - m)**2 }
|
17
|
+
Math.sqrt(sum / (length - 1).to_f)
|
18
|
+
end
|
19
|
+
|
20
|
+
def median
|
21
|
+
return -1 if send(:length) == 0
|
22
|
+
middle = length / 2
|
23
|
+
sorted = sort
|
24
|
+
length.even? ? (sorted[middle] + sorted[middle - 1]) / 2 : sorted[middle]
|
25
|
+
end
|
26
|
+
end
|
@@ -0,0 +1,69 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
module ZombieCheck
|
3
|
+
module Ping
|
4
|
+
class Checker
|
5
|
+
attr_accessor :hosts_file, :delay, :hosts, :report, :interrupted
|
6
|
+
|
7
|
+
def initialize(options = {})
|
8
|
+
@hosts_file ||= options[:hosts_file] || "hosts.txt"
|
9
|
+
@delay ||= (options[:delay] || 1000).to_i / 1000.0
|
10
|
+
@hosts ||= []
|
11
|
+
@report = CheckerReport.new
|
12
|
+
setup_interruptor
|
13
|
+
end
|
14
|
+
|
15
|
+
def start
|
16
|
+
puts "Using file #{hosts_file}, delay #{delay}, for exit press Ctrl+C"
|
17
|
+
loop do
|
18
|
+
update_hosts!
|
19
|
+
@hosts.each { |host| Thread.new { ping host } }
|
20
|
+
if interrupted
|
21
|
+
exit_all_threads!
|
22
|
+
puts @report.generate
|
23
|
+
exit 0
|
24
|
+
end
|
25
|
+
sleep @delay
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
def ping(host)
|
30
|
+
icmp = Net::Ping::ICMP.new(host)
|
31
|
+
icmp.ping
|
32
|
+
@report << icmp
|
33
|
+
end
|
34
|
+
|
35
|
+
private
|
36
|
+
|
37
|
+
def update_hosts!
|
38
|
+
check_file_exists! hosts_file_path
|
39
|
+
result = File.open(hosts_file_path, "r") { |f| f.readlines.map(&:chomp) }
|
40
|
+
check_result_not_empty! result
|
41
|
+
@hosts = result
|
42
|
+
end
|
43
|
+
|
44
|
+
def check_file_exists!(file)
|
45
|
+
return if File.exist?(file)
|
46
|
+
puts "No #{@hosts_file} file"
|
47
|
+
exit 0
|
48
|
+
end
|
49
|
+
|
50
|
+
def check_result_not_empty!(result)
|
51
|
+
return unless result.empty? && @hosts.empty?
|
52
|
+
puts "#{@hosts_file} is empty"
|
53
|
+
exit 0
|
54
|
+
end
|
55
|
+
|
56
|
+
def hosts_file_path
|
57
|
+
File.expand_path(@hosts_file)
|
58
|
+
end
|
59
|
+
|
60
|
+
def setup_interruptor
|
61
|
+
trap("INT") { @interrupted = true }
|
62
|
+
end
|
63
|
+
|
64
|
+
def exit_all_threads!
|
65
|
+
Thread.list.reject { |t| t == Thread.current }.each(&:exit)
|
66
|
+
end
|
67
|
+
end
|
68
|
+
end
|
69
|
+
end
|
@@ -0,0 +1,41 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
module ZombieCheck
|
3
|
+
module Ping
|
4
|
+
class CheckerReport
|
5
|
+
PRECISION = 3
|
6
|
+
attr_accessor :stat
|
7
|
+
|
8
|
+
def initialize
|
9
|
+
@stat ||= {}
|
10
|
+
end
|
11
|
+
|
12
|
+
def <<(ping)
|
13
|
+
host = @stat[ping.host] ||= {}
|
14
|
+
host[:durations] ||= []
|
15
|
+
host[:lost] ||= 0
|
16
|
+
if (ping_duration = ping.duration)
|
17
|
+
host[:durations] << (ping_duration * 1000).round(PRECISION)
|
18
|
+
else
|
19
|
+
host[:lost] += 1
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
def generate
|
24
|
+
[].tap do |result|
|
25
|
+
stat.each_pair do |host, log|
|
26
|
+
total = log[:durations].size + log[:lost]
|
27
|
+
percentage = total > 0 ? log[:lost] / total : 0
|
28
|
+
|
29
|
+
result << <<-REPORT
|
30
|
+
|
31
|
+
To #{host} total sent #{total} pings, lost #{log[:lost]} (#{percentage}%). Time(ms):
|
32
|
+
AVG #{log[:durations].mean.round(PRECISION)} MIN #{log[:durations].min} \
|
33
|
+
MAX #{log[:durations].max} sigma #{log[:durations].sigma.round(PRECISION)} \
|
34
|
+
median #{log[:durations].median.round(PRECISION)}
|
35
|
+
REPORT
|
36
|
+
end
|
37
|
+
end.join "\n"
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
@@ -0,0 +1,27 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
# frozen_string_literal: true
|
3
|
+
lib = File.expand_path("../lib", __FILE__)
|
4
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
5
|
+
require "zombie_check/version"
|
6
|
+
|
7
|
+
Gem::Specification.new do |spec|
|
8
|
+
spec.name = "zombie_check"
|
9
|
+
spec.version = ZombieCheck::VERSION
|
10
|
+
spec.authors = ["Kvokka"]
|
11
|
+
spec.email = ["root_p@mail.ru"]
|
12
|
+
|
13
|
+
spec.summary = "Ping all from file list"
|
14
|
+
spec.description = "Ping all from file list"
|
15
|
+
spec.homepage = "https://github.com/kvokka/zombie_check"
|
16
|
+
|
17
|
+
spec.files = `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
|
18
|
+
spec.executables = ["zombie_check"]
|
19
|
+
spec.require_paths = ["lib"]
|
20
|
+
spec.license = "MIT"
|
21
|
+
spec.required_ruby_version = ">= 2.0.0"
|
22
|
+
|
23
|
+
spec.add_development_dependency "bundler", "~> 1.11"
|
24
|
+
spec.add_development_dependency "rake", "~> 10.0"
|
25
|
+
spec.add_development_dependency "rspec", "~> 3.0"
|
26
|
+
spec.add_development_dependency "pry"
|
27
|
+
end
|
metadata
ADDED
@@ -0,0 +1,120 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: zombie_check
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.1.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Kvokka
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2016-05-27 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: bundler
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - "~>"
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '1.11'
|
20
|
+
type: :development
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - "~>"
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '1.11'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: rake
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - "~>"
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '10.0'
|
34
|
+
type: :development
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - "~>"
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '10.0'
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: rspec
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - "~>"
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: '3.0'
|
48
|
+
type: :development
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - "~>"
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '3.0'
|
55
|
+
- !ruby/object:Gem::Dependency
|
56
|
+
name: pry
|
57
|
+
requirement: !ruby/object:Gem::Requirement
|
58
|
+
requirements:
|
59
|
+
- - ">="
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: '0'
|
62
|
+
type: :development
|
63
|
+
prerelease: false
|
64
|
+
version_requirements: !ruby/object:Gem::Requirement
|
65
|
+
requirements:
|
66
|
+
- - ">="
|
67
|
+
- !ruby/object:Gem::Version
|
68
|
+
version: '0'
|
69
|
+
description: Ping all from file list
|
70
|
+
email:
|
71
|
+
- root_p@mail.ru
|
72
|
+
executables:
|
73
|
+
- zombie_check
|
74
|
+
extensions: []
|
75
|
+
extra_rdoc_files: []
|
76
|
+
files:
|
77
|
+
- ".gitignore"
|
78
|
+
- ".rspec"
|
79
|
+
- ".rubocop.yml"
|
80
|
+
- ".rubocop_todo.yml"
|
81
|
+
- ".travis.yml"
|
82
|
+
- Gemfile
|
83
|
+
- README.md
|
84
|
+
- Rakefile
|
85
|
+
- bin/console
|
86
|
+
- bin/setup
|
87
|
+
- bin/zombie_check
|
88
|
+
- hosts.txt
|
89
|
+
- lib/zombie_check.rb
|
90
|
+
- lib/zombie_check/core_ext/ennumerable.rb
|
91
|
+
- lib/zombie_check/ping/checker.rb
|
92
|
+
- lib/zombie_check/ping/checker_report.rb
|
93
|
+
- lib/zombie_check/version.rb
|
94
|
+
- zombie_check.gemspec
|
95
|
+
homepage: https://github.com/kvokka/zombie_check
|
96
|
+
licenses:
|
97
|
+
- MIT
|
98
|
+
metadata: {}
|
99
|
+
post_install_message:
|
100
|
+
rdoc_options: []
|
101
|
+
require_paths:
|
102
|
+
- lib
|
103
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
104
|
+
requirements:
|
105
|
+
- - ">="
|
106
|
+
- !ruby/object:Gem::Version
|
107
|
+
version: 2.0.0
|
108
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
109
|
+
requirements:
|
110
|
+
- - ">="
|
111
|
+
- !ruby/object:Gem::Version
|
112
|
+
version: '0'
|
113
|
+
requirements: []
|
114
|
+
rubyforge_project:
|
115
|
+
rubygems_version: 2.5.1
|
116
|
+
signing_key:
|
117
|
+
specification_version: 4
|
118
|
+
summary: Ping all from file list
|
119
|
+
test_files: []
|
120
|
+
has_rdoc:
|