1 #
2 # CDDL HEADER START
3 #
4 # The contents of this file are subject to the terms of the
5 # Common Development and Distribution License (the "License").
6 # You may not use this file except in compliance with the License.
7 #
8 # You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9 # or http://www.opensolaris.org/os/licensing.
10 # See the License for the specific language governing permissions
11 # and limitations under the License.
12 #
13 # When distributing Covered Code, include this CDDL HEADER in each
14 # file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15 # If applicable, add the following below this CDDL HEADER, with the
16 # fields enclosed by brackets "[]" replaced with your own identifying
17 # information: Portions Copyright [yyyy] [name of copyright owner]
18 #
19 # CDDL HEADER END
20 #
21 # Copyright 2009 Sun Microsystems, Inc. All rights reserved.
22 # Use is subject to license terms.
23 #
24 # Slim Install Transfer Module
25 #
26 import os
27 import errno
28 import time
29 import sys
30 import shutil
31 import traceback
32 import fcntl
33 import array
34 import string
35 import threading
36 import logging
37 import operator
38 import tempfile
39 from stat import *
40 from subprocess import *
41 from osol_install.install_utils import *
42
43 #
44 # Modules specific to Slim Install
45 #
46 import liblogsvc as logsvc
47 import libtransfer as tmod
48
49 class TM_defs(object):
50 """Class that holds some globally used values
51 """
52 MAX_NUMFILES = 200000.0
53 FIND_PERCENT = 4
54 KBD_DEVICE = "/dev/kbd"
55 CPIO = "/usr/bin/cpio"
56 PKG = "/usr/bin/pkg"
57 KBD_LAYOUT_FILE = "/usr/share/lib/keytables/type_6/kbd_layouts"
58 KBD_DEFAULTS_FILE = "etc/default/kbd"
59 MOUNT = "/usr/sbin/mount -o ro,nologging "
60 GZCAT = "/usr/bin/gzcat "
61 GZCAT_DST = "/var/run/boot_archive"
62
63 def __init__(self):
64 self.tm_lock = None
65 self.do_abort = 0
66 self.percent = 0.0
67
68 class Cpio_spec(object):
69 """Class used to hold values specifying a mountpoint for cpio operation
70 """
71 def __init__(self, chdir_prefix=None, cpio_dir=None,
72 match_pattern=None, clobber_files=0, cpio_args="pdum",
73 file_list=None):
74 self.chdir_prefix = chdir_prefix
75 self.cpio_dir = cpio_dir
76 self.match_pattern = match_pattern
77 self.clobber_files = clobber_files
78 self.cpio_args = cpio_args
79 self.file_list = file_list
80
81 class Flist(object):
82 """Class used to hold file list entries for cpio operation
83 """
84 def __init__(self, name=None, chdir_prefix=None, clobber_files=0,
85 cpio_args=None):
86 self.name = name
87 self.chdir_prefix = chdir_prefix
88 self.clobber_files = clobber_files
89 self.cpio_args = cpio_args
90
91 def open(self):
92 self.handle = open(self.name, "w+")
93
94 class TError(Exception):
95 """Base class for Transfer Module Exceptions.
96 """
97 pass
98
99 class TValueError(TError):
100 """Exception raised for errors in the input key/value list.
101
102 Attributes:
103 message -- explanation of the error
104 retcode -- error return code
105 """
106
107 def __init__(self, message, retcode):
108 self.message = message
109 self.retcode = retcode
110
111 class TAbort(TError):
112 """Exception raised when the caller requests to abort transfer
113 operation midway or a failure is detected.
114
115 Attributes:
116 message -- explanation of the abort operation
117 retcode -- error return code
118 """
119
120 def __init__(self, message, retcode = errno.EINTR):
121 self.message = message
122 self.retcode = retcode
123
124 class TIPSPkgmissing :
125 """Exception raised if an IPS package is missing
126 Attribute: retcode -- error return code
127 """
128
129 def __init__(self, retcode = errno.ENOENT):
130 self.retcode = retcode
131
132 def tm_abort_transfer() :
133 """"Method to signal to abort the transfer
134 """
135 if params.tm_lock.locked():
136 params.tm_lock.release()
137 else:
138 params.do_abort = 1;
139
140 def tm_abort_signaled():
141 """Method to detect abort
142 """
143 return params.do_abort
144
145 class ProgressMon(object):
146
147 def startmonitor(self, fs, distrosize, message, initpct=0, endpct=100):
148 """Start thread to monitor progress in populating file system
149 fs - file system to monitor
150 distrosize = full distro size in kilobytes
151 message = progress message to log.
152 initpct = base percent value from which to start
153 calculating.
154 endpct = percentage value at which to stop calculating
155 """
156 self.message = message
157 self.distrosize = distrosize
158 self.initpct = initpct
159 self.endpct = endpct
160 self.done = False
161 self.thread1 = threading.Thread( target=self.__progressthread,
162 args=( fs,))
163 self.thread1.start()
164 return 0
165
166 def wait(self):
167 self.thread1.join()
168
169 def __progressthread(self, fs):
170 """Monitor progress in populating file system
171 fs - file system to monitor
172 """
173 initsize = self.__fssize(fs)
174 totpct = self.endpct - self.initpct
175 prevpct = -1
176
177 # Loop until the user aborts or we're done transferring.
178 # Keep track of the percentage done and let the user know
179 # how far the transfer has progressed.
180 while 1:
181 # Compute increase in fs size
182 fssz = self.__fssize(fs)
183 if (fssz == -1):
184 return -1
185 fsgain = fssz - initsize
186
187 # Compute percentage transfer
188 actualpct = fsgain * 100 / self.distrosize
189 # Compute percentage transfer in terms of stated range
190 pct = fsgain * totpct / self.distrosize + self.initpct
191 # Do not exceed limits
192 if pct >= self.endpct or actualpct > 100:
193 pct = self.endpct
194 # If the percentage has changed at all, log the
195 # progress so the user can see something is going on.
196 if (pct != prevpct):
197 tmod.logprogress(int(pct), self.message)
198 prevpct = pct
199 if pct >= self.endpct:
200 return 0
201 time.sleep(2)
202 if tm_abort_signaled() or self.done:
203 return 0
204
205 def __fssize(self, fs):
206 """Find the current size of the specified file system.
207 fs - file system to monitor
208 Returns the size of the filesystem in kilobytes
209 """
210 cmd = "/usr/bin/df -k " + fs
211 p = os.popen(cmd)
212 # Read the header and throw it away....
213 dfout = p.readline()
214 if not dfout:
215 logsvc.write_log(TRANSFER_ID, "FS size computation " +
216 "failed for " + fs + "\n")
217 return -1
218 # read the line with the size information.
219 dfout = p.readline()
220 if not dfout:
221 logsvc.write_log(TRANSFER_ID, "FS size computation " +
222 "failed for " + fs + "\n")
223 return -1
224 # and yank out the size information
225 line_tokens = dfout.split()
226 return int(line_tokens[2])
227
228
229 class Transfer_cpio(object):
230 """This class contains all the methods used to actually transfer
231 files from the src_mntpt or / to the dst_mntpt
232 """
233
234 def __init__(self):
235 self.dst_mntpt = ""
236 self.src_mntpt = ""
237 self.cpio_action = ""
238 self.cpio_args = "pdum"
239 self.list_file = ""
240 self.skip_file_list = ""
241 self.tformat = "%a, %d %b %Y %H:%M:%S +0000"
242 self.debugflag = 0
243 self.cpio_prefixes = []
244 self.image_info = ""
245 self.distro_size = 0
246 self.log_handler = None
247 self.unpack_archive = None
248
249 # TODO: This is live media specific and shouldn't be part
250 # of transfer mod.
251 # List of toplevel directories that should be transferred
252 self.cpio_prefixes.append(Cpio_spec(chdir_prefix="/", \
253 cpio_dir="."))
254 self.cpio_prefixes.append(Cpio_spec(chdir_prefix="/", \
255 cpio_dir="usr"))
256 self.cpio_prefixes.append(Cpio_spec(chdir_prefix="/", \
257 cpio_dir="opt"))
258 self.cpio_prefixes.append(Cpio_spec(chdir_prefix="/", \
259 cpio_dir="dev"))
260 self.cpio_prefixes.append(Cpio_spec(chdir_prefix="/mnt/misc", \
261 cpio_dir=".", clobber_files=1, cpio_args="pdm"))
262 # When a file_list is provided, the cpio operation
263 # will be done to the list of files provided in that file.
264 # There will not be a os.walk() of the "chdir_prefix".
265 # The "chdir_prefix" value is still important here, because
266 # the content list is generated assuming "chdir_prefix"
267 # is the root.
268 self.cpio_prefixes.append(Cpio_spec(chdir_prefix="/.cdrom", \
269 cpio_dir=".", file_list="/.cdrom/.livecd-cdrom-content"))
270
271
272 def info_msg(self, msg):
273 """Log an informational message to logging service
274 """
275 if (self.log_handler != None):
276 self.log_handler.info(msg)
277 else:
278 logsvc.write_log(TRANSFER_ID, msg + "\n")
279
280 def prerror(self, msg):
281 """Log an error message to logging service and stderr
282 """
283 if (self.log_handler != None):
284 self.log_handler.error(msg)
285 else:
286 msg1 = msg + "\n"
287 logsvc.write_dbg(TRANSFER_ID, logsvc.LS_DBGLVL_ERR,
288 msg1)
289 sys.stderr.write(msg1)
290 sys.stderr.flush()
291
292 def dbg_msg(self, msg):
293 """Log detailed debugging messages to logging service
294 """
295 if (self.log_handler != None):
296 self.log_handler.debug(msg)
297 else:
298 if (self.debugflag > 0):
299 logsvc.write_dbg(TRANSFER_ID,
300 logsvc.LS_DBGLVL_INFO, msg + "\n")
301
302 # TODO : This shouldn't be part of transfer_mod
303 def do_clobber_files(self, flist_file):
304 """Given a file containing a list of pathnames this function
305 will search for those entries in the alternate root and
306 delete all matching pathnames from the alternate root that
307 are symbolic links.
308 This process is required because of the way the LiveCD env
309 is constructed. Some of the entries in the boot_archive are
310 symbolic links to files mounted off a compressed lofi file.
311 This is done to drastically reduce space usage by the
312 boot_archive.
313 """
314 self.dbg_msg("File list for clobber: " + flist_file)
315 fh = open(flist_file, 'r')
316 os.chdir(self.dst_mntpt)
317 for line in fh:
318 line = line[:-1]
319
320 try:
321 mst = os.lstat(line)
322 if S_ISLNK(mst.st_mode):
323 self.dbg_msg("Unlink: " + line)
324 os.unlink(line)
325 except OSError:
326 pass
327 fh.close()
328
329 # TODO: This shouldn't be part of transfer_mod
330 def get_kbd_layout_name(self, lnum):
331 """Given a keyboard layout number return the layout string.
332 We should not be doing this here, but unfortunately there
333 is no interface in the OpenSolaris keyboard API to perform
334 this mapping for us - RFE.
335 """
336 fh = open(TM_defs.KBD_LAYOUT_FILE, 'r')
337
338 name = ""
339 for line in fh:
340 if line[0] == '#':
341 continue
342
343 if line.find('=') == -1:
344 continue
345
346 (name, num) = line.split('=')
347 if int(num) == lnum:
348 break
349 fh.close()
350 return name
351
352 def check_abort(self):
353 if tm_abort_signaled() == 1:
354 raise TAbort("User aborted transfer")
355
356 def build_cpio_entire_file_list(self):
357 """Do a file tree walk of all the mountpoints provided and
358 build up pathname lists. Pathname lists of all mountpoints
359 under the same prefix are aggregated in the same file to
360 reduce the number of cpio invocations.
361 """
362
363 self.info_msg("-- Starting transfer process, " +
364 time.strftime(self.tformat) + " --")
365 self.check_abort()
366
367 tmod.logprogress(0, "Building file lists for cpio")
368
369 if self.src_mntpt != "" and self.src_mntpt != "/":
370 self.cpio_prefixes = []
371 self.cpio_prefixes.append(Cpio_spec(
372 chdir_prefix=self.src_mntpt, cpio_dir=".",
373 cpio_args=self.cpio_args))
374
375 total_find_percent = (len(self.cpio_prefixes) - 1) * \
376 TM_defs.FIND_PERCENT
377
378 # Get the optimized libc overlay out of the way.
379 # Errors from umount intentionally ignored
380 os.system("umount /lib/libc.so.1")
381
382 self.info_msg("Building cpio file lists")
383
384 # set up some variables with startup values.
385 opercent = 0.0
386
387 # Original values to compare against to see if
388 # we need to generate a different list of files
389 # to cpio.
390 old_cprefix = ""
391 patt = None
392 i = 0
393 nfiles = 0.0
394 # file entry list. List of files containing the files
395 # to cpio.
396 fent_list = []
397 fent = None
398 self.check_abort()
399
400 #
401 # Do a file tree walk of all the mountpoints provided and
402 # build up pathname lists. Pathname lists of all mountpoints
403 # under the same prefix are aggregated in the same file to
404 # reduce the number of cpio invocations.
405 #
406 # This loop builds a list where each entry points to a file
407 # containing a pathname list and mentions other info like the
408 # mountpoint from which to copy etc.
409 #
410 for cp in self.cpio_prefixes:
411 self.dbg_msg("Cpio dir: " + cp.cpio_dir +
412 " Chdir to: " + cp.chdir_prefix)
413 patt = cp.match_pattern
414 self.check_abort()
415
416 if patt != None and patt[0] == '!':
417 negate = 1
418 patt = patt[1:]
419 else:
420 negate = 0
421
422 # Check to be sure the specified cpio source
423 # directory is accessable.
424 st = None
425 try:
426 os.chdir(cp.chdir_prefix)
427 st = os.stat(cp.cpio_dir)
428 except OSError:
429 raise TAbort("Failed to access Cpio dir: " +
430 traceback.format_exc(),
431 TM_E_CPIO_ENTIRE_FAILED)
432
433 # Create a new file if the prefix, or cpio_args
434 # or clobber files, have changed, or a
435 # file containing a pre-generated list of
436 # content is provided.
437 if (old_cprefix != cp.chdir_prefix or
438 patt != None or
439 cp.clobber_files == 1 or cp.cpio_args != None or
440 cp.file_list != None):
441 # create a temporary file that will
442 # contain the list of files to cpio
443 # temp files are /var/run/flist<number>
444 fent = Flist()
445 fent_list.append(fent)
446 old_cprefix = cp.chdir_prefix
447 fent.name = "/var/run/flist" + str(i)
448 fent.open()
449 i = i + 1
450 self.dbg_msg(" File list tempfile:" +
451 fent.name)
452 fent.handle = open(fent.name, "w+")
453 fent.chdir_prefix = cp.chdir_prefix
454 fent.clobber_files = cp.clobber_files
455 if (cp.cpio_args):
456 fent.cpio_args = cp.cpio_args
457
458 # If the matching pattern is negated (!)
459 # flag this and remove the ! from the pattern
460 # before compiling it.
461 if (patt != None):
462 cpatt = re.compile(patt)
463
464 self.info_msg("Scanning " + cp.chdir_prefix + "/" +
465 cp.cpio_dir)
466
467 # This is for temporarily storing the list of inode
468 # numbers and their corresponding file names. This
469 # list will later be sorted by the inode number before
470 # it is written out to the cpio file list.
471 tmp_flist=[]
472
473 if (cp.file_list):
474 try:
475 image_content = open(cp.file_list, 'r')
476 except IOError:
477 raise TAbort("Failed to access " +
478 cp.file_list,
479 TM_E_INVALID_CPIO_FILELIST_ATTR)
480
481 for fname in image_content:
482
483 # Remove the '\n' character from
484 # each of the lines read from the file
485 if (fname[-1:] == '\n'):
486 fname = fname[:-1]
487 try:
488 st1 = os.lstat(fname)
489 except Exception, ex:
490 self.info_msg("Warning: Error" +
491 " processing " + fname +
492 "from file " + cp.file_list)
493 continue
494
495 # Store the extent location of the
496 # hsfs file and the filename to a
497 # temporary list
498 tmp_flist.append((st1.st_ino, fname))
499 else:
500 #
501 # os.walk does not recurse into directory
502 # symlinks so nftw(..., FTW_PHYS) is satisfied.
503 # In addition, we want to restrict our search
504 # only to the current filesystem which is
505 # handled below.
506 #
507 for root, dirs, files in os.walk(cp.cpio_dir):
508 self.check_abort()
509 for name in files:
510 match = None
511 fname = root + "/" + name
512 if patt != None:
513 match = \
514 cpatt.match(name)
515 # If we have a match on
516 # the name but the
517 # pattern was !, then
518 # that's really a
519 # non-match. Also, if
520 # no match is found
521 # but the pattern
522 # was not ! it's a
523 # non-match.
524 if (match != None \
525 and negate == 1) \
526 or (match == None \
527 and negate != 1):
528 self.dbg_msg(
529 "Non " \
530 "match. " \
531 "Skipped:" \
532 + fname)
533 continue
534
535 try:
536 st1 = os.lstat(fname)
537 except:
538 self.info_msg(
539 "Warning: Error" +
540 " processing " +
541 fname)
542 continue
543
544 # Store the extent location of
545 # the hsfs file and the
546 # filename to a temporary list
547 tmp_flist.append((st1.st_ino,
548 fname))
549
550 nfiles = nfiles + 1
551 params.percent = int(nfiles /
552 TM_defs.MAX_NUMFILES *
553 total_find_percent)
554 if params.percent - opercent \
555 > 1:
556 tmod.logprogress(
557 params.percent,
558 "Building cpio " \
559 "file lists")
560 opercent = \
561 params.percent
562
563 #
564 # Identify directories that we do not
565 # want to traverse. These are those
566 # that can't be read for some reason
567 # or those holding other mounted
568 # filesystems.
569 #
570 rmlist = []
571 for name in dirs:
572 dname = root + "/" + name
573 try:
574 st1 = os.stat(dname);
575 except:
576 rmlist.append(name)
577 continue
578
579 # Store the extent location of
580 # the hsfs file and the
581 # filename to a temporary list
582 tmp_flist.append((st1.st_ino,
583 dname))
584
585 # Emulate nftw(..., FTW_MOUNT)
586 # for directories.
587 if st1.st_dev != st.st_dev:
588 rmlist.append(name)
589
590 #
591 # Remove directories so that they are
592 # not traversed. os.walk allows dirs
593 # to be modified in place.
594 #
595 for dname in rmlist:
596 dirs.remove(dname)
597
598 # Write file list out to the file, after sorting
599 # by the inode number, which is the first item
600 tmp_flist.sort(key=operator.itemgetter(0))
601 lf = fent.handle
602 for f in map(operator.itemgetter(1), tmp_flist):
603 lf.write(f + "\n")
604 lf.flush()
605
606 for fent in fent_list:
607 fent.handle.close()
608 fent.handle = None
609 return fent_list
610
611 def cpio_skip_files(self):
612 """Function to remove the files listed in the skip file list.
613 Copying and then deleting the files is equivalent to not copying
614 them at all or "skipping" them.
615 """
616 try:
617 skip_file = open(self.skip_file_list, 'r')
618 except IOError:
619 raise TAbort("Failed to access " +
620 self.skip_file_list, TM_E_INVALID_CPIO_ACT_ATTR)
621
622 for line in skip_file:
623 os.unlink(self.dst_mntpt + "/" + line.rstrip())
624
625 skip_file.close()
626
627 def run_command(self, cmd):
628 try:
629 rt = call(cmd, shell=True)
630 if rt < 0:
631 raise TAbort("Command " + cmd +
632 " terminated with signal", -rt)
633 elif rt > 0:
634 raise TAbort("Command " + cmd + " failed", rt)
635 except OSError, e:
636 raise TAbort("Execution of " + cmd + " failed", e)
637
638 def mount_archive(self, mntdir):
639 self.run_command(TM_defs.GZCAT + self.unpack_archive + " > " +
640 TM_defs.GZCAT_DST)
641 self.run_command(TM_defs.MOUNT + TM_defs.GZCAT_DST + " " +
642 mntdir)
643
644 def cpio_transfer_entire_directory(self):
645 # If an unpack archive was specified, mount it first and
646 # prepend it to the list of prefixes in order to ensure its
647 # contents can be overlaid by contents from the running instance
648 if (self.unpack_archive != None):
649 mntdir = tempfile.mkdtemp(dir="/var/run")
650 self.mount_archive(mntdir)
651 self.cpio_prefixes.insert(0,
652 Cpio_spec(chdir_prefix=mntdir, cpio_dir="."))
653
654 fent_list = self.build_cpio_entire_file_list()
655 self.cpio_transfer_filelist(fent_list, TM_E_CPIO_ENTIRE_FAILED)
656 for fent in fent_list:
657 os.unlink(fent.name)
658 fent.name = ""
659
660 if self.skip_file_list:
661 self.cpio_skip_files()
662
663 def cpio_transfer_filelist(self, fent_list, err_code):
664 self.info_msg("Beginning cpio actions")
665
666 #
667 # Now process each entry in the list. cpio is executed with the
668 # -V option so that it prints a dot for each pathname processed.
669 # This is needed to provide the ability to abort midway.
670 #
671
672 #
673 # Start the disk space monitor thread
674 #
675 if self.distro_size:
676 pmon = ProgressMon()
677 pmon.startmonitor(self.dst_mntpt, self.distro_size,
678 "Transferring Contents", params.percent, 95)
679
680 # Walk file lists, cpio'ing each in turn.
681 for fent in fent_list:
682 self.check_abort()
683
684 if fent.clobber_files == 1:
685 self.do_clobber_files(fent.name)
686
687 try:
688 os.chdir(fent.chdir_prefix)
689 except OSError:
690 raise TAbort("Failed to access " +
691 fent.chdir_prefix, err_code)
692 cmd = TM_defs.CPIO + " -" + fent.cpio_args + "V " + \
693 self.dst_mntpt + " < " + fent.name
694 self.dbg_msg("Executing: " + cmd + " CWD: " +
695 fent.chdir_prefix)
696 err_file = os.tmpfile()
697 if (self.log_handler != None):
698 rt = exec_cmd_outputs_to_log(cmd.split(),
699 self.log_handler)
700 if (rt != 0):
701 self.log_handler.error(cmd +
702 " had errors")
703 else:
704 pipe = Popen(cmd, shell=True, stdout=PIPE,
705 stderr=err_file, close_fds=True)
706 while 1:
707 ch = pipe.stdout.read(1)
708 self.check_abort()
709 if not ch:
710 break;
711 rt = pipe.wait()
712
713 if rt != 0 and self.debugflag == 1:
714 err_file.seek(0)
715 self.info_msg("WARNING: " + cmd
716 + " had errors")
717 self.info_msg(" "
718 + err_file.read())
719
720 err_file.close()
721
722 if self.distro_size:
723 pmon.done = True
724 pmon.wait()
725
726
727 def perform_transfer(self, args):
728 """Main function for doing the copying of bits
729 """
730 for opt, val in args:
731 if opt == TM_ATTR_MECHANISM:
732 continue
733 elif opt == "dbgflag":
734 if val == "true":
735 self.debugflag = 1
736 else:
737 self.debugflag = 0
738
739 elif opt == TM_CPIO_DST_MNTPT:
740 self.dst_mntpt = val
741 elif opt == TM_CPIO_SRC_MNTPT:
742 self.src_mntpt = val
743 elif opt == TM_CPIO_ACTION:
744 self.cpio_action = val
745 elif opt == TM_CPIO_LIST_FILE:
746 self.list_file = val
747 elif opt == TM_ATTR_IMAGE_INFO:
748 self.image_info = val
749 elif opt == TM_CPIO_ENTIRE_SKIP_FILE_LIST:
750 self.skip_file_list = val
751 elif opt == TM_CPIO_ARGS:
752 self.cpio_args = val
753 elif opt == TM_PYTHON_LOG_HANDLER:
754 self.log_handler = val
755 elif opt == TM_UNPACK_ARCHIVE:
756 self.unpack_archive = val
757 else:
758 raise TValueError("Invalid attribute " +
759 str(opt), TM_E_INVALID_TRANSFER_TYPE_ATTR)
760
761 if self.cpio_action == TM_CPIO_LIST and \
762 self.list_file == "":
763 raise TValueError("No list file for List Cpio action",
764 TM_E_INVALID_CPIO_FILELIST_ATTR)
765
766 if self.dst_mntpt == "":
767 raise TValueError("Target mountpoint not set",
768 TM_E_INVALID_CPIO_ACT_ATTR)
769
770 if self.cpio_action == TM_CPIO_ENTIRE and \
771 self.image_info == "":
772 self.image_info = "/.cdrom/.image_info"
773
774 # Check that the dst_mntpt really exists. If not, error.
775 try:
776 mst = os.lstat(self.dst_mntpt)
777 if not S_ISDIR(mst.st_mode):
778 raise TValueError("Destination mountpoint "
779 "doesn't exist", TM_E_INVALID_CPIO_ACT_ATTR)
780 except OSError:
781 raise TValueError("Destination mountpoint is "
782 "inaccessible", TM_E_INVALID_CPIO_ACT_ATTR)
783
784 #
785 # Read in approx size of the entire distribution from
786 # .image_info file. This is used for disk space usage
787 # monitoring.
788 #
789 if self.image_info:
790 try:
791 ih = open(self.image_info, 'r')
792 except IOError:
793 raise TAbort("Failed to access " +
794 self.image_info, TM_E_INVALID_CPIO_ACT_ATTR)
795
796 for line in ih:
797 (opt, val) = line.split("=")
798 if opt == "IMAGE_SIZE":
799 # Remove the '\n' character read from
800 # the file, and convert to integer
801 self.distro_size = int(val.rstrip('\n'))
802 break
803 ih.close()
804
805 if (self.distro_size == 0):
806 # We should have read in a size by now
807 raise TAbort("Unable to read " \
808 "IMAGE_SIZE in " + self.image_info,
809 TM_E_INVALID_CPIO_ACT_ATTR)
810
811 try:
812 os.putenv('TMPDIR', '/tmp')
813 except:
814 pass
815
816 if self.cpio_action == TM_CPIO_ENTIRE:
817 self.cpio_transfer_entire_directory()
818 elif self.cpio_action == TM_CPIO_LIST:
819 try:
820 open(self.list_file, 'r')
821 except:
822 raise TAbort("Unable to open " + self.list_file,
823 TM_E_INVALID_CPIO_ACT_ATTR);
824 fent_list = []
825 fent = Flist()
826 fent_list.append(fent)
827 fent.name = self.list_file
828 fent.chdir_prefix = self.src_mntpt
829 fent.cpio_args = self.cpio_args
830 self.cpio_transfer_filelist(fent_list,
831 TM_E_CPIO_LIST_FAILED)
832 else:
833 raise TAbort("Invalid CPIO action",
834 TM_E_INVALID_CPIO_ACT_ATTR)
835
836 tmod.logprogress(100, "Complete transfer process")
837 self.info_msg("-- Completed transfer process, " +
838 time.strftime(self.tformat) + " --")
839
840 class Transfer_ips(object):
841 """This class contains all the methods used to create an IPS
842 image and populate it
843 """
844
845 def __init__(self):
846 self._action = ""
847 self._pkg_url = ""
848 self._pkg_auth = ""
849 self._init_mntpt = ""
850 self._pkgs_file = ""
851 self.debugflag = 0
852 self._image_type = "F"
853 self._image_create_force_flag = ""
854 self._alt_auth = ""
855 self._alt_url = ""
856 self._pref_flag = ""
857 self._mirr_flag = ""
858 self._no_index_flag = ""
859 self._refresh_flag = "--no-refresh"
860 self._prop_name = ""
861 self._prop_value = ""
862 self._log_handler = None
863 self._verbose_mode = ""
864
865 def prerror(self, msg):
866 """Log an error message to logging service and stderr
867 """
868 msg1 = msg + "\n"
869 logsvc.write_dbg(TRANSFER_ID, logsvc.LS_DBGLVL_ERR, msg1)
870 sys.stderr.write(msg1)
871 sys.stderr.flush()
872
873 def perform_ips_init(self):
874 """Perform an IPS image-create call.
875 Raises TAbort if unable to create the IPS image
876 """
877 # Check that the required values have been set.
878 if self._pkg_url == "":
879 raise TValueError("IPS repository not set",
880 TM_E_INVALID_IPS_ACT_ATTR)
881
882 if self._pkg_auth== "":
883 raise TValueError("IPS publisher not set",
884 TM_E_INVALID_IPS_ACT_ATTR)
885
886 # Generate the command to create the IPS image
887 cmd = TM_defs.PKG + " image-create %s -%s -p %s=%s %s" % \
888 (self._image_create_force_flag, self._image_type,
889 self._pkg_auth, self._pkg_url, self._init_mntpt)
890
891 try:
892 if (self._log_handler != None):
893 status = exec_cmd_outputs_to_log(cmd.split(),
894 self._log_handler)
895 else:
896 status = call(cmd, shell=True)
897 if status:
898 raise TAbort("Unable to initialize the "
899 "pkg image area at "
900 + self._init_mntpt, TM_E_IPS_INIT_FAILED)
901 except OSError:
902 raise TAbort("Unable to initialize the pkg image area "
903 "at " + self._init_mntpt, TM_E_IPS_INIT_FAILED)
904
905 def perform_ips_repo_contents_verify(self):
906 """Verify the packages specified by the user are actually
907 contained in the repository they specify.
908 Raises TAbort if unable to verify the IPS repository
909 """
910 # Verifying that needed packages are in the repository...
911 # Fetching list of repository packages...
912
913 # Check that the required parameters are set.
914 if self._pkgs_file == "":
915 raise TValueError("IPS pkg list not set",
916 TM_E_INVALID_IPS_ACT_ATTR)
917
918 # Check that the init_mntpt really exists. If not, error.
919 try:
920 mst = os.lstat(self._init_mntpt)
921 if not S_ISDIR(mst.st_mode):
922 raise TValueError("Specified IPS image area "
923 "doesn't exist", TM_E_INVALID_IPS_ACT_ATTR)
924 except OSError:
925 raise TValueError("Specified IPS image area is "
926 "inaccessible", TM_E_INVALID_IPS_ACT_ATTR)
927
928
929 # Checking against list of requested packages..
930 try:
931 pkgfile = open(self._pkgs_file, 'r')
932 except:
933 raise TAbort("Unable to open the IPS packages file "
934 + self._pkgs_file,
935 TM_E_IPS_REPO_CONTENTS_VERIFY_FAILED)
936
937 # For each package in our pkgs file, see if it's in the
938 # IPS repository.
939 pkglist = ""
940 for line in pkgfile:
941 pkglist += " " + line.rstrip()
942 pkgfile.close()
943 cmd = TM_defs.PKG + " -R %s list %s -a %s" % \
944 (self._init_mntpt, self._verbose_mode, pkglist)
945 try:
946 if (self._log_handler != None):
947 status = exec_cmd_outputs_to_log(cmd.split(),
948 self._log_handler)
949 else:
950 status = call(cmd, shell=True)
951 if status:
952 raise TIPSPkgmissing(TM_E_IPS_PKG_MISSING)
953 except OSError:
954 raise TAbort("Unable to verify the contents of"
955 " the IPS repository",
956 TM_E_IPS_REPO_CONTENTS_VERIFY_FAILED)
957
958
959 def perform_ips_set_prop(self):
960 """Perform an IPS set-property of the property
961 specified.
962 Raises: TAbort if unable to set property.
963 """
964
965 # Check that the required parameters are set.
966 if self._prop_name == "":
967 raise TValueError("IPS property name not set",
968 TM_E_INVALID_IPS_ACT_ATTR)
969
970 if self._prop_value == "":
971 raise TValueError("IPS property value not set",
972 TM_E_INVALID_IPS_ACT_ATTR)
973
974 # Check that the init_mntpt really exists. If not, error.
975 try:
976 mst = os.lstat(self._init_mntpt)
977 if not S_ISDIR(mst.st_mode):
978 raise TValueError("Specified IPS image area "
979 "doesn't exist", TM_E_INVALID_IPS_ACT_ATTR)
980 except OSError:
981 raise TValueError("Specified IPS image area is "
982 "inaccessible", TM_E_INVALID_IPS_ACT_ATTR)
983
984 cmd = TM_defs.PKG + \
985 " -R %s set-property %s %s" % \
986 (self._init_mntpt, self._prop_name, self._prop_value)
987
988 try:
989 if (self._log_handler != None):
990 status = exec_cmd_outputs_to_log(cmd.split(),
991 self._log_handler)
992 else:
993 status = call(cmd, shell=True)
994 if status:
995 raise TAbort("Unable to set property", \
996 TM_E_IPS_SET_PROP_FAILED)
997 except OSError:
998 raise TAbort("Unable to set property",
999 TM_E_IPS_SET_PROP_FAILED)
1000
1001 def perform_ips_set_auth(self):
1002 """Perform an IPS set-publisher of the additional publisher
1003 specified. By default, the --no-refresh flag is used
1004 so the catalog doesn't get refreshed when set-publisher
1005 is run. If the caller wants the catalog to be refreshed,
1006 specify the TM_IPS_REFRESH_CATALOG=true option when calling
1007 this function.
1008 Raises: TAbort if unable to set the additional publisher.
1009 """
1010
1011 # Check that the required parameters are set.
1012 if self._alt_auth == "":
1013 raise TValueError("IPS alternate publisher not set",
1014 TM_E_INVALID_IPS_ACT_ATTR)
1015
1016 if self._alt_url == "":
1017 raise TValueError("IPS alternate publisher url not set",
1018 TM_E_INVALID_IPS_ACT_ATTR)
1019
1020 # Check that the init_mntpt really exists. If not, error.
1021 try:
1022 mst = os.lstat(self._init_mntpt)
1023 if not S_ISDIR(mst.st_mode):
1024 raise TValueError("Specified IPS image area "
1025 "doesn't exist", TM_E_INVALID_IPS_ACT_ATTR)
1026 except OSError:
1027 raise TValueError("Specified IPS image area is "
1028 "inaccesible", TM_E_INVALID_IPS_ACT_ATTR)
1029
1030 if self._pref_flag and self._mirr_flag:
1031 raise TValueError("Unable to perform IPS set-publisher "
1032 "with -p and -m flags in same transaction",
1033 TM_E_INVALID_IPS_ACT_ATTR)
1034
1035 if self._mirr_flag:
1036 cmd = TM_defs.PKG + \
1037 " -R %s set-publisher %s %s %s %s" % \
1038 (self._init_mntpt, self._mirr_flag, self._alt_url,
1039 self._refresh_flag, self._alt_auth)
1040 else:
1041 cmd = TM_defs.PKG + \
1042 " -R %s set-publisher %s -O %s %s %s" % \
1043 (self._init_mntpt, self._pref_flag, self._alt_url,
1044 self._refresh_flag, self._alt_auth)
1045 try:
1046 if (self._log_handler != None):
1047 status = exec_cmd_outputs_to_log(cmd.split(),
1048 self._log_handler)
1049 else:
1050 status = call(cmd, shell=True)
1051 if status:
1052 raise TAbort("Unable to set an additional " \
1053 "publisher", TM_E_IPS_SET_AUTH_FAILED)
1054 except OSError:
1055 raise TAbort("Unable to set an additional publisher",
1056 TM_E_IPS_SET_AUTH_FAILED)
1057
1058
1059 def perform_ips_refresh(self):
1060 """Perform an IPS refresh if the image area
1061 Raises: TAbort if unable to refresh the IPS image
1062 """
1063
1064 # Check that the init_mntpt really exists. If not, error.
1065 try:
1066 mst = os.lstat(self._init_mntpt)
1067 if not S_ISDIR(mst.st_mode):
1068 raise TValueError("Specified IPS image area "
1069 "doesn't exist", TM_E_INVALID_IPS_ACT_ATTR)
1070 except OSError:
1071 raise TValueError("Specified IPS image area is "
1072 "inaccessible", TM_E_INVALID_IPS_ACT_ATTR)
1073
1074
1075 cmd = TM_defs.PKG + " -R %s refresh" % self._init_mntpt
1076 try:
1077 if (self._log_handler != None):
1078 status = exec_cmd_outputs_to_log(cmd.split(),
1079 self._log_handler)
1080 else:
1081 status = call(cmd, shell=True)
1082 if status:
1083 raise TAbort("Unable to refresh the IPS image",
1084 TM_E_IPS_REFRESH_FAILED)
1085 except OSError:
1086 raise TAbort("Unable to refresh the IPS image",
1087 TM_E_IPS_REFRESH_FAILED)
1088
1089
1090 def perform_ips_unset_auth(self):
1091 """Perform an IPS unset-publisher of the specified publisher
1092 Raises: TAbort if unable to unset the publisher
1093 """
1094
1095 # Check that the required parameters are set.
1096 if self._alt_auth == "":
1097 raise TValueError("IPS alternate publisher not set",
1098 TM_E_INVALID_IPS_ACT_ATTR)
1099
1100 # Check that the init_mntpt really exists. If not, error.
1101 try:
1102 mst = os.lstat(self._init_mntpt)
1103 if not S_ISDIR(mst.st_mode):
1104 raise TValueError("Specified IPS image area "
1105 "doesn't exist", TM_E_INVALID_IPS_ACT_ATTR)
1106 except OSError:
1107 raise TValueError("Specified IPS image area is "
1108 "inaccessible", TM_E_INVALID_IPS_ACT_ATTR)
1109
1110 cmd = TM_defs.PKG +" -R %s unset-publisher %s" % \
1111 (self._init_mntpt, self._alt_auth)
1112 try:
1113 if (self._log_handler != None):
1114 status = exec_cmd_outputs_to_log(cmd.split(),
1115 self._log_handler)
1116 else:
1117 status = call(cmd, shell=True)
1118 if status:
1119 raise TAbort("Unable to unset-publisher",
1120 TM_E_IPS_UNSET_AUTH_FAILED)
1121 except OSError:
1122 raise TAbort("Unable to unset-publisher",
1123 TM_E_IPS_UNSET_AUTH_FAILED)
1124
1125
1126 def perform_ips_pkg_op(self, action_str):
1127 """Perform an IPS pkg install/uninstall of the packages
1128 specified.
1129 argument:
1130 action_str: "install" indicates that this is for doing
1131 a "pkg install" of packages. "uninstall"
1132 means this is for doing a "pkg uninstall"
1133 of packages.
1134 Raises: TAbort if unable to install/uninstall the packages.
1135 """
1136
1137 # make sure action_str is defined and it is a valid action
1138 if ((action_str != "install") and (action_str != "uninstall")):
1139 raise TValueError("Invalid action string: "
1140 + action_str)
1141
1142 # Check that the required parameters are set.
1143 if self._pkgs_file == "":
1144 raise TValueError("IPS package file not set",
1145 TM_E_INVALID_IPS_ACT_ATTR)
1146
1147 # Check that the init_mntpt really exists. If not, error.
1148 try:
1149 mst = os.lstat(self._init_mntpt)
1150 if not S_ISDIR(mst.st_mode):
1151 raise TValueError("Specified IPS image area "
1152 "doesn't exist", TM_E_INVALID_IPS_ACT_ATTR)
1153 except OSError:
1154 raise TValueError("Specified IPS image area is "
1155 "inaccessible", TM_E_INVALID_IPS_ACT_ATTR)
1156
1157 # Open the file that contains the packages to work on
1158 try:
1159 pkgfile = open(self._pkgs_file, 'r')
1160 except IOError:
1161 raise TAbort("Unable to read list of packages "
1162 " to " + action_str, TM_E_IPS_RETRIEVE_FAILED)
1163
1164 # install/uninstall each package, keeping track if
1165 # any are missing.
1166 missingpkg = 0
1167 for line in pkgfile:
1168 cmd = (TM_defs.PKG + " -R %s %s %s %s %s") % \
1169 (self._init_mntpt, action_str, self._verbose_mode,
1170 self._no_index_flag, line)
1171 try:
1172 if (self._log_handler != None):
1173 status = exec_cmd_outputs_to_log \
1174 (cmd.split(), self._log_handler)
1175 else:
1176 status = call(cmd, shell=True)
1177 if status:
1178 missingpkg = 1
1179 err_str = ("Unable to " + action_str + \
1180 " %s in %s") \
1181 % (str.rstrip(line),
1182 self._init_mntpt)
1183 if (self._log_handler != None):
1184 self._log_handler.error(err_str)
1185 else:
1186 print err_str
1187 except OSError:
1188 raise TAbort("Unable to "
1189 + action_str + " %s in %s"
1190 % (line, self._init_mntpt),
1191 TM_E_IPS_RETRIEVE_FAILED)
1192
1193 pkgfile.close()
1194
1195 # If there was a missing package, raise an exception
1196 # so the caller can decide what to do.
1197 if missingpkg:
1198 raise TIPSPkgmissing(TM_E_IPS_PKG_MISSING)
1199
1200 def perform_ips_purge_hist(self):
1201 """Perform an IPS pkg purge-history.
1202 Raises: TAbort if unable to purge the history.
1203 """
1204
1205 # Check that the init_mntpt really exists. If not, error.
1206 try:
1207 mst = os.lstat(self._init_mntpt)
1208 if not S_ISDIR(mst.st_mode):
1209 raise TValueError("Specified IPS image area "
1210 "doesn't exist", TM_E_INVALID_IPS_ACT_ATTR)
1211 except OSError:
1212 raise TValueError("Specified IPS image area is "
1213 "inaccessible", TM_E_INVALID_IPS_ACT_ATTR)
1214
1215 cmd = TM_defs.PKG + " -R %s purge-history" % \
1216 (self._init_mntpt)
1217 try:
1218 if (self._log_handler != None):
1219 status = exec_cmd_outputs_to_log(cmd.split(),
1220 self._log_handler)
1221 else:
1222 status = call(cmd, shell=True)
1223 if status:
1224 raise TAbort("Unable to pkg purge-history "
1225 " the IPS image at " + self._init_mntpt)
1226 except OSError:
1227 raise TAbort("Unable to pkg purge-history "
1228 "the IPS image at " + self._init_mntpt,
1229 TM_E_IPS_RETRIEVE_FAILED)
1230
1231 def perform_transfer(self, args):
1232 """Perform a transfer using IPS.
1233 Input: args - specifies what IPS action to
1234 perform, init, contents verify, retrieve/install,
1235 set-publisher, refresh, or unset-publisher.
1236 Raises: TAbort
1237 """
1238
1239 for opt, val in args:
1240 if opt == TM_ATTR_MECHANISM:
1241 continue
1242 elif opt == TM_IPS_ACTION:
1243 self._action = val
1244 elif opt == TM_IPS_PKG_URL:
1245 self._pkg_url = val
1246 elif opt == TM_IPS_PKG_AUTH:
1247 self._pkg_auth = val
1248 elif opt == TM_IPS_INIT_MNTPT:
1249 self._init_mntpt = val
1250 elif opt == TM_IPS_PKGS:
1251 self._pkgs_file = val
1252 elif opt == TM_IPS_IMAGE_TYPE:
1253 self._image_type = val
1254 elif opt == TM_IPS_IMAGE_CREATE_FORCE:
1255 if val == "true":
1256 self._image_create_force_flag = "-f"
1257 elif opt == TM_IPS_VERBOSE_MODE:
1258 if val == "true":
1259 self._verbose_mode = "-v"
1260 elif opt == TM_IPS_ALT_AUTH:
1261 self._alt_auth = val
1262 elif opt == TM_IPS_ALT_URL:
1263 self._alt_url = val
1264 elif opt == TM_IPS_PREF_FLAG:
1265 self._pref_flag = val
1266 elif opt == TM_IPS_MIRROR_FLAG:
1267 self._mirr_flag = val
1268 elif opt == TM_IPS_GENERATE_SEARCH_INDEX:
1269 # This is only used for install/uninstall
1270 # operations
1271 if val.lower() != "true":
1272 self._no_index_flag = "--no-index"
1273 elif opt == TM_IPS_REFRESH_CATALOG:
1274 # This is only used for set-publisher
1275 if (self._action != TM_IPS_SET_AUTH):
1276 raise TValueError("Attribute "
1277 + str(opt) + "is only used " \
1278 "for set-publisher",
1279 TM_E_INVALID_TRANSFER_TYPE_ATTR)
1280 if val.lower() == "true":
1281 self._refresh_flag = ""
1282 elif opt == TM_IPS_PROP_NAME:
1283 # The prop name has already been set. Only one
1284 # property name is allowed so error out.
1285 if self._prop_name != "":
1286 raise TValueError("Only one property "
1287 "can be set per call.", TM_E_INVALID_IPS_ACT_ATTR)
1288 self._prop_name = val
1289 elif opt == TM_IPS_PROP_VALUE:
1290 # The prop value has already been set. Only one
1291 # property value is allowed so error out.
1292 if self._prop_value != "":
1293 raise TValueError("Only one property value"
1294 " can be specified per call.",
1295 TM_E_INVALID_IPS_ACT_ATTR)
1296 self._prop_value = val
1297 elif opt == TM_PYTHON_LOG_HANDLER:
1298 self._log_handler = val
1299 elif opt == "dbgflag":
1300 if val == "true":
1301 self.debugflag = 1
1302 else:
1303 self.debugflag = 0
1304 else:
1305 raise TValueError("Invalid attribute " +
1306 str(opt), TM_E_INVALID_TRANSFER_TYPE_ATTR)
1307
1308 if self._init_mntpt == "":
1309 raise TValueError("Image mountpoint not set",
1310 TM_E_INVALID_IPS_ACT_ATTR)
1311
1312 if self._action == "":
1313 raise TValueError("TM_IPS_ACTION not set",
1314 TM_E_INVALID_IPS_ACT_ATTR)
1315 elif self._action == TM_IPS_INIT:
1316 self.perform_ips_init()
1317 elif self._action == TM_IPS_REPO_CONTENTS_VERIFY:
1318 self.perform_ips_repo_contents_verify()
1319 elif self._action == TM_IPS_RETRIEVE:
1320 self.perform_ips_pkg_op("install")
1321 elif self._action == TM_IPS_SET_AUTH:
1322 self.perform_ips_set_auth()
1323 elif self._action == TM_IPS_REFRESH:
1324 self.perform_ips_refresh()
1325 elif self._action == TM_IPS_UNSET_AUTH:
1326 self.perform_ips_unset_auth()
1327 elif self._action == TM_IPS_PURGE_HIST:
1328 self.perform_ips_purge_hist()
1329 elif self._action == TM_IPS_UNINSTALL:
1330 self.perform_ips_pkg_op("uninstall")
1331 elif self._action == TM_IPS_SET_PROP:
1332 self.perform_ips_set_prop()
1333 else:
1334 raise TValueError("Invalid TM_IPS_ACTION",
1335 TM_E_INVALID_IPS_ACT_ATTR)
1336
1337 def tm_perform_transfer(args, callback=None):
1338 """Transfer data via cpio or IPS from a specified source to
1339 destination. The cpio transfer can be either an entire directory
1340 or a list of files. The IPS functionality that is supported is
1341 image-create, content verification, set-publisher, refresh,
1342 unset-publisher, and retrieval.
1343 Arguments: nvlist specifying the transfer characteristics
1344 callback function for logging.
1345 Returns: TM_E_SUCCESS
1346 TM_E_IPS_PKG_MISSING
1347 TM_E_IPS_RETRIEVE_FAILED
1348 TM_E_IPS_SET_AUTH_FAILED
1349 TM_E_IPS_UNSET_AUTH_FAILED
1350 TM_E_IPS_REFRESH_FAILED
1351 TM_E_IPS_REPO_CONTENTS_VERIFY_FAILED
1352 TM_E_IPS_INIT_FAILED
1353 TM_E_INVALID_CPIO_ACT_ATTR
1354 TM_E_INVALID_CPIO_FILELIST_ATTR
1355 """
1356
1357 # lock, so there isn't more than 1 transfer running at a time
1358 params.tm_lock = threading.Lock()
1359
1360 try:
1361 params.tm_lock.acquire()
1362
1363 rv = TM_E_SUCCESS
1364
1365 # If the callback is specified, set the python
1366 # callback function in the associated transfer mod
1367 # C code.
1368 if callback != None:
1369 # Set the python callback function
1370 tmod.set_py_callback(callback);
1371
1372 action = ""
1373 for opt, val in args:
1374 if opt == TM_ATTR_MECHANISM:
1375 action = val
1376 break
1377
1378 if action == TM_PERFORM_IPS:
1379 tobj = Transfer_ips()
1380 elif action == TM_PERFORM_CPIO:
1381 tobj = Transfer_cpio()
1382 else:
1383 if params.tm_lock.locked():
1384 params.tm_lock.release()
1385 rv = TM_E_INVALID_TRANSFER_TYPE_ATTR
1386 return rv
1387
1388 try:
1389 tobj.perform_transfer(args)
1390 except IOError, (errno, strerror):
1391 tobj.prerror("File operation error: ")
1392 tobj.prerror(traceback.format_exc())
1393 logsvc.write_log(TRANSFER_ID, "IOERROR\n")
1394 rv = TM_E_PYTHON_ERROR
1395
1396 except (TValueError, TAbort), v:
1397 tobj.prerror(v.message)
1398 logsvc.write_log(TRANSFER_ID, "TValueError or TABort\n")
1399 rv = v.retcode
1400
1401 except TIPSPkgmissing, v:
1402 logsvc.write_log(TRANSFER_ID, "pkg missing\n")
1403 rv = v.retcode
1404
1405 except:
1406 logsvc.write_log(TRANSFER_ID, "everything else\n")
1407 tobj.prerror(traceback.format_exc())
1408 rv = TM_E_PYTHON_ERROR
1409
1410 finally:
1411 if params.tm_lock.locked():
1412 params.tm_lock.release()
1413
1414 return rv
1415
1416 # global parameters
1417 params = TM_defs()
1418 # Grab defines from transfermod.h
1419 execfile('/usr/lib/python2.4/vendor-packages/osol_install/transfer_defs.py')