zombie_check 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- 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:
|