mtik 3.0.5 → 3.1.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/CHANGELOG.txt +14 -0
- data/README.txt +1 -1
- data/Rakefile +12 -1
- data/VERSION.txt +1 -1
- data/examples/tikfetch.rb +1 -1
- data/lib/mtik.rb +6 -4
- data/lib/mtik/connection.rb +66 -50
- data/lib/mtik/fatalerror.rb +1 -1
- data/lib/mtik/reply.rb +13 -7
- data/lib/mtik/request.rb +26 -25
- metadata +2 -2
data/CHANGELOG.txt
CHANGED
|
@@ -1,3 +1,17 @@
|
|
|
1
|
+
2010-04-24 (24 APR 2010) VERSION 3.1.0 Aaron D. Gifford (http://www.aarongifford.com)
|
|
2
|
+
* Added find_sentences() method to MTik::Reply -- just sugar to Array.select()
|
|
3
|
+
* Changed MTik::Connection.fetch() method to add an optional timeout parameter
|
|
4
|
+
which should not affect the API and should be backward compatible. By default,
|
|
5
|
+
there is no inactivity timeout for downloads. But if you set the timeout parameter
|
|
6
|
+
to a positive number, when a reply arrives and no progress/activity has been
|
|
7
|
+
made for timeout seconds, the command will be cancelled. This should help with
|
|
8
|
+
stalled downloads (i.e. the remote side has stopped sending but the TCP connection
|
|
9
|
+
remains open/active).
|
|
10
|
+
* Also add the MTik::Request object as a parameter to the MTik::Connection.fetch()
|
|
11
|
+
method's callback so that a script could use the request object to cancel the
|
|
12
|
+
command if needed. Due to this change, I decided to bump the version to 3.1.0.
|
|
13
|
+
* Fixed RDoc formatting in several files, and added an RDocTask to the Rakefile
|
|
14
|
+
|
|
1
15
|
2010-04-23 (23 APR 2010) VERSION 3.0.5 Aaron D. Gifford (http://www.aarongifford.com)
|
|
2
16
|
* Double bug-fix (typo fix and logic fix) to request.rb thanks to Allan Eising and
|
|
3
17
|
Søren Daugaard. Thank you both for the patch!
|
data/README.txt
CHANGED
data/Rakefile
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
require 'rubygems'
|
|
2
2
|
require 'rake/gempackagetask'
|
|
3
|
+
require 'rake/rdoctask'
|
|
3
4
|
|
|
4
5
|
gemspec = Gem::Specification.new do |spec|
|
|
5
6
|
spec.name = 'mtik'
|
|
@@ -39,5 +40,15 @@ Rake::GemPackageTask.new(gemspec) do |pkg|
|
|
|
39
40
|
pkg.need_tar = true
|
|
40
41
|
end
|
|
41
42
|
|
|
42
|
-
|
|
43
|
+
Rake::RDocTask.new do |rdoc|
|
|
44
|
+
rdoc.name = 'rdoc'
|
|
45
|
+
rdoc.main = 'README.txt'
|
|
46
|
+
rdoc.rdoc_dir = 'doc'
|
|
47
|
+
rdoc.rdoc_files.include('README.txt', 'LICENSE.txt', 'CHANGELOG.txt', 'lib/**/*.rb')
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
task :default => [
|
|
51
|
+
'pkg/mtik-' + File.open('VERSION.txt','r').to_a.join.strip + '.gem',
|
|
52
|
+
:rdoc
|
|
53
|
+
]
|
|
43
54
|
|
data/VERSION.txt
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
3.0
|
|
1
|
+
3.1.0
|
data/examples/tikfetch.rb
CHANGED
|
@@ -73,7 +73,7 @@ while ARGV.length > 0
|
|
|
73
73
|
print ">>> ERROR: There is a file named '#{filename}' already on the device.\n"
|
|
74
74
|
else
|
|
75
75
|
print ">>> OK: Fetching file '#{filename}' from URL '#{url}'...\n"
|
|
76
|
-
mt.fetch(url, filename) do |status, total, bytes|
|
|
76
|
+
mt.fetch(url, filename, 120) do |status, total, bytes, req|
|
|
77
77
|
case status
|
|
78
78
|
when 'failed'
|
|
79
79
|
print ">>> ERROR: File '#{filename}' download failed!\n"
|
data/lib/mtik.rb
CHANGED
|
@@ -188,10 +188,12 @@ module MTik
|
|
|
188
188
|
## more commands, retrieve the response(s), close the connection,
|
|
189
189
|
## and return the response(s).
|
|
190
190
|
##
|
|
191
|
-
## *WARNING
|
|
192
|
-
##
|
|
193
|
-
##
|
|
194
|
-
##
|
|
191
|
+
## *WARNING*:
|
|
192
|
+
## If you use this call with an API command like <i>"/tool/fetch"</i>
|
|
193
|
+
## or a similar command that generates replies until canceled,
|
|
194
|
+
## it will forever keep reading replies, never returning. So do
|
|
195
|
+
## _NOT_ use this with any API command that does not complete with
|
|
196
|
+
## a <i>"!done"</i> response without any additional interaction.
|
|
195
197
|
def self.command(args)
|
|
196
198
|
tk = MTik::Connection.new(
|
|
197
199
|
:host => args[:host],
|
data/lib/mtik/connection.rb
CHANGED
|
@@ -43,16 +43,16 @@ class MTik::Connection
|
|
|
43
43
|
## key/value pair style arguments must be specified. The one
|
|
44
44
|
## required argument is the host or IP of the device to connect
|
|
45
45
|
## to.
|
|
46
|
-
##
|
|
47
|
-
##
|
|
48
|
-
##
|
|
49
|
-
##
|
|
50
|
-
##
|
|
51
|
-
##
|
|
52
|
-
##
|
|
53
|
-
##
|
|
54
|
-
##
|
|
55
|
-
##
|
|
46
|
+
## +host+:: This is the only _required_ argument. Example:
|
|
47
|
+
## <i> :host => "rb411.example.org" </i>
|
|
48
|
+
## +port+:: Override the default API port (8728)
|
|
49
|
+
## +user+:: Override the default API username ('admin')
|
|
50
|
+
## +pass+:: Override the default API password (blank)
|
|
51
|
+
## +conn_timeout+:: Override the default connection
|
|
52
|
+
## timeout (60 seconds) -- *NOT USED*
|
|
53
|
+
## +cmd_timeout+:: Override the default command timeout
|
|
54
|
+
## (60 seconds) -- the number of seconds
|
|
55
|
+
## to wait for additional API input.
|
|
56
56
|
def initialize(args)
|
|
57
57
|
@sock = nil
|
|
58
58
|
@requests = Hash.new
|
|
@@ -321,18 +321,18 @@ class MTik::Connection
|
|
|
321
321
|
end
|
|
322
322
|
|
|
323
323
|
## Send a request to the device.
|
|
324
|
-
##
|
|
325
|
-
##
|
|
326
|
-
##
|
|
327
|
-
##
|
|
328
|
-
##
|
|
329
|
-
##
|
|
330
|
-
##
|
|
331
|
-
##
|
|
332
|
-
##
|
|
333
|
-
##
|
|
334
|
-
##
|
|
335
|
-
##
|
|
324
|
+
## +await_completion+:: Boolean indicating whether to execute callbacks
|
|
325
|
+
## only once upon request completion (if set to _true_)
|
|
326
|
+
## or to execute for every received complete sentence
|
|
327
|
+
## (if set to _false_). ALTERNATIVELY, this parameter
|
|
328
|
+
## may be an object (MTik::Request) to be sent, in which
|
|
329
|
+
## case any command and/or arguments will be treated as
|
|
330
|
+
## additional arguments to the request contained in the
|
|
331
|
+
## object.
|
|
332
|
+
## +command+:: The command to be executed.
|
|
333
|
+
## +args+:: Zero or more arguments to the command
|
|
334
|
+
## +callback+:: Proc/lambda code (or code block if not provided as
|
|
335
|
+
## an argument) to be called. (See the +await_completion+
|
|
336
336
|
##
|
|
337
337
|
def send_request(await_completion, command, *args, &callback)
|
|
338
338
|
if await_completion.is_a?(MTik::Request)
|
|
@@ -367,18 +367,19 @@ class MTik::Connection
|
|
|
367
367
|
|
|
368
368
|
## Send a command, then wait for the command to complete, then return
|
|
369
369
|
## the completed reply.
|
|
370
|
-
##
|
|
371
|
-
##
|
|
372
|
-
##
|
|
373
|
-
##
|
|
374
|
-
##
|
|
375
|
-
##
|
|
376
|
-
##
|
|
377
|
-
##
|
|
378
|
-
##
|
|
379
|
-
##
|
|
380
|
-
##
|
|
381
|
-
##
|
|
370
|
+
##
|
|
371
|
+
## +command+:: The command to execute
|
|
372
|
+
## +args+:: Arguments (if any)
|
|
373
|
+
## +callback+:: Proc/lambda or code block to act as callback
|
|
374
|
+
##
|
|
375
|
+
## *NOTE*: This call has its own event loop that will cycle until
|
|
376
|
+
## the command in question completes. You should:
|
|
377
|
+
## * NOT call get_reply with a command that may not
|
|
378
|
+
## complete with a "!done" response on its own
|
|
379
|
+
## (with no additional intervention); and
|
|
380
|
+
## * BE CAREFUL to understand how things interact if
|
|
381
|
+
## you mix this call with requests that generate
|
|
382
|
+
## continuous output.
|
|
382
383
|
def get_reply(command, *args, &callback)
|
|
383
384
|
req = send_request(true, command, *args, &callback)
|
|
384
385
|
wait_for_request(req)
|
|
@@ -483,20 +484,29 @@ class MTik::Connection
|
|
|
483
484
|
## Utility to execute the "/tool/fetch" command, instructing
|
|
484
485
|
## the device to download a file from the specified URL.
|
|
485
486
|
## Status updates are provided via the provided callback.
|
|
486
|
-
##
|
|
487
|
-
##
|
|
488
|
-
##
|
|
489
|
-
##
|
|
490
|
-
##
|
|
491
|
-
##
|
|
492
|
-
##
|
|
493
|
-
##
|
|
494
|
-
##
|
|
495
|
-
##
|
|
496
|
-
|
|
497
|
-
|
|
487
|
+
## +url+:: The URL to fetch the file from
|
|
488
|
+
## +filename+:: The filename to use on the device
|
|
489
|
+
## +timeout+:: Cancel command if a reply indicates the
|
|
490
|
+
## download has stalled for +timeout+ seconds.
|
|
491
|
+
## This is disabled by default. Disable by
|
|
492
|
+
## setting +timeout+ to nil or zero, enable by
|
|
493
|
+
## supplying a positive number of seconds.
|
|
494
|
+
## (OPTIONAL argument)
|
|
495
|
+
## +callback+:: Callback called for status updates.
|
|
496
|
+
##
|
|
497
|
+
## The arguments passed to the callback are:
|
|
498
|
+
## +status+:: Either 'downloading', 'connecting',
|
|
499
|
+
## 'failed', 'requesting', or 'finished',
|
|
500
|
+
## otherwise a '!trap' error occured,
|
|
501
|
+
## and the value is the trap message.
|
|
502
|
+
## +total+:: Final expected file size in bytes
|
|
503
|
+
## +bytes+:: Number of bytes transferred so far
|
|
504
|
+
## +request+:: The MTik::Request object
|
|
505
|
+
def fetch(url, filename, timeout=nil, &callback)
|
|
506
|
+
total = bytes = oldbytes = 0
|
|
498
507
|
status = ''
|
|
499
508
|
done = false
|
|
509
|
+
lastactivity = Time.now
|
|
500
510
|
req = get_reply_each(
|
|
501
511
|
'/tool/fetch',
|
|
502
512
|
'=url=' + url,
|
|
@@ -511,12 +521,18 @@ class MTik::Connection
|
|
|
511
521
|
when 'downloading'
|
|
512
522
|
total = s['total'].to_i
|
|
513
523
|
bytes = s['downloaded'].to_i
|
|
514
|
-
|
|
524
|
+
if bytes != oldbytes
|
|
525
|
+
lastactivity = Time.now
|
|
526
|
+
elsif timeout != 0 && !timeout.nil? && Time.now - lastactivity > timeout
|
|
527
|
+
## Cancel the request (idle too long):
|
|
528
|
+
get_reply('/cancel', '=tag=' + req.tag) {}
|
|
529
|
+
end
|
|
530
|
+
callback.call(status, total, bytes, req)
|
|
515
531
|
when 'connecting', 'requesting'
|
|
516
|
-
callback.call(status, 0, 0)
|
|
532
|
+
callback.call(status, 0, 0, req)
|
|
517
533
|
when 'failed', 'finished'
|
|
518
534
|
bytes = total if status == 'finished'
|
|
519
|
-
callback.call(status, total, bytes)
|
|
535
|
+
callback.call(status, total, bytes, req)
|
|
520
536
|
done = true
|
|
521
537
|
## Now terminate the download request (since it's done):
|
|
522
538
|
get_reply('/cancel', '=tag=' + req.tag) {}
|
|
@@ -526,7 +542,7 @@ class MTik::Connection
|
|
|
526
542
|
elsif s.key?('!trap')
|
|
527
543
|
## Pass trap message back (unless finished--in which case we
|
|
528
544
|
## ignore the 'interrrupted' trap message):
|
|
529
|
-
callback.call(s['message'], total, bytes) if !done
|
|
545
|
+
callback.call(s['message'], total, bytes, req) if !done
|
|
530
546
|
end
|
|
531
547
|
end
|
|
532
548
|
end
|
data/lib/mtik/fatalerror.rb
CHANGED
|
@@ -32,6 +32,6 @@
|
|
|
32
32
|
## THE POSSIBILITY OF SUCH DAMAGE.
|
|
33
33
|
############################################################################
|
|
34
34
|
|
|
35
|
-
## Execption raised when a
|
|
35
|
+
## Execption raised when a <i>"!fatal"</i> response is received from a device
|
|
36
36
|
class MTik::FatalError < MTik::Error ; end
|
|
37
37
|
|
data/lib/mtik/reply.rb
CHANGED
|
@@ -34,17 +34,23 @@
|
|
|
34
34
|
|
|
35
35
|
## A MikroTik API reply is stored as an array of response sentences. Each
|
|
36
36
|
## sentence is a key/value Hash object. The MTik::Reply class is simply
|
|
37
|
-
## a basic Ruby Array with
|
|
37
|
+
## a basic Ruby Array with find_sentence and find_sentences methods added.
|
|
38
38
|
class MTik::Reply < Array
|
|
39
|
+
## This method is nearly identical to Array.select{|i| i.key?(key)}[0]
|
|
40
|
+
## except that this method short-circuits and returns when the first
|
|
41
|
+
## match is found.
|
|
39
42
|
def find_sentence(key)
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
if self[i].key?(key)
|
|
43
|
-
return self[i]
|
|
44
|
-
end
|
|
45
|
-
i += 1
|
|
43
|
+
self.each do |sentence|
|
|
44
|
+
return sentence if sentence.key?(key)
|
|
46
45
|
end
|
|
47
46
|
return nil
|
|
48
47
|
end
|
|
48
|
+
|
|
49
|
+
## This method is simply an alias for Array.select{|i| i.key?(key)}
|
|
50
|
+
def find_sentences(key)
|
|
51
|
+
return self.select do |sentence|
|
|
52
|
+
sentence.key?(key)
|
|
53
|
+
end
|
|
54
|
+
end
|
|
49
55
|
end
|
|
50
56
|
|
data/lib/mtik/request.rb
CHANGED
|
@@ -35,33 +35,34 @@
|
|
|
35
35
|
## A MikroTik API request object is stored as an array of MikroTik
|
|
36
36
|
## API-style words, the first word being the command, subsequent words
|
|
37
37
|
## (if any) are command arguments. Each request will automatically
|
|
38
|
-
## have a unique tag generated (so any
|
|
39
|
-
## ignored). A request is incomplete until the final
|
|
40
|
-
## sentence has been received.
|
|
38
|
+
## have a unique tag generated (so any <i>".tag=value"</i> arguments
|
|
39
|
+
## will be ignored). A request is incomplete until the final
|
|
40
|
+
## <i>"!done"</i> response sentence has been received.
|
|
41
41
|
class MTik::Request < Array
|
|
42
42
|
@@tagspace = 0 ## Used to keep all tags unique.
|
|
43
43
|
|
|
44
44
|
## Create a new MTik::Request.
|
|
45
|
-
## +await_completion
|
|
46
|
-
##
|
|
47
|
-
##
|
|
48
|
-
##
|
|
49
|
-
##
|
|
50
|
-
##
|
|
51
|
-
## +command
|
|
52
|
-
##
|
|
53
|
-
##
|
|
54
|
-
##
|
|
55
|
-
## +args
|
|
56
|
-
##
|
|
57
|
-
##
|
|
58
|
-
##
|
|
59
|
-
##
|
|
60
|
-
##
|
|
61
|
-
##
|
|
62
|
-
##
|
|
63
|
-
##
|
|
64
|
-
##
|
|
45
|
+
## +await_completion+:: A boolean parameter indicating when callback(s)
|
|
46
|
+
## should be called. A value of _true_ will result
|
|
47
|
+
## in callback(s) only being called once, when the
|
|
48
|
+
## final +!done+ response is received. A value of
|
|
49
|
+
## _false_ means callback(s) will be called _each_
|
|
50
|
+
## time a response sentence is received.
|
|
51
|
+
## +command+:: The MikroTik API command to execute (a String).
|
|
52
|
+
## Examples:
|
|
53
|
+
## "/interface/getall"
|
|
54
|
+
## "/ip/route/add"
|
|
55
|
+
## +args+:: Zero or more String arguments for the command,
|
|
56
|
+
## already encoded in <i>"=key=value"</i>,
|
|
57
|
+
## <i>".id=value"</i>, or <i>"?query"</i> API
|
|
58
|
+
## format.
|
|
59
|
+
## +callback+:: A Proc or code block may be passed which will
|
|
60
|
+
## be called with two arguments:
|
|
61
|
+
## 1. this MTik::Request object; and
|
|
62
|
+
## 2. the most recently received response sentence.
|
|
63
|
+
## When or how often callbacks are called depends on
|
|
64
|
+
## whether the +await_completion+ parameter is _true_
|
|
65
|
+
## or _false_ (see above).
|
|
65
66
|
def initialize(await_completion, command, *args, &callback)
|
|
66
67
|
@reply = MTik::Reply.new
|
|
67
68
|
@command = command
|
|
@@ -175,7 +176,7 @@ class MTik::Request < Array
|
|
|
175
176
|
end
|
|
176
177
|
|
|
177
178
|
## Another utility method to encode a byte string as a
|
|
178
|
-
## valid API
|
|
179
|
+
## valid API <i>"word"</i>
|
|
179
180
|
def self.to_tikword(str)
|
|
180
181
|
str = str.dup
|
|
181
182
|
if RUBY_VERSION >= '1.9.0'
|
|
@@ -260,7 +261,7 @@ class MTik::Request < Array
|
|
|
260
261
|
end
|
|
261
262
|
|
|
262
263
|
## Method the internal parser calls to flag this reply as completed
|
|
263
|
-
## upon receipt of a
|
|
264
|
+
## upon receipt of a <i>"!done"</i> reply sentence.
|
|
264
265
|
def done!
|
|
265
266
|
@state = 'complete'
|
|
266
267
|
return true
|