zombie_check 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
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
@@ -0,0 +1,11 @@
1
+ /.bundle/
2
+ /.yardoc
3
+ /Gemfile.lock
4
+ /_yardoc/
5
+ /coverage/
6
+ /doc/
7
+ /pkg/
8
+ /spec/reports/
9
+ /tmp/
10
+ /tmp/*
11
+ /.idea/
data/.rspec ADDED
@@ -0,0 +1,2 @@
1
+ --format documentation
2
+ --color
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
@@ -0,0 +1,4 @@
1
+ language: ruby
2
+ rvm:
3
+ - 2.3.1
4
+ before_install: gem install bundler -v 1.11.2
data/Gemfile ADDED
@@ -0,0 +1,8 @@
1
+ # frozen_string_literal: true
2
+ source "https://rubygems.org"
3
+
4
+ # Specify your gem's dependencies in zombie_check.gemspec
5
+
6
+ gem "net-ping"
7
+
8
+ gemspec
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
@@ -0,0 +1,7 @@
1
+ # frozen_string_literal: true
2
+ require "bundler/gem_tasks"
3
+ require "rspec/core/rake_task"
4
+
5
+ RSpec::Core::RakeTask.new(:spec)
6
+
7
+ task default: :spec
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
@@ -0,0 +1,8 @@
1
+ #!/usr/bin/env bash
2
+ set -euo pipefail
3
+ IFS=$'\n\t'
4
+ set -vx
5
+
6
+ bundle install
7
+
8
+ # Do any other automated setup that you need to do here
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/hosts.txt ADDED
@@ -0,0 +1,5 @@
1
+ 127.0.0.1
2
+ 8.8.8.8
3
+ mail.ru
4
+ google.ru
5
+ ya.ru
@@ -0,0 +1,9 @@
1
+ # frozen_string_literal: true
2
+ require "zombie_check/core_ext/ennumerable"
3
+ require "zombie_check/version"
4
+ require "zombie_check/ping/checker"
5
+ require "zombie_check/ping/checker_report"
6
+ require "net/ping"
7
+
8
+ module ZombieCheck
9
+ end
@@ -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,4 @@
1
+ # frozen_string_literal: true
2
+ module ZombieCheck
3
+ VERSION = "0.1.0"
4
+ 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: