r2083 + setSwitchVLANs(): use oneLiner(), handle "C" messages
[racktables] / gateways / switchvlans / main
1 #!/bin/sh
2
3 # This is a RackTables gateway for changing switch ports membership
4 # across VLANs. It works accordingly to the gateway protocol described
5 # in gateways.php and accepts the following commands on its stdin:
6 #
7 # * connect: connect to a switch, fetch all necessary data, store and
8 # disconnect
9 #
10 # * listvlans: list all VLANs found on the switch, propably filtering
11 # out those administratively prohibited. Only the VLANs from this
12 # list will be allowed as new destination for 'set' command.
13 #
14 # * listports: list all ports on the switch and their current status.
15 # Untagged (switchport mode access) ports will be shown with their
16 # VLAN ID and tagged ports will be shown as 'trunk' regardless of
17 # how many VLANs they are members of.
18 #
19 # * listmacs: output unsorted list of all dynamically learned MAC
20 # addresses present on the switch
21 #
22
23 endpoint=
24 hw=
25 sw=
26 user=
27 handler=
28 CONNECTED=0
29 MYDIR=`dirname $0`
30
31 decode_error()
32 {
33 case "$1" in
34 0)
35 echo -n 'success'
36 ;;
37 1)
38 echo -n 'internal error 1'
39 ;;
40 2)
41 echo -n 'internal error 2'
42 ;;
43 3)
44 echo -n 'password not found'
45 ;;
46 4)
47 echo -n 'invalid password'
48 ;;
49 5)
50 echo -n 'cannot create temporary files'
51 ;;
52 6)
53 echo -n 'invalid command'
54 ;;
55 7)
56 echo -n 'unknown host OS'
57 ;;
58 *)
59 echo -n 'unknown error'
60 ;;
61 esac
62 }
63
64 # Not only connect, but gather all the data at once and remember the context.
65 do_connect()
66 {
67 endpoint=`echo $args | cut -s -d' ' -f1`
68 hw=`echo $args | cut -s -d' ' -f2`
69 sw=`echo $args | cut -s -d' ' -f3`
70 user=`echo $args | cut -s -d' ' -f4`
71 # sanity checks
72 if [ -z "$endpoint" -o -z "$hw" -o -z "$sw" -o -z "$user" ]; then
73 echo 'ERR!too few arguments to connect'
74 return
75 fi
76 case "$sw" in
77 Cisco+IOS+12.0|Cisco+IOS+12.1|Cisco+IOS+12.2)
78 handler=cisco
79 ;;
80 *)
81 echo "ERR!Don't know how to handle $sw on $endpoint"
82 return
83 ;;
84 esac
85
86 # prepare temp files
87 PORTINFO=`mktemp /tmp/racktables.XXXX`
88 if ! [ -f "$PORTINFO" ]; then
89 echo 'ERR!could not create portinfo tmpfile'
90 return
91 fi
92 VLANINFO=`mktemp /tmp/racktables.XXXX`
93 if ! [ -f "$VLANINFO" ]; then
94 echo 'ERR!could not create vlaninfo tmpfile'
95 rm -f "$PORTINFO"
96 return
97 fi
98 MACINFO=`mktemp /tmp/racktables.XXXX`
99 if ! [ -f "$MACINFO" ]; then
100 echo 'ERR!could not create MACinfo tmpfile'
101 rm -f "$PORTINFO" "$VLANINFO"
102 return
103 fi
104
105 # get the data
106 "$MYDIR/$handler.connector" $endpoint $hw $sw fetch "$VLANINFO" "$PORTINFO" "$MACINFO"
107 ret=$?
108 if [ $ret = 0 ]; then
109 CONNECTED=1
110 echo "OK!connected to $endpoint";
111 else
112 echo -n "ERR!Cannot connect to $endpoint ("
113 decode_error $ret
114 echo ')'
115 fi
116 }
117
118 do_listfile()
119 {
120 local F=$1
121 if ! [ -f "$F" ]; then
122 echo "ERR!Lost temp file '$F' on the way"
123 return
124 fi
125 echo -n 'OK!'
126 local semicolon=''
127 # tr might do the work, but use our chance to perform filtering once more
128 cat "$F" | while read line; do
129 [ "$line" = "" ] && continue
130 echo -n "$semicolon$line"
131 semicolon=';'
132 done
133 echo
134 }
135
136 do_set()
137 {
138 # sanity checks
139 local setline=$1
140 if [ -z "$setline" ]; then
141 echo 'ERR!missing set argument'
142 return
143 fi
144 local REQUESTS=`mktemp /tmp/racktables.XXXX`
145 local REPLIES=`mktemp /tmp/racktables.XXXX`
146 echo $1 | tr ';' '\n' | while read setexpr; do
147 portname=`echo $setexpr | cut -s -d'=' -f1`
148 newvlan=`echo $setexpr | cut -s -d'=' -f2`
149 curvlan=`egrep "^$portname=" $PORTINFO | cut -s -d'=' -f2 | cut -d',' -f2`
150 if [ -z "$curvlan" ]; then
151 echo "C!167!$portname" >> "$REPLIES"
152 continue
153 fi
154 if [ "$curvlan" = "trunk" ]; then
155 echo "C!168!$portname" >> "$REPLIES"
156 continue
157 fi
158 [ "$curvlan" = "$newvlan" ] && continue
159 echo "$portname $newvlan" >> "$REQUESTS"
160 cmembers=`grep -c ",$newvlan$" "$PORTINFO"`
161 if [ "$cmembers" = "0" -a $newvlan -lt 4096 ]; then
162 echo "C!203!$portname!$newvlan" >> "$REPLIES"
163 echo "C!204" >> "$REPLIES"
164 fi
165 done
166 nr=`egrep -c '^C!1.' "$REPLIES"`
167 if [ "$nr" -ge 1 ]; then
168 echo "C!205!$nr" >> "$REPLIES"
169 fi
170
171 nq=`egrep -c '^.' "$REQUESTS"`
172 if [ "$nq" -ge 1 ]; then
173 # Go!
174 "$MYDIR/$handler.connector" $endpoint $hw $sw push "$REQUESTS" "$REPLIES" "$MACINFO"
175 local ret=$?
176
177 if [ $ret != 0 ]; then
178 echo "C!169!$endpoint!$ret"
179 return
180 fi
181 echo "C!63!$nq" >> "$REPLIES"
182 fi
183 echo -n 'OK!'
184 local SEMICOLON=
185 while read reply; do
186 echo -n $SEMICOLON$reply
187 SEMICOLON=';'
188 timestamp=`date '+%Y-%m-%d %H:%M:%S'`
189 [ -w "$MYDIR/changes.log" ] && echo "$timestamp $user@$endpoint $reply" >> "$MYDIR/changes.log"
190 done < "$REPLIES"
191 echo
192 rm -f "$REQUESTS" "$REPLIES"
193 }
194
195 # main loop
196 while read cmd args; do
197 case $cmd in
198 connect)
199 if [ $CONNECTED = 1 ]; then
200 echo 'ERR!Already connected'
201 else
202 do_connect $args
203 fi
204 ;;
205 listvlans)
206 if [ $CONNECTED = 1 ]; then
207 do_listfile "$VLANINFO"
208 else
209 echo 'ERR!Not connected'
210 fi
211 ;;
212 listports)
213 if [ $CONNECTED = 1 ]; then
214 do_listfile "$PORTINFO"
215 else
216 echo 'ERR!Not connected'
217 fi
218 ;;
219 listmacs)
220 if [ $CONNECTED = 1 ]; then
221 do_listfile "$MACINFO"
222 else
223 echo 'ERR!Not connected'
224 fi
225 ;;
226 set)
227 if [ $CONNECTED = 1 ]; then
228 do_set $args
229 else
230 echo 'ERR!Not connected'
231 fi
232 ;;
233 *)
234 echo "ERR!unknown command $cmd"
235 esac
236 done
237
238 rm -f "$PORTINFO" "$VLANINFO" "$MACINFO"
239 exit 0