1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
| | # -*- encoding: binary -*-
require "socket"
require "set"
require_relative 'local_file'
module Regurgitator::Local # :nodoc:
# This is used to register local storage endpoints so we can
# short-circuit and avoid making HTTP requests to devices
# Must be configured by the user
STORE_PATHS = Hash.new do |h,port|
h[port] = {} # key: directory root path, value: whatever...
end.compare_by_identity
@local_addrs = {}
@local_addrs_lock = Mutex.new
def self.include?(addr)
@local_addrs_lock.synchronize { @local_addrs.include?(addr) }
end
def self.addr
(@local_addrs_lock.synchronize { @local_addrs.first })[0]
end
def self.addrs
@local_addrs_lock.synchronize { @local_addrs.keys }
end
# Normally not needed unless you dynamically bring up/down network devices.
# It may be useful to call this periodically or to integrate with
# some network device state monitoring system.
def self.refresh_addrs!
@local_addrs_lock.synchronize do
tmp = {}
Socket.ip_address_list.keep_if do |ip|
ip.ipv4? && ! ip.ipv4_multicast?
end.each { |ip| tmp[ip.ip_address.freeze] = true }
@local_addrs = tmp
end
end
refresh_addrs!
# registers a local path for a given +tcp_port+ and +directory_root+
def self.register(tcp_port, directory_root)
directory_root = directory_root.gsub(%r{/+\z}, "")
dev_dirs = Dir.foreach(directory_root).grep(/\Adev\d+\z/)
raise ArgumentError, 'no /dev\d+/ directories found' if dev_dirs.empty?
STORE_PATHS[tcp_port][directory_root] = Set.new(dev_dirs)
end
# returns +nil+ if nothing was found
# returns a path and associated File::Stat to the local FS if a
# matching path is possible
def device_path_stat(uri)
Regurgitator::Local.include?(uri.host) or return
(roots = STORE_PATHS[uri.port]).empty? and return
uri.path =~ %r{\A/(dev\d+)/} or
raise "BUG: path needs to match '\\A/dev\d+/' (#{uri})"
devN = $1
rv = nil
roots.each do |root, dev_dirs|
dev_dirs.include?(devN) or next
begin
path = "#{root}#{uri.path}"
stat = File.stat(path)
# multiple "/devN" paths for the same ports would be ambiguous,
# so we cannot optimize away the HTTP request for those
return nil if rv
rv = [ path, stat ]
rescue => e
warn "E: #{e.message}, root=#{root} failed for #{uri}"
end
end
rv
end
def trylocal(env, uri_group)
STORE_PATHS.empty? and return
uri_group.flatten.each do |uri|
path_stat = device_path_stat(uri) or next
path, stat = path_stat
return Regurgitator::LocalFile.new(env, path, uri, stat)
end
nil
end
end
|