def test_logging_no_hang(self):
        '''Try to ensure Popen.check_call doesn't hang when trying to do
        logging'''

        # To ensure the logger keyword arg is implemented in a way that
        # doesn't cause hangs, and since the use of logger causes blocking
        # behavior, spawn a non-blocking subprocess that spawns a blocking
        # subprocess. If the non-blocking subprocess doesn't complete
        # in a reasonable amount of time, kill both and fail
        cmd = [
            sys.executable, "-c",
            "from solaris_install import Popen; import logging; "
            "Popen.check_call(['/usr/bin/pkg', 'foo'], "
            "logger=logging.getLogger())"
        ]

        popen = Popen(cmd, stdout=Popen.DEVNULL, stderr=Popen.DEVNULL)
        for wait_count in xrange(15):
            # 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("subprocess hung while attempting logging")
Пример #2
0
def do_enable_service(cmd_options=None):
    ''' Enable a service

    Parse the supplied arguments then enable the specified service.

    Input:
        List of command line options
    Return:
        None
    Raises:
        SystemExit if missing permissions, invalid service name, or
        if attempt to enable the service or the smf service fails.

    '''
    logging.log(XDEBUG, '**** START do_enable_service ****')

    # check for authorization and euid
    try:
        check_auth_and_euid(SERVICE_AUTH)
    except UnauthorizedUserError as err:
        raise SystemExit(err)

    usage = '\n' + get_enable_usage()
    parser = OptionParser(usage=usage)

    args = parser.parse_args(cmd_options)[1]

    # Check for correct number of args
    if len(args) != 1:
        if len(args) == 0:
            parser.error(_("Missing required argument, <svcname>"))
        else:
            parser.error(_("Too many arguments: %s") % args)

    svcname = args[0]

    if not config.is_service(svcname):
        err_msg = _("The service does not exist: %s\n") % svcname
        parser.error(err_msg)

    # Verify that the server settings are not obviously broken.
    # These checks cannot be complete, but do check for things
    # which will definitely cause failure.
    ret = Popen([CHECK_SETUP_SCRIPT]).wait()
    if ret:
        return 1

    logging.log(XDEBUG, 'Enabling install service %s', svcname)
    try:
        service = AIService(svcname)
        service.enable()
    except (aismf.ServicesError, config.ServiceCfgError, ImageError,
            MountError) as err:
        raise SystemExit(err)
    except InvalidServiceError as err:
        raise SystemExit(
            cw(
                _("\nThis service may not be enabled until all "
                  "invalid manifests and profiles have been "
                  "corrected or removed.\n")))
Пример #3
0
    def _show_url_cb(self):
        '''timer callback to show the URL outside the
           main thread.
        '''
        if self.show_url:
            Popen([FIREFOX, self.show_url])
            self.show_url = None

        return False
Пример #4
0
def is_multihomed():
    ''' Determines if system is multihomed
    Returns True if multihomed, False if not

    '''
    global _IS_MULTIHOMED
    if _IS_MULTIHOMED is None:
        logging.debug("is_multihomed(): Calling %s", MULTIHOMED_TEST)
        _IS_MULTIHOMED = Popen(MULTIHOMED_TEST, shell=True).wait()
    return (_IS_MULTIHOMED != 0)
Пример #5
0
 def call_cmd(self, cmdlist, error_string):
     '''
     Call a command.
     '''
     subproc = Popen(cmdlist, stderr=Popen.STDOUT, stdout=Popen.PIPE)
     if subproc.returncode:
         self.logger.critical(MSG_HEADER + error_string)
         return ""
     else:
         return subproc.stdout.readline().strip()
Пример #6
0
def _fork_proc(cmd, timeout=15):
    '''Run a command in a forked process, with a timeout.

    Kills the process on timeout.
    Logs errors.
    Handles processes with large amounts of data so check_call() is not used.

    Args:
      - cmd: a list of commandline arguments
      - timeout: timeout in seconds
    Returns:
      - Return status:
        - return status of the command run, if command completes.
        - SupportInfo.PH_TIMEOUT if timeout occurs.
      - stdout and stderr of the run process.
    '''
    timeout_tenths = timeout * 10

    def read_subproc(subproc, outbuf, errbuf):
        intoutbuf = interrbuf = ""
        try:
            intoutbuf = subproc.stdout.read()
            outbuf = "".join([outbuf, intoutbuf])
        except IOError as err:
            if err.errno != errno.EAGAIN:
                raise
        try:
            interrbuf = subproc.stderr.read()
            errbuf = "".join([errbuf, interrbuf])
        except IOError as err:
            if err.errno != errno.EAGAIN:
                raise
        return (outbuf, errbuf)

    # Can throw an child_exception if command cannot be started.
    subproc = Popen(cmd, stdout=Popen.PIPE, stderr=Popen.PIPE)

    flags = fcntl.fcntl(subproc.stdout.fileno(), fcntl.F_GETFL)
    fcntl.fcntl(subproc.stdout.fileno(), fcntl.F_SETFL, flags | os.O_NONBLOCK)
    flags = fcntl.fcntl(subproc.stderr.fileno(), fcntl.F_GETFL)
    fcntl.fcntl(subproc.stderr.fileno(), fcntl.F_SETFL, flags | os.O_NONBLOCK)

    (outbuf, errbuf) = read_subproc(subproc, "", "")
    while subproc.poll() is None and timeout_tenths > 0:
        timeout_tenths -= 1
        time.sleep(0.1)
        (outbuf, errbuf) = read_subproc(subproc, outbuf, errbuf)
    if subproc.returncode is None:
        subproc.terminate()
        return (SupportInfo.PH_TIMEOUT, outbuf, errbuf)
    else:
        (outbuf, errbuf) = read_subproc(subproc, outbuf, errbuf)
    return (subproc.returncode, outbuf, errbuf)
Пример #7
0
def do_create_service(cmd_options=None):
    ''' Create either a base service or an alias '''

    # check that we are root
    if os.geteuid() != 0:
        raise SystemExit(
            _("Error: Root privileges are required for this "
              "command.\n"))

    logging.log(com.XDEBUG, '**** START do_create_service ****')

    options = parse_options(cmd_options)

    logging.debug('options: %s', options)

    # Check the network configuration. Verify that the server settings
    # are not obviously broken (i.e., check for things which will definitely
    # cause failure).
    logging.debug('Check if the host server can support AI Install services.')
    cmd = [
        com.CHECK_SETUP_SCRIPT,
        options.dhcp_ip_start if options.dhcp_ip_start else ''
    ]
    logging.debug('Calling %s', cmd)
    # CHECK_SETUP_SCRIPT does math processing that needs to run in "C" locale
    # to avoid problems with alternative # radix point representations
    # (e.g. ',' instead of '.' in cs_CZ.*-locales).
    # Because ksh script uses built-in math we need to set locale here and we
    # can't set it in script itself
    modified_env = os.environ.copy()
    lc_all = modified_env.get('LC_ALL', '')
    if lc_all != '':
        modified_env['LC_MONETARY'] = lc_all
        modified_env['LC_MESSAGES'] = lc_all
        modified_env['LC_COLLATE'] = lc_all
        modified_env['LC_TIME'] = lc_all
        modified_env['LC_CTYPE'] = lc_all
        del modified_env['LC_ALL']
    modified_env['LC_NUMERIC'] = 'C'
    if Popen(cmd, env=modified_env).wait():
        raise SystemExit(1)

    # convert options.bootargs to a string
    if options.bootargs:
        options.bootargs = ",".join(options.bootargs) + ","
    else:
        options.bootargs = ''

    if options.aliasof:
        return (do_alias_service(options))
    else:
        return (do_create_baseservice(options))
Пример #8
0
    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)
Пример #10
0
    # 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()
Пример #11
0
    def _transfer(self):
        '''Method to transfer from the source to the destination'''
        if self.give_progress:
            # Start up the ProgressMon to report progress
            # while the actual transfer is taking place.
            self.pmon = ProgressMon(logger=self.logger)
            # Note: startmonitor assumes there is another thread creating a
            # file system.  If this is not the case (as may be when testing
            # this module in abnormal conditions), startmonitor will hang.
            # Just create the self.dst as a directory in this case.
            self.pmon.startmonitor(self.dst, self.total_size, 0, 100)

        # Perform the transfer specific operations.

        try:
            for trans_val in self._transfer_list:

                # Get the arguments for the transfer process
                arglist = trans_val.get(SVR4_ARGS).split(' ')

                # Parse the components to determine the transfer action
                if trans_val.get(ACTION) == 'install':
                    self.check_cancel_event()
                    self.logger.info("Installing SVR4 packages")
                    cmd = [AbstractSVR4.PKGADD] + arglist + \
                        trans_val.get(CONTENTS)

                elif trans_val.get(ACTION) == 'uninstall':
                    self.check_cancel_event()
                    self.logger.info("Uninstalling SVR4 packages")
                    cmd = [AbstractSVR4.PKGRM] + arglist + \
                        trans_val.get(CONTENTS)

                else:
                    self.logger.warning("Transfer action, %s, is not valid" %
                                        trans_val.get(ACTION))
                    self.check_cancel_event()
                    continue

                if self.dry_run:
                    self.logger.debug("Would execute the following transfer "
                                      "command: %s" % cmd)
                else:
                    self.logger.debug("Executing the following transfer "
                                      "command: %s" % cmd)
                    self.check_cancel_event()
                    pkg_proc = Popen(cmd,
                                     shell=False,
                                     stdout=Popen.PIPE,
                                     stderr=Popen.STDOUT)
                    while 1:
                        self.check_cancel_event()
                        pkgoutput = pkg_proc.stdout.readline()
                        if not pkgoutput:
                            retcode = pkg_proc.poll()
                            if retcode != 0:
                                self.svr4_process = None
                                raise OSError(
                                    retcode, "SVR4 transfer error while "
                                    "adding packages")
                            break
                        pkgoutput = pkgoutput[:-1]
                        if not pkgoutput.strip():
                            continue
                        self.logger.debug("%s", pkgoutput)
                    self.svr4_process = None

        finally:
            if self.pmon:
                self.pmon.done = True
                self.pmon.wait()
                self.pmon = None
Пример #12
0
    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 ")
Пример #13
0
def parse_options(cmd_options=None):
    '''
    Parse and validate options
    '''
    def check_MAC_address(option, opt_str, value, parser):
        '''
        Check MAC address as an OptionParser callback
        Postcondition: sets value to proper option if check passes
        Raises: OptionValueError if MAC address is malformed
        '''
        try:
            value = str(com.MACAddress(value))
        except com.MACAddress.MACAddressError as err:
            raise OptionValueError(str(err))
        setattr(parser.values, option.dest, value)

    usage = '\n' + get_usage()
    parser = OptionParser(usage=usage)

    # accept multiple -b options (so append to a list)
    parser.add_option("-b",
                      "--boot-args",
                      dest="boot_args",
                      action="append",
                      type="string",
                      nargs=1,
                      help=_("boot arguments to pass to Solaris kernel"))
    parser.add_option("-e",
                      "--macaddr",
                      dest="mac_address",
                      action="callback",
                      nargs=1,
                      type="string",
                      help=_("MAC address of client to add"),
                      callback=check_MAC_address)
    parser.add_option("-n",
                      "--service",
                      dest="service_name",
                      action="store",
                      type="string",
                      help=_("Service to associate client with"),
                      nargs=1)
    (options, args) = parser.parse_args(cmd_options)

    if args:
        parser.error(_("Unexpected argument(s): %s" % args))

    # check that we got a service name and mac address
    if options.service_name is None:
        parser.error(
            _("Service name is required "
              "(-n|--service <service name>)."))
    if options.mac_address is None:
        parser.error(_("MAC address is required (-e|--macaddr <macaddr>)."))

    # Verify that the server settings are not obviously broken.
    # These checks cannot be complete, but check for things which
    # will definitely cause failure.
    logging.debug("Calling %s", com.CHECK_SETUP_SCRIPT)
    ret = Popen([com.CHECK_SETUP_SCRIPT]).wait()
    if ret:
        raise SystemExit(1)

    # validate service name
    try:
        com.validate_service_name(options.service_name)
    except ValueError as err:
        raise SystemExit(err)

    # check that the service exists
    service_props = config.get_service_props(options.service_name)
    if not service_props:
        raise SystemExit(
            _("The specified service does not exist: %s\n") %
            options.service_name)

    # get the image_path from the service
    try:
        # set image to be a InstalladmImage object
        image = svc.AIService(options.service_name).image
    except KeyError:
        raise SystemExit(
            _("\nThe specified service does not have an "
              "image_path property.\n"))

    # ensure we are not passed bootargs for a SPARC as we do not
    # support that
    if options.boot_args and image.arch == "sparc":
        parser.error(_("Boot arguments not supported for SPARC clients.\n"))

    options.arch = image.arch

    logging.debug("options = %s", options)

    return options
Пример #14
0
 def releasenotes_cb(self):
     '''timer callback to show the release notes outside the
        main thread.
     '''
     Popen([FIREFOX, self.RELEASENOTESURL])
Пример #15
0
    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())