omf_rc 6.0.0.pre.2
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/.gitignore +4 -0
- data/Gemfile +4 -0
- data/Rakefile +16 -0
- data/bin/omf_rc +64 -0
- data/config/omf_rc.yml +70 -0
- data/integration_test/omf_rc/resource_proxy/app_spec.rb +25 -0
- data/integration_test/omf_rc/resource_proxy/interface_spec.rb +26 -0
- data/integration_test/omf_rc/resource_proxy/mock_spec.rb +58 -0
- data/integration_test/omf_rc/resource_proxy/node_spec.rb +26 -0
- data/integration_test/omf_rc/resource_proxy/wifi_spec.rb +30 -0
- data/integration_test/test_helper.rb +5 -0
- data/lib/omf_rc/deferred_process.rb +20 -0
- data/lib/omf_rc/message_process_error.rb +11 -0
- data/lib/omf_rc/resource_factory.rb +74 -0
- data/lib/omf_rc/resource_proxy/abstract_resource.rb +257 -0
- data/lib/omf_rc/resource_proxy/interface.rb +48 -0
- data/lib/omf_rc/resource_proxy/mock.rb +16 -0
- data/lib/omf_rc/resource_proxy/node.rb +17 -0
- data/lib/omf_rc/resource_proxy/wifi.rb +8 -0
- data/lib/omf_rc/resource_proxy_dsl.rb +170 -0
- data/lib/omf_rc/util/iw.rb +22 -0
- data/lib/omf_rc/util/mock.rb +27 -0
- data/lib/omf_rc/util/mod.rb +23 -0
- data/lib/omf_rc/util/package.rb +17 -0
- data/lib/omf_rc/version.rb +3 -0
- data/lib/omf_rc.rb +6 -0
- data/omf_rc.gemspec +25 -0
- data/test/omf_rc/deferred_process_spec.rb +39 -0
- data/test/omf_rc/resource_factory_spec.rb +18 -0
- data/test/omf_rc/resource_proxy/abstract_resource_spec.rb +82 -0
- data/test/omf_rc/resource_proxy_dsl_spec.rb +43 -0
- data/test/test_helper.rb +11 -0
- metadata +128 -0
@@ -0,0 +1,257 @@
|
|
1
|
+
require 'omf_common'
|
2
|
+
require 'omf_rc/deferred_process'
|
3
|
+
require 'omf_rc/message_process_error'
|
4
|
+
require 'securerandom'
|
5
|
+
require 'hashie'
|
6
|
+
|
7
|
+
class OmfRc::ResourceProxy::AbstractResource
|
8
|
+
# Time to wait before shutting down event loop, wait for deleting pubsub nodes
|
9
|
+
DISCONNECT_WAIT = 5
|
10
|
+
# Time to wait before releasing resource, wait for deleting pubsub nodes
|
11
|
+
RELEASE_WAIT = 5
|
12
|
+
attr_accessor :uid, :hrn, :type, :comm
|
13
|
+
attr_reader :opts, :children, :host
|
14
|
+
|
15
|
+
# Initialisation
|
16
|
+
#
|
17
|
+
# @param [Symbol] type resource proxy type
|
18
|
+
# @param [Hash] opts options to be initialised
|
19
|
+
# @option opts [String] :uid Unique identifier
|
20
|
+
# @option opts [String] :hrn Human readable name
|
21
|
+
# @option opts [String] :pubsub_host pubsub server subdomain, default to 'pubsub'
|
22
|
+
# @option opts [String] :dsl Which pubsub DSL to be used for pubsub communication
|
23
|
+
# @option opts [String] :user pubsub user id
|
24
|
+
# @option opts [String] :password pubsub user password
|
25
|
+
# @option opts [String] :server pubsub server domain
|
26
|
+
# @param [Comm] comm communicator instance, pass this to new resource proxy instance if want to use a common communicator instance.
|
27
|
+
def initialize(type, opts = nil, comm = nil)
|
28
|
+
@opts = Hashie::Mash.new(opts)
|
29
|
+
@type = type
|
30
|
+
@uid = @opts.uid || SecureRandom.uuid
|
31
|
+
@hrn = @opts.hrn
|
32
|
+
@children ||= []
|
33
|
+
@host = nil
|
34
|
+
|
35
|
+
@comm = comm || OmfCommon::Comm.new(@opts.dsl)
|
36
|
+
# Fire when connection to pubsub server established
|
37
|
+
@comm.when_ready do
|
38
|
+
logger.info "CONNECTED: #{@comm.jid.inspect}"
|
39
|
+
@host = "#{@opts.pubsub_host}.#{@comm.jid.domain}"
|
40
|
+
|
41
|
+
# Once connection established, create a pubsub node, then subscribe to it
|
42
|
+
@comm.create_node(uid, host) do |s|
|
43
|
+
# Creating node failed, no point to continue; clean up and disconnect
|
44
|
+
# Otherwise go subscribe to this pubsub node
|
45
|
+
s.error? ? disconnect : @comm.subscribe(uid, host)
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
# Fire when message published
|
50
|
+
@comm.node_event do |e|
|
51
|
+
e.items.each do |item|
|
52
|
+
process_omf_message(item.payload, e.node)
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
# Generic pubsub event
|
57
|
+
@comm.pubsub_event do |e|
|
58
|
+
logger.debug "PUBSUB GENERIC EVENT: #{e}"
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
# Connect to pubsub server
|
63
|
+
def connect
|
64
|
+
@comm.connect(opts.user, opts.password, opts.server)
|
65
|
+
end
|
66
|
+
|
67
|
+
# Try to clean up pubsub nodes, and wait for DISCONNECT_WAIT seconds, then shutdown event machine loop
|
68
|
+
def disconnect
|
69
|
+
@comm.pubsub.affiliations(host) do |a|
|
70
|
+
my_pubsub_nodes = a[:owner] ? a[:owner].size : 0
|
71
|
+
if my_pubsub_nodes > 0
|
72
|
+
logger.info "Cleaning #{my_pubsub_nodes} pubsub node(s)"
|
73
|
+
a[:owner].each { |node| @comm.delete_node(node, host) }
|
74
|
+
else
|
75
|
+
logger.info "Disconnecting now"
|
76
|
+
@comm.disconnect(host)
|
77
|
+
end
|
78
|
+
end
|
79
|
+
logger.info "Disconnecting in #{DISCONNECT_WAIT} seconds"
|
80
|
+
EM.add_timer(DISCONNECT_WAIT) do
|
81
|
+
@comm.disconnect(host)
|
82
|
+
end
|
83
|
+
end
|
84
|
+
|
85
|
+
# Create a new resource in the context of this resource. This resource becomes parent, and newly created resource becomes child
|
86
|
+
#
|
87
|
+
# @param (see #initialize)
|
88
|
+
def create(type, opts = nil)
|
89
|
+
new_resource = OmfRc::ResourceFactory.new(type.to_sym, opts, @comm)
|
90
|
+
children << new_resource
|
91
|
+
new_resource
|
92
|
+
end
|
93
|
+
|
94
|
+
# Release a resource
|
95
|
+
#
|
96
|
+
def release
|
97
|
+
pubsub_nodes_left = []
|
98
|
+
children.each do |c|
|
99
|
+
c.before_release if c.respond_to? :before_release
|
100
|
+
pubsub_nodes_left << c.uid
|
101
|
+
c.freeze
|
102
|
+
end.clear
|
103
|
+
before_release if respond_to? :before_release
|
104
|
+
freeze
|
105
|
+
pubsub_nodes_left
|
106
|
+
end
|
107
|
+
|
108
|
+
# Return a list of all properties can be requested and configured
|
109
|
+
#
|
110
|
+
def request_available_properties
|
111
|
+
Hashie::Mash.new(request: [], configure: []).tap do |mash|
|
112
|
+
methods.each do |m|
|
113
|
+
mash[$1] << $2.to_sym if m =~ /(request|configure)_(.+)/ && $2 != "available_properties"
|
114
|
+
end
|
115
|
+
end
|
116
|
+
end
|
117
|
+
|
118
|
+
# Make uid accessible through pubsub interface
|
119
|
+
def request_uid
|
120
|
+
uid
|
121
|
+
end
|
122
|
+
|
123
|
+
# Make hrn accessible through pubsub interface
|
124
|
+
def request_hrn
|
125
|
+
hrn
|
126
|
+
end
|
127
|
+
|
128
|
+
# Make hrn configurable through pubsub interface
|
129
|
+
def configure_hrn(hrn)
|
130
|
+
@hrn = hrn
|
131
|
+
end
|
132
|
+
|
133
|
+
# Request child resources
|
134
|
+
# @return [Mash] child resource mash with uid and hrn
|
135
|
+
def request_child_resources
|
136
|
+
Hashie::Mash.new.tap do |mash|
|
137
|
+
children.each do |c|
|
138
|
+
mash[c.uid] ||= c.hrn
|
139
|
+
end
|
140
|
+
end
|
141
|
+
end
|
142
|
+
|
143
|
+
# Parse omf message and execute as instructed by the message
|
144
|
+
#
|
145
|
+
def process_omf_message(pubsub_item_payload, node)
|
146
|
+
dp = OmfRc::DeferredProcess.new
|
147
|
+
|
148
|
+
dp.callback do |end_result|
|
149
|
+
if end_result
|
150
|
+
case end_result[:operation]
|
151
|
+
when :create
|
152
|
+
new_uid = end_result[:result]
|
153
|
+
@comm.create_node(new_uid, host) do
|
154
|
+
@comm.subscribe(new_uid, host) do
|
155
|
+
inform_msg = OmfCommon::Message.inform(end_result[:context_id], 'CREATED') do |i|
|
156
|
+
i.element('resource_id', new_uid)
|
157
|
+
i.element('resource_address', new_uid)
|
158
|
+
end.sign
|
159
|
+
@comm.publish(end_result[:inform_to], inform_msg, host)
|
160
|
+
end
|
161
|
+
end
|
162
|
+
when :request
|
163
|
+
inform_msg = OmfCommon::Message.inform(end_result[:context_id], 'STATUS') do |i|
|
164
|
+
end_result[:result].each_pair do |k, v|
|
165
|
+
i.property(k) { |p| p.element('current', v) }
|
166
|
+
end
|
167
|
+
end.sign
|
168
|
+
@comm.publish(end_result[:inform_to], inform_msg, host)
|
169
|
+
|
170
|
+
when :configure
|
171
|
+
inform_msg = OmfCommon::Message.inform(end_result[:context_id], 'STATUS') do |i|
|
172
|
+
end_result[:result].each_pair do |k, v|
|
173
|
+
i.property(k) { |p| p.element('current', v) }
|
174
|
+
end
|
175
|
+
end.sign
|
176
|
+
@comm.publish(end_result[:inform_to], inform_msg, host)
|
177
|
+
when :release
|
178
|
+
inform_msg = OmfCommon::Message.inform(end_result[:context_id], 'RELEASED') do |i|
|
179
|
+
i.element('resource_id', end_result[:inform_to])
|
180
|
+
end
|
181
|
+
|
182
|
+
end_result[:result].each do |n|
|
183
|
+
@comm.delete_node(n, host)
|
184
|
+
end
|
185
|
+
|
186
|
+
EM.add_timer(RELEASE_WAIT) do
|
187
|
+
@comm.publish(end_result[:inform_to], inform_msg, host)
|
188
|
+
end
|
189
|
+
end
|
190
|
+
end
|
191
|
+
end
|
192
|
+
|
193
|
+
dp.errback do |e|
|
194
|
+
inform_msg = OmfCommon::Message.inform(e.context_id, 'FAILED') do |i|
|
195
|
+
i.element("error_message", e.message)
|
196
|
+
end.sign
|
197
|
+
@comm.publish(e.inform_to, inform_msg, host)
|
198
|
+
end
|
199
|
+
|
200
|
+
dp.fire do
|
201
|
+
message = OmfCommon::Message.parse(pubsub_item_payload)
|
202
|
+
# Get the context id, which will be included when informing
|
203
|
+
context_id = message.read_content("context_id")
|
204
|
+
|
205
|
+
obj = node == uid ? self : children.find { |v| v.uid == node }
|
206
|
+
|
207
|
+
begin
|
208
|
+
raise "Resource disappeard #{node}" if obj.nil?
|
209
|
+
|
210
|
+
case message.operation
|
211
|
+
when :create
|
212
|
+
create_opts = opts.dup
|
213
|
+
create_opts.uid = nil
|
214
|
+
result = obj.create(message.read_property(:type), create_opts)
|
215
|
+
message.read_element("//property").each do |p|
|
216
|
+
unless p.attr('key') == 'type'
|
217
|
+
method_name = "configure_#{p.attr('key')}"
|
218
|
+
result.send(method_name, p.content) if result.respond_to? method_name
|
219
|
+
end
|
220
|
+
end
|
221
|
+
{ operation: :create, result: result.uid, context_id: context_id, inform_to: uid }
|
222
|
+
when :request
|
223
|
+
result = Hashie::Mash.new.tap do |mash|
|
224
|
+
message.read_element("//property").each do |p|
|
225
|
+
method_name = "request_#{p.attr('key')}"
|
226
|
+
if obj.respond_to? method_name
|
227
|
+
mash[p.attr('key')] ||= obj.send(method_name)
|
228
|
+
end
|
229
|
+
end
|
230
|
+
end
|
231
|
+
{ operation: :request, result: result, context_id: context_id, inform_to: obj.uid }
|
232
|
+
when :configure
|
233
|
+
result = Hashie::Mash.new.tap do |mash|
|
234
|
+
message.read_element("//property").each do |p|
|
235
|
+
method_name = "configure_#{p.attr('key')}"
|
236
|
+
if obj.respond_to? method_name
|
237
|
+
mash[p.attr('key')] ||= obj.send(method_name, p.content)
|
238
|
+
end
|
239
|
+
end
|
240
|
+
end
|
241
|
+
{ operation: :configure, result: result, context_id: context_id, inform_to: obj.uid }
|
242
|
+
when :release
|
243
|
+
{ operation: :release, result: obj.release, context_id: context_id, inform_to: obj.uid }
|
244
|
+
when :inform
|
245
|
+
# We really don't care about inform messages which created from here
|
246
|
+
nil
|
247
|
+
else
|
248
|
+
raise "Unknown OMF operation #{message.operation}"
|
249
|
+
end
|
250
|
+
rescue => e
|
251
|
+
logger.error e.message
|
252
|
+
logger.error e.backtrace.join("\n")
|
253
|
+
raise OmfRc::MessageProcessError.new(context_id, obj.uid, e.message)
|
254
|
+
end
|
255
|
+
end
|
256
|
+
end
|
257
|
+
end
|
@@ -0,0 +1,48 @@
|
|
1
|
+
require 'hashie'
|
2
|
+
|
3
|
+
module OmfRc::ResourceProxy::Interface
|
4
|
+
include OmfRc::ResourceProxyDSL
|
5
|
+
|
6
|
+
register_proxy :interface
|
7
|
+
|
8
|
+
IPTABLES = 'iptables'
|
9
|
+
ROUTE = 'route'
|
10
|
+
|
11
|
+
def configure_property(property, value)
|
12
|
+
case property
|
13
|
+
when /^forwarding$/
|
14
|
+
OmfRc::Cmd.exec("echo #{value ? '1' : '0'} > /proc/sys/net/ipv4/conf/#{uid}/forwarding")
|
15
|
+
when /^gateway$/
|
16
|
+
# FIXME Not sure about this one, hard coded everything?
|
17
|
+
OmfRc::Cmd.exec("route del default dev eth1; route add default gw #{value}; route add 224.10.10.6 dev eth1")
|
18
|
+
when /^route$/
|
19
|
+
value = Hashie::Mash.new(value)
|
20
|
+
arguments = %w(net gw mask).map {|v| "-#{v} #{value.send(v)}" if value.send(v)}.join(' ')
|
21
|
+
OmfRc::Cmd.exec("#{ROUTE} #{value.op} #{arguments} dev #{uid}")
|
22
|
+
when /^filter$/
|
23
|
+
operation = case value.op
|
24
|
+
when /^add$/
|
25
|
+
'-A'
|
26
|
+
when /^del$/
|
27
|
+
'-D'
|
28
|
+
when /^clear$/
|
29
|
+
'-F'
|
30
|
+
end
|
31
|
+
chain = "#{value.chain.upcase} -i #{uid}" if value.chain
|
32
|
+
protocol = case value.proto
|
33
|
+
when /^(tcp|udp)$/
|
34
|
+
[ ("-p #{value.proto}"),
|
35
|
+
("-s #{value.src}" if value.src),
|
36
|
+
("-d #{value.dst}" if value.dst),
|
37
|
+
("--sport #{value.sport}" if value.sport),
|
38
|
+
("--dport #{value.dport}" if value.dport) ].join(' ')
|
39
|
+
when /^mac$/
|
40
|
+
"-m mac --mac-source #{value.src}"
|
41
|
+
end
|
42
|
+
target = "#{value.target.upcase}" if value.target
|
43
|
+
OmfRc::Cmd.exec("#{IPTABLES} #{operation} #{chain} #{protocol} #{chain}")
|
44
|
+
else
|
45
|
+
super
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
@@ -0,0 +1,16 @@
|
|
1
|
+
module OmfRc::ResourceProxy::Mock
|
2
|
+
include OmfRc::ResourceProxyDSL
|
3
|
+
|
4
|
+
register_proxy :mock
|
5
|
+
|
6
|
+
utility :mock
|
7
|
+
|
8
|
+
register_hook :before_ready do |resource|
|
9
|
+
logger.info "#{resource.uid} is now ready"
|
10
|
+
end
|
11
|
+
|
12
|
+
register_hook :before_release do |resource|
|
13
|
+
logger.info "#{resource.uid} is to be released"
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
@@ -0,0 +1,17 @@
|
|
1
|
+
module OmfRc::ResourceProxy::Node
|
2
|
+
include OmfRc::ResourceProxyDSL
|
3
|
+
|
4
|
+
register_proxy :node
|
5
|
+
|
6
|
+
register_hook :before_ready do |resource|
|
7
|
+
logger.info "#{resource.uid} is now ready"
|
8
|
+
end
|
9
|
+
|
10
|
+
register_hook :before_release do |resource|
|
11
|
+
logger.info "#{resource.uid} is now released"
|
12
|
+
end
|
13
|
+
|
14
|
+
register_request :proxies do
|
15
|
+
OmfRc::ResourceFactory.proxy_list
|
16
|
+
end
|
17
|
+
end
|
@@ -0,0 +1,170 @@
|
|
1
|
+
# DSL contains some helper methods to ease the process defining resource proxies
|
2
|
+
#
|
3
|
+
module OmfRc::ResourceProxyDSL
|
4
|
+
PROXY_DIR = "omf_rc/resource_proxy"
|
5
|
+
UTIL_DIR = "omf_rc/util"
|
6
|
+
|
7
|
+
def self.included(base)
|
8
|
+
base.extend(ClassMethods)
|
9
|
+
end
|
10
|
+
|
11
|
+
# Methods defined here will be available in resource/utility definition files
|
12
|
+
#
|
13
|
+
module ClassMethods
|
14
|
+
# Register a named proxy entry with factory class, normally this should be done in the proxy module
|
15
|
+
#
|
16
|
+
# @param [Symbol] name of the resource proxy
|
17
|
+
# @example suppose we define a module for wifi
|
18
|
+
#
|
19
|
+
# module OmfRc::ResourceProxy::Wifi
|
20
|
+
# include OmfRc::ResourceProxyDSL
|
21
|
+
#
|
22
|
+
# # Let the factory know it is available
|
23
|
+
# register_proxy :wifi
|
24
|
+
# end
|
25
|
+
#
|
26
|
+
def register_proxy(name)
|
27
|
+
name = name.to_sym
|
28
|
+
OmfRc::ResourceFactory.register_proxy(name)
|
29
|
+
end
|
30
|
+
|
31
|
+
# Register some hooks which can be called at certain stage of the operation
|
32
|
+
#
|
33
|
+
# Currently the system supports two hooks:
|
34
|
+
#
|
35
|
+
# * before_ready, called when a resource created, before creating an associated pubsub node
|
36
|
+
# * before_release, called before a resource released
|
37
|
+
#
|
38
|
+
# @param [Symbol] name hook name. :before_create or :before_release
|
39
|
+
# @yieldparam [AbstractResource] resource pass the current resource object to the block
|
40
|
+
# @example
|
41
|
+
#
|
42
|
+
# module OmfRc::ResourceProxy::Node
|
43
|
+
# include OmfRc::ResourceProxyDSL
|
44
|
+
#
|
45
|
+
# register_proxy :node
|
46
|
+
#
|
47
|
+
# register_hook :before_ready do |resource|
|
48
|
+
# logger.info "#{resource.uid} is now ready"
|
49
|
+
# end
|
50
|
+
#
|
51
|
+
# register_hook :before_release do |resource|
|
52
|
+
# logger.info "#{resource.uid} is now released"
|
53
|
+
# end
|
54
|
+
# end
|
55
|
+
def register_hook(name, ®ister_block)
|
56
|
+
define_method(name) do
|
57
|
+
register_block.call(self) if register_block
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
# Include the utility by providing a name
|
62
|
+
#
|
63
|
+
# The utility file can be added to the default utility directory UTIL_DIR, or defined inline.
|
64
|
+
#
|
65
|
+
# @param [Symbol] name of the utility
|
66
|
+
# @example assume we have a module called iw.rb in the omf_rc/util directory, providing a module named OmfRc::Util::Iw with functionalities based on iw cli
|
67
|
+
#
|
68
|
+
# module OmfRc::ResourceProxy::Wifi
|
69
|
+
# include OmfRc::ResourceProxyDSL
|
70
|
+
#
|
71
|
+
# # Simply include this util module
|
72
|
+
# utility :iw
|
73
|
+
# end
|
74
|
+
def utility(name)
|
75
|
+
name = name.to_s
|
76
|
+
begin
|
77
|
+
# In case of module defined inline
|
78
|
+
include "OmfRc::Util::#{name.camelcase}".constant
|
79
|
+
rescue NameError
|
80
|
+
begin
|
81
|
+
# Then we try to require the file and include the module
|
82
|
+
require "#{UTIL_DIR}/#{name}"
|
83
|
+
include "OmfRc::Util::#{name.camelcase}".constant
|
84
|
+
rescue LoadError => le
|
85
|
+
logger.error le.message
|
86
|
+
rescue NameError => ne
|
87
|
+
logger.error ne.message
|
88
|
+
end
|
89
|
+
end
|
90
|
+
end
|
91
|
+
|
92
|
+
# Register a named utility entry with factory class, normally this should be done in the utility module
|
93
|
+
#
|
94
|
+
# @param [Symbol] name of the resource proxy
|
95
|
+
# @example suppose we define a utility for iw command interaction
|
96
|
+
#
|
97
|
+
# module OmfRc::Util::Iw
|
98
|
+
# include OmfRc::ResourceProxyDSL
|
99
|
+
#
|
100
|
+
# # Let the factory know it is available
|
101
|
+
# register_utility :iw
|
102
|
+
# end
|
103
|
+
def register_utility(name)
|
104
|
+
name = name.to_sym
|
105
|
+
OmfRc::ResourceFactory.register_utility(name)
|
106
|
+
end
|
107
|
+
|
108
|
+
# Register a configurable property
|
109
|
+
#
|
110
|
+
# @param [Symbol] name of the property
|
111
|
+
# @yieldparam [AbstractResource] resource pass the current resource object to the block
|
112
|
+
# @yieldparam [Object] value pass the value to be configured
|
113
|
+
# @example suppose we define a utility for iw command interaction
|
114
|
+
#
|
115
|
+
# module OmfRc::Util::Iw
|
116
|
+
# include OmfRc::ResourceProxyDSL
|
117
|
+
#
|
118
|
+
# register_configure :freq do |resource, value|
|
119
|
+
# Command.execute("iw #{resource.hrn} set freq #{value}")
|
120
|
+
# end
|
121
|
+
#
|
122
|
+
# # or use iterator to define multiple properties
|
123
|
+
# %w(freq channel type).each do |p|
|
124
|
+
# register_configure p do |resource, value|
|
125
|
+
# Command.execute("iw #{resource.hrn} set freq #{value}")
|
126
|
+
# end
|
127
|
+
# end
|
128
|
+
#
|
129
|
+
# # or we can try to parse iw's help page to extract valid properties and then automatically register them
|
130
|
+
# Command.execute("iw help").chomp.gsub(/^\t/, '').split("\n").map {|v| v.match(/[phy|dev] <.+> set (\w+) .*/) && $1 }.compact.uniq.each do |p|
|
131
|
+
# register_configure p do |resource, value|
|
132
|
+
# Command.execute("iw #{resource.hrn} set #{p} #{value}")
|
133
|
+
# end
|
134
|
+
# end
|
135
|
+
# end
|
136
|
+
#
|
137
|
+
# @see OmfCommon::Command.execute
|
138
|
+
#
|
139
|
+
def register_configure(name, ®ister_block)
|
140
|
+
define_method("configure_#{name.to_s}") do |*args, &block|
|
141
|
+
register_block.call(self, *args, block) if register_block
|
142
|
+
end
|
143
|
+
end
|
144
|
+
|
145
|
+
# Register a property that could be requested
|
146
|
+
#
|
147
|
+
# @param (see #register_configure)
|
148
|
+
# @yieldparam [AbstractResource] resource pass the current resource object to the block
|
149
|
+
# @example suppose we define a utility for iw command interaction
|
150
|
+
# module OmfRc::Util::Iw
|
151
|
+
# include OmfRc::ResourceProxyDSL
|
152
|
+
#
|
153
|
+
# register_request :freq do |resource|
|
154
|
+
# Command.execute("iw #{resource.hrn} link").match(/^(freq):\W*(.+)$/) && $2
|
155
|
+
# end
|
156
|
+
#
|
157
|
+
# # or we can grab everything from output of iw link command and return as a hash(mash)
|
158
|
+
# Command.execute("iw #{resource.hrn} link").chomp.gsub(/^\t/, '').split("\n").drop(1).each do |v|
|
159
|
+
# v.match(/^(.+):\W*(.+)$/).tap do |m|
|
160
|
+
# m && known_properties[m[1].downcase.gsub(/\W+/, '_')] = m[2].gsub(/^\W+/, '')
|
161
|
+
# end
|
162
|
+
# end
|
163
|
+
# end
|
164
|
+
def register_request(name, ®ister_block)
|
165
|
+
define_method("request_#{name.to_s}") do |*args|
|
166
|
+
register_block.call(self) if register_block
|
167
|
+
end
|
168
|
+
end
|
169
|
+
end
|
170
|
+
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
require 'hashie'
|
2
|
+
module OmfRc::Util::Iw
|
3
|
+
include OmfRc::ResourceProxyDSL
|
4
|
+
|
5
|
+
OmfCommon::Command.execute("iw help").chomp.gsub(/^\t/, '').split("\n").map {|v| v.match(/[phy|dev] <.+> set (\w+) .*/) && $1 }.compact.uniq.each do |p|
|
6
|
+
register_configure p do |resource, value|
|
7
|
+
OmfCommon::Command.execute("#{IW_CMD} #{resource.hrn} set #{p} #{value}")
|
8
|
+
end
|
9
|
+
end
|
10
|
+
|
11
|
+
register_request :link do |resource|
|
12
|
+
known_properties = Hashie::Mash.new
|
13
|
+
|
14
|
+
OmfCommon::Command.execute("iw #{resource.hrn} link").chomp.gsub(/^\t/, '').split("\n").drop(1).each do |v|
|
15
|
+
v.match(/^(.+):\W*(.+)$/).tap do |m|
|
16
|
+
m && known_properties[m[1].downcase.gsub(/\W+/, '_')] = m[2].gsub(/^\W+/, '')
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
known_properties
|
21
|
+
end
|
22
|
+
end
|
@@ -0,0 +1,27 @@
|
|
1
|
+
module OmfRc::Util::Mock
|
2
|
+
include OmfRc::ResourceProxyDSL
|
3
|
+
|
4
|
+
register_utility :mock
|
5
|
+
|
6
|
+
register_request :nothing do |resource|
|
7
|
+
resource.uid
|
8
|
+
end
|
9
|
+
|
10
|
+
register_configure :nothing
|
11
|
+
|
12
|
+
register_configure :hrn do |resource, hrn|
|
13
|
+
resource.hrn = hrn
|
14
|
+
end
|
15
|
+
|
16
|
+
register_request :resource_proxy_list do
|
17
|
+
OmfRc::ResourceFactory.proxy_list
|
18
|
+
end
|
19
|
+
|
20
|
+
register_request :resource_utility_list do
|
21
|
+
OmfRc::ResourceFactory.utility_list
|
22
|
+
end
|
23
|
+
|
24
|
+
register_request :kernel_version do
|
25
|
+
OmfCommon::Command.execute("uname -r")
|
26
|
+
end
|
27
|
+
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
module OmfRc::Util::Mod
|
2
|
+
|
3
|
+
LSMOD_CMD = 'lsmod'
|
4
|
+
MODPROBE_CMD = 'modprobe'
|
5
|
+
|
6
|
+
def request_property(property)
|
7
|
+
case property
|
8
|
+
when /^(mod|driver)_(.+)$/
|
9
|
+
OmfRc::Cmd.exec(LSMOD).match(/^#{$+}( )+/) ? true : false
|
10
|
+
else
|
11
|
+
super
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
def configure_property(property, value)
|
16
|
+
case property
|
17
|
+
when /^(mod|driver)_(.+)$/
|
18
|
+
OmfRc::Cmd.exec("#{MODPROBE} #{$+}")
|
19
|
+
else
|
20
|
+
super
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
module OmfRc::ResourceProxy::Package
|
2
|
+
include OmfRc::ResourceProxyDSL
|
3
|
+
|
4
|
+
register_utility :package
|
5
|
+
|
6
|
+
register_request :package_version do |resource|
|
7
|
+
OmfCommon::Command.execute("dpkg -l #{resource.hrn} | awk 'END { print $3 }'")
|
8
|
+
end
|
9
|
+
|
10
|
+
register_configure :install_package do |resource|
|
11
|
+
OmfCommon::Command.execute("apt-get install -y --reinstall --allow-unauthenticated -qq #{resource.hrn}")
|
12
|
+
end
|
13
|
+
|
14
|
+
register_configure :remove_package do |resource|
|
15
|
+
OmfCommon::Command.execute("apt-get purge -y -qq #{resource.hrn}")
|
16
|
+
end
|
17
|
+
end
|
data/lib/omf_rc.rb
ADDED
data/omf_rc.gemspec
ADDED
@@ -0,0 +1,25 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
$:.push File.expand_path("../lib", __FILE__)
|
3
|
+
require "omf_rc/version"
|
4
|
+
|
5
|
+
Gem::Specification.new do |s|
|
6
|
+
s.name = "omf_rc"
|
7
|
+
s.version = OmfRc::VERSION
|
8
|
+
s.authors = ["NICTA"]
|
9
|
+
s.email = ["omf-user@lists.nicta.com.au"]
|
10
|
+
s.homepage = "https://www.mytestbed.net"
|
11
|
+
s.summary = %q{OMF resource controller}
|
12
|
+
s.description = %q{Resource controller of OMF, a generic framework for controlling and managing networking testbeds.}
|
13
|
+
|
14
|
+
s.rubyforge_project = "omf_rc"
|
15
|
+
|
16
|
+
s.files = `git ls-files`.split("\n")
|
17
|
+
s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
|
18
|
+
s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
|
19
|
+
s.require_paths = ["lib"]
|
20
|
+
|
21
|
+
# specify any dependencies here; for example:
|
22
|
+
s.add_development_dependency "minitest", "~> 2.11.3"
|
23
|
+
s.add_development_dependency "em-minitest-spec", "~> 1.1.1"
|
24
|
+
s.add_runtime_dependency "omf_common", "~> 6.0.0.pre"
|
25
|
+
end
|
@@ -0,0 +1,39 @@
|
|
1
|
+
require 'test_helper'
|
2
|
+
require 'em/minitest/spec'
|
3
|
+
require 'omf_rc/deferred_process'
|
4
|
+
|
5
|
+
describe OmfRc::DeferredProcess do
|
6
|
+
describe "when use to deferred process to execute code asynchronously" do
|
7
|
+
include EM::MiniTest::Spec
|
8
|
+
|
9
|
+
it "must execute and return result eventually, inside the EM loop" do
|
10
|
+
dp = OmfRc::DeferredProcess.new
|
11
|
+
|
12
|
+
dp.callback do |result|
|
13
|
+
result.must_equal "hello world"
|
14
|
+
done!
|
15
|
+
end
|
16
|
+
|
17
|
+
dp.fire do
|
18
|
+
"hello world"
|
19
|
+
end
|
20
|
+
|
21
|
+
wait!
|
22
|
+
end
|
23
|
+
|
24
|
+
it "must capture errors properly inside the EM loop" do
|
25
|
+
dp = OmfRc::DeferredProcess.new
|
26
|
+
|
27
|
+
dp.errback do |exception|
|
28
|
+
exception.must_be_kind_of StandardError
|
29
|
+
done!
|
30
|
+
end
|
31
|
+
|
32
|
+
dp.fire do
|
33
|
+
raise StandardError
|
34
|
+
end
|
35
|
+
|
36
|
+
wait!
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|