def create(self, dry_run): """ create - method to create, newfs and mount a lofi device """ # create the ramdisk (if needed) self.create_ramdisk(dry_run) # create the lofi device cmd = [LOFIADM, "-a", self.ramdisk] if not dry_run: p = Popen.check_call(cmd, stdout=Popen.STORE, stderr=Popen.STORE, logger=ILN) self.lofi_device = p.stdout.strip() # newfs it cmd = [NEWFS, "-m", "0", "-o", "space"] if self.nbpi is not None: cmd.append("-i") cmd.append(str(self.nbpi)) cmd.append(self.lofi_device.replace("lofi", "rlofi")) if not dry_run: # due to the way Popen works, we can not assign a logger to the # call, otherwise the process will complete before we can pass the # "y" to newfs logger = logging.getLogger(ILN) logger.debug("Executing: %s" % " ".join(cmd)) p = Popen(cmd, stdin=Popen.PIPE, stdout=Popen.DEVNULL, stderr=Popen.DEVNULL) p.communicate("y\n") # ensure a directory exists to mount the lofi device to if not os.path.exists(self.mountpoint) and not dry_run: os.makedirs(self.mountpoint) cmd = [ MOUNT, "-F", "ufs", "-o", "rw", self.lofi_device, self.mountpoint ] if not dry_run: Popen.check_call(cmd, stdout=Popen.STORE, stderr=Popen.STORE, logger=ILN) self.mounted = True
def create(self, dry_run): """ create - method to create, newfs and mount a lofi device """ # create the ramdisk (if needed) self.create_ramdisk(dry_run) # create the lofi device cmd = [LOFIADM, "-a", self.ramdisk] if not dry_run: p = Popen.check_call(cmd, stdout=Popen.STORE, stderr=Popen.STORE, logger=ILN) self.lofi_device = p.stdout.strip() # newfs it cmd = [NEWFS, "-m", "0", "-o", "space"] if self.nbpi is not None: cmd.append("-i") cmd.append(str(self.nbpi)) cmd.append(self.lofi_device.replace("lofi", "rlofi")) if not dry_run: # due to the way Popen works, we can not assign a logger to the # call, otherwise the process will complete before we can pass the # "y" to newfs logger = logging.getLogger(ILN) logger.debug("Executing: %s" % " ".join(cmd)) p = Popen(cmd, stdin=Popen.PIPE, stdout=Popen.DEVNULL, stderr=Popen.DEVNULL) p.communicate("y\n") # ensure a directory exists to mount the lofi device to if not os.path.exists(self.mountpoint) and not dry_run: os.makedirs(self.mountpoint) cmd = [MOUNT, "-F", "ufs", "-o", "rw", self.lofi_device, self.mountpoint] if not dry_run: Popen.check_call(cmd, stdout=Popen.STORE, stderr=Popen.STORE, logger=ILN) self.mounted = True
def test_devnull(self): '''Test using Popen.DEVNULL for stdin''' popen = Popen(["/usr/bin/cat"], stdin=Popen.DEVNULL, stdout=Popen.PIPE) # Use PIPE for stdout as, for a failure case, the subprocess call # could hang indefinitely, so we can't block on it for wait_count in xrange(10): # If it's not done nearly instantly, something is wrong. # However, give the benefit of the doubt by waiting up to # 5 seconds for completion if popen.poll() is not None: break else: time.sleep(0.5) else: popen.kill() self.fail("stdin=Popen.DEVNULL did not work") stdout = popen.communicate()[0] self.assertEqual("", stdout)
def test_devnull(self): '''Test using Popen.DEVNULL for stdin''' popen = Popen(["/usr/bin/cat"], stdin=Popen.DEVNULL, stdout=Popen.PIPE) # Use PIPE for stdout as, for a failure case, the subprocess call # could hang indefinitely, so we can't block on it for wait_count in xrange(10): # If it's not done nearly instantly, something is wrong. # However, give the benefit of the doubt by waiting up to # 5 seconds for completion if popen.poll() is not None: break else: time.sleep(0.5) else: popen.kill() self.fail("stdin=Popen.DEVNULL did not work") stdout = popen.communicate()[0] self.assertEqual("", stdout)
# Generate incorporation pkg incorp_pkg_name = "pkg://%s/%s@%s" % \ (args.publisher, INCORP_NAME, incorp_version) dep_pkg_name = "%s@%s" % (AI_PKG_NAME, ai_pkg_version) # Generate the manifest manifest = ('set name=pkg.fmri value=%(incorppkg)s\n' 'depend type=incorporate fmri=%(depname)s' % {'incorppkg': incorp_pkg_name, 'depname': dep_pkg_name}) print "\nManifest contents:\n%s" % manifest print "\nPublishing %s" % incorp_pkg_name cmd = [PKGSEND, "-s", args.repo, "publish"] pkgsend = Popen(cmd, stdin=Popen.PIPE, stdout=Popen.PIPE, stderr=Popen.PIPE) stdout, stderr = pkgsend.communicate(manifest) if stderr.strip() or pkgsend.returncode: pkgsend.stdout = stdout pkgsend.stderr = stderr raise CalledProcessError(pkgsend.returncode, cmd, popen=pkgsend) else: print stdout.strip() # Refresh the repository cmd = [PKGREPO, "-s", args.repo, "refresh"] run(cmd) print "Finished at " + time.asctime()
def execute(self, dry_run=False): '''Validate script and then run it.''' script_name = os.path.abspath(self.dmd.script) # Verify type of script. Assumes this module is being run with enough # privilege to read the script. linecache.checkcache(script_name) first_line = linecache.getline(script_name, 1) if (first_line == ""): errmsg = (MSG_HEADER + "Error opening scriptfile %s" % script_name) self.logger.critical(errmsg) raise DMMScriptAccessError(errmsg) # Look for appropriate shebang line to denote a supported script. # Note their appearance may have "/usr" prepended to them. first_line = first_line.strip() if not first_line.startswith("#!"): errmsg = ( MSG_HEADER + 'File %s: first line does not start with "#!".' % script_name) self.logger.critical(errmsg) raise DMMScriptInvalidError(errmsg) # Verify accessibility of script. # Owner must be aiuser, or file mode must include o+rx. script_stat = os.stat(script_name) mode = stat.S_IMODE(script_stat.st_mode) self.logger.info(MSG_HEADER + "Script to run: " + script_name) self.logger.info(MSG_HEADER + "script mode is 0%o, uid is %d, gid is %d\n" % (mode, script_stat.st_uid, script_stat.st_gid)) self.logger.info(MSG_HEADER + "Script validated. Running in subprocess...") # Verify that the aiuser can access the script. cmdlist = [ SU, AIUSER_ACCOUNT_NAME, "-c", TEST + " -r " + script_name + " -a -x " + script_name ] try: Popen.check_call(cmdlist) except CalledProcessError: errmsg = MSG_HEADER + \ "Error accessing Derived Manifest script as aiuser" self.logger.critical(errmsg) raise DMMScriptInvalidError(errmsg) cmdlist = [SU, AIUSER_ACCOUNT_NAME, "-c", script_name] subproc = Popen(cmdlist, stderr=Popen.STDOUT, stdout=Popen.PIPE, preexec_fn=self.subproc_env_setup) self.logger.info(MSG_HEADER + "script output follows: ") outerr, dummy = subproc.communicate() while (subproc.returncode is None): outerr = outerr.split("\n") for line in outerr: self.logger.info("> " + line) outerr, dummy = subproc.communicate() outerr = outerr.split("\n") for line in outerr: self.logger.info("> " + line) self.logger.info(MSG_HEADER + "aimanifest logfile output follows: ") try: with open(self.aim_logfile, 'r') as aim_log: for line in aim_log: self.logger.info(">> " + line.strip()) except (OSError, IOError) as err: self.logger.error("Error reading aimanifest logfile: %s:%s" % (err.filename, err.strerror)) try: os.unlink(self.aim_logfile) except OSError as err: self.logger.warning("MSG_HEADER: Warning: Could not delete " "aimanifest logfile %s: %s" % (self.aim_logfile, err.strerror)) if subproc.returncode < 0: # Would be nice to convert number to signal string, but no # facility for this exists in python. errmsg = ( MSG_HEADER + "Script was terminated by signal %d" % -subproc.returncode) elif subproc.returncode > 0: # Note: can't get 128 or 129 (as can be returned by a shell when it # cannot access or run a script) because that has already been # checked for. errmsg = (MSG_HEADER + "Script \"" + self.dmd.script + \ "\" terminated on error.") if subproc.returncode != 0: self.logger.critical(errmsg) raise DMMExecutionError(errmsg) else: self.logger.info(MSG_HEADER + "script completed successfully") # Try to validate against a schema specified in the manifest DOCTYPE # header, if it is there. Else fallback to a hardwired default. try: tree = etree.parse(self.mpd.manifest) except etree.XMLSyntaxError as err: self.logger.critical(MSG_HEADER + "Error parsing final manifest") self.logger.critical(str(err)) errmsg = MSG_HEADER + "Final manifest failed XML validation" self.logger.critical(errmsg) raise DMMValidationError(errmsg) if ((tree.docinfo is not None) and (tree.docinfo.system_url is not None) and os.access(tree.docinfo.system_url, os.R_OK)): dtd = tree.docinfo.system_url self.logger.info(MSG_HEADER + "Using DTD from header of manifest.") else: dtd = SYS_AI_MANIFEST_DTD self.logger.info(MSG_HEADER + "Manifest header refers to no DTD.") self.logger.info(MSG_HEADER + "Validating against DTD: %s" % dtd) try: validate_manifest(tree, dtd, self.logger) except (ManifestError) as err: # Note: validate_manifest already logged the errors. errmsg = MSG_HEADER + "Final manifest failed XML validation" self.logger.critical(errmsg) raise DMMValidationError(errmsg) else: self.logger.info(MSG_HEADER + "XML validation completed successfully ")
def create_repository(self): """ class method to create the repository """ self.logger.info("Creating repository") # Create the repository (as needed) if it's a local path (no scheme) # or file:/// scheme. scheme = urlparse.urlsplit(self.pkg_repo).scheme if scheme in ("file", ""): # Try to create the repo (it may already exist) cmd = [cli.PKGREPO, "create", self.pkg_repo] repo = run(cmd, check_result=Popen.ANY) if repo.returncode == 0: # New repo was created. Add the publisher and make it default cmd = [ cli.PKGREPO, "-s", self.pkg_repo, "add-publisher", self.publisher ] run(cmd) cmd = [ cli.PKGREPO, "-s", self.pkg_repo, "set", "publisher/prefix=%s" % self.publisher ] run(cmd) # Generate a manifest file cmd = [cli.PKGSEND, "generate", self.pkg_img_path] generate = run(cmd) manifest = [generate.stdout] manifest.append('license lic_OTN license=lic_OTN must-accept=true\n') manifest.append('set name=pkg.summary ' 'value="Automated Installation boot image"\n') manifest.append("set name=org.opensolaris.consolidation " "value=install\n") manifest.append('set name=info.classification ' 'value="org.opensolaris.category.2008:' 'System/Administration and Configuration"\n') arch = platform.processor() manifest.append("set name=variant.arch value=%s\n" % arch) manifest.append("set name=%s value=%s variant.arch=%s\n" % (self.SVC_NAME_ATTR, self.service_name, arch)) manifest.append("set name=pkg.fmri value=%s\n" % self.pkg_name) manifest.append("depend fmri=pkg:/system/core-os type=exclude\n") manifest = "".join(manifest) self.logger.info("Publishing %s", self.pkg_name) cmd = [ cli.PKGSEND, "-s", self.pkg_repo, "publish", "-d", self.pkg_img_path, "-d", self.tmp_dir ] pkgsend = Popen(cmd, stdin=Popen.PIPE, stdout=Popen.PIPE, stderr=Popen.PIPE) stdout, stderr = pkgsend.communicate(manifest) if stderr.strip() or pkgsend.returncode: pkgsend.stdout = stdout pkgsend.stderr = stderr raise CalledProcessError(pkgsend.returncode, cmd, popen=pkgsend) else: self.logger.info(stdout.strip())
def execute(self, dry_run=False): '''Validate script and then run it.''' script_name = os.path.abspath(self.dmd.script) # Verify type of script. Assumes this module is being run with enough # privilege to read the script. linecache.checkcache(script_name) first_line = linecache.getline(script_name, 1) if (first_line == ""): errmsg = (MSG_HEADER + "Error opening scriptfile %s" % script_name) self.logger.critical(errmsg) raise DMMScriptAccessError(errmsg) # Look for appropriate shebang line to denote a supported script. # Note their appearance may have "/usr" prepended to them. first_line = first_line.strip() if not first_line.startswith("#!"): errmsg = (MSG_HEADER + 'File %s: first line does not start with "#!".' % script_name) self.logger.critical(errmsg) raise DMMScriptInvalidError(errmsg) # Verify accessibility of script. # Owner must be aiuser, or file mode must include o+rx. script_stat = os.stat(script_name) mode = stat.S_IMODE(script_stat.st_mode) self.logger.info(MSG_HEADER + "Script to run: " + script_name) self.logger.info(MSG_HEADER + "script mode is 0%o, uid is %d, gid is %d\n" % (mode, script_stat.st_uid, script_stat.st_gid)) self.logger.info(MSG_HEADER + "Script validated. Running in subprocess...") # Verify that the aiuser can access the script. cmdlist = [SU, AIUSER_ACCOUNT_NAME, "-c", TEST + " -r " + script_name + " -a -x " + script_name] try: Popen.check_call(cmdlist) except CalledProcessError: errmsg = MSG_HEADER + \ "Error accessing Derived Manifest script as aiuser" self.logger.critical(errmsg) raise DMMScriptInvalidError(errmsg) cmdlist = [SU, AIUSER_ACCOUNT_NAME, "-c", script_name] subproc = Popen(cmdlist, stderr=Popen.STDOUT, stdout=Popen.PIPE, preexec_fn=self.subproc_env_setup) self.logger.info(MSG_HEADER + "script output follows: ") outerr, dummy = subproc.communicate() while (subproc.returncode is None): outerr = outerr.split("\n") for line in outerr: self.logger.info("> " + line) outerr, dummy = subproc.communicate() outerr = outerr.split("\n") for line in outerr: self.logger.info("> " + line) self.logger.info(MSG_HEADER + "aimanifest logfile output follows: ") try: with open(self.aim_logfile, 'r') as aim_log: for line in aim_log: self.logger.info(">> " + line.strip()) except (OSError, IOError) as err: self.logger.error("Error reading aimanifest logfile: %s:%s" % (err.filename, err.strerror)) try: os.unlink(self.aim_logfile) except OSError as err: self.logger.warning("MSG_HEADER: Warning: Could not delete " "aimanifest logfile %s: %s" % (self.aim_logfile, err.strerror)) if subproc.returncode < 0: # Would be nice to convert number to signal string, but no # facility for this exists in python. errmsg = (MSG_HEADER + "Script was terminated by signal %d" % -subproc.returncode) elif subproc.returncode > 0: # Note: can't get 128 or 129 (as can be returned by a shell when it # cannot access or run a script) because that has already been # checked for. errmsg = (MSG_HEADER + "Script \"" + self.dmd.script + \ "\" terminated on error.") if subproc.returncode != 0: self.logger.critical(errmsg) raise DMMExecutionError(errmsg) else: self.logger.info(MSG_HEADER + "script completed successfully") # Try to validate against a schema specified in the manifest DOCTYPE # header, if it is there. Else fallback to a hardwired default. try: tree = etree.parse(self.mpd.manifest) except etree.XMLSyntaxError as err: self.logger.critical(MSG_HEADER + "Error parsing final manifest") self.logger.critical(str(err)) errmsg = MSG_HEADER + "Final manifest failed XML validation" self.logger.critical(errmsg) raise DMMValidationError(errmsg) if ((tree.docinfo is not None) and (tree.docinfo.system_url is not None) and os.access(tree.docinfo.system_url, os.R_OK)): dtd = tree.docinfo.system_url self.logger.info(MSG_HEADER + "Using DTD from header of manifest.") else: dtd = SYS_AI_MANIFEST_DTD self.logger.info(MSG_HEADER + "Manifest header refers to no DTD.") self.logger.info(MSG_HEADER + "Validating against DTD: %s" % dtd) try: validate_manifest(tree, dtd, self.logger) except (ManifestError) as err: # Note: validate_manifest already logged the errors. errmsg = MSG_HEADER + "Final manifest failed XML validation" self.logger.critical(errmsg) raise DMMValidationError(errmsg) else: self.logger.info(MSG_HEADER + "XML validation completed successfully ")