/[gli]/trunk/src/GLIUtility.py
Gentoo

Contents of /trunk/src/GLIUtility.py

Parent Directory Parent Directory | Revision Log Revision Log


Revision 701 - (show annotations) (download) (as text)
Tue Jun 21 16:22:26 2005 UTC (13 years, 6 months ago) by agaffney
File MIME type: text/x-python
File size: 17126 byte(s)
Fix undefined variable 'output' issue in spawn()

1 """
2 Gentoo Linux Installer
3 Copyright 2005 Gentoo Technologies Inc.
4
5 The GLIUtility module contians all utility functions used throughout GLI.
6 """
7
8 import string, os, re, shutil, sys, random, commands, crypt
9 from GLIException import *
10
11 ##
12 # Check to see if a string is actually a string, and if it is not null. Returns bool.
13 # @param string_a string to be checked.
14 def is_realstring(string):
15 # Make sure it is a string
16 if not isinstance(string, (str, unicode)):
17 return False
18
19 return True
20
21 ##
22 # Checks to see if x is a numeral by doing a type conversion.
23 # @param x value to be checked
24 def is_numeric(x):
25 try:
26 float(x)
27 except ValueError:
28 return False
29 else:
30 return True
31
32 ##
33 # Check to see if a string is a valid ip. Returns bool.
34 # @param ip ip to be checked.
35 def is_ip(ip):
36 # Make sure it is a string
37 if not is_realstring(ip):
38 return False
39
40 # Compile the regular expression that validates an IP. It will also check for valid ranges.
41 expr = re.compile('(([0-9]|[01]?[0-9]{2}|2([0-4][0-9]|5[0-5]))\.){3}([0-9]|[01]?[0-9]{2}|2([0-4][0-9]|5[0-5]))$')
42
43 # Run the test.
44 res = expr.match(ip)
45
46 # Return True only if there are results.
47 return(res != None)
48
49 ##
50 # Check to see if mac is a valid MAC address. Make sure use format_mac
51 # before using this function. Returns bool.
52 # @param mac mac address to be checked.
53 def is_mac(mac):
54 expr = re.compile('([0-9A-F]{2}:){5}[0-9A-F]{2}')
55 res = expr.match(mac)
56 return(res != None)
57
58 ##
59 # Format's a mac address properly. Returns the correctly formatted MAC. (a string)
60 # @param mac mac address to be formatted
61 def format_mac(mac):
62 mac = string.replace(mac, '-', ':')
63 mac = string.upper(mac)
64
65 mac = string.split(mac, ':')
66 for i in range(0, len(mac)):
67 if len(mac[i]) < 2:
68 mac[i] = "0" + mac[i]
69 return string.join(mac, ":")
70
71 ##
72 # Removes leading zero's from an IP address. For example
73 # trim_ip('192.168.01.002') => '192.168.1.2'
74 # @param ip IP address to be trimmed
75 def trim_ip(ip):
76 # Remove leading zero's on the first octet
77 ip = re.sub('^0{1,2}','',ip)
78
79 # Remove leading zero's from the other octets
80 res = re.sub('((?<=\.)(00|0)(?P<num>\d))','\g<num>',ip)
81
82 return(res)
83
84 ##
85 # Check to see if the string passed is a valid device. Returns bool.
86 # @param device device to be checked
87 def is_device(device):
88 # Make sure it is a string
89 if not is_realstring(device):
90 return False
91
92 # Make sure the string starts with /dev/
93 if device[0:5] != '/dev/':
94 return False
95
96 # Check to make sure the device exists
97 return os.access(device, os.F_OK)
98
99 ##
100 # Check to see if the string is a valid hostname. Returns bool.
101 # @param hostname host to be checked
102 def is_hostname(hostname):
103 # Make sure it is a string
104 if not is_realstring(hostname):
105 return False
106
107 expr = re.compile('^([a-zA-Z0-9-_\.])+\.[a-zA-Z]{2,4}$')
108 res = expr.match(hostname)
109
110 return(res != None)
111
112 ##
113 # Check to see if the string is a valid path. Returns bool.
114 # @param path Path to be checked.
115 def is_path(path):
116 # Make sure it is a string
117 if not is_realstring(path):
118 return False
119
120 # Create a regular expression that matches all words and the symbols '-_./' _ is included in the \w
121 expr = re.compile('^[\w\.\-\/~]+$')
122
123 # Run the match
124 res = expr.match(path)
125
126 # Return True only if there are results
127 return(res != None)
128
129 ##
130 # Check to see if the string is a valid file. Returns bool.
131 # @param file file to be checked for validity.
132 def is_file(file):
133 # Make sure it is a string
134 if not is_realstring(file):
135 return False
136
137 # Check to make sure the device exists
138 return os.access(file, os.F_OK)
139
140 ##
141 # Parse a URI. Returns a tuple (protocol, username, password, host, port, path)
142 # Returns None if URI is invalid.
143 # @param uri URI to be parsed
144 def parse_uri(uri):
145 # Compile the regex
146 expr = re.compile('(\w+)://(?:(\w+)(?::(\w+))?@)?(?:([a-zA-Z0-9.-]+)(?::(\d+))?)?(/.+)')
147
148 # Run it against the URI
149 res = expr.match(uri)
150
151 if not res:
152 # URI doesn't match regex and therefore is invalid
153 return None
154
155 # Get tuple of matches
156 # 0 - Protocol
157 # 1 - Username
158 # 2 - Password
159 # 3 - Host
160 # 4 - Port
161 # 5 - Path
162 uriparts = res.groups()
163 return uriparts
164
165 ##
166 # Check to see if the string is a valid URI. Returns bool.
167 # @param uri URI to be validated
168 # @param checklocal=True Whether to look for a local uri.
169 def is_uri(uri, checklocal=True):
170 # Make sure it is a string
171 if not is_realstring(uri):
172 return False
173
174 # Set the valid uri types
175 valid_uri_types = ('ftp', 'rsync', 'http', 'file', 'https')
176
177 # Parse the URI
178 uriparts = parse_uri(uri)
179 if not uriparts:
180 # Invalid URI
181 return False
182
183 # Check for valid uri type
184 if not uriparts[0] in valid_uri_types:
185 return False
186
187 # If checklocal and the URI is a local file, check to see if the file exists
188 if uriparts[0] == "file" and checklocal:
189 if not is_file(uriparts[5]):
190 return False
191
192 return True
193
194 ##
195 # Converts a string to a boolean value. anything not "True" is deemed false.
196 # @param input must be a string so it can be converted to boolean.
197 def strtobool(input):
198 if type(input) != str:
199 raise GLIException("GLIUtilityError", 'fatal','strtobool',"The input must be a string!")
200
201 if string.lower(input) == 'true':
202 return True
203 else:
204 return False
205
206 ##
207 # Check to see if device is a valid ethernet device. Returns bool.
208 # @param device device to be checked
209 def is_eth_device(device):
210 # Make sure it is a string
211 if not is_realstring(device):
212 return False
213
214 # Old way w/ reg ex here:
215 # Create a regular expression to test the specified device.
216 #expr = re.compile('^(eth|wlan|ppp)([0-9]{1,2})(:[0-9]{1,2})?$')
217 # Run the match
218 #res = expr.match(device)
219 # Return True only if there are results
220 #return(res != None)
221
222 status, output = spawn("/sbin/ifconfig -a | grep -e '^[A-Za-z]'| cut -d ' ' -f 1 | grep '"+ device + "'", return_output=True)
223 if output:
224 return True
225 return False
226
227 ##
228 # Will return a list of devices found in ifconfig.
229 def get_eth_devices():
230 status, output = spawn("/sbin/ifconfig -a | grep -e '^[A-Za-z]'| cut -d ' ' -f 1", return_output=True)
231 return output.split()
232
233 ##
234 # Checks to see if device is a valid NFS device
235 # @param device device to be checked
236 def is_nfs(device):
237 if not is_realstring(device):
238 return False
239
240 colon_location = device.find(':')
241
242 if colon_location == -1:
243 return False
244
245 host = device[:colon_location]
246 path = device[colon_location+1:]
247
248 return((is_ip(host) or is_hostname(host)) and is_path(path))
249
250 ##
251 # Sets the network ip (used for the livecd environment)
252 # @param dev device to be configured
253 # @param ip ip address of device
254 # @param broadcast broadcast address of device
255 # @param netmask netmask address of device
256 def set_ip(dev, ip, broadcast, netmask):
257 if not is_ip(ip) or not is_ip(netmask) or not is_ip(broadcast):
258 raise GLIException("GLIUtilityError", 'fatal','set_ip', ip + ", " + netmask + "and, " + broadcast + "must be a valid IP's!")
259 if not is_eth_device(dev):
260 raise GLIException("GLIUtilityError", 'fatal','set_ip', dev + "is not a valid ethernet device!")
261
262 options = "%s inet %s broadcast %s netmask %s" % (dev, ip, broadcast, netmask)
263
264 status = spawn("ifconfig " + options)
265
266 if not exitsuccess(status):
267 return False
268
269 return True
270
271 ##
272 # Sets the default route (used for the livecd environment)
273 # @param route ip addresss of gateway.
274 def set_default_route(route):
275 if not is_ip(route):
276 raise GLIException("GLIUtilityError", 'fatal', 'set_default_route', route + " is not an ip address!")
277 status = spawn("route add default gw " + route)
278
279 if not exitsuccess(status):
280 return False
281
282 return True
283
284 ##
285 # Will run a command with various flags for the style of output and logging.
286 #
287 # @param cmd The command to be run
288 # @param quiet=False Whether or not to filter output to /dev/null
289 # @param logfile=None if provied will log output to the given filename
290 # @param display_on_tty8=False will output to tty8 instead of the screen.
291 # @param chroot=None will run the command inside the new chroot env.
292 # @param append_log=False whether to start over on the logfile or append.
293 # @param return_output=False Returns the output along with the exit status
294 def spawn(cmd, quiet=False, logfile=None, display_on_tty8=False, chroot=None, append_log=False, return_output=False):
295 # quiet and return_output really do the same thing. One of them need to be removed.
296 if chroot != None:
297 wrapper = open(chroot+"/var/tmp/spawn.sh", "w")
298 wrapper.write("#!/bin/bash\n\nsource /etc/profile\n" + cmd + "\nexit $?\n")
299 wrapper.close()
300 cmd = "chmod a+x " + chroot + "/var/tmp/spawn.sh && chroot " + chroot + " /var/tmp/spawn.sh 2>&1"
301 else:
302 cmd += " 2>&1 "
303
304 output = ""
305
306 if logfile:
307 if append_log:
308 fd_logfile = open(logfile,'a')
309 else:
310 fd_logfile = open(logfile,'w')
311
312 if display_on_tty8:
313 fd_tty = open('/dev/tty8','w')
314
315 # open a read only pipe
316 ro_pipe = os.popen(cmd, 'r')
317
318 # read a line from the pipe and loop until
319 # pipe is empty
320 data = ro_pipe.readline()
321
322 while data:
323 if logfile:
324 fd_logfile.write(data)
325 # fd_logfile.flush()
326
327 if display_on_tty8:
328 fd_tty.write(data)
329 # fd_tty.flush()
330
331 if return_output:
332 output = output + data
333
334 data = ro_pipe.readline()
335
336 # close the file descriptors
337 if logfile: fd_logfile.close()
338 if display_on_tty8: fd_tty.close()
339
340 # close the pipe and save return value
341 ret = ro_pipe.close() or 0
342
343 if return_output:
344 return ret, output
345 else:
346 return ret
347
348 ##
349 # Will check the status of a spawn result to see if it did indeed return successfully.
350 # @param status Parameter description
351 def exitsuccess(status):
352 if os.WIFEXITED(status) and os.WEXITSTATUS(status) == 0:
353 return True
354 return False
355
356 ##
357 # Will produce a bash shell with a special prompt for the installer.
358 def spawn_bash():
359 os.putenv("PROMPT_COMMAND","echo \"Type 'exit' to return to the installer.\"")
360 status = spawn("bash")
361 return status
362
363 ##
364 # Will download or copy a file/uri to a location
365 # @param uri uri to be fetched.
366 # @param path destination for the file.
367 def get_uri(uri, path):
368 uri = uri.strip()
369 status = 0
370
371 if re.match('^(ftp|http(s)?)://',uri):
372 status = spawn("wget --quiet " + uri + " -O " + path)
373
374 elif re.match('^rsync://', uri):
375 status = spawn("rsync --quiet " + uri + " " + path)
376
377 elif re.match('^file://', uri):
378 r_file = uri[7:]
379 if os.path.isfile(r_file):
380 shutil.copy(r_file, path)
381 if not os.path.isfile(path):
382 raise GLIException("GLIUtilityError", 'fatal', 'get_uri', "Cannot copy " + r_file + " to " + path)
383 else:
384 # Just in case a person forgets file://
385 if os.path.isfile(uri):
386 shutil.copy(uri, path)
387 if not os.path.isfile(path):
388 raise GLIException("GLIUtilityError", 'fatal', 'get_uri', "Cannot copy " + r_file + " to " + path)
389 else:
390 raise GLIException("GLIUtilityError", 'fatal', 'get_uri', "File does not exist or URI is invalid!")
391
392 if exitsuccess(status) and is_file(path):
393 return True
394
395 return False
396
397 ##
398 # Pings a host. Used to test network connectivity.
399 # @param host host to be pinged.
400 def ping(host):
401 host = str(host)
402 if not (is_hostname(host) or is_ip(host)):
403 return False #invalid IP or hostname
404 status = spawn("ping -n -c 3 " + host)
405 if not exitsuccess(status):
406 return False
407 return True
408
409 ##
410 # Pass in the eth device's number (0, 1, 2, etc).
411 # Returns network information in a tuple.
412 # Order is hw_addr, ip_addr, mask, bcast, route, and
413 # whether it's up (True or False).
414 # @param device device to gather info from.
415 def get_eth_info(device):
416 """Pass in the eth device's number (0, 1, 2, etc).
417 Returns network information in a tuple.
418 Order is hw_addr, ip_addr, mask, bcast, route, and
419 whether it's up (True or False).
420 """
421
422 hw_addr = 'None'
423 ip_addr = 'None'
424 mask = 'None'
425 bcast = 'None'
426 gw = 'None'
427 up = False
428
429 if not is_eth_device("eth" + str(device)):
430 raise GLIException("GLIUtilityError", 'fatal', "get_eth_info", "eth" + str(device) +" is not a valid ethernet device!")
431
432 status, device_info = spawn("/sbin/ifconfig eth" + str(device), return_output=True)
433 if exitsuccess(status):
434 for line in device_info.splitlines():
435 line = line.strip()
436 if 'HWaddr' in line:
437 hw_addr = line.split('HWaddr',1)[1].strip()
438 if 'inet addr' in line:
439 ip_addr = line.split(' ')[0].split(':')[1]
440 if 'Bcast' in line:
441 bcast = line.split(' ')[1].split(':')[1]
442 if 'Mask' in line:
443 mask = line.split(' ')[2].split(':')[1]
444 if line.startswith('UP'):
445 up = True
446 else:
447 raise GLIException("GLIUtilityError", 'fatal', "get_eth_info", device_info)
448 status, route_info = spawn("netstat -nr", return_output=True)
449 for line in route_info[1].splitlines():
450 if line.startswith('0.0.0.0'):
451 gw = line.split('0.0.0.0')[1].strip()
452
453 return (hw_addr, ip_addr, mask, bcast, gw, up)
454
455 ##
456 # Will take a uri and get and unpack a tarball into the destination.
457 # @param tarball_uri URI of tarball
458 # @param target_directory destination
459 # @param temp_directory="/tmp" a temporary location (used for dealing with the
460 # ramdisk size limitations of the livecd env.
461 # @param keep_permissions=False Whether or not to keep permissions (-p)
462 def fetch_and_unpack_tarball(tarball_uri, target_directory, temp_directory="/tmp", keep_permissions=False):
463 "Fetches a tarball from tarball_uri and extracts it into target_directory"
464
465 # Get tarball info
466 tarball_filename = tarball_uri.split("/")[-1]
467
468 # Get the tarball
469 if not get_uri(tarball_uri, temp_directory + "/" + tarball_filename):
470 raise GLIException("GLIUtilityError", 'fatal', 'fetch_and_unpack_tarball',"Could not fetch " + tarball_uri)
471
472 # Reset tar options
473 tar_options = "xv"
474
475 # If the tarball is bzip'd
476 if tarball_filename.split(".")[-1] == "tbz" or tarball_filename.split(".")[-1] == "bz2":
477 tar_options = tar_options + "j"
478
479 # If the tarball is gzip'd
480 elif tarball_filename.split(".")[-1] == "tgz" or tarball_filename.split(".")[-1] == "gz":
481 tar_options = tar_options + "z"
482
483 # If we want to keep permissions
484 if keep_permissions:
485 tar_options = tar_options + "p"
486
487 # Unpack the tarball
488 exitstatus = spawn("tar -" + tar_options + " -f " + temp_directory + "/" + tarball_filename + " -C " + target_directory, display_on_tty8=True, logfile="/tmp/compile_output.log", append_log=True) # change this to the logfile variable
489
490 if not exitsuccess(exitstatus):
491 raise GLIException("GLIUtilityError", 'fatal', 'fetch_and_unpack_tarball',"Could not unpack " + tarball_uri + " to " + target_directory)
492
493 ##
494 # OLD Will generate a random password. Used when the livecd didn't auto-scramble the root password.
495 # can probably be removed but is good to keep around.
496 def generate_random_password():
497 s = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890$%^&*[]{}-=+_,|'\"<>:/"
498 s = list(s)
499
500 for i in range(0,len(s)/2):
501 x = random.randint(0,len(s)-1)
502 y = random.randint(0,len(s)-1)
503 tmp = s[x]
504 s[x] = s[y]
505 s[y] = tmp
506
507 passwd = ""
508 for i in range(0,random.randint(8,12)):
509 passwd += s[i]
510
511 return passwd
512
513 ##
514 # Will grab a value from a specified file after sourcing it
515 # @param filename file to get the value from
516 # @param value value to look for
517 def get_value_from_config(filename, value):
518 #OLD WAY: return string.strip(commands.getoutput("source " + filename + " && echo $" + value))
519 status, output = spawn("source " + filename + " && echo $" + value, return_output=True)
520 return string.strip(output)
521
522
523 ##
524 # Will take a password and return it hashed in md5 format
525 # @param password the password to be hashed
526 def hash_password(password):
527 salt = "$1$"
528 chars = "./abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"
529 for i in range(0, 8):
530 salt += chars[random.randint(0, len(chars)-1)]
531 salt += "$"
532 passwd_hash = crypt.crypt(password, salt)
533
534 return passwd_hash
535
536 ##
537 # Returns the real name (manufacturer and model) of a network interface
538 # @param interface Name of interface (like in ifconfig)
539 def get_interface_realname(interface):
540 # TODO: rewrite with 2.4 support
541 if is_file("/sys/class/net/" + interface + "/device"):
542 return spawn("/sbin/lspci | grep $(basename $(readlink /sys/class/net/" + interface + r"/device)) | sed -e 's|^.\+ Ethernet controller: ||'", return_output=True)[1].strip()
543 else:
544 return "No Information Found"
545
546 def list_stage_tarballs_from_mirror(mirror, arch, subarch):
547 return spawn("wget -O - " + mirror + "/releases/" + arch + "/current/stages/" + subarch + r"/ 2> /dev/null | grep 'bz2\"' | sed -e 's:^.\+href=\"\(.\+\)\".\+$:\1:i'", return_output=True)[1].split("\n")
548
549 def list_subarch_from_mirror(mirror, arch):
550 return spawn("wget -O - " + mirror + "/releases/" + arch + r"/current/stages/ 2> /dev/null | grep folder.gif | sed -e 's:^.\+href=\"\(.\+\)\".\+$:\1:i'", return_output=True)[1].split("\n")
551
552 def list_mirrors():
553 mirrors = []
554 mirrorlist = spawn(r"wget -O - 'http://www.gentoo.org/main/en/mirrors.xml?passthru=1' 2>/dev/null | /bin/sed -ne '/^[[:space:]]\+<uri link=\"\(http\|ftp\|rsync\):\/\/[^\"]\+\">/{s/^[[:space:]]\+<uri link=\"\([^\"]\+\)\">\(.*\)<\/uri>.*$/\1|\2/;p}'", return_output=True)[1].split("\n")
555 for mirror in mirrorlist:
556 mirror = mirror.strip()
557 mirrors.append(mirror.split("|"))
558 return mirrors

Properties

Name Value
svn:eol-style native

  ViewVC Help
Powered by ViewVC 1.1.20