Internal/OpenFlow/XORP: lb9a.rb

File lb9a.rb, 6.5 KB (added by akoshibe, 13 years ago)

companion library to network configuration script

Line 
1#!/usr/bin/ruby1.8 -w
2
3###
4# lb9a.rb
5# libraries that behave as a "wrapper" to XorPlus on the LB9A (Quanta) switch.
6# 8/28/11 - added dry-run capability
7# 8/29/11 - string/array processing functions as class extensions; some cleanup of Session class
8###
9
10require 'net/telnet'
11require 'singleton'
12require 'logger'
13
14LOG = Logger.new(STDOUT)
15
16$portrng = (1..48) #48 GBe ports on switch
17$vlanrng = (1..1024) #up to 1024 VLAN IDs per switch
18
19
20# Deals with telnet session/CLI interactions
21class Session
22
23 attr_reader :hname, :port, :dbg
24
25 def initialize(hostname, port = 23, debug = false, dryrun = false)
26 @hname = hostname
27 @port = port
28 @dbg = debug #verbose mode
29 @drun = dryrun #do not actually send command via serial
30 end
31
32 # init - initialize telnet session
33 def init()
34 begin
35 if @dbg then LOG.level = Logger::DEBUG else LOG.level = Logger::INFO end
36 unless @drun
37 @conn = Net::Telnet::new( "Host" => @hname, "Port" => @port )
38 @conn.cmd("configure") #enter config mode
39 LOG.debug("Session.init: initializing telnet session")
40 else
41 @conn = nil
42 LOG.debug("Session.init: initializing dry-run")
43 end
44 rescue Exception => e
45 LOG.fatal("Session.init: #{e.class}, #{e.message}")
46 raise
47 end
48 end
49
50private
51 # rncmd - send command for each port in iflist, replacing '##' in command with proper port number
52 # cannot take commands with a scrolling output buffer (To-do)
53 def rncmd(command, iflist)
54 begin
55 LOG.debug("Session.range_cmd: command: #{command}")
56 LOG.debug("Session.range_cmd: iflist: #{iflist}")
57 iflist.map do |iface|
58 out = command.gsub(/##/, iface.to_s)
59 unless @drun then ret = @conn.cmd(out) else puts "#{out}" end
60 LOG.debug("Session.range_cmd: command: #{out}")
61 LOG.debug("Session.range_cmd: return: #{ret}")
62 end
63 rescue Exception => e
64 LOG.fatal("Session.rncmd: #{e.class} #{e.message}")
65 raise
66 end
67 end
68
69public
70 # set_iface - sets management interface address
71 def set_iface(iface, addr, del = false)
72 begin
73 if del then dflg = "delete " else dflg = "set " end
74 if (iface.match(/eth(0|1)/))&&(addr.match(/\d+\.\d+\.\d+\.\d+\/\d+/))
75 out = "#{dflg}interface management-ethernet #{iface} address #{addr}"
76 unless @drun
77 @conn.cmd(out) do |ret|
78 LOG.debug("Session.set_iface: return: #{ret}")
79 end
80 else
81 puts "#{out}"
82 end
83 return true
84 else
85 puts "\tiface [eth0|eth1] [IP addr/mask]"
86 return false
87 end
88 rescue Exception => e
89 LOG.fatal("Session.set_iface: #{e.class} #{e.message}")
90 raise
91 end
92 end
93
94 # set_vlan - sets up a new VLAN
95 # To-do : add multiple VLAN delete facility
96 def set_vlan(id, del = false)
97 begin
98 if del then dflg = "delete " else dflg = "set " end
99 if ($vlanrng.member? id)
100 out = "#{dflg}vlans vlan-id #{id}"
101 unless @drun
102 @conn.cmd(out) do |ret|
103 LOG.debug("Session.set_vlan: return: #{ret}")
104 end
105 else
106 puts "#{out}"
107 end
108 return true
109 else
110 puts "\tvlan [VLAN ID]"
111 puts "\tVLAN ID must be value between [1..1024]"
112 return false
113 end
114 rescue Exception => e
115 LOG.fatal("Session.set_vlan: #{e.class} #{e.message}")
116 raise
117 end
118 end
119
120 # set_ports - configurations of various port properties
121 def set_ports(ctxt, iflist)
122 @vlans = []
123 @tflag = false
124 @cmd = ' '
125
126 args = ctxt.split(' ') # args - array containing components of command
127 LOG.debug("Session.set_ports: args: #{args[0]},#{args[1]}")
128
129 # assign the proper command to @cmd based on input
130 case args[0]
131 when "delete"
132 @cmd = "delete interface gigabit-ethernet ge-1/1/## family ethernet-switching"
133 when "vlan"
134 if ($vlanrng.member? args[1].to_i)
135 @cmd = "set interface gigabit-ethernet ge-1/1/## family ethernet-switching native-vlan-id #{args[1]}"
136 else
137 puts "\tVLAN ID must be value between [1..1024]"
138 end
139 when "mode"
140 if args[1] == "access"
141 @cmd = "set interface gigabit-ethernet ge-1/1/## family ethernet-switching port-mode access"
142 elsif args[1] == "trunk"
143 @tflag = true
144 print "Member VLANs (X,Y,Z...): "
145 @vlans = gets.chomp.split(',')
146
147 LOG.debug("Session.set_ports: vlans #{@vlans.class}: #{@vlans}")
148 @cmd = "set interface gigabit-ethernet ge-1/1/## family ethernet-switching port-mode trunk"
149 else
150 puts "\t#{args[1]}: Unknown port mode"
151 end
152 else
153 puts "\t#{ctxt}: Unknown command"
154 end
155 LOG.debug("Session.set_ports: @cmd: #{@cmd}")
156
157 # invoke rncmd with proper command
158 if @tflag
159 for i in @vlans
160 rncmd("set interface gigabit-ethernet ge-1/1/## family ethernet-switching vlan members #{i}",iflist)
161 end
162 end
163
164 unless @cmd.empty?
165 rncmd(@cmd, iflist)
166 return true
167 else
168 return false
169 end
170 end
171
172 # commit - saves configurations
173 def commit()
174 puts "saving settings (this may take a while)..."
175 unless @drun then @conn.cmd("commit") else puts "commit" end
176 sleep 5
177 unless @drun then @conn.cmd("save running-to-startup") else puts "save running-to-startup" end
178 end
179
180 # logoff - close connection after exiting enabled mode
181 def logoff(saved)
182 begin
183 unless @drun
184 unless saved then @conn.cmd("exit discard") end
185 @conn.cmd("exit")
186 @conn.close
187 else
188 puts "exit"
189 end
190 rescue Exception => e
191 LOG.fatal("Session.logoff: #{e.class} #{e.message}")
192 raise
193 end
194 end
195
196end
197
198# to_rng - flattens nested arrays with ranges e.g. [3..5,20,[13..15]] -> [3,4,5,20,13,14,15]
199class Array
200 def to_rng()
201 self.map do |x|
202 if x.is_a? Range
203 x.to_a
204 elsif x.is_a? Fixnum
205 x
206 elsif x.is_a? Array
207 x.to_rng
208 else
209 raise "Array.to_rng:#{x}:not Fixnum or Range"
210 end
211 end.flatten
212 end
213end
214
215# to_ra - convers string of numbers and rangesto array with Range and Fixnum objects e.g. "1..3,5" -> [1..3,5]
216class String
217 def to_ra()
218 self.split(',').map do |x|
219 if x.length <= 2
220 Integer(x)
221 elsif x.include? ".."
222 ends = x.split("..").map{|i| Integer(i)}
223 x = ends[0]..ends[1]
224 else
225 raise "Sring.to_ra:#{x}:Invalid range string format"
226 end
227 end
228 end
229end
230
231=begin
232 # the set_* family of commands without subcontexts follow this pattern...
233 # If functions can be used as input, this is a legitimate template.
234 def set_ptype(name, matchstr, cmd, syntax)
235 begin
236 if (matchstr)
237 out = cmd
238 unless @drun
239 @conn.cmd(out) do |ret|
240 LOG.debug("Session.#{name}: return: #{ret}")
241 end
242 else
243 puts "#{out}"
244 end
245 else
246 puts "#{syntax}"
247 end
248 rescue Exception => e
249 LOG.fatal("Session.set_iface: #{e.class} #{e.message}")
250 raise
251 end
252 end
253=end