mtik_directory_2_address_list 1.0.2 → 1.0.3

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.
@@ -3,42 +3,59 @@ require 'optparse'
3
3
  require 'syslog'
4
4
  require 'mtik_directory_2_address_list'
5
5
 
6
- class Args < OptionParser
7
- attr_reader :prefix, :quiet
8
- attr_reader :dir, :mtik
6
+ class Args
7
+ Error = Class.new(StandardError)
8
+
9
+ attr_reader :verbose, :prefix, :retry_timeout
9
10
 
10
11
  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.empty?
16
- STDOUT << to_s << <<-EOS.gsub(/^ +/, '')
17
-
18
- Symbolic links in <dir>, named like an IP address, will be
19
- sent to the Mikrotik device at <host>. The address-list name
20
- will be formed by the PREFIX + whatever the link points to.
21
-
22
- For example, the following symbolic link:
23
- \t1.2.3.4 -> 15mbps
24
- With --prefix set to "down_speed_", will add the IP 1.2.3.4,
25
- to the address-list named down_speed_15mbps.
26
-
27
- EOS
28
- exit(2)
29
- elsif ARGV.size != 4
30
- puts to_s
31
- puts "Missing required argument"
32
- exit(2)
33
- else
34
- @dir = ARGV.shift
35
- @mtik = Hash[[:host, :user, :pass].zip(ARGV.shift(3))]
36
- end
37
- rescue OptionParser::ParseError => err
38
- puts to_s
39
- puts "Error: #{err}"
12
+ @verbose = STDOUT.tty?
13
+ @prefix = 'md2al_'
14
+ @retry_timeout = 15 # seconds
15
+ end
16
+
17
+ def self.parse
18
+ self.new.tap { |args| args.parse }
19
+ end
20
+
21
+ def parse
22
+ opts = OptionParser.new
23
+ opts.banner = 'Usage: <host> <username> <password> <dir> [options]'
24
+
25
+ opts.separator ''
26
+ opts.separator 'Options:'
27
+ opts.on('-p', '--prefix PREFIX', 'Mikrotik address-list prefix',
28
+ "(default: #{@prefix})") { |s| @prefix = s }
29
+ opts.on('-v', '--[no-]verbose', 'Print debug messages to stdout',
30
+ '(default: true if stdout is a TTY)') { |v| @verbose = v }
31
+ opts.on('--retry-timeout SECONDS', Integer, 'Wait before restarting after an error',
32
+ "(default: #{@retry_timeout})") { |i| @retry_timeout = i }
33
+
34
+ opts.separator ''
35
+ opts.separator 'Symbolic links in <dir>, named like an IP address, will be sent to the'
36
+ opts.separator 'Mikrotik device at <host>. The address-list name will be formed by the'
37
+ opts.separator 'PREFIX + whatever the link points to.'
38
+ opts.separator ''
39
+ opts.separator 'For example, the following symbolic link: 1.2.3.4 -> 15mbps'
40
+ opts.separator 'With --prefix set to "down_speed_", will add the IP 1.2.3.4,'
41
+ opts.separator 'to the address-list named down_speed_15mbps.'
42
+ opts.separator ''
43
+
44
+ opts.parse!
45
+ ARGV.size == 4 || raise(Error, "need 4 arguments, but #{ARGV.size} provided")
46
+
47
+ rescue Error, OptionParser::ParseError => err
48
+ STDERR << opts << 'Error: ' << err << "\n"
40
49
  exit(2)
41
50
  end
51
+
52
+ def directory
53
+ ARGV[3]
54
+ end
55
+
56
+ def mikrotik
57
+ Hash[[:host, :user, :pass].zip(ARGV[0, 3])]
58
+ end
42
59
  end
43
60
 
44
61
  class Main
@@ -48,17 +65,19 @@ class Main
48
65
  end
49
66
 
50
67
  def args
51
- @args ||= Args.new.tap do |args|
68
+ @args ||= Args.parse.tap do |args|
52
69
  Syslog.open($PROGRAM_NAME, Syslog::LOG_PID, Syslog::LOG_DAEMON)
53
- args.quiet || MtikDirectory2AddressList::Log.output(&method(:info))
70
+ args.verbose && MtikDirectory2AddressList::Log.output(&method(:info))
54
71
  end
55
72
  end
56
73
 
57
74
  def sync
58
- MtikDirectory2AddressList.sync(args.dir, args.mtik, args.prefix)
75
+ MtikDirectory2AddressList.sync(args.directory, args.mikrotik, args.prefix)
59
76
  rescue => err
60
- err("Error: #{err} [#{err.backtrace.first}]")
61
- sleep(30) ; retry
77
+ err("Error: #{err}")
78
+ err.backtrace.each { |line| err(" #{line}") }
79
+ sleep(args.retry_timeout)
80
+ retry
62
81
  end
63
82
 
64
83
  # Send +message+ to syslog and STDERR.
@@ -73,7 +92,7 @@ class Main
73
92
  # @param [String] message
74
93
  # @return [void]
75
94
  def info(message)
76
- STDOUT << message << "\n" if STDOUT.tty?
95
+ STDOUT << message << "\n"
77
96
  end
78
97
  end
79
98
 
@@ -12,7 +12,7 @@ module MtikDirectory2AddressList
12
12
  # @param [String] src The directory where to look for symbolic links
13
13
  # @param [Hash] dst The object passed to the +mtik+ gem (keys: +host+, +user+, +pass+)
14
14
  # @param [String] prefix The prefix an address list name must have for it to be used
15
- # @return [nil]
15
+ # @return [void]
16
16
  def sync(src, dst, prefix = "dir_")
17
17
  Log.info { "Synchronizing [#{src}] with router at [#{dst[:host]}]" }
18
18
  mtik = Mikrotik.new(dst.dup.merge(prefix:prefix))
@@ -2,9 +2,15 @@
2
2
 
3
3
  module MtikDirectory2AddressList
4
4
  class Directory
5
+ Error = Class.new(StandardError)
6
+
7
+ # The {#list} method will not enumerate symbolic links that do not match this regular expression.
8
+ # Other methods may raise Error.
9
+ IP_RE = %r{ \A \d{1,3} \. \d{1,3} \. \d{1,3} \. \d{1,3} \z }x
10
+
5
11
  # Manage a directory of symbolic links.
6
12
  #
7
- # The IP address is the name of a symlink link in +@path+.
13
+ # The IP address is the name of the symbolic link.
8
14
  # The address list name is what the symbolic link points to.
9
15
  #
10
16
  # @param [Hash] params
@@ -13,23 +19,21 @@ module MtikDirectory2AddressList
13
19
  @path = params[:path]
14
20
  end
15
21
 
16
- # Return the address list associated with the specified IP.
22
+ # Return the address list associated with +ip+.
17
23
  #
18
- # @param [String] ip The IP to search for
24
+ # @param [String] ip
19
25
  #
20
- # @return [String] The value associated with +ip+
21
- # @return [nil] When the +ip+ is not found
26
+ # @return [String] The address list associated with +ip+
27
+ # @return [nil] When the +ip+ is not found or is not a symbolic link
22
28
  def [](ip)
23
- File.readlink(File.join(@path, ip))
24
- rescue Errno::EINVAL
25
- nil # Not a symlink
29
+ (ip =~ IP_RE) || raise(Error, "Invalid IP [#{ip}]")
30
+ file = File.join(@path, ip)
31
+ File.readlink(file)
32
+ rescue Errno::ENOENT, Errno::EINVAL
33
+ nil # symlink deleted, not a symlink
26
34
  end
27
35
 
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+.
36
+ # Enumerate IPs associated with an address list.
33
37
  #
34
38
  # @return [Enumerator] Arrays with: IP, address list
35
39
  def list
@@ -40,11 +44,14 @@ module MtikDirectory2AddressList
40
44
  end
41
45
  end
42
46
 
43
- # Yield whenever +@path+ is modified.
47
+ # Yield whenever an association is modified.
44
48
  #
45
- # @yield Right after being called
46
- # @yield After every update to +@path+
49
+ # This is based on the mtime of the directory being watched.
50
+ # If the mtime is not updated when a symbolic link is changed,
51
+ # the directory must be touched manually.
47
52
  #
53
+ # @yield Right after being called
54
+ # @yield After every association update
48
55
  # @return [void]
49
56
  def watch
50
57
  before = nil
@@ -54,12 +61,11 @@ module MtikDirectory2AddressList
54
61
  end
55
62
  end
56
63
 
57
- # Watch +path+, and yield +list+ as a hash.
64
+ # Like the instance method {#watch}, but yield a Hash with the associations.
58
65
  #
59
66
  # @param [String] path The directory to watch for modifications
60
67
  #
61
68
  # @yieldparam Hash{String=>String} Map IP addresses to address list names
62
- #
63
69
  # @return [void]
64
70
  def self.watch(path)
65
71
  dir = self.new(path:path)
@@ -3,9 +3,9 @@ require 'mtik'
3
3
 
4
4
  module MtikDirectory2AddressList
5
5
  class Mikrotik
6
- class Error < StandardError ; end
7
- class RouterError < StandardError ; end
8
- class SyncError < StandardError ; end
6
+ Error = Class.new(StandardError)
7
+ RouterError = Class.new(Error)
8
+ SyncError = Class.new(Error)
9
9
 
10
10
  def initialize(params)
11
11
  @prefix = (params[:prefix] || 'md2al_')
@@ -1,3 +1,3 @@
1
1
  module MtikDirectory2AddressList
2
- VERSION = '1.0.2'
2
+ VERSION = '1.0.3'
3
3
  end
@@ -8,7 +8,7 @@ Gem::Specification.new do |spec|
8
8
  spec.version = MtikDirectory2AddressList::VERSION
9
9
  spec.authors = ['Andre Luiz dos Santos']
10
10
  spec.email = ['andre.netvision.com.br@gmail.com']
11
- spec.summary = %q{Synchronize a directory with a Mikrotik address list}
11
+ spec.summary = %q{Synchronize a directory with a Mikrotik address list.}
12
12
 
13
13
  spec.files = `git ls-files -z`.split("\x0")
14
14
  spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: mtik_directory_2_address_list
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.0.2
4
+ version: 1.0.3
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -9,7 +9,7 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2015-01-12 00:00:00.000000000 Z
12
+ date: 2015-01-16 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: bundler
@@ -151,7 +151,7 @@ rubyforge_project:
151
151
  rubygems_version: 1.8.23
152
152
  signing_key:
153
153
  specification_version: 3
154
- summary: Synchronize a directory with a Mikrotik address list
154
+ summary: Synchronize a directory with a Mikrotik address list.
155
155
  test_files:
156
156
  - test/test_directory.rb
157
157
  - test/test_mikrotik.rb