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

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