/[shmookey]/portconf/pycisco/pycisco.py


UCC Code Repository

Contents of /portconf/pycisco/pycisco.py

Parent Directory Parent Directory | Revision Log Revision Log


Revision 112 - (show annotations) (download) (as text)
Wed May 28 08:40:26 2008 UTC (13 years, 6 months ago) by shmookey
File MIME type: text/x-python
File size: 11842 byte(s)
database integration COMPLETE! FUCK YES! now I can get to work on NEW AND EXCITING FEATURES! :D

1 ''' pycisco - a library for interacting with Cisco devices.
2 Written by Luke Williams <[email protected]>
3 Requires Python 2.4 and pexpect.
4
5 Licensed under an MIT-style license: see LICENSE file for details.
6 '''
7
8 from pexpect import *
9 import re
10 from string import ascii_letters, digits, maketrans
11
12 ''' For easier understanding, I have categorized the functions
13 here into three classes:
14 Interface functions - useful to people using pycisco as a module
15 Helper functions - used internally to collect raw data
16 Utility functions - formatting and processing, may have uses externally
17 '''
18
19
20 #
21 # Exceptions
22 #
23
24 class NoSuchSwitchException (Exception): pass
25 class TrunkException (Exception): pass
26 class VLANDisallowedException (Exception): pass
27 class NotConnectedException (Exception): pass
28
29 class NetworkDevice:
30
31 def __init__ (self):
32 self.hostname = ""
33 self.username = ""
34 self.password = ""
35 self.enable = ""
36 self.connected = False
37 self.session = None
38
39 def IsConnected (self): return self.connected
40
41 #
42 # CreateSession (switchNumber : integer)
43 # Helper function.
44 #
45 # Logs on to a switch with telnet and returns a pexpect object containing
46 # the telnet session.
47 # Uses an index in the switches table rather than a string representation
48 # of a switch name so that the string provided by the user will never be
49 # accidentally executed or passed around to places we don't want it to go.
50 #
51
52 def Connect (self, hostname, username, password, enable = ""):
53 self.hostname = hostname
54 self.username = username
55 self.password = password
56 self.enable = enable
57
58 session = spawn ("telnet " + self.hostname)
59 session.setwinsize (480,640)
60 session.setecho (False)
61 session.expect ("Username: ")
62 session.sendline (self.username)
63 session.expect ("Password: ")
64 session.sendline (self.password)
65 session.expect ("[a-z0-9.-]*>")
66 session.sendline ("enable")
67 session.expect ("Password: ")
68 session.sendline (self.enable)
69 session.expect ("[a-z0-9.-]*#")
70 self.session = session
71 self.connected = True
72 return True
73
74 #
75 # GetPortSummary (switchName : string)
76 # Interface function.
77 #
78 # Returns a list of dictionaries containing information about the
79 # ports on the switch provided as an argument, gathered from
80 # 'show interface status'
81 #
82 # The keys in the dictionary are always strings:
83 # name - the name of the port in shortened form.
84 # description - the description on the port, truncated at 19 characters.
85 # connectstate - the current connected state of the port.
86 # vlan - the VLAN number the port is configured to accces, or "Trunk".
87 # duplex - the duplex mode of the port if forced, otherwise "auto".
88 # speed - the speed the port is running at if forced, otherwise "auto".
89 # media - the media type of the port.
90 # portfast - always "Check" in this (telnet) implementation.
91 # errors - always "Check" in this (telnet) implementation.
92 #
93
94 def GetPortSummary (self):
95 if not self.connected: raise NotConnectedException ()
96
97 self.session.sendline ("show interface status")
98 self.session.expect ("[a-z0-9.-]*#")
99 raw_result = self.session.before
100
101 # This next line chops off the first three lines, which are invariably
102 # whitespace and headings.
103 result = "\n".join (raw_result.split ("\r\n")[3:-1])
104
105 lines = result.split ("\n")
106 portSummary = []
107
108 # Parse the output of the port summary. Still easier than SNMP!
109 for line in lines:
110 # Create a new dictionary for the port description and split it up into words.
111 portDetails = dict ()
112 words = line.split ()
113
114 portDetails ["name"] = words[0]
115 # Some fields can have spaces in them, so we should deal with this as such.
116 if words[1] == "connected" or words[1] == "notconnect" or words[1] == "err-disabled" or words[1] == "disabled":
117 words.insert (1, "")
118 while not (words[2] == "connected" or words[2] == "notconnect" or words[2] == "err-disabled" or words[2] == "disabled"):
119 words[1] += " " + words[2]
120 del words[2]
121 portDetails ["description"] = words[1]
122 portDetails ["connectstate"] = words[2]
123 portDetails ["vlan"] = words[3]
124 portDetails ["duplex"] = words[4]
125 portDetails ["speed"] = words[5]
126 # Word 6 onwards has to be the media type.
127 portDetails ["media"] = " ".join (words[6:])
128
129 # There are some details not in the port summary that we want.
130 # It would take too long to fetch them all before the page renders, so we leave it as "Check" until the user asks.
131 portDetails ["portfast"] = "Check"
132 portDetails ["errors"] = "Check"
133
134 portSummary.append (portDetails)
135 self.session.sendline ("")
136 return portSummary
137
138 #
139 # SanitisePortName (portName : string)
140 # Utility function.
141 #
142 # Returns a copy of the provided string with unwanted characters removed, so
143 # that it can be safely passed to other functions.
144 #
145
146 def SanitisePortName (self, portName):
147 # Check the portname with a regex. Break it up into parts and put it back together.
148 p = re.compile ("([A-Z][a-z])((?:[0-9]/){0,1})([0-9]/)([0-9]{1,2})")
149 m = p.match (portName)
150 sanitisedName = "".join (m.groups())
151 return sanitisedName
152
153 #
154 # CheckForTrunk (switchSession : integer, portNumber : string)
155 # Helper function.
156 #
157 # Returns True the provided port on the switch is a trunk and False otherwise.
158 # Does not do sanity checking on the port name, it is expected that the
159 # calling function will do this. The session must be in enable mode.
160 #
161
162 def CheckForTrunk (self, portNumber):
163 if not self.connected: raise NotConnectedException ()
164
165 self.session.sendline ("show running-config interface " + portNumber)
166 self.session.expect ("[a-z0-9.-]*#")
167 if self.session.before.find ("trunk") != -1:
168 return True
169 return False
170
171 #
172 # CheckForShutdown (switchSession, portNumber)
173 # Helper function.
174 #
175 # Returns True if the provided port on the switch is shutdown and false otherwise.
176 # Does not do sanity checking on the port name, it is expected that the
177 # calling function will do this. The session must be in enable mode.
178 #
179
180 def CheckForShutdown (self, portNumber):
181 if not self.connected: raise NotConnectedException ()
182
183 self.session.sendline ("show running-config interface " + portNumber)
184 self.session.expect ("[a-z0-9.-]*#")
185 if self.session.before.find ("shutdown") != -1:
186 return True
187 return False
188
189 #
190 # GetErrors (switchName : string, portNumber : string)
191 # Interface function.
192 #
193 # Returns a string containing information about the errors deteced on the port.
194 #
195
196 def GetErrors (self, portNumber):
197 if not self.connected: raise NotConnectedException ()
198
199 self.session.sendline ("show interface " + portNumber + " | inc (error)|(failure)")
200 self.session.expect ("[a-z0-9.-]*#")
201 raw_result = "\n".join (self.session.before.split ("\r\n")[1:])
202 result = ",".join (raw_result.translate(maketrans("",""), "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz,").split ())
203 return raw_result
204
205 #
206 # SetDescription (switchName : string, portNumber : string, description : string)
207 # Interface function.
208 #
209 # Sets the description associated with the port, after stripping out unwanted characters.
210 # Returns the sanitized description.
211 #
212
213 def SetDescription (self, portNumber, description):
214 if not self.connected: raise NotConnectedException ()
215
216 safePortNumber = self.SanitisePortName (portNumber)
217 safeDescription = "".join (self.SanitiseDescription (description))
218 self.session.sendline ("configure terminal")
219 self.session.expect ("[a-z0-9.-]*\(config\)#")
220 self.session.sendline ("interface " + safePortNumber)
221 self.session.expect ("[a-z0-9.-]*\(config-if\)#")
222 self.session.sendline ("description " + safeDescription)
223 self.session.expect ("[a-z0-9.-]*\(config-if\)#")
224
225 return safeDescription
226
227 #
228 # SetVLAN (switchName : string, portNumber : string, vlan : string)
229 # Interface function.
230 #
231 # Changes the access VLAN on the port provided. Returns the VLAN ID if successful.
232 #
233
234 def SetVLAN (self, portNumber, vlan):
235 if not self.connected: raise NotConnectedException ()
236
237 safePortNumber = self.SanitisePortName (portNumber)
238
239 self.session.sendline ("configure terminal")
240 self.session.expect ("[a-z0-9.-]*\(config\)#")
241 self.session.sendline ("interface " + safePortNumber)
242 self.session.expect ("[a-z0-9.-]*\(config-if\)#")
243 self.session.sendline ("switchport access vlan " + vlan)
244 self.session.expect ("[a-z0-9.-]*\(config-if\)#")
245
246 return vlan
247
248 #
249 # CheckForPortfast (switchName : string, portNumber : string [, switchSession : integer])
250 # Interface function / Helper function.
251 #
252 # Returns True if the provided port has portfasting enabled or False otherwise. Can take
253 # a switch name or an existing switch session.
254 #
255
256 def CheckForPortfast (self, portNumber):
257 if not self.connected: raise NotConnectedException ()
258
259 safePortNumber = self.SanitisePortName (portNumber)
260
261 self.session.sendline ("show running-config interface " + safePortNumber)
262 self.session.expect ("[a-z0-9.-]*#")
263 if not self.session.before.find ("portfast") == -1:
264 return True
265 return False
266
267 #
268 # GetConnectedStatus (switchName : string, portNumber : string [, switchSession : integer])
269 # Interface function / Helper function.
270 #
271 # Returns the "connected" state of the port provided. Can take a switch name or and existing
272 # switch session.
273 #
274
275 def GetConnectedStatus (self, portNumber):
276 if not self.connected: raise NotConnectedException ()
277
278 safePortNumber = self.SanitisePortName (portNumber)
279
280 self.session.sendline ("do show interface status | include " + safePortNumber)
281 self.session.expect ("[a-z0-9.-]*#")
282 result = self.session.before
283 if not result.find ("connected") == -1:
284 return "connected"
285 if not result.find ("notconnect") == -1:
286 return "notconnect"
287 if not result.find ("disabled") == -1:
288 return "disabled"
289 if not result.find ("err-disable") == -1:
290 return "err-disabled"
291 return "Mystery!"
292
293 #
294 # SanitiseDescription (desc : string)
295 # Utility function.
296 #
297 # Generator to remove unwanted characters from a port description.
298 #
299
300 def SanitiseDescription (self, desc):
301 for c in desc:
302 if c in (ascii_letters + digits + " \/()"):
303 yield c
304
305 #
306 # TogglePortfast (switchName : string, portNumber : string)
307 # Interface function.
308 #
309 # Toggles the portfast option on and off for the given port. Returns
310 # "On" or "Off".
311 #
312
313 def TogglePortfast (self, portNumber):
314 if not self.connected: raise NotConnectedException ()
315
316 safePortNumber = self.SanitisePortName (portNumber)
317 currentValue = self.CheckForPortfast (switchName, portNumber, ss)
318
319 self.session.sendline ("configure terminal")
320 self.session.expect ("[a-z0-9.-]*\(config\)#")
321 self.session.sendline ("interface " + safePortNumber)
322 self.session.expect ("[a-z0-9.-]*\(config-if\)#")
323 self.session.sendline ( ((not currentValue) and [""] or ["no"])[0] + " spanning-tree portfast")
324 self.session.expect ("[a-z0-9.-]*\(config-if\)#")
325 self.session.sendline ("exit\nexit\n\exit")
326 return (not currentValue) and "On" or "Off"
327
328 #
329 # ToggleShutdown (switchName : string, portNumber : string)
330 # Interface function.
331 #
332 # Shuts down and enables the given port. Returns the "connected" status of the
333 # port after this operation.
334 #
335
336 def ToggleShutdown (self, portNumber):
337 if not self.connected: raise NotConnectedException ()
338
339 safePortNumber = self.SanitisePortName (portNumber)
340
341 currentValue = self.CheckForShutdown (safePortNumber)
342
343 self.session.sendline ("configure terminal")
344 self.session.expect ("[a-z0-9.-]*\(config\)#")
345 self.session.sendline ("interface " + safePortNumber)
346 self.session.expect ("[a-z0-9.-]*\(config-if\)#")
347 self.session.sendline ( ((not currentValue) and [""] or ["no"])[0] + " shutdown")
348 self.session.expect ("[a-z0-9.-]*\(config-if\)#")
349 return self.GetConnectedStatus (portNumber)
350 self.session.sendline ("exit\nexit\n")

Properties

Name Value
svn:executable *

Managed by UCC Webmasters ViewVC Help
Powered by ViewVC 1.1.26