5e82a4df63b2ed6e2c2d94411a7ac7a009015cee
[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 . "$MYDIR/../lib.sh"
31
32 # Not only connect, but gather all the data at once and remember the context.
33 do_connect()
34 {
35 endpoint=`echo $args | cut -s -d' ' -f1`
36 hw=`echo $args | cut -s -d' ' -f2`
37 sw=`echo $args | cut -s -d' ' -f3`
38 user=`echo $args | cut -s -d' ' -f4`
39 # sanity checks
40 if [ -z "$endpoint" -o -z "$hw" -o -z "$sw" -o -z "$user" ]; then
41 echo 'ERR!too few arguments to connect'
42 return
43 fi
44 case "$sw" in
45 Cisco+IOS+12.0|Cisco+IOS+12.1|Cisco+IOS+12.2)
46 handler=cisco
47 ;;
48 *)
49 echo "ERR!Don't know how to handle $sw on $endpoint"
50 return
51 ;;
52 esac
53
54 # authorize user, look for "connect" privilege
55 if ! authorized $endpoint $user connect; then
56 echo "ERR!User $user is not authorized to connect to $endpoint"
57 return
58 fi
59
60 # prepare temp files
61 PORTINFO=`mktemp /tmp/racktables.XXXX`
62 if ! [ -f "$PORTINFO" ]; then
63 echo 'ERR!could not create portinfo tmpfile'
64 return
65 fi
66 VLANINFO=`mktemp /tmp/racktables.XXXX`
67 if ! [ -f "$VLANINFO" ]; then
68 echo 'ERR!could not create vlaninfo tmpfile'
69 rm -f "$PORTINFO"
70 return
71 fi
72 MACINFO=`mktemp /tmp/racktables.XXXX`
73 if ! [ -f "$MACINFO" ]; then
74 echo 'ERR!could not create MACinfo tmpfile'
75 rm -f "$PORTINFO" "$VLANINFO"
76 return
77 fi
78
79 # get the data
80 "$MYDIR/$handler.connector" $endpoint $hw $sw fetch "$VLANINFO" "$PORTINFO" "$MACINFO"
81 ret=$?
82 if [ $ret = 0 ]; then
83 CONNECTED=1
84 echo "OK!connected to $endpoint";
85 else
86 echo "ERR!Cannot connect to $endpoint, connector returned code $ret"
87 fi
88 }
89
90 do_listfile()
91 {
92 local F=$1
93 if ! [ -f "$F" ]; then
94 echo "ERR!Lost temp file '$F' on the way"
95 return
96 fi
97 echo -n 'OK!'
98 local semicolon=''
99 # tr might do the work, but use our chance to perform filtering once more
100 cat "$F" | while read line; do
101 [ "$line" = "" ] && continue
102 echo -n "$semicolon$line"
103 semicolon=';'
104 done
105 echo
106 }
107
108 do_set()
109 {
110 # sanity checks
111 local setline=$1
112 if [ -z "$setline" ]; then
113 echo 'ERR!missing set argument'
114 return
115 fi
116 local REQUESTS=`mktemp /tmp/racktables.XXXX`
117 local REPLIES=`mktemp /tmp/racktables.XXXX`
118 echo $1 | tr ';' '\n' | while read setexpr; do
119 portname=`echo $setexpr | cut -s -d'=' -f1`
120 newvlan=`echo $setexpr | cut -s -d'=' -f2`
121 curvlan=`egrep "^$portname=" $PORTINFO | cut -s -d'=' -f2 | cut -d',' -f2`
122 if [ -z "$curvlan" ]; then
123 echo "E!Could not find port $portname" >> "$REPLIES"
124 continue
125 fi
126 if [ "$curvlan" = "trunk" ]; then
127 echo "E!Port $portname is a trunk" >> "$REPLIES"
128 continue
129 fi
130 [ "$curvlan" = "$newvlan" ] && continue
131 # Authorize user for each change.
132 if ! authorized $endpoint $user change $curvlan $newvlan; then
133 echo "E!User $user is not authorized to assign port $portname@$endpoint from VLAN $curvlan to VLAN $newvlan" >> "$REPLIES"
134 continue
135 fi
136 echo "$portname $newvlan" >> "$REQUESTS"
137 cmembers=`grep -c ",$newvlan$" "$PORTINFO"`
138 if [ "$cmembers" = "0" -a $newvlan -lt 4096 ]; then
139 echo "W!Port $portname seems to be the first in VLAN $newvlan at this switch." >> "$REPLIES"
140 echo "W!Check uplink/downlink configuration for proper operation." >> "$REPLIES"
141 fi
142 done
143 nr=`egrep -c '^E!.' "$REPLIES"`
144 if [ "$nr" -ge 1 ]; then
145 echo "W!$nr change request(s) have been ignored" >> "$REPLIES"
146 fi
147
148 nq=`egrep -c '^.' "$REQUESTS"`
149 if [ "$nq" -ge 1 ]; then
150 # Go!
151 "$MYDIR/$handler.connector" $endpoint $hw $sw push "$REQUESTS" "$REPLIES" "$MACINFO"
152 local ret=$?
153
154 if [ $ret != 0 ]; then
155 echo "ERR!Failed to configure $endpoint, connector returned code $ret"
156 return
157 fi
158 echo "I!$nq change request(s) have been processed" >> "$REPLIES"
159 fi
160 echo -n 'OK!'
161 local SEMICOLON=
162 while read reply; do
163 echo -n $SEMICOLON$reply
164 SEMICOLON=';'
165 timestamp=`date '+%Y-%m-%d %H:%M:%S'`
166 [ -w "$MYDIR/changes.log" ] && echo "$timestamp $user@$endpoint $reply" >> "$MYDIR/changes.log"
167 done < "$REPLIES"
168 echo
169 rm -f "$REQUESTS" "$REPLIES"
170 }
171
172 # main loop
173 while read cmd args; do
174 case $cmd in
175 connect)
176 if [ $CONNECTED = 1 ]; then
177 echo 'ERR!Already connected'
178 else
179 do_connect $args
180 fi
181 ;;
182 listvlans)
183 if [ $CONNECTED = 1 ]; then
184 do_listfile "$VLANINFO"
185 else
186 echo 'ERR!Not connected'
187 fi
188 ;;
189 listports)
190 if [ $CONNECTED = 1 ]; then
191 do_listfile "$PORTINFO"
192 else
193 echo 'ERR!Not connected'
194 fi
195 ;;
196 listmacs)
197 if [ $CONNECTED = 1 ]; then
198 do_listfile "$MACINFO"
199 else
200 echo 'ERR!Not connected'
201 fi
202 ;;
203 set)
204 if [ $CONNECTED = 1 ]; then
205 do_set $args
206 else
207 echo 'ERR!Not connected'
208 fi
209 ;;
210 *)
211 echo "ERR!unknown command $cmd"
212 esac
213 done
214
215 rm -f "$PORTINFO" "$VLANINFO" "$MACINFO"
216 exit 0