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.
@@ -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,8 @@
1
+ module OmfRc::ResourceProxy::Wifi
2
+ include OmfRc::ResourceProxyDSL
3
+
4
+ register_proxy :wifi
5
+
6
+ #utility :mod
7
+ utility :iw
8
+ 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, &register_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, &register_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, &register_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
@@ -0,0 +1,3 @@
1
+ module OmfRc
2
+ VERSION = "6.0.0.pre.2"
3
+ end
data/lib/omf_rc.rb ADDED
@@ -0,0 +1,6 @@
1
+ require "omf_rc/version"
2
+
3
+ module OmfRc
4
+ module ResourceProxy; end
5
+ module Util; end
6
+ end
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