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