etcd-tools 0.2.9 → 0.3.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,15 +1,15 @@
1
1
  ---
2
2
  !binary "U0hBMQ==":
3
3
  metadata.gz: !binary |-
4
- ZDUzOGVkNzk2YWIyNzhkY2Q1N2FhN2IwZGZiZDRhNjc1YWE3MDc2NA==
4
+ Yzc0YWVjZTY0ODdhOTA1NGE4YTAyMGI2MDk4NDc2YzVkYzk0ZWYwYg==
5
5
  data.tar.gz: !binary |-
6
- NTQ1YWJkZWRkYTc4MDVlM2EyNjc1ZWRkNDc3NmQ3YzdkZGNlNjU5ZA==
6
+ M2JjMWVhOGIzZGRjMjMwOGYzODQ2ZmVkNTYzNjRhNGFkOTViOGY0Zg==
7
7
  SHA512:
8
8
  metadata.gz: !binary |-
9
- YmRiYmZkNGEwM2E5NjFjMzFmM2RkZmE2MDExZTYzMTQzNTgwMmUzMGIzNTk1
10
- YzRjYTkwMTg2NzU3Mjk3ODY2MGY1ZjVlNTdmZDE3ZjFlYzcwMDllNzI4NmY4
11
- YTMyMGU1NWUwNTY3N2YzZDZhMDIxMjhlYjAwY2MzZmMwOTc4Mzk=
9
+ NTc3MTExOGFjY2M1NGM5YWM5NzVhYzcyYTAxZjgwMzQ1NDYzODEzYmZhNDdm
10
+ MWViYmZjMDE0NmJlZGIxNTUxN2Q1Yzc1ZWRhMDI0MDI3NWQyZmI3MDlmNzdh
11
+ ZGIzN2Y0NzNhZGUzNzk1NDg3YThmZTI4Yzk3MGZhOWFkYTc0NDk=
12
12
  data.tar.gz: !binary |-
13
- NTg1YjcwZGFmOWU1NjM0NTQ1ODgzYjM3ZWFmYTI3MTk3OGEzMjQ0MTIyYjRk
14
- ZWYzYmY4ZjkwOTYzNjkyZWY2ZGIyYWNlNGQ5M2QzNWRhMTVkNTJlMGRjNDll
15
- NmVlNjU3OTEwNTBiOTU0YTlmZTk2MDU5MGUzZDkzMmQ2MjcwYjI=
13
+ NTQ2NzE2NmM3NjU2ZGNhOGVjYTVjYTcyMzkyYTk2ODFiYjNhZmFiYmRhOGY0
14
+ OWZhZTk0Y2E3OTM5MDkxYThkOTNiODEyYjhiOTE0YmQ4ZWRjN2RkMThhNDFj
15
+ MTNmNTUyZGE2ZmQzMWE4OTJiNzJiNWEzNzFhNjQzMzZkMWY3ZDk=
@@ -15,10 +15,9 @@ module EtcdTools
15
15
  end
16
16
  @etcd = etcd
17
17
  compiler = ::ERB::Compiler.new('-')
18
- set_eoutvar(compiler, "_erbout")
18
+ set_eoutvar(compiler, '_erbout')
19
19
  @src, @enc = *compiler.compile(template)
20
20
  @filename = nil
21
- # super template
22
21
  end
23
22
 
24
23
  def result
@@ -26,7 +25,7 @@ module EtcdTools
26
25
  end
27
26
 
28
27
  def value path
29
- return @etcd.get('/' + path.sub(/^\//, '')).value
28
+ @etcd.get('/' + path.sub(/^\//, '')).value
30
29
  end
31
30
 
32
31
  def keys path
@@ -39,15 +38,13 @@ module EtcdTools
39
38
  end
40
39
 
41
40
  def hash path
42
- begin
43
- etcd2hash @etcd, path
44
- rescue
45
- {}
46
- end
41
+ etcd2hash @etcd, path
42
+ rescue
43
+ {}
47
44
  end
48
45
 
49
46
  def members
50
- Hash[ @etcd.members.map { |id, md| [ id, md.merge({ "ip" => md["clientURLs"].first.sub(/https?:\/\//, '').sub(/:[0-9]+/, '') }) ] } ]
47
+ Hash[ @etcd.members.map { |id, md| [ id, md.merge({ 'ip' => md['clientURLs'].first.sub(/https?:\/\//, '').sub(/:[0-9]+/, '') }) ] } ]
51
48
  end
52
49
 
53
50
  end
@@ -3,7 +3,7 @@ require 'etcd-tools/mixins'
3
3
 
4
4
  module EtcdTools
5
5
  module Etcd
6
- def etcd_connect (url)
6
+ def etcd_connect(url)
7
7
  (host, port) = url.gsub(/^https?:\/\//, '').gsub(/\/$/, '').split(':')
8
8
  etcd = ::Etcd.client(host: host, port: port)
9
9
  begin
@@ -14,36 +14,32 @@ module EtcdTools
14
14
  end
15
15
  end
16
16
 
17
- def hash2etcd (etcd, hash, path="")
18
- begin
19
- hash.each do |key, value|
20
- path = "" if path == '/'
21
- etcd_key = path + '/' + key.to_s
22
- if value.class == Hash
23
- hash2etcd(etcd, value, etcd_key)
24
- else
25
- etcd.set(etcd_key, value: value)
26
- end
17
+ def hash2etcd(etcd, hash, path = '')
18
+ hash.each do |key, value|
19
+ path = "" if path == '/'
20
+ etcd_key = path + '/' + key.to_s
21
+ if value.class == Hash
22
+ hash2etcd(etcd, value, etcd_key)
23
+ else
24
+ etcd.set(etcd_key, value: value)
27
25
  end
28
- rescue Exception => e
29
- raise e #fixme
30
26
  end
27
+ rescue Exception => e
28
+ raise e #fixme
31
29
  end
32
30
 
33
- def etcd2hash (etcd, path="")
34
- begin
35
- h = {}
36
- etcd.get(path).children.each do |child|
37
- if etcd.get(child.key).directory?
38
- h[child.key.split('/').last.to_s] = etcd2hash etcd, child.key
39
- else
40
- h[child.key.split('/').last.to_s] = child.value
41
- end
31
+ def etcd2hash(etcd, path = '')
32
+ h = {}
33
+ etcd.get(path).children.each do |child|
34
+ if etcd.get(child.key).directory?
35
+ h[child.key.split('/').last.to_s] = etcd2hash etcd, child.key
36
+ else
37
+ h[child.key.split('/').last.to_s] = child.value
42
38
  end
43
- return Hash[h.sort]
44
- rescue Exception => e
45
- return nil
46
39
  end
40
+ return Hash[h.sort]
41
+ rescue Exception => e
42
+ return nil
47
43
  end
48
44
  end
49
45
  end
@@ -1,8 +1,6 @@
1
+ require 'etcd-tools/mixins'
2
+
1
3
  module EtcdTools
2
4
  module Cli
3
5
  end
4
-
5
- module Watchdog
6
- end
7
-
8
6
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: etcd-tools
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.2.9
4
+ version: 0.3.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Radek 'blufor' Slavicinsky
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2016-01-14 00:00:00.000000000 Z
11
+ date: 2016-01-20 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: etcd
@@ -30,32 +30,9 @@ dependencies:
30
30
  - - ! '>='
31
31
  - !ruby/object:Gem::Version
32
32
  version: 0.3.0
33
- - !ruby/object:Gem::Dependency
34
- name: net-ping
35
- requirement: !ruby/object:Gem::Requirement
36
- requirements:
37
- - - ~>
38
- - !ruby/object:Gem::Version
39
- version: '1.7'
40
- - - ! '>='
41
- - !ruby/object:Gem::Version
42
- version: 1.7.8
43
- type: :runtime
44
- prerelease: false
45
- version_requirements: !ruby/object:Gem::Requirement
46
- requirements:
47
- - - ~>
48
- - !ruby/object:Gem::Version
49
- version: '1.7'
50
- - - ! '>='
51
- - !ruby/object:Gem::Version
52
- version: 1.7.8
53
- description: A set of handful daemons and command-line utils for ETCD (is part of
54
- PortAuthority)
33
+ description: A set of handful command-line utils for ETCD
55
34
  email: radek.slavicinsky@gmail.com
56
35
  executables:
57
- - etcd-watchdog-haproxy
58
- - etcd-watchdog-vip
59
36
  - etcd-erb
60
37
  - yaml2etcd
61
38
  - etcd2yaml
@@ -63,26 +40,15 @@ extensions: []
63
40
  extra_rdoc_files: []
64
41
  files:
65
42
  - bin/etcd-erb
66
- - bin/etcd-watchdog-haproxy
67
- - bin/etcd-watchdog-vip
68
43
  - bin/etcd2yaml
69
44
  - bin/yaml2etcd
45
+ - lib/etcd-tools.rb
70
46
  - lib/etcd-tools/cli/etcd2yaml.rb
71
47
  - lib/etcd-tools/cli/etcd_erb.rb
72
48
  - lib/etcd-tools/cli/yaml2etcd.rb
73
49
  - lib/etcd-tools/erb.rb
74
50
  - lib/etcd-tools/etcd.rb
75
51
  - lib/etcd-tools/mixins.rb
76
- - lib/etcd-tools/watchdog/haproxy.rb
77
- - lib/etcd-tools/watchdog/init.rb
78
- - lib/etcd-tools/watchdog/threads/etcd.rb
79
- - lib/etcd-tools/watchdog/threads/icmp.rb
80
- - lib/etcd-tools/watchdog/util/config.rb
81
- - lib/etcd-tools/watchdog/util/etcd.rb
82
- - lib/etcd-tools/watchdog/util/helpers.rb
83
- - lib/etcd-tools/watchdog/util/logger.rb
84
- - lib/etcd-tools/watchdog/vip.rb
85
- - lib/etcd_tools.rb
86
52
  homepage: https://github.com/blufor/etcd-tools
87
53
  licenses:
88
54
  - GPLv2
@@ -1,4 +0,0 @@
1
- #!/usr/bin/env ruby
2
- require 'etcd-tools/etcd_watchdog_haproxy'
3
- app = EtcdTools::Watchdog::HAproxy.new
4
- app.run
@@ -1,4 +0,0 @@
1
- #!/usr/bin/env ruby
2
- require 'etcd-tools/watchdog/vip'
3
- app = EtcdTools::Watchdog::Vip.new
4
- app.run
@@ -1,23 +0,0 @@
1
- require 'etcd-tools/watchdog/init'
2
- # require 'etcd-tools/watchdogthreads/haproxy'
3
- require 'etcd-tools/erb'
4
-
5
- module EtcdTools
6
- module Watchdog
7
- class HAproxy < EtcdTools::Watchdog::Init
8
-
9
- def run
10
- while @etcd.watch(@config[:haproxy_cfg]) do
11
-
12
- end
13
- end
14
-
15
- def generate_config
16
- end
17
-
18
- def reload_haproxy
19
- end
20
-
21
- end
22
- end
23
- end
@@ -1,43 +0,0 @@
1
- require 'timeout'
2
- require 'json'
3
- require 'time'
4
- require 'etcd-tools/mixins'
5
- require 'etcd-tools/watchdog/util/config'
6
- require 'etcd-tools/watchdog/util/logger'
7
- require 'etcd-tools/watchdog/util/helpers'
8
- require 'etcd-tools/watchdog/util/etcd'
9
- require 'etcd-tools/watchdog/threads/etcd'
10
-
11
- module EtcdTools
12
- module Watchdog
13
- class Init
14
-
15
- include EtcdTools::Watchdog::Util::Config
16
- include EtcdTools::Watchdog::Util::Logger
17
- include EtcdTools::Watchdog::Util::Helpers
18
- include EtcdTools::Watchdog::Util::Etcd
19
- include EtcdTools::Watchdog::Threads
20
-
21
- def initialize
22
- @config = { debug: false }
23
- @config = config
24
- @exit = false
25
- @exit_sigs = ['INT', 'TERM']
26
- @exit_sigs.each { |sig| Signal.trap(sig) { @exit = true } }
27
- Signal.trap('USR1') { @config[:debug] = false }
28
- Signal.trap('USR2') { @config[:debug] = true }
29
- Signal.trap('HUP') { @config = config }
30
- end
31
-
32
- def setup(proc_name, nice = -20)
33
- if RUBY_VERSION >= '2.1'
34
- Process.setproctitle(proc_name)
35
- else
36
- $0 = proc_name
37
- end
38
- Process.setpriority(Process::PRIO_PROCESS, 0, nice)
39
- # TODO: Process.daemon ...
40
- end
41
- end
42
- end
43
- end
@@ -1,20 +0,0 @@
1
- module EtcdTools
2
- module Watchdog
3
- module Threads
4
- def thread_etcd
5
- Thread.new do
6
- debug '<etcd> starting thread...'
7
- etcd = etcd_connect!
8
- while !@exit do
9
- debug '<etcd> checking etcd state'
10
- status = leader? etcd
11
- @semaphore[:etcd].synchronize { @status_etcd = status }
12
- debug "<etcd> i am #{status ? 'the leader' : 'not a leader' }"
13
- sleep @config[:parameters][:etcd_interval]
14
- end
15
- info '<etcd> ending thread...'
16
- end
17
- end
18
- end
19
- end
20
- end
@@ -1,22 +0,0 @@
1
- require 'net/ping'
2
-
3
- module EtcdTools
4
- module Watchdog
5
- module Threads
6
- def thread_icmp
7
- Thread.new do
8
- debug '<icmp> starting thread...'
9
- icmp = Net::Ping::ICMP.new(@config[:parameters][:vip])
10
- while !@exit do
11
- debug '<icmp> checking state by ping'
12
- status = vip_alive? icmp
13
- @semaphore[:icmp].synchronize { @status_icmp = status }
14
- debug "<icmp> VIP is #{status ? 'alive' : 'down' }"
15
- sleep @config[:parameters][:icmp_interval]
16
- end
17
- info '<icmp> ending thread...'
18
- end
19
- end
20
- end
21
- end
22
- end
@@ -1,43 +0,0 @@
1
- require 'yaml'
2
- require 'etcd-tools/mixins'
3
-
4
- module EtcdTools
5
- module Watchdog
6
- module Util
7
- module Config
8
- private
9
- def default_config
10
- { debug: false,
11
- parameters: { interface: 'eth0',
12
- vip: '192.168.0.168',
13
- mask: '255.255.255.0',
14
- interval: 1,
15
- etcd_endpoint: 'http://127.0.0.1:4001',
16
- etcd_interval: 1,
17
- etcd_timeout: 5,
18
- icmp_count: 2,
19
- icmp_interval: 1,
20
- arping_count: 1,
21
- arping_wait: 1 },
22
- commands: { arping: `which arping`.chomp,
23
- iproute: `which ip`.chomp,
24
- arp: `which arp`.chomp } }
25
- end
26
-
27
- def config
28
- cfg = default_config
29
- if File.exist? '/etc/etcd-watchdog.yaml'
30
- cfg = cfg.deep_merge YAML.load_file('/etc/etcd-watchdog.yaml')
31
- puts 'loaded config from /etc/etcd-watchdog.yaml'
32
- elsif File.exist? './etcd-watchdog.yaml'
33
- cfg = cfg.deep_merge YAML.load_file('./etcd-watchdog.yaml')
34
- puts 'loaded config from ./etcd-watchdog.yaml'
35
- else
36
- puts 'no config file loaded, using defaults'
37
- end
38
- cfg
39
- end
40
- end
41
- end
42
- end
43
- end
@@ -1,34 +0,0 @@
1
- require 'etcd'
2
-
3
- module EtcdTools
4
- module Watchdog
5
- module Util
6
- module Etcd
7
- # connect to ETCD
8
- def etcd_connect!
9
- (host, port) = @config[:parameters][:etcd_endpoint].gsub(/^https?:\/\//, '').gsub(/\/$/, '').split(':')
10
- etcd = ::Etcd.client(host: host, port: port)
11
- begin
12
- versions = JSON.parse(etcd.version)
13
- info "<etcd> conncted to ETCD at #{@config[:parameters][:etcd_endpoint]}"
14
- info "<etcd> server version: #{versions['etcdserver']}"
15
- info "<etcd> cluster version: #{versions['etcdcluster']}"
16
- info "<etcd> healthy: #{etcd.healthy?}"
17
- return etcd
18
- rescue Exception => e
19
- err "<etcd> couldn't connect to etcd at #{host}:#{port}"
20
- err "<etcd> #{e.message}"
21
- @exit = true
22
- end
23
- end
24
-
25
- # is my ETCD the leader?
26
- # <IMPLEMENTED>
27
- def leader?(etcd)
28
- etcd.stats(:self)['id'] == etcd.stats(:self)['leaderInfo']['leader']
29
- end
30
-
31
- end
32
- end
33
- end
34
- end
@@ -1,25 +0,0 @@
1
- require 'socket'
2
-
3
- module EtcdTools
4
- module Watchdog
5
- module Util
6
- module Helpers
7
- def hostname
8
- @hostname ||= Socket.gethostname
9
- end
10
-
11
- def arping
12
- @config[:commands][:arping]
13
- end
14
-
15
- def iproute
16
- @config[:commands][:iproute]
17
- end
18
-
19
- def arp
20
- @config[:commands][:arp]
21
- end
22
- end
23
- end
24
- end
25
- end
@@ -1,44 +0,0 @@
1
- require 'logger'
2
-
3
- module EtcdTools
4
- module Watchdog
5
- module Util
6
- module Logger
7
- def info(message)
8
- if @config[:debug]
9
- @semaphore[:log].synchronize do
10
- $stdout.puts(Time.now.to_s + ' INFO (TID:' + Thread.current.object_id.to_s + ') ' + message.to_s)
11
- $stdout.flush
12
- end
13
- else
14
- @semaphore[:log].synchronize do
15
- $stdout.puts(Time.now.to_s + ' INFO ' + message.to_s)
16
- $stdout.flush
17
- end
18
- end
19
- end
20
-
21
- def err(message)
22
- if @config[:debug]
23
- @semaphore[:log].synchronize do
24
- $stdout.puts(Time.now.to_s + ' ERROR (TID:' + Thread.current.object_id.to_s + ') ' + message.to_s)
25
- $stdout.flush
26
- end
27
- else
28
- @semaphore[:log].synchronize do
29
- $stdout.puts(Time.now.to_s + ' ERROR ' + message.to_s)
30
- $stdout.flush
31
- end
32
- end
33
- end
34
-
35
- def debug(message)
36
- @semaphore[:log].synchronize do
37
- $stdout.puts(Time.now.to_s + ' DEBUG (TID:' + Thread.current.object_id.to_s + ') ' + message.to_s)
38
- $stdout.flush
39
- end if @config[:debug]
40
- end
41
- end
42
- end
43
- end
44
- end
@@ -1,170 +0,0 @@
1
- require 'ipaddr'
2
- require 'etcd-tools/watchdog/init'
3
- require 'etcd-tools/watchdog/threads/icmp'
4
-
5
- module EtcdTools
6
- module Watchdog
7
- class Vip < EtcdTools::Watchdog::Init
8
-
9
- def run
10
- if Process.euid != 0
11
- $stderr.puts 'Must run under root user!'
12
- exit! 1
13
- end
14
- setup 'etcd-vip-watchdog'
15
- @semaphore = {
16
- log: Mutex.new,
17
- etcd: Mutex.new,
18
- icmp: Mutex.new
19
- }
20
- @thread = { icmp: thread_icmp, etcd: thread_etcd }
21
- @status_etcd = false
22
- @status_icmp = false
23
- @thread.each_value(&:run)
24
- sleep @config[:parameters][:interval]
25
- first_cycle = true
26
- while !@exit do
27
- status_etcd = status_icmp = false # FIXME: introduce CVs...
28
- @semaphore[:icmp].synchronize { status_icmp = @status_icmp }
29
- @semaphore[:etcd].synchronize { status_etcd = @status_etcd }
30
- if status_etcd
31
- if got_vip?
32
- debug '<main> i am the leader with VIP, that is OK'
33
- else
34
- info '<main> i am the leader without VIP, checking whether it is free'
35
- if status_icmp
36
- info '<main> VIP is still up! (ICMP)'
37
- # FIXME: notify by sensu client socket
38
- else
39
- info '<main> VIP is unreachable by ICMP, checking for duplicates on L2'
40
- if vip_dup?
41
- info '<main> VIP is still assigned! (ARP)'
42
- # FIXME: notify by sensu client socket
43
- else
44
- info '<main> VIP is free, assigning'
45
- vip_handle! status_etcd
46
- info '<main> updating other hosts about change'
47
- vip_update_arp!
48
- end
49
- end
50
- end
51
- else
52
- if got_vip?
53
- info '<main> i got VIP and should not, removing'
54
- vip_handle! status_etcd
55
- info '<main> updating other hosts about change'
56
- vip_update_arp!
57
- else
58
- debug '<main> i am not a leader and i do not have the VIP, that is OK'
59
- end
60
- end
61
- sleep @config[:parameters][:interval]
62
- if first_cycle
63
- @semaphore[:icmp].synchronize { status_icmp = @status_icmp }
64
- @semaphore[:etcd].synchronize { status_etcd = @status_etcd }
65
- info "<main> i #{status_etcd ? 'AM' : 'am NOT'} the leader"
66
- info "<main> i #{got_vip? ? 'DO' : 'do NOT'} have the VIP"
67
- info "<main> i #{status_icmp ? 'CAN' : 'CANNOT'} see the VIP"
68
- end
69
- first_cycle = false
70
- end
71
- info '<main> terminated!'
72
- if got_vip?
73
- info '<main> removing VIP'
74
- vip_handle! false
75
- vip_update_arp!
76
- end
77
- info '<main> stopping threads...'
78
- @thread.each_value(&:join)
79
- info '<main> exiting...'
80
- exit 0
81
- end
82
-
83
- # add or remove VIP on interface
84
- # <IMPLEMENTED>
85
- def vip_handle!(leader)
86
- ip = IPAddr.new(@config[:parameters][:vip])
87
- mask = @config[:parameters][:mask]
88
- cmd = [ iproute,
89
- 'address',
90
- '',
91
- "#{ip}/#{mask}",
92
- 'dev',
93
- @config[:parameters][:interface],
94
- 'label',
95
- @config[:parameters][:interface] + '-vip',
96
- '>/dev/null 2>&1'
97
- ]
98
- leader ? cmd[2] = 'add' : cmd[2] = 'delete'
99
- debug "<shell> #{cmd.join(' ')}"
100
- if system(cmd.join(' '))
101
- return true
102
- else
103
- return false
104
- end
105
- end
106
-
107
- # send gratuitous ARP to the network
108
- # <IMPLEMENTED>
109
- def vip_update_arp!
110
- cmd = [ arping, '-U', '-q',
111
- '-c', @config[:parameters][:arping_count],
112
- '-I', @config[:parameters][:interface],
113
- @config[:parameters][:vip] ]
114
- debug "<shell> #{cmd.join(' ')}"
115
- if system(cmd.join(' '))
116
- return true
117
- else
118
- return false
119
- end
120
- end
121
-
122
- # check whether VIP is assigned to me
123
- # <IMPLEMENTED>
124
- def got_vip?
125
- cmd = [ iproute,
126
- 'address',
127
- 'show',
128
- 'label',
129
- "#{@config[:parameters][:interface]}-vip",
130
- '|',
131
- 'grep',
132
- '-q',
133
- "#{@config[:parameters][:interface]}-vip"
134
- ]
135
- debug "<shell> #{cmd.join(' ')}"
136
- if system(cmd.join(' '))
137
- return true
138
- else
139
- return false
140
- end
141
- end
142
-
143
- # check reachability of VIP by ICMP echo
144
- # <--- REWORK
145
- def vip_alive?(icmp)
146
- (1..@config[:parameters][:icmp_count]).each { return true if icmp.ping }
147
- return false
148
- end
149
-
150
- # check whether the IP is registered anywhere
151
- #
152
- def vip_dup?
153
- cmd_arp = [ arp, '-d', @config[:parameters][:vip], '>/dev/null 2>&1' ]
154
- cmd_arping = [ arping, '-D', '-q',
155
- '-c', @config[:parameters][:arping_count],
156
- '-w', @config[:parameters][:arping_wait],
157
- '-I', @config[:parameters][:interface],
158
- @config[:parameters][:vip] ]
159
- debug "<shell> #{cmd_arp.join(' ')}"
160
- system(cmd_arp.join(' '))
161
- debug "<shell> #{cmd_arping.join(' ')}"
162
- if system(cmd_arping.join(' '))
163
- return false
164
- else
165
- return true
166
- end
167
- end
168
- end
169
- end
170
- end