mtik_directory_2_address_list 1.0.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.
- data/.simplecov +3 -0
- data/Gemfile +4 -0
- data/Rakefile +15 -0
- data/bin/mtik_directory_2_address_list +68 -0
- data/lib/mtik_directory_2_address_list/directory.rb +77 -0
- data/lib/mtik_directory_2_address_list/log.rb +18 -0
- data/lib/mtik_directory_2_address_list/mikrotik.rb +149 -0
- data/lib/mtik_directory_2_address_list/version.rb +3 -0
- data/lib/mtik_directory_2_address_list.rb +40 -0
- data/mtik_directory_2_address_list.gemspec +25 -0
- data/test/test_directory.rb +62 -0
- data/test/test_mikrotik.rb +113 -0
- metadata +156 -0
data/.simplecov
ADDED
data/Gemfile
ADDED
data/Rakefile
ADDED
@@ -0,0 +1,15 @@
|
|
1
|
+
require 'bundler/gem_tasks'
|
2
|
+
require 'rake/testtask'
|
3
|
+
|
4
|
+
Rake::TestTask.new do |t|
|
5
|
+
t.libs << 'test'
|
6
|
+
end
|
7
|
+
|
8
|
+
desc 'Run tests with simplecov enabled'
|
9
|
+
task :coverage do
|
10
|
+
ENV['COVERAGE'] = 'yes'
|
11
|
+
Rake::Task['test'].execute
|
12
|
+
end
|
13
|
+
|
14
|
+
desc 'Run tests'
|
15
|
+
task :default => [:test]
|
@@ -0,0 +1,68 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
require 'optparse'
|
3
|
+
require 'syslog'
|
4
|
+
require 'mtik_directory_2_address_list'
|
5
|
+
|
6
|
+
class Args < OptionParser
|
7
|
+
attr_reader :prefix, :quiet
|
8
|
+
attr_reader :dir, :mtik
|
9
|
+
|
10
|
+
def initialize
|
11
|
+
super 'Usage: <dir> <host> <user> <pass>'
|
12
|
+
on('-p', '--prefix PREFIX', "Mikrotik address-list prefix (default: #{@prefix = 'md2al_'})") { |str| @prefix = str }
|
13
|
+
on('-q', '--quiet', "Be quiet (default: #{@quiet = false})") { |bool| @quiet = bool }
|
14
|
+
parse!
|
15
|
+
if ARGV.size != 4
|
16
|
+
puts to_s
|
17
|
+
puts "Missing required argument"
|
18
|
+
exit(2)
|
19
|
+
else
|
20
|
+
@dir = ARGV.shift
|
21
|
+
@mtik = Hash[[:host, :user, :pass].zip(ARGV.shift(3))]
|
22
|
+
end
|
23
|
+
rescue OptionParser::ParseError => err
|
24
|
+
puts to_s
|
25
|
+
puts "Error: #{err}"
|
26
|
+
exit(2)
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
class Main
|
31
|
+
def initialize
|
32
|
+
args
|
33
|
+
sync
|
34
|
+
end
|
35
|
+
|
36
|
+
def args
|
37
|
+
@args ||= Args.new.tap do |args|
|
38
|
+
Syslog.open($PROGRAM_NAME, Syslog::LOG_PID, Syslog::LOG_DAEMON)
|
39
|
+
args.quiet || MtikDirectory2AddressList::Log.output(&method(:info))
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
def sync
|
44
|
+
MtikDirectory2AddressList.sync(args.dir, args.mtik, args.prefix)
|
45
|
+
rescue => err
|
46
|
+
err("Error: #{err} [#{err.backtrace.first}]")
|
47
|
+
sleep(30) ; retry
|
48
|
+
end
|
49
|
+
|
50
|
+
# Send +message+ to syslog and STDERR.
|
51
|
+
# @param [String] message
|
52
|
+
# @return [void]
|
53
|
+
def err(message)
|
54
|
+
Syslog.err('%s', message)
|
55
|
+
STDERR << message << "\n" if STDERR.tty?
|
56
|
+
end
|
57
|
+
|
58
|
+
# Send +message+ to STDOUT.
|
59
|
+
# @param [String] message
|
60
|
+
# @return [void]
|
61
|
+
def info(message)
|
62
|
+
STDOUT << message << "\n" if STDOUT.tty?
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
66
|
+
if __FILE__ == $PROGRAM_NAME
|
67
|
+
Main.new
|
68
|
+
end
|
@@ -0,0 +1,77 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
|
3
|
+
module MtikDirectory2AddressList
|
4
|
+
class Directory
|
5
|
+
# Manage a directory of symbolic links.
|
6
|
+
#
|
7
|
+
# The IP address is the name of a symlink link in +@path+.
|
8
|
+
# The address list name is what the symbolic link points to.
|
9
|
+
#
|
10
|
+
# @param [Hash] params
|
11
|
+
# @option params [String] :path The path to the directory containing symbolic links
|
12
|
+
def initialize(params)
|
13
|
+
@path = params[:path]
|
14
|
+
end
|
15
|
+
|
16
|
+
# Return the address list associated with the specified IP.
|
17
|
+
#
|
18
|
+
# @param [String] ip The IP to search for
|
19
|
+
#
|
20
|
+
# @return [String] The value associated with +ip+
|
21
|
+
# @return [nil] When the +ip+ is not found
|
22
|
+
def [](ip)
|
23
|
+
File.readlink(File.join(@path, ip))
|
24
|
+
rescue Errno::EINVAL
|
25
|
+
nil # Not a symlink
|
26
|
+
end
|
27
|
+
|
28
|
+
# The {#list} method will not enumerate symbolic links that do not match this regular expression.
|
29
|
+
# The {#[]} method will read any symbolic link, even if its name does not match this regular expression.
|
30
|
+
IP_RE = %r{ \A \d{1,3} \. \d{1,3} \. \d{1,3} \. \d{1,3} \z }x
|
31
|
+
|
32
|
+
# Enumerates every IP in +@path+.
|
33
|
+
#
|
34
|
+
# @return [Enumerator] Arrays with: IP, address list
|
35
|
+
def list
|
36
|
+
Enumerator.new do |y|
|
37
|
+
Dir.foreach(@path) do |de| file = File.basename(de)
|
38
|
+
(file =~ IP_RE) && (value = self[file]) && (y << [file, value])
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
# Yield whenever +@path+ is modified.
|
44
|
+
#
|
45
|
+
# @yield Right after being called
|
46
|
+
# @yield After every update to +@path+
|
47
|
+
#
|
48
|
+
# @return [void]
|
49
|
+
def watch
|
50
|
+
before = nil
|
51
|
+
loop do sleep(1)
|
52
|
+
next if before == (after = mtime)
|
53
|
+
yield ; before = after
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
# Watch +path+, and yield +list+ as a hash.
|
58
|
+
#
|
59
|
+
# @param [String] path The directory to watch for modifications
|
60
|
+
#
|
61
|
+
# @yieldparam Hash{String=>String} Map IP addresses to address list names
|
62
|
+
#
|
63
|
+
# @return [void]
|
64
|
+
def self.watch(path)
|
65
|
+
dir = self.new(path:path)
|
66
|
+
dir.watch do
|
67
|
+
yield(Hash[dir.list.to_a])
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
71
|
+
private
|
72
|
+
|
73
|
+
def mtime
|
74
|
+
File.mtime(@path)
|
75
|
+
end
|
76
|
+
end
|
77
|
+
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
|
3
|
+
module MtikDirectory2AddressList
|
4
|
+
module Log
|
5
|
+
module_function
|
6
|
+
|
7
|
+
# Call #output to replace this method.
|
8
|
+
def info
|
9
|
+
end
|
10
|
+
|
11
|
+
# Set the logging method.
|
12
|
+
def output(&block)
|
13
|
+
define_singleton_method(:info) do |&message|
|
14
|
+
block.call(message.call)
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
@@ -0,0 +1,149 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
require 'mtik'
|
3
|
+
|
4
|
+
module MtikDirectory2AddressList
|
5
|
+
class Mikrotik
|
6
|
+
class Error < StandardError ; end
|
7
|
+
class RouterError < StandardError ; end
|
8
|
+
class SyncError < StandardError ; end
|
9
|
+
|
10
|
+
def initialize(params)
|
11
|
+
@prefix = (params[:prefix] || 'md2al_')
|
12
|
+
@mtik_params = params.select { |k,_| %w(host user pass).include? k.to_s }
|
13
|
+
fetch
|
14
|
+
end
|
15
|
+
|
16
|
+
# Enumerates every IP in address lists prefixed with +@prefix+.
|
17
|
+
#
|
18
|
+
# @return [Enumerator] Arrays with: IP, address list
|
19
|
+
def list
|
20
|
+
Enumerator.new do |y|
|
21
|
+
@cache.each_value do |re|
|
22
|
+
y << [re['address'], re['list']]
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
# Add IP to address list.
|
28
|
+
#
|
29
|
+
# @param [String] ip The IP address to add
|
30
|
+
# @param [String] address_list The address list to add the IP into
|
31
|
+
#
|
32
|
+
# @raise [SyncError] When the IP already exists in the local cache
|
33
|
+
# @raise [RouterError] When the IP already exists in the Mikrotik router
|
34
|
+
#
|
35
|
+
# @return [self]
|
36
|
+
def add(ip, address_list)
|
37
|
+
if @cache.key?(ip)
|
38
|
+
raise(SyncError, "IP #{ip} already in cache")
|
39
|
+
end
|
40
|
+
id = address_list_add(ip, @prefix + address_list)
|
41
|
+
@cache[ip] = {".id"=>id, "address"=>ip, "list"=>address_list}
|
42
|
+
return self
|
43
|
+
end
|
44
|
+
|
45
|
+
# Move IP to a different address list.
|
46
|
+
#
|
47
|
+
# @param [String] ip The IP address to move
|
48
|
+
# @param [String] address_list The address list to move the IP into
|
49
|
+
#
|
50
|
+
# @raise [SyncError] When the IP is not found in the local cache
|
51
|
+
# @raise [RouterError] When the IP is not found in the Mikrotik router
|
52
|
+
#
|
53
|
+
# @return [self]
|
54
|
+
def update(ip, address_list)
|
55
|
+
unless (re = @cache[ip])
|
56
|
+
raise(SyncError, "IP #{ip} not in cache")
|
57
|
+
end
|
58
|
+
address_list_set(re['.id'], @prefix + address_list)
|
59
|
+
re['list'] = address_list
|
60
|
+
return self
|
61
|
+
end
|
62
|
+
|
63
|
+
# Remove IP from its current address list.
|
64
|
+
#
|
65
|
+
# @param [String] ip The IP address to remove
|
66
|
+
#
|
67
|
+
# @raise [SyncError] When the IP is not found in the local cache
|
68
|
+
# @raise [RouterError] When the IP is not found in the Mikrotik router
|
69
|
+
#
|
70
|
+
# @return [self]
|
71
|
+
def delete(ip)
|
72
|
+
unless (re = @cache.delete(ip))
|
73
|
+
raise(SyncError, "IP #{ip} not in cache")
|
74
|
+
end
|
75
|
+
address_list_remove(re['.id'])
|
76
|
+
return self
|
77
|
+
end
|
78
|
+
|
79
|
+
# Delete all IPs from address lists prefixed with +@prefix+.
|
80
|
+
#
|
81
|
+
# @return [self]
|
82
|
+
def clear
|
83
|
+
@cache.each_value do |re|
|
84
|
+
address_list_remove(re['.id'])
|
85
|
+
end
|
86
|
+
@cache.clear
|
87
|
+
return self
|
88
|
+
end
|
89
|
+
|
90
|
+
private
|
91
|
+
|
92
|
+
# @return [MTik::Connection]
|
93
|
+
def connection
|
94
|
+
@connection ||= MTik::Connection.new(@mtik_params).tap do
|
95
|
+
Log.info { "Connecting to Mikrotik at #{@mtik_params.inspect}" }
|
96
|
+
end
|
97
|
+
end
|
98
|
+
|
99
|
+
# @return [String, nil]
|
100
|
+
def request(*args)
|
101
|
+
Log.info { "Mikrotik request: #{args.inspect}" }
|
102
|
+
connection.get_reply_each(*args) do |request|
|
103
|
+
while (re = request.reply.shift)
|
104
|
+
Log.info { "Mikrotik reply: #{re.inspect}" }
|
105
|
+
if re.key?('!re') ; yield(re)
|
106
|
+
elsif re.key?('!done') ; return re['ret']
|
107
|
+
elsif re.key?('!trap') ; raise(RouterError, re['message'])
|
108
|
+
else raise(Error, "Unrecognized Mikrotik reply: #{re.inspect}")
|
109
|
+
end
|
110
|
+
end
|
111
|
+
end
|
112
|
+
raise(Error, "Missing reply: no !re, !done, !trap received")
|
113
|
+
end
|
114
|
+
|
115
|
+
# @return [void]
|
116
|
+
def fetch
|
117
|
+
@cache = {}
|
118
|
+
request('/ip/firewall/address-list/print', '=.proplist=.id,address,list') do |re|
|
119
|
+
re['list'].sub!(/\A#{Regexp.quote(@prefix)}/) do
|
120
|
+
@cache[re['address']] = re
|
121
|
+
next '' # replaces @prefix on re['list']
|
122
|
+
end
|
123
|
+
end
|
124
|
+
rescue
|
125
|
+
@cache = nil
|
126
|
+
raise
|
127
|
+
end
|
128
|
+
|
129
|
+
# @param [String] ip
|
130
|
+
# @param [String] address_list
|
131
|
+
# @return [String] The ID of the added item
|
132
|
+
def address_list_add(ip, address_list)
|
133
|
+
request('/ip/firewall/address-list/add', "=address=#{ip}", "=list=#{address_list}")
|
134
|
+
end
|
135
|
+
|
136
|
+
# @param [String] id
|
137
|
+
# @param [String] address_list
|
138
|
+
# @return [void]
|
139
|
+
def address_list_set(id, address_list)
|
140
|
+
request('/ip/firewall/address-list/set', "=.id=#{id}", "=list=#{address_list}")
|
141
|
+
end
|
142
|
+
|
143
|
+
# @param [String] id
|
144
|
+
# @return [void]
|
145
|
+
def address_list_remove(id)
|
146
|
+
request('/ip/firewall/address-list/remove', "=.id=#{id}")
|
147
|
+
end
|
148
|
+
end
|
149
|
+
end
|
@@ -0,0 +1,40 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
require 'mtik_directory_2_address_list/version'
|
3
|
+
require 'mtik_directory_2_address_list/log'
|
4
|
+
require 'mtik_directory_2_address_list/directory'
|
5
|
+
require 'mtik_directory_2_address_list/mikrotik'
|
6
|
+
|
7
|
+
module MtikDirectory2AddressList
|
8
|
+
module_function
|
9
|
+
|
10
|
+
# Synchronize a directory containing symbolic links with a Mikrotik address list.
|
11
|
+
#
|
12
|
+
# @param [String] src The directory where to look for symbolic links
|
13
|
+
# @param [Hash] dst The object passed to the +mtik+ gem (keys: +host+, +user+, +pass+)
|
14
|
+
# @param [String] prefix The prefix an address list name must have for it to be used
|
15
|
+
# @return [nil]
|
16
|
+
def sync(src, dst, prefix = "dir_")
|
17
|
+
Log.info { "Synchronizing [#{src}] with router at [#{dst[:host]}]" }
|
18
|
+
mtik = Mikrotik.new(dst.dup.merge(prefix:prefix))
|
19
|
+
Directory.watch(src) do |dm| # Directory Map {ip => address_list}
|
20
|
+
mm = Hash[mtik.list.to_a] # Mikrotik Map {ip => address_list}
|
21
|
+
dm.each_pair do |d_ip, d_address_list|
|
22
|
+
if mm.key? d_ip
|
23
|
+
if d_address_list != mm[d_ip]
|
24
|
+
Log.info { "Updating IP [#{d_ip}] to list [#{d_address_list}]" }
|
25
|
+
mtik.update(d_ip, d_address_list)
|
26
|
+
end
|
27
|
+
else
|
28
|
+
Log.info { "Adding IP [#{d_ip}] to list [#{d_address_list}]" }
|
29
|
+
mtik.add(d_ip, d_address_list)
|
30
|
+
end
|
31
|
+
end
|
32
|
+
mm.each_key do |m_ip|
|
33
|
+
unless dm.key? m_ip
|
34
|
+
Log.info { "Deleting IP [#{m_ip}]" }
|
35
|
+
mtik.delete(m_ip)
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
lib = File.expand_path('../lib', __FILE__)
|
3
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
|
+
require 'mtik_directory_2_address_list/version'
|
5
|
+
|
6
|
+
Gem::Specification.new do |spec|
|
7
|
+
spec.name = 'mtik_directory_2_address_list'
|
8
|
+
spec.version = MtikDirectory2AddressList::VERSION
|
9
|
+
spec.authors = ['Andre Luiz dos Santos']
|
10
|
+
spec.email = ['andre.netvision.com.br@gmail.com']
|
11
|
+
spec.summary = %q{Synchronize a directory with a Mikrotik address list}
|
12
|
+
|
13
|
+
spec.files = `git ls-files -z`.split("\x0")
|
14
|
+
spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
|
15
|
+
spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
|
16
|
+
spec.require_paths = ['lib']
|
17
|
+
|
18
|
+
spec.add_development_dependency 'bundler', '~> 1.6'
|
19
|
+
spec.add_development_dependency 'rake', '~> 10.0'
|
20
|
+
spec.add_development_dependency 'minitest', '~> 5.5'
|
21
|
+
spec.add_development_dependency 'mocha', '~> 1.1'
|
22
|
+
spec.add_development_dependency 'simplecov', '~> 0.9'
|
23
|
+
|
24
|
+
spec.add_runtime_dependency 'mtik', '~> 4.0'
|
25
|
+
end
|
@@ -0,0 +1,62 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
require 'simplecov'
|
3
|
+
gem 'minitest'
|
4
|
+
require 'minitest/autorun'
|
5
|
+
require 'mocha/setup'
|
6
|
+
require 'mtik_directory_2_address_list'
|
7
|
+
|
8
|
+
describe "OS" do
|
9
|
+
# man READLINK(2) says:
|
10
|
+
# EINVAL The named file is not a symbolic link.
|
11
|
+
it "readlink should yield EINVAL on non-symlinks" do
|
12
|
+
begin
|
13
|
+
File.readlink("/") # "/" is always a directory
|
14
|
+
rescue => err
|
15
|
+
assert_instance_of(Errno::EINVAL, err)
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
module MtikDirectory2AddressList
|
21
|
+
describe "Directory" do
|
22
|
+
describe "sample-1" do
|
23
|
+
let :path do
|
24
|
+
"/tmp/sample-1"
|
25
|
+
end
|
26
|
+
|
27
|
+
before do
|
28
|
+
FileUtils.mkdir(path)
|
29
|
+
Dir.chdir(path) do
|
30
|
+
FileUtils.touch(%w(ignored 9.8.7.6))
|
31
|
+
File.symlink("account-name-1", "1.2.3.4")
|
32
|
+
File.symlink("account-name-2", "1.2.3.5")
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
after do
|
37
|
+
FileUtils.rm_rf(path)
|
38
|
+
end
|
39
|
+
|
40
|
+
it "should list two links" do
|
41
|
+
res = Directory.new(path:path).list
|
42
|
+
assert_equal([%w(1.2.3.4 account-name-1), %w(1.2.3.5 account-name-2)], res.to_a)
|
43
|
+
end
|
44
|
+
|
45
|
+
it "should yield on different mtime" do
|
46
|
+
dir = Directory.new(path:path)
|
47
|
+
dir.stubs(:sleep)
|
48
|
+
dir.stubs(:loop).multiple_yields(*4.times)
|
49
|
+
File.stubs(:mtime).with(path).returns(10, 10, 20, 20)
|
50
|
+
Directory.expects(:new).with(path:path).returns(dir)
|
51
|
+
times = 0
|
52
|
+
Directory.watch(path) do |dem| # Directory Entries Map
|
53
|
+
assert_equal({'1.2.3.4' => 'account-name-1', '1.2.3.5' => 'account-name-2'}, dem)
|
54
|
+
times += 1
|
55
|
+
end
|
56
|
+
# 'watch' should yield first right after starting,
|
57
|
+
# and then a second time when the 'mtime' changes.
|
58
|
+
assert_equal(2, times)
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
@@ -0,0 +1,113 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
require 'simplecov'
|
3
|
+
gem 'minitest'
|
4
|
+
require 'minitest/autorun'
|
5
|
+
require 'mocha/setup'
|
6
|
+
require 'mtik_directory_2_address_list'
|
7
|
+
|
8
|
+
begin
|
9
|
+
file = File.join(File.dirname(__FILE__), 'mikrotik.eval')
|
10
|
+
$mtik_params = eval(IO.read(file))
|
11
|
+
rescue => err
|
12
|
+
puts '*' * 79
|
13
|
+
puts "Cannot read Mikrotik configuration file: #{err}"
|
14
|
+
puts ''
|
15
|
+
puts "Create a file named mikrotik.eval for testing the Mikrotik code."
|
16
|
+
puts "Example: {host:'10.9.8.7', user:'login', pass:'123', prefix:'download_speed_'}"
|
17
|
+
puts '*' * 79
|
18
|
+
else
|
19
|
+
module MtikDirectory2AddressList
|
20
|
+
describe "Mikrotik" do
|
21
|
+
subject do
|
22
|
+
Mikrotik.new($mtik_params)
|
23
|
+
end
|
24
|
+
|
25
|
+
before do
|
26
|
+
subject.clear
|
27
|
+
end
|
28
|
+
|
29
|
+
it "#add" do
|
30
|
+
subject.add('1.2.3.4', 'address-list-name')
|
31
|
+
assert_equal([%w(1.2.3.4 address-list-name)], subject.list.to_a)
|
32
|
+
end
|
33
|
+
|
34
|
+
it "raises SyncError on duplicated IP on #add" do
|
35
|
+
subject.add('1.2.3.4', 'address-list-name')
|
36
|
+
assert_raises(Mikrotik::SyncError) { subject.add('1.2.3.4', 'address-list-name') }
|
37
|
+
end
|
38
|
+
|
39
|
+
it "#list" do
|
40
|
+
subject.add('1.2.3.4', 'address-list-name')
|
41
|
+
subject.send(:fetch)
|
42
|
+
assert_equal([%w(1.2.3.4 address-list-name)], subject.list.to_a)
|
43
|
+
end
|
44
|
+
|
45
|
+
it "#delete" do
|
46
|
+
subject.add('1.2.3.4', 'address-list-name')
|
47
|
+
subject.send(:fetch)
|
48
|
+
subject.delete('1.2.3.4')
|
49
|
+
assert_equal([], subject.list.to_a)
|
50
|
+
end
|
51
|
+
|
52
|
+
it "#delete after #add" do
|
53
|
+
subject.add('1.2.3.4', 'address-list-name')
|
54
|
+
subject.delete('1.2.3.4')
|
55
|
+
assert_equal([], subject.list.to_a)
|
56
|
+
subject.send(:fetch)
|
57
|
+
assert_equal([], subject.list.to_a)
|
58
|
+
end
|
59
|
+
|
60
|
+
it "raises SyncError on missing IP on #delete" do
|
61
|
+
assert_raises(Mikrotik::SyncError) { subject.delete('1.2.3.4') }
|
62
|
+
end
|
63
|
+
|
64
|
+
it "#update" do
|
65
|
+
subject.add('1.2.3.4', 'address-list-name')
|
66
|
+
subject.send(:fetch)
|
67
|
+
subject.update('1.2.3.4', 'brand-new-name')
|
68
|
+
assert_equal([%w(1.2.3.4 brand-new-name)], subject.list.to_a)
|
69
|
+
subject.send(:fetch)
|
70
|
+
assert_equal([%w(1.2.3.4 brand-new-name)], subject.list.to_a)
|
71
|
+
end
|
72
|
+
|
73
|
+
it "#update after #add" do
|
74
|
+
subject.add('1.2.3.4', 'address-list-name')
|
75
|
+
subject.update('1.2.3.4', 'brand-new-name')
|
76
|
+
assert_equal([%w(1.2.3.4 brand-new-name)], subject.list.to_a)
|
77
|
+
subject.send(:fetch)
|
78
|
+
assert_equal([%w(1.2.3.4 brand-new-name)], subject.list.to_a)
|
79
|
+
end
|
80
|
+
|
81
|
+
it "raises SyncError on missing IP on #update" do
|
82
|
+
assert_raises(Mikrotik::SyncError) { subject.update('1.2.3.4', 'address-list-name') }
|
83
|
+
end
|
84
|
+
|
85
|
+
it "raises RouterError on !trap" do
|
86
|
+
mtik_api = %w(/ip/firewall/address-list/remove =.id=XXX)
|
87
|
+
err = assert_raises(Mikrotik::RouterError) { subject.send(:request, *mtik_api) }
|
88
|
+
assert_equal('no such item', err.to_s) # ID XXX is impossible/invalid
|
89
|
+
end
|
90
|
+
|
91
|
+
it "raises Error on unrecognized Mikrotik reply" do
|
92
|
+
re = {'unexpected'=>nil}
|
93
|
+
request = mock(:reply => [re])
|
94
|
+
subject.send(:connection).stubs(:get_reply_each).yields(request)
|
95
|
+
err = assert_raises(Mikrotik::Error) { subject.send(:request, '/xxx') }
|
96
|
+
assert_equal('Unrecognized Mikrotik reply: {"unexpected"=>nil}', err.to_s)
|
97
|
+
end
|
98
|
+
|
99
|
+
it "raises Error on incomplete Mikrotik reply" do
|
100
|
+
subject.send(:connection).stubs(:get_reply_each)
|
101
|
+
err = assert_raises(Mikrotik::Error) { subject.send(:request, '/xxx') }
|
102
|
+
assert_equal('Missing reply: no !re, !done, !trap received', err.to_s)
|
103
|
+
end
|
104
|
+
|
105
|
+
it "nil @cache on fetch error" do
|
106
|
+
class MyError < StandardError ; end
|
107
|
+
subject.stubs(:request).raises(MyError)
|
108
|
+
assert_raises(MyError) { subject.send(:fetch) }
|
109
|
+
assert_equal(nil, subject.instance_variable_get(:@cache))
|
110
|
+
end
|
111
|
+
end
|
112
|
+
end
|
113
|
+
end
|
metadata
ADDED
@@ -0,0 +1,156 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: mtik_directory_2_address_list
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 1.0.0
|
5
|
+
prerelease:
|
6
|
+
platform: ruby
|
7
|
+
authors:
|
8
|
+
- Andre Luiz dos Santos
|
9
|
+
autorequire:
|
10
|
+
bindir: bin
|
11
|
+
cert_chain: []
|
12
|
+
date: 2015-01-12 00:00:00.000000000 Z
|
13
|
+
dependencies:
|
14
|
+
- !ruby/object:Gem::Dependency
|
15
|
+
name: bundler
|
16
|
+
requirement: !ruby/object:Gem::Requirement
|
17
|
+
none: false
|
18
|
+
requirements:
|
19
|
+
- - ~>
|
20
|
+
- !ruby/object:Gem::Version
|
21
|
+
version: '1.6'
|
22
|
+
type: :development
|
23
|
+
prerelease: false
|
24
|
+
version_requirements: !ruby/object:Gem::Requirement
|
25
|
+
none: false
|
26
|
+
requirements:
|
27
|
+
- - ~>
|
28
|
+
- !ruby/object:Gem::Version
|
29
|
+
version: '1.6'
|
30
|
+
- !ruby/object:Gem::Dependency
|
31
|
+
name: rake
|
32
|
+
requirement: !ruby/object:Gem::Requirement
|
33
|
+
none: false
|
34
|
+
requirements:
|
35
|
+
- - ~>
|
36
|
+
- !ruby/object:Gem::Version
|
37
|
+
version: '10.0'
|
38
|
+
type: :development
|
39
|
+
prerelease: false
|
40
|
+
version_requirements: !ruby/object:Gem::Requirement
|
41
|
+
none: false
|
42
|
+
requirements:
|
43
|
+
- - ~>
|
44
|
+
- !ruby/object:Gem::Version
|
45
|
+
version: '10.0'
|
46
|
+
- !ruby/object:Gem::Dependency
|
47
|
+
name: minitest
|
48
|
+
requirement: !ruby/object:Gem::Requirement
|
49
|
+
none: false
|
50
|
+
requirements:
|
51
|
+
- - ~>
|
52
|
+
- !ruby/object:Gem::Version
|
53
|
+
version: '5.5'
|
54
|
+
type: :development
|
55
|
+
prerelease: false
|
56
|
+
version_requirements: !ruby/object:Gem::Requirement
|
57
|
+
none: false
|
58
|
+
requirements:
|
59
|
+
- - ~>
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: '5.5'
|
62
|
+
- !ruby/object:Gem::Dependency
|
63
|
+
name: mocha
|
64
|
+
requirement: !ruby/object:Gem::Requirement
|
65
|
+
none: false
|
66
|
+
requirements:
|
67
|
+
- - ~>
|
68
|
+
- !ruby/object:Gem::Version
|
69
|
+
version: '1.1'
|
70
|
+
type: :development
|
71
|
+
prerelease: false
|
72
|
+
version_requirements: !ruby/object:Gem::Requirement
|
73
|
+
none: false
|
74
|
+
requirements:
|
75
|
+
- - ~>
|
76
|
+
- !ruby/object:Gem::Version
|
77
|
+
version: '1.1'
|
78
|
+
- !ruby/object:Gem::Dependency
|
79
|
+
name: simplecov
|
80
|
+
requirement: !ruby/object:Gem::Requirement
|
81
|
+
none: false
|
82
|
+
requirements:
|
83
|
+
- - ~>
|
84
|
+
- !ruby/object:Gem::Version
|
85
|
+
version: '0.9'
|
86
|
+
type: :development
|
87
|
+
prerelease: false
|
88
|
+
version_requirements: !ruby/object:Gem::Requirement
|
89
|
+
none: false
|
90
|
+
requirements:
|
91
|
+
- - ~>
|
92
|
+
- !ruby/object:Gem::Version
|
93
|
+
version: '0.9'
|
94
|
+
- !ruby/object:Gem::Dependency
|
95
|
+
name: mtik
|
96
|
+
requirement: !ruby/object:Gem::Requirement
|
97
|
+
none: false
|
98
|
+
requirements:
|
99
|
+
- - ~>
|
100
|
+
- !ruby/object:Gem::Version
|
101
|
+
version: '4.0'
|
102
|
+
type: :runtime
|
103
|
+
prerelease: false
|
104
|
+
version_requirements: !ruby/object:Gem::Requirement
|
105
|
+
none: false
|
106
|
+
requirements:
|
107
|
+
- - ~>
|
108
|
+
- !ruby/object:Gem::Version
|
109
|
+
version: '4.0'
|
110
|
+
description:
|
111
|
+
email:
|
112
|
+
- andre.netvision.com.br@gmail.com
|
113
|
+
executables:
|
114
|
+
- mtik_directory_2_address_list
|
115
|
+
extensions: []
|
116
|
+
extra_rdoc_files: []
|
117
|
+
files:
|
118
|
+
- .simplecov
|
119
|
+
- Gemfile
|
120
|
+
- Rakefile
|
121
|
+
- bin/mtik_directory_2_address_list
|
122
|
+
- lib/mtik_directory_2_address_list.rb
|
123
|
+
- lib/mtik_directory_2_address_list/directory.rb
|
124
|
+
- lib/mtik_directory_2_address_list/log.rb
|
125
|
+
- lib/mtik_directory_2_address_list/mikrotik.rb
|
126
|
+
- lib/mtik_directory_2_address_list/version.rb
|
127
|
+
- mtik_directory_2_address_list.gemspec
|
128
|
+
- test/test_directory.rb
|
129
|
+
- test/test_mikrotik.rb
|
130
|
+
homepage:
|
131
|
+
licenses: []
|
132
|
+
post_install_message:
|
133
|
+
rdoc_options: []
|
134
|
+
require_paths:
|
135
|
+
- lib
|
136
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
137
|
+
none: false
|
138
|
+
requirements:
|
139
|
+
- - ! '>='
|
140
|
+
- !ruby/object:Gem::Version
|
141
|
+
version: '0'
|
142
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
143
|
+
none: false
|
144
|
+
requirements:
|
145
|
+
- - ! '>='
|
146
|
+
- !ruby/object:Gem::Version
|
147
|
+
version: '0'
|
148
|
+
requirements: []
|
149
|
+
rubyforge_project:
|
150
|
+
rubygems_version: 1.8.23
|
151
|
+
signing_key:
|
152
|
+
specification_version: 3
|
153
|
+
summary: Synchronize a directory with a Mikrotik address list
|
154
|
+
test_files:
|
155
|
+
- test/test_directory.rb
|
156
|
+
- test/test_mikrotik.rb
|