omf_rc 6.0.0.pre.5 → 6.0.0.pre.6

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.
Files changed (45) hide show
  1. data/Rakefile +0 -6
  2. data/bin/omf_rc +2 -2
  3. data/lib/omf_rc/resource_factory.rb +10 -4
  4. data/lib/omf_rc/resource_proxy/abstract_resource.rb +120 -79
  5. data/lib/omf_rc/resource_proxy/generic_application.rb +471 -0
  6. data/lib/omf_rc/resource_proxy/net.rb +7 -0
  7. data/lib/omf_rc/resource_proxy/node.rb +33 -7
  8. data/lib/omf_rc/resource_proxy/openflow_slice.rb +79 -0
  9. data/lib/omf_rc/resource_proxy/openflow_slice_factory.rb +71 -0
  10. data/lib/omf_rc/resource_proxy/wlan.rb +20 -0
  11. data/lib/omf_rc/resource_proxy_dsl.rb +142 -8
  12. data/lib/omf_rc/util/common_tools.rb +61 -0
  13. data/lib/omf_rc/util/hostapd.rb +52 -0
  14. data/lib/omf_rc/util/ip.rb +28 -0
  15. data/lib/omf_rc/util/iw.rb +119 -6
  16. data/lib/omf_rc/util/mock.rb +2 -1
  17. data/lib/omf_rc/util/openflow_tools.rb +103 -0
  18. data/lib/omf_rc/util/platform_tools.rb +164 -0
  19. data/lib/omf_rc/util/wpa.rb +42 -0
  20. data/lib/omf_rc/version.rb +1 -1
  21. data/omf_rc.gemspec +3 -1
  22. data/test/fixture/ip/addr_show +5 -0
  23. data/test/fixture/iw/info +4 -0
  24. data/test/fixture/sys/class/ieee80211/phy0/device/uevent +6 -0
  25. data/test/fixture/sys/class/ieee80211/phy0/uevent +0 -0
  26. data/test/fixture/sys/class/net/eth0/device/uevent +6 -0
  27. data/test/fixture/sys/class/net/eth0/uevent +2 -0
  28. data/test/fixture/sys/class/net/wlan0/device/uevent +6 -0
  29. data/test/fixture/sys/class/net/wlan0/uevent +3 -0
  30. data/test/omf_rc/message_process_error_spec.rb +11 -0
  31. data/test/omf_rc/resource_factory_spec.rb +8 -1
  32. data/test/omf_rc/resource_proxy/abstract_resource_spec.rb +57 -1
  33. data/test/omf_rc/resource_proxy/generic_application_spec.rb +347 -0
  34. data/test/omf_rc/resource_proxy/mock_spec.rb +15 -0
  35. data/test/omf_rc/resource_proxy/node_spec.rb +32 -1
  36. data/test/omf_rc/resource_proxy_dsl_spec.rb +81 -10
  37. data/test/omf_rc/util/common_tools_spec.rb +30 -0
  38. data/test/omf_rc/util/ip_spec.rb +51 -0
  39. data/test/omf_rc/util/iw_spec.rb +136 -25
  40. data/test/omf_rc/util/mock_spec.rb +26 -0
  41. data/test/omf_rc/util/mod_spec.rb +8 -11
  42. data/test/test_helper.rb +11 -0
  43. metadata +62 -6
  44. data/lib/omf_rc/resource_proxy/wifi.rb +0 -8
  45. data/test/mock_helper.rb +0 -15
@@ -0,0 +1,20 @@
1
+ module OmfRc::ResourceProxy::Wlan
2
+ include OmfRc::ResourceProxyDSL
3
+
4
+ register_proxy :wlan
5
+
6
+ utility :ip
7
+ utility :mod
8
+ utility :iw
9
+
10
+ hook :before_release do |device|
11
+ case device.property.mode.to_sym
12
+ when :master
13
+ device.stop_hostapd
14
+ when :managed
15
+ device.stop_wpa
16
+ end
17
+ #TODO need to remove all virtual interfaces of that phy device
18
+ #device.remove_all_interfaces
19
+ end
20
+ end
@@ -14,6 +14,9 @@ module OmfRc::ResourceProxyDSL
14
14
  # Register a named proxy entry with factory class, normally this should be done in the proxy module
15
15
  #
16
16
  # @param [Symbol] name of the resource proxy
17
+ # @param [Hash] opts options to be passed to proxy registration
18
+ # @option opts [String, Array] :create_by resource can only be created by these resources.
19
+ #
17
20
  # @example suppose we define a module for wifi
18
21
  #
19
22
  # module OmfRc::ResourceProxy::Wifi
@@ -21,19 +24,26 @@ module OmfRc::ResourceProxyDSL
21
24
  #
22
25
  # # Let the factory know it is available
23
26
  # register_proxy :wifi
27
+ #
28
+ # # or use option :create_by
29
+ # register_proxy :wifi, :create_by => :node
24
30
  # end
25
31
  #
26
- def register_proxy(name)
32
+ def register_proxy(name, opts = {})
27
33
  name = name.to_sym
28
- OmfRc::ResourceFactory.register_proxy(name)
34
+ if opts[:create_by] && !opts[:create_by].kind_of?(Array)
35
+ opts[:create_by] = [opts[:create_by]]
36
+ end
37
+ OmfRc::ResourceFactory.register_proxy(name => opts)
29
38
  end
30
39
 
31
40
  # Register some hooks which can be called at certain stage of the operation
32
41
  #
33
- # Currently the system supports two hooks:
42
+ # Currently the system supports these hooks:
34
43
  #
35
44
  # * before_ready, called when a resource created, before creating an associated pubsub topic
36
45
  # * before_release, called before a resource released
46
+ # * before_create, called before parent creates the child resource. (in the context of parent resource)
37
47
  #
38
48
  # @param [Symbol] name hook name. :before_create or :before_release
39
49
  # @yieldparam [AbstractResource] resource pass the current resource object to the block
@@ -44,6 +54,26 @@ module OmfRc::ResourceProxyDSL
44
54
  #
45
55
  # register_proxy :node
46
56
  #
57
+ # # before_create hook
58
+ # #
59
+ # # the optional block will have access to three variables:
60
+ # # * resource: the parent resource itself
61
+ # # * new_resource_type: a string or symbol represents the new resource to be created
62
+ # # * new_resource_options: the options hash to be passed to the new resource
63
+ # #
64
+ # # this hook enable us to do things like:
65
+ # # * validating child resources: e.g. if parent could create this new resource
66
+ # # * setting up default child properties based on parent's property value
67
+ # hook :before_create do |resource, new_resource_type, new_resource_options|
68
+ # if new_resource_type.to_sym == :wifi
69
+ # logger.info "Resource type wifi is allowed"
70
+ # else
71
+ # raise "Go away, I can't create #{new_resource_type}"
72
+ # end
73
+ # new_resource_options.property ||= Hashie::Mash.new
74
+ # new_resource_options.property.node_info = "Node #{resource.uid}"
75
+ # end
76
+ #
47
77
  # hook :before_ready do |resource|
48
78
  # logger.info "#{resource.uid} is now ready"
49
79
  # end
@@ -53,8 +83,8 @@ module OmfRc::ResourceProxyDSL
53
83
  # end
54
84
  # end
55
85
  def hook(name, &register_block)
56
- define_method(name) do
57
- register_block.call(self) if register_block
86
+ define_method(name) do |*args, &block|
87
+ register_block.call(self, *args, block) if register_block
58
88
  end
59
89
  end
60
90
 
@@ -76,11 +106,13 @@ module OmfRc::ResourceProxyDSL
76
106
  begin
77
107
  # In case of module defined inline
78
108
  include "OmfRc::Util::#{name.camelize}".constantize
109
+ extend "OmfRc::Util::#{name.camelize}".constantize
79
110
  rescue NameError
80
111
  begin
81
112
  # Then we try to require the file and include the module
82
113
  require "#{UTIL_DIR}/#{name}"
83
114
  include "OmfRc::Util::#{name.camelize}".constantize
115
+ extend "OmfRc::Util::#{name.camelize}".constantize
84
116
  rescue LoadError => le
85
117
  logger.error le.message
86
118
  rescue NameError => ne
@@ -122,7 +154,8 @@ module OmfRc::ResourceProxyDSL
122
154
  #
123
155
  def configure(name, &register_block)
124
156
  define_method("configure_#{name.to_s}") do |*args, &block|
125
- register_block.call(self, *args, block) if register_block
157
+ args[0] = Hashie::Mash.new(args[0]) if args[0].class == Hash
158
+ register_block.call(self, *args, block) if register_block
126
159
  end
127
160
  end
128
161
 
@@ -146,9 +179,110 @@ module OmfRc::ResourceProxyDSL
146
179
  # end
147
180
  # end
148
181
  def request(name, &register_block)
149
- define_method("request_#{name.to_s}") do |*args|
150
- register_block.call(self) if register_block
182
+ define_method("request_#{name.to_s}") do |*args, &block|
183
+ args[0] = Hashie::Mash.new(args[0]) if args[0].class == Hash
184
+ register_block.call(self, *args, block) if register_block
185
+ end
186
+ end
187
+
188
+ # Define an arbitrary method to do some work, can be included in configure & request block
189
+ #
190
+ # @param (see #configure)
191
+ # @yieldparam [AbstractResource] resource pass the current resource object to the block
192
+ # @example suppose we define a simple os checking method
193
+ #
194
+ # work :os do
195
+ # Command.execute("uname")
196
+ # end
197
+ #
198
+ # # then this os method will be available in all proxy definitions which includes this work method definition.
199
+ # # if for some reason you need to call 'os' method within the same module, you need to access it via the resource instance.
200
+ #
201
+ # work :install_software do |resource|
202
+ # raise 'Can not support non-linux distro yet' if resource.os == 'Linux'
203
+ # end
204
+ def work(name, &register_block)
205
+ define_method(name) do |*args, &block|
206
+ register_block.call(self, *args, block) if register_block
151
207
  end
152
208
  end
209
+
210
+ # Extend existing hook definition by alias existing method name as "orig_[method_name]"
211
+ #
212
+ # @param [#to_s] hook_name name of existing hook
213
+ # @example extend a hook definition
214
+ #
215
+ # # suppose existing hook defined as this:
216
+ # hook :before_ready do |resource|
217
+ # logger.info "I am ready"
218
+ # end
219
+ #
220
+ # # now in a new proxy where we want to extend this hook, add more functionality:
221
+ #
222
+ # extend_hook :before_ready
223
+ #
224
+ # hook :before_ready do |resource|
225
+ # resource.orig_before_ready
226
+ #
227
+ # logger.info "Now I am really ready"
228
+ # end
229
+ #
230
+ # # if we simply want to overwrite the existing hook, just define the same hook without using extend_hook
231
+ #
232
+ # hook :before_ready do |resource|
233
+ # logger.info "Not sure if I am ready or not"
234
+ # end
235
+ #
236
+ def extend_hook(hook_name)
237
+ hook_name = hook_name.to_s
238
+ alias_method "orig_#{hook_name}", hook_name
239
+ end
240
+
241
+ # Extend existing work definition by alias existing method name as "orig_[method_name]"
242
+ #
243
+ # @see #extend_hook
244
+ #
245
+ def extend_work(work_name)
246
+ work_name = work_name.to_s
247
+ alias_method "orig_#{work_name}", work_name
248
+ end
249
+
250
+ # Extend existing configure definition
251
+ #
252
+ # @param [#to_s] configure_name name of existing configurable property
253
+ #
254
+ # Slightly different to extend_hook, the actual method_name defined by a configure property is "configure_[configurable_property_name]"
255
+ #
256
+ # @example to extend a configurable property
257
+ #
258
+ # configure :bob do |resource, value|
259
+ # resource.property.bob = value
260
+ # end
261
+ #
262
+ # # To extend this, simply do
263
+ #
264
+ # extend_configure :bob
265
+ #
266
+ # configure :bob do |resource, value|
267
+ # resource.orig_configure_bob(value)
268
+ # resource.property.bob = "New value: #{value}"
269
+ # end
270
+ #
271
+ # @see #extend_hook
272
+ #
273
+ def extend_configure(configure_name)
274
+ configure_name = configure_name.to_s
275
+ alias_method "orig_configure_#{configure_name}", "configure_#{configure_name}"
276
+ end
277
+
278
+ # Extend existing request definition
279
+ #
280
+ # @see #extend_hook
281
+ # @see #extend_configure
282
+ def extend_request(request_name)
283
+ request_name = request_name.to_s
284
+ alias_method "orig_request_#{request_name}", "request_#{request_name}"
285
+ end
286
+
153
287
  end
154
288
  end
@@ -0,0 +1,61 @@
1
+ #
2
+ # Copyright (c) 2012 National ICT Australia (NICTA), Australia
3
+ #
4
+ # Permission is hereby granted, free of charge, to any person obtaining a copy
5
+ # of this software and associated documentation files (the "Software"), to deal
6
+ # in the Software without restriction, including without limitation the rights
7
+ # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8
+ # copies of the Software, and to permit persons to whom the Software is
9
+ # furnished to do so, subject to the following conditions:
10
+ #
11
+ # The above copyright notice and this permission notice shall be included in
12
+ # all copies or substantial portions of the Software.
13
+ #
14
+ # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15
+ # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16
+ # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17
+ # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18
+ # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19
+ # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
20
+ # THE SOFTWARE.
21
+ #
22
+
23
+ #
24
+ # This module defines a Utility with some common work blocks that could be
25
+ # useful to any type of Resource Proxy (RP)
26
+ #
27
+ module OmfRc::Util::CommonTools
28
+ include OmfRc::ResourceProxyDSL
29
+
30
+ # This utility block logs an error/warn String S on the resource proxy side
31
+ # and publish an INFORM message on the resources pubsub topic. This INFORM
32
+ # message will have the type ERROR/WARN, and its 'reason' element set to the
33
+ # String S
34
+ #
35
+ # @yieldparam [String] msg the error or warning message
36
+ #
37
+ %w(error warn).each do |type|
38
+ work("log_inform_#{type}") do |res, msg|
39
+ logger.send(type, msg)
40
+ res.comm.publish(
41
+ res.uid,
42
+ OmfCommon::Message.inform(type.upcase) do |message|
43
+ message.element('reason' , msg)
44
+ end
45
+ )
46
+ end
47
+ end
48
+
49
+ # This utility block returns true if its given value parameter is a Boolean,
50
+ # which in Ruby means that it is either of the class TrueClass or FalseClass
51
+ #
52
+ # @yieldparam [Object] obj the Object to test as Boolean
53
+ #
54
+ # [Boolean] true or fals
55
+ #
56
+ work('boolean?') do |res,obj|
57
+ result = false
58
+ result = true if obj.kind_of?(TrueClass) || obj.kind_of?(FalseClass)
59
+ result
60
+ end
61
+ end
@@ -0,0 +1,52 @@
1
+ require 'hashie'
2
+ require 'cocaine'
3
+
4
+ module OmfRc::Util::Hostapd
5
+ include OmfRc::ResourceProxyDSL
6
+ include Cocaine
7
+
8
+ # Initialise access point conf and pid location
9
+ #
10
+ work :init_ap_conf_pid do |device|
11
+ device.property.ap_conf = "/tmp/hostapd.#{device.hrn}.conf"
12
+ device.property.ap_pid = "/tmp/hostapd.#{device.hrn}.pid"
13
+ end
14
+ # Set up and run a hostapd instance
15
+ #
16
+ work :hostapd do |device|
17
+ device.init_ap_conf_pid
18
+
19
+ File.open(device.property.ap_conf, "w") do |f|
20
+ f << "driver=nl80211\ninterface=#{device.hrn}\nssid=#{device.property.essid}\nchannel=#{device.property.channel}\n"
21
+ f << "hw_mode=#{device.property.hw_mode}\n" if %w(a b g).include? device.property.hw_mode
22
+ if device.property.hw_mode == 'n'
23
+ if device.property.channel.to_i < 15
24
+ f << "hw_mode=g\n"
25
+ else device.property.channel.to_i > 15
26
+ f << "hw_mode=a\n"
27
+ end
28
+ f << "wmm_enabled=1\nieee80211n=1\nht_capab=[HT20-]\n"
29
+ end
30
+ end
31
+
32
+ CommandLine.new("hostapd", "-B -P :ap_pid :ap_conf",
33
+ :ap_pid => device.property.ap_pid,
34
+ :ap_conf => device.property.ap_conf).run
35
+ end
36
+
37
+ work :stop_hostapd do |device|
38
+ begin
39
+ File.open(device.property.ap_pid,'r') do |f|
40
+ logger.debug "Stopping hostapd process at PID: #{device.property.ap_pid}"
41
+ CommandLine.new("kill", "-9 :pid", :pid => f.read.chomp).run
42
+ end
43
+
44
+ CommandLine.new("rm", "-f :ap_pid :ap_conf",
45
+ :ap_pid => device.property.ap_pid,
46
+ :ap_conf => device.property.ap_conf).run
47
+ rescue => e
48
+ logger.warn "Failed to clean hostapd and its related files '#{device.property.ap_pid}' and '#{device.property.ap_conf}'!"
49
+ logger.warn e.message
50
+ end
51
+ end
52
+ end
@@ -0,0 +1,28 @@
1
+ require 'cocaine'
2
+
3
+ module OmfRc::Util::Ip
4
+ include OmfRc::ResourceProxyDSL
5
+ include Cocaine
6
+
7
+ request :ip_addr do |resource|
8
+ addr = CommandLine.new("ip", "addr show dev :device", :device => resource.hrn).run
9
+ addr && addr.chomp.match(/inet ([[0-9]\:\/\.]+)/) && $1
10
+ end
11
+
12
+ request :mac_addr do |resource|
13
+ addr = CommandLine.new("ip", "addr show dev :device", :device => resource.hrn).run
14
+ addr && addr.chomp.match(/link\/ether ([\d[a-f][A-F]\:]+)/) && $1
15
+ end
16
+
17
+ configure :ip_addr do |resource, value|
18
+ CommandLine.new("ip", "addr add :ip_address dev :device",
19
+ :ip_address => value,
20
+ :device => resource.hrn
21
+ ).run
22
+ resource.request_ip_addr
23
+ end
24
+
25
+ work :interface_up do |resource|
26
+ CommandLine.new("ip", "link set :dev up", :dev => resource.hrn).run
27
+ end
28
+ end
@@ -1,18 +1,34 @@
1
1
  require 'hashie'
2
+ require 'cocaine'
2
3
 
3
4
  module OmfRc::Util::Iw
4
5
  include OmfRc::ResourceProxyDSL
6
+ include Cocaine
7
+ include Hashie
5
8
 
6
- OmfCommon::Command.execute("iw help").chomp.gsub(/^\t/, '').split("\n").map {|v| v.match(/[phy|dev] <.+> set (\w+) .*/) && $1 }.compact.uniq.each do |p|
7
- configure p do |resource, value|
8
- OmfCommon::Command.execute("iw #{resource.hrn} set #{p} #{value}")
9
+ utility :ip
10
+ utility :wpa
11
+ utility :hostapd
12
+
13
+ # Parse iw help page and set up all configure methods available for iw command
14
+ #
15
+ CommandLine.new("iw", "help").run.chomp.gsub(/^\t/, '').split("\n").map {|v| v.match(/[phy|dev] <.+> set (\w+) .*/) && $1 }.compact.uniq.each do |p|
16
+ configure p do |device, value|
17
+ CommandLine.new("iw", "dev :dev set :property :value",
18
+ :dev => device.hrn,
19
+ :property => p,
20
+ :value => value).run
9
21
  end
10
22
  end
11
23
 
12
- request :link do |resource|
13
- known_properties = Hashie::Mash.new
24
+ # Parse iw link command output and return as a mash
25
+ #
26
+ request :link do |device|
27
+ known_properties = Mash.new
28
+
29
+ command = CommandLine.new("iw", "dev :dev link", :dev => device.hrn)
14
30
 
15
- OmfCommon::Command.execute("iw #{resource.hrn} link").chomp.gsub(/^\t/, '').split("\n").drop(1).each do |v|
31
+ command.run.chomp.gsub(/^\t/, '').split("\n").drop(1).each do |v|
16
32
  v.match(/^(.+):\W*(.+)$/).tap do |m|
17
33
  m && known_properties[m[1].downcase.gsub(/\W+/, '_')] = m[2].gsub(/^\W+/, '')
18
34
  end
@@ -20,4 +36,101 @@ module OmfRc::Util::Iw
20
36
 
21
37
  known_properties
22
38
  end
39
+
40
+ # Parse iw info command output and return as a mash
41
+ #
42
+ request :info do |device|
43
+ known_properties = Mash.new
44
+
45
+ command = CommandLine.new("iw", "dev :dev info", :dev => device.hrn)
46
+
47
+ command.run.chomp.split("\n").drop(1).each do |v|
48
+ v.match(/^\W*(.+) (.+)$/).tap do |m|
49
+ m && known_properties[m[1].downcase.gsub(/\W+/, '_')] = m[2].gsub(/^\W+/, '')
50
+ end
51
+ end
52
+
53
+ known_properties
54
+ end
55
+
56
+ # Delete current interface, clean up
57
+ #
58
+ work :delele_interface do |device|
59
+ CommandLine.new("iw", "dev :dev del", :dev => device.hrn).run
60
+ end
61
+
62
+ # Add interface to device
63
+ #
64
+ work :add_interface do |device, type|
65
+ CommandLine.new("iw", "phy :phy interface add :dev type :type",
66
+ :phy => device.property.phy,
67
+ :dev => device.hrn,
68
+ :type => type.to_s).run
69
+ end
70
+
71
+ # Set up or join a ibss network
72
+ #
73
+ work :join_ibss do |device|
74
+ CommandLine.new("iw", "dev :device ibss join :essid :frequency",
75
+ :device => device.hrn.to_s,
76
+ :essid => device.property.essid.to_s,
77
+ :frequency => device.property.frequency.to_s).run
78
+ end
79
+
80
+ # Validate internal properties based on interface mode
81
+ #
82
+ work :validate_iw_properties do |device|
83
+ raise ArgumentError, "Missing phyical device name" if device.property.phy.nil?
84
+
85
+ unless %w(master managed adhoc monitor).include? device.property.mode
86
+ raise ArgumentError, "Mode must be master, managed, adhoc, or monitor, got #{device.property.mode}"
87
+ end
88
+
89
+ case device.property.mode.to_sym
90
+ when :master
91
+ unless %w(a b g n).include? device.property.hw_mode
92
+ raise ArgumentError, "Hardware mode must be a, b, g, or n, got #{device.property.hw_mode}"
93
+ end
94
+ %w(channel essid).each do |p|
95
+ raise ArgumentError, "#{p} must not be nil" if device.property.send(p).nil?
96
+ end
97
+ when :managed
98
+ %w(essid).each do |p|
99
+ raise ArgumentError, "#{p} must not be nil" if device.property.send(p).nil?
100
+ end
101
+ when :adhoc
102
+ %w(essid frequency).each do |p|
103
+ raise ArgumentError, "#{p} must not be nil" if device.property.send(p).nil?
104
+ end
105
+ end
106
+ end
107
+
108
+ # Configure the interface with mode managed, master, adhoc or monitor
109
+ #
110
+ configure :mode do |device, value|
111
+ # capture value hash and store internally
112
+ device.property.update(value)
113
+
114
+ device.validate_iw_properties
115
+
116
+ device.delele_interface rescue logger.warn "Interface #{device.hrn} not found"
117
+
118
+ # TODO should just remove all interfaces from physical device, at least make it optional
119
+
120
+ case device.property.mode.to_sym
121
+ when :master
122
+ device.add_interface(:managed)
123
+ device.hostapd
124
+ when :managed
125
+ device.add_interface(:managed)
126
+ device.wpasup
127
+ when :adhoc
128
+ device.add_interface(:adhoc)
129
+ device.interface_up
130
+ device.join_ibss
131
+ when :monitor
132
+ device.add_interface(:monitor)
133
+ device.interface_up
134
+ end
135
+ end
23
136
  end
@@ -5,7 +5,8 @@ module OmfRc::Util::Mock
5
5
  resource.uid
6
6
  end
7
7
 
8
- configure :nothing
8
+ configure :nothing do
9
+ end
9
10
 
10
11
  configure :hrn do |resource, hrn|
11
12
  resource.hrn = hrn
@@ -0,0 +1,103 @@
1
+ require 'xmlrpc/client'
2
+
3
+ module OmfRc::Util::OpenflowTools
4
+ include OmfRc::ResourceProxyDSL
5
+
6
+ # The version of the flowvisor that this resource is able to control
7
+ FLOWVISOR_VERSION = "FV version=flowvisor-0.8.4"
8
+
9
+ # Parts of the regular expression that describes a flow entry for flowvisor
10
+ FLOWVISOR_FLOWENTRY_REGEXP_DEVIDED = [
11
+ /dpid=\[(?<device>.+)\]/,
12
+ /ruleMatch=\[OFMatch\[(?<match>.+)\]\]/,
13
+ /actionsList=\[Slice:(?<slice>.+)=(?<actions>.+)\]/,
14
+ /id=\[(?<id>.+)\]/,
15
+ /priority=\[(?<priority>.+)\]/
16
+ ]
17
+
18
+ # The regular expression that describes a flow entry for flowvisor
19
+ FLOWVISOR_FLOWENTRY_REGEXP = /FlowEntry\[#{FLOWVISOR_FLOWENTRY_REGEXP_DEVIDED.join(',')},\]/
20
+
21
+ # The names of the flow (or flow entry) features
22
+ FLOW_FEATURES = %w{device match slice actions id priority}
23
+
24
+ # The names of the flow (or flow entry) features that are specified by the "match" feature
25
+ FLOW_MATCH_FEATURES = %w{in_port eth_src eth_dst ip_src ip_dst}
26
+
27
+ # The default features of a new flow (or flow entry)
28
+ FLOW_DEFAULTS = {
29
+ priority: "10",
30
+ actions: "4"
31
+ }
32
+
33
+
34
+ # Returns the flows (flow entries) that exist for this flowvisor
35
+ request :flows do |resource, filter = nil|
36
+ resource.flows(filter)
37
+ end
38
+
39
+
40
+ # Internal function that creates a connection with a flowvisor instance and checks it
41
+ work :flowvisor_connection do |resource|
42
+ xmlrpc_client = XMLRPC::Client.new_from_hash(resource.property.flowvisor_connection_args)
43
+ xmlrpc_client.instance_variable_get("@http").verify_mode = OpenSSL::SSL::VERIFY_NONE
44
+ ping_msg = "test"
45
+ pong_msg = "PONG(#{resource.property.flowvisor_connection_args[:user]}): #{FLOWVISOR_VERSION}::#{ping_msg}"
46
+ raise "Connection with #{FLOWVISOR_VERSION} was not successful" if xmlrpc_client.call("api.ping", ping_msg) != pong_msg
47
+ xmlrpc_client
48
+ end
49
+
50
+ # Internal function that returns the flows (flow entries) that exist in the connected flowvisor instance
51
+ work :flows do |resource, filter = nil|
52
+ result = resource.flowvisor_connection.call("api.listFlowSpace")
53
+ result.map! do |line|
54
+ array_values = line.match(FLOWVISOR_FLOWENTRY_REGEXP)[1..-1]
55
+ # Example of above array's content: %w{00:00:...:01 in_port=1 test 4 30 10}
56
+ array_features_values_zipped = FLOW_FEATURES.zip(array_values)
57
+ # Example of above array's content: %w{device 00:00:...:01 match in_port=1 slice test actions 4 id 30 priority 10}
58
+ hash = Hashie::Mash.new(Hash[array_features_values_zipped])
59
+ # The following code adds extra features that are specified by the "match" feature
60
+ hash["match"].split(",").each do |couple|
61
+ array = couple.split("=")
62
+ hash[array[0]] = array[1]
63
+ end
64
+ hash
65
+ end
66
+ result.delete_if {|hash| hash["slice"] != resource.property.name} if resource.type.to_sym == :openflow_slice
67
+ FLOW_FEATURES.each do |feature|
68
+ result.delete_if {|hash| hash[feature] != filter[feature].to_s} if filter[feature]
69
+ end if filter
70
+ result
71
+ end
72
+
73
+ work :transformed_parameters do |resource, parameters|
74
+
75
+ match = []
76
+ FLOW_MATCH_FEATURES.each do |feature|
77
+ match << "#{feature}=#{parameters[feature]}" if parameters[feature]
78
+ end
79
+ match = match.join(",")
80
+
81
+ result = []
82
+ case parameters.operation
83
+ when "add"
84
+ h = Hashie::Mash.new
85
+ h.operation = parameters.operation.upcase
86
+ h.priority = parameters.priority ? parameters.priority.to_s : FLOW_DEFAULTS[:priority]
87
+ h.dpid = parameters.device.to_s
88
+ h.actions = "Slice:#{resource.property.name}=#{(parameters.actions ? parameters.actions : FLOW_DEFAULTS[:actions])}"
89
+ h.match = "OFMatch[#{match}]"
90
+ result << h
91
+ when "remove"
92
+ resource.flows(parameters).each do |f|
93
+ if f.match == match
94
+ h = Hashie::Mash.new
95
+ h.operation = parameters.operation.upcase
96
+ h.id = f.id
97
+ result << h
98
+ end
99
+ end
100
+ end
101
+ result
102
+ end
103
+ end