| 1 | [[TOC]] |
| 2 | |
| 3 | This driver adds functionality to OMF so that you can use a Teltonika WiMAX USB device in an OMF script. |
| 4 | |
| 5 | |
| 6 | The syntax to connect to a WiMAX channel in OMF is (for example, if the center frequency of the channel is 2.59GHz and the channel bandwidth is 10MHz): |
| 7 | {{{ |
| 8 | node.net.t0.channel = "2595000,10" |
| 9 | }}} |
| 10 | |
| 11 | |
| 12 | Note: this work assumes default settings on the Teltonika device (e.g. default telnet settings, IP settings, etc.) |
| 13 | |
| 14 | == Installing the driver == |
| 15 | |
| 16 | === Patches to omf-resctl === |
| 17 | |
| 18 | In /usr/share/omf-resctl-5.3/omf-resctl/omf_agent/nodeAgent.rb: |
| 19 | |
| 20 | {{{ |
| 21 | tel_count = 0 |
| 22 | if (LSUSB) |
| 23 | IO.popen("#{LSUSB} | grep 'Intel Corp. WiMAX Connection 2400m' | wc -l") {|u| |
| 24 | wimax_count += u.gets.to_i |
| 25 | } |
| 26 | # Note: look for vendor:product rather than string to make sure usb-modeswitch works |
| 27 | IO.popen("#{LSUSB} | grep '148e:099a' | wc -l") {|u| |
| 28 | tel_count += u.gets.to_i |
| 29 | } |
| 30 | end |
| 31 | if wimax_count > 0 |
| 32 | require 'omf-resctl/omf_driver/wimaxcu' |
| 33 | MObject.info "Found Intel WiMAX - using wimaxcu interface" |
| 34 | AgentCommands::DEV_MAPPINGS['net/x0'] = WimaxcuDevice.new('net/x0', 'wmx0') |
| 35 | AgentCommands::DEV_MAPPINGS['net/x1'] = WimaxcuDevice.new('net/x1', 'wmx1') |
| 36 | end |
| 37 | if tel_count > 0 |
| 38 | require 'omf-resctl/omf_driver/teltonika' |
| 39 | MObject.info "Found Teltonika WiMAX - using teltonika interface" |
| 40 | AgentCommands::DEV_MAPPINGS['net/t0'] = TeltonikaDevice.new('net/t0', 'tel0') |
| 41 | AgentCommands::DEV_MAPPINGS['net/t1'] = TeltonikaDevice.new('net/t1', 'tel1') |
| 42 | end |
| 43 | end |
| 44 | }}} |
| 45 | |
| 46 | In /usr/share/omf-resctl-5.3/omf-resctl/omf_driver/teltonika.rb: |
| 47 | |
| 48 | {{{ |
| 49 | # == Description |
| 50 | # |
| 51 | # This file defines the class TeltonikaDevice which is a sub-class of |
| 52 | # WimaxcuDevice. |
| 53 | # |
| 54 | require 'omf-resctl/omf_driver/wimax' |
| 55 | require 'net/telnet' |
| 56 | require 'ipaddr' |
| 57 | |
| 58 | class TeltonikaDevice < WimaxDevice |
| 59 | |
| 60 | def unload |
| 61 | # don't unload the kernel module |
| 62 | #super() |
| 63 | debug "Disconnecting WiMAX #{@deviceName}" |
| 64 | dconnect = `#{@wget} --http-user admin --http-password admin -qO - "http://192.168.0.1/cgi/cli?stopSs"` |
| 65 | clear = `#{@wget} --http-user admin --http-password admin -qO - "http://192.168.0.1/cgi/cli?clearScannerChannels"` |
| 66 | if @deldef == false |
| 67 | unroute = `#{@route} delete default gw 192.168.0.1` |
| 68 | else |
| 69 | unroute = `#{@route} del -net #{@routip} netmask #{@fnm} gw 192.168.0.1` |
| 70 | end |
| 71 | |
| 72 | clearIP = deconfig |
| 73 | @mode = @chnnl = @frequency = @bandwidth = @ip = @subnet = @nm = @fnm = @gw = @routip = @oldroutip = @oldnetmask = nil; |
| 74 | @deldef = false |
| 75 | end |
| 76 | |
| 77 | def deconfig |
| 78 | host = Net::Telnet::new("Host" => "192.168.0.1", |
| 79 | "Timeout" => 10, |
| 80 | "Port" => 700, |
| 81 | "Prompt" => /[$%#>] \z/n) |
| 82 | host.login("admin", "admin01") { |c| print c } |
| 83 | host.cmd("export interface=icc0"){ |c| print c } |
| 84 | host.cmd("/etc/udhcpc.script deconfig"){ |c| print c } |
| 85 | host.close |
| 86 | end |
| 87 | |
| 88 | def telnetIfConf |
| 89 | # If values are not set, use defaults |
| 90 | ip = @ip || "0.0.0.0" |
| 91 | netmask = @nm || "255.255.255.0" |
| 92 | subnet = @subnet || 24 |
| 93 | router = @gw || "" |
| 94 | rip = IPAddr.new "#{ip}" |
| 95 | @routip = rip.mask(subnet) |
| 96 | deldef = @deldef |
| 97 | if !deldef and ip != "0.0.0.0" |
| 98 | tlres = "#{@route} del default gw 192.168.0.1; sleep 2; #{@route} add -net #{@routip} netmask #{netmask} gw 192.168.0.1" |
| 99 | @deldef = true |
| 100 | @oldroutip = @routip |
| 101 | @oldnetmask = netmask |
| 102 | end |
| 103 | if netmask != "255.255.255.0" and deldef and ip != "0.0.0.0"#del route and add a new one if the netmask is different than the default |
| 104 | tlres = "#{@route} del -net #{@oldroutip} netmask #{@oldnetmask} gw 192.168.0.1; sleep 2; #{@route} add -net #{@routip} netmask #{netmask} gw 192.168.0.1" |
| 105 | end |
| 106 | |
| 107 | |
| 108 | host = Net::Telnet::new("Host" => "192.168.0.1", |
| 109 | "Timeout" => 10, |
| 110 | "Port" => 700, |
| 111 | "Prompt" => /[$%#>] \z/n) |
| 112 | host.login("admin", "admin01") { |c| print c } |
| 113 | host.cmd("ifconfig icc0 #{ip} netmask #{netmask}") { |c| print c } |
| 114 | host.cmd("export ip=#{ip}"){ |c| print c } |
| 115 | host.cmd("export subnet=#{subnet}"){ |c| print c } |
| 116 | host.cmd("export interface=icc0"){ |c| print c } |
| 117 | host.cmd("export router=#{router}"){ |c| print c } |
| 118 | host.cmd("/etc/udhcpc.script bound"){ |c| print c } |
| 119 | host.close |
| 120 | |
| 121 | @fnm = netmask #final netmask. This will be used in 'unload' |
| 122 | return tlres |
| 123 | end |
| 124 | |
| 125 | def initialize(logicalName, deviceName) |
| 126 | super(logicalName, deviceName) |
| 127 | @mode = @ip = @subnet = @nm = @gw = nil |
| 128 | @ifconfig = '/sbin/ifconfig' |
| 129 | @wget = '/usr/bin/wget' |
| 130 | @route = '/sbin/route' |
| 131 | @deldef = false |
| 132 | IO.popen("#{@ifconfig} #{deviceName} 192.168.0.8 netmask 255.255.255.0") |
| 133 | end |
| 134 | |
| 135 | def buildCmd |
| 136 | return nil if @mode.nil? |
| 137 | cmd = nil |
| 138 | case @mode |
| 139 | when :channel |
| 140 | return nil if @chnnl.nil? |
| 141 | cmd = "#{@wget} --http-user admin --http-password admin -qO - 'http://192.168.0.1/cgi/cli?stopSs'; #{@wget} --http-user admin --http-password admin -qO - 'http://192.168.0.1/cgi/cli?addChannel frequency=#{@frequency} bandwidth=#{@bandwidth}'; #{@wget} --http-user admin --http-password admin -qO - 'http://192.168.0.1/cgi/cli?startSs'; #{@route} add default gw 192.168.0.1" |
| 142 | when :ip |
| 143 | return nil if @ip.nil? |
| 144 | comRes = telnetIfConf |
| 145 | cmd = comRes unless comRes.nil? |
| 146 | when :netmask |
| 147 | return nil if @nm.nil? |
| 148 | comRes = telnetIfConf |
| 149 | cmd = comRes unless comRes.nil? |
| 150 | when :gway |
| 151 | return nil if @gw.nil? |
| 152 | comRes = telnetIfConf |
| 153 | cmd = comRes unless comRes.nil? |
| 154 | else |
| 155 | raise "Unknown mode '#{@mode}'. Should be 'initiate', 'channel', 'mtu' or 'ip'" |
| 156 | end |
| 157 | return cmd |
| 158 | end |
| 159 | # |
| 160 | # Return the specific command required to configure a given property of this |
| 161 | # device. When a property does not exist for this device, check if it does |
| 162 | # for its super-class. |
| 163 | # |
| 164 | # - prop = the property to configure |
| 165 | # - value = the value to configure that property to |
| 166 | # |
| 167 | def getConfigCmd(prop, value) |
| 168 | |
| 169 | @propertyList[prop.to_sym] = value |
| 170 | case prop |
| 171 | when "ip" #ifconfig 192... etc.. |
| 172 | @mode = :ip |
| 173 | @ip = value |
| 174 | return buildCmd |
| 175 | when "netmask" #ifconfig 192... etc.. |
| 176 | @mode = :netmask |
| 177 | @nm = value |
| 178 | @subnet = IPAddr.new("#{@nm}").to_i.to_s(2).count("1") |
| 179 | return buildCmd |
| 180 | when "gway" |
| 181 | @mode = :gway |
| 182 | @gw = value |
| 183 | return buildCmd |
| 184 | when "channel" #assign a frequency and a bandwidth |
| 185 | @mode = :channel |
| 186 | @chnnl = value.split(/, */,2) |
| 187 | @frequency = @chnnl[0] |
| 188 | @bandwidth = @chnnl[1] |
| 189 | return buildCmd |
| 190 | end |
| 191 | super |
| 192 | end |
| 193 | def get_property_value(prop) |
| 194 | # Note: for now we are returning values set by a CONFIGURE command |
| 195 | # when refactoring the device handling scheme, we may want to query |
| 196 | # the system here to find out the real value of the property |
| 197 | result = super(prop) #gets mac and ip addresses from ethernetDevice.rb |
| 198 | result = @propertyList[prop] if !result |
| 199 | return result |
| 200 | end |
| 201 | |
| 202 | end |
| 203 | }}} |
| 204 | |
| 205 | === Patches to omf-expctl === |
| 206 | |
| 207 | In /usr/share/omf-expctl-5.3/omf-expctl/node/nodeSetPath.rb: |
| 208 | |
| 209 | {{{ |
| 210 | class NodeSetPath < MObject |
| 211 | attr_reader :nodeSet, :path |
| 212 | |
| 213 | # List of valid 'PATHS' for a NodeSet |
| 214 | VALID_PATHS_WITH_VALUES = { |
| 215 | "mode=" => %r{net/[ew][01]}, |
| 216 | "profile=" => %r{net/[x][01]}, |
| 217 | "network=" => %r{net/[x][01]}, |
| 218 | "type=" => %r{net/[ew][01]}, |
| 219 | "rts=" => %r{net/[ew][01]}, |
| 220 | "rate=" => %r{net/[ew][01]}, |
| 221 | "essid=" => %r{net/[ew][01]}, |
| 222 | "ip=" => %r{net/[ewxt][01]}, |
| 223 | "channel=" => %r{net/[ewt][01]}, |
| 224 | "tx_power=" => %r{net/[ew][01]}, |
| 225 | "netmask=" => %r{net/[ewxt][01]}, |
| 226 | "mac=" => %r{net/[ewx][01]}, |
| 227 | "mtu=" => %r{net/[ewxt][01]}, |
| 228 | "arp=" => %r{net/[ewx][01]}, |
| 229 | "enforce_link=" => %r{net/[ewx][01]}, |
| 230 | "route" => %r{net/[ewxt][01]}, |
| 231 | "gway" => %r{net/[t][01]}, |
| 232 | "filter" => %r{net/[ewx][01]}, |
| 233 | "net" => // |
| 234 | } |
| 235 | VALID_PATHS_WITHOUT_VALUES = { |
| 236 | "down" => %r{net/[ewxt][01]}, |
| 237 | "up" => %r{net/[ewxt][01]}, |
| 238 | } |
| 239 | VALID_PATHS = VALID_PATHS_WITH_VALUES.merge(VALID_PATHS_WITHOUT_VALUES) |
| 240 | VALID_PATHS_RE = { |
| 241 | /[ewxt][01]/ => /net/ |
| 242 | } |
| 243 | }}} |
| 244 | |
| 245 | === Other required configuration on node === |
| 246 | |
| 247 | Install usb-modeswitch-data and usb-modeswitch (need recent version, >=1.1.9, such as the one provided for oneiric) |
| 248 | |
| 249 | |
| 250 | In /etc/udev/rules.d/70-persistent-net.rules, add the following (so that teltonika device will get the name tel0): |
| 251 | {{{ |
| 252 | KERNEL=="eth*", DRIVERS=="cdc_ether", NAME="tel0" |
| 253 | }}} |
| 254 | |
| 255 | == Usage: Sample Scripts == |
| 256 | === With Dynamic Addressing === |
| 257 | |
| 258 | {{{ |
| 259 | defProperty('runtime',20,"Time in second for the experiment is to run") |
| 260 | defProperty('client',"128.238.66.220","IP address of iperf server") |
| 261 | defProperty('interval', 1, "Interval of Iperf measurements") |
| 262 | defProperty('sender', 'omf.witest.node10', "ID of sender node") |
| 263 | |
| 264 | defGroup('Group', property.sender) do |node| |
| 265 | node.net.t0.channel = "2595000,10" |
| 266 | node.net.t0.mtu=1470 |
| 267 | node.addApplication("test:app:iperf") do |app| |
| 268 | app.setProperty('client', property.client) |
| 269 | app.setProperty('interval', property.interval) |
| 270 | app.setProperty('time', property.runtime) |
| 271 | app.measure('TCP_Info', :samples =>1) |
| 272 | end |
| 273 | end |
| 274 | |
| 275 | onEvent(:ALL_UP_AND_INSTALLED) do |event| |
| 276 | wait 50 |
| 277 | info "This is an iperf experiment using a teltonika modem" |
| 278 | allGroups.startApplications |
| 279 | wait property.runtime |
| 280 | wait 2 |
| 281 | allGroups.stopApplications |
| 282 | wait 2 |
| 283 | Experiment.done |
| 284 | end |
| 285 | }}} |
| 286 | |
| 287 | |
| 288 | === With Static Addressing === |
| 289 | |
| 290 | {{{ |
| 291 | defProperty('runtime',20,"Time in second for the experiment is to run") |
| 292 | defProperty('client',"192.168.10.1","IP address of iperf server") |
| 293 | defProperty('interval', 1, "Interval of Iperf measurements") |
| 294 | defProperty('sender', 'omf.witest.node10', "ID of sender node") |
| 295 | defProperty('sender', 'omf.witest.node1', "ID of sender node") |
| 296 | |
| 297 | defGroup('Group', property.sender) do |node| |
| 298 | node.net.t0.channel = "2595000,10" |
| 299 | node.net.t0.mtu=1470 |
| 300 | node.net.t0.ip = "192.168.10.110" |
| 301 | node.net.t0.netmask = "255.255.255.0" |
| 302 | node.addApplication("test:app:iperf") do |app| |
| 303 | app.setProperty('client', property.client) |
| 304 | app.setProperty('interval', property.interval) |
| 305 | app.setProperty('time', property.runtime) |
| 306 | app.measure('TCP_Info', :samples =>1) |
| 307 | end |
| 308 | end |
| 309 | |
| 310 | defGroup('Group', property.receiver) do |node| |
| 311 | node.net.x0.profile = '51' |
| 312 | node.net.x0.ip = property.client |
| 313 | node.net.x0.netmask = "255.255.255.0" |
| 314 | node.addApplication("test:app:iperf") do |app| |
| 315 | app.setProperty('server', true) |
| 316 | app.setProperty('interval', property.interval) |
| 317 | app.measure('TCP_Info', :samples =>1) |
| 318 | end |
| 319 | end |
| 320 | |
| 321 | |
| 322 | onEvent(:ALL_UP_AND_INSTALLED) do |event| |
| 323 | wait 50 |
| 324 | info "This is an iperf experiment using a teltonika modem" |
| 325 | allGroups.startApplications |
| 326 | wait property.runtime |
| 327 | wait 2 |
| 328 | allGroups.stopApplications |
| 329 | wait 2 |
| 330 | Experiment.done |
| 331 | end |
| 332 | }}} |