mtik 3.0.5 → 3.1.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|