Exemple #1
0
def VerifyUpdate(directory):
    """
    Verify the update in the directory is valid -- the manifest
    is sane, any signature is valid, the package files necessary to
    update are present, and have a valid checksum.  Returns either
    a file object if it's valid (the file object is locked), None
    if it doesn't exist, or it raises an exception -- one of
    UpdateIncompleteCacheException or UpdateInvalidCacheException --
    if necessary.
    """
    import fcntl

    # First thing we do is get the systen configuration and
    # systen manifest
    conf = Configuration.Configuration()
    mani = conf.SystemManifest()

    # Next, let's see if the directory exists.
    if not os.path.exists(directory):
        return None
    # Open up the manifest file.  Assuming it exists.
    try:
        mani_file = open(directory + "/MANIFEST", "r+")
    except:
        # Doesn't exist.  Or we can't get to it, which would be weird.
        return None
    # Let's try getting an exclusive lock on the manifest
    try:
        fcntl.lockf(mani_file, fcntl.LOCK_EX | fcntl.LOCK_NB, 0, 0)
    except:
        # Well, if we can't acquire the lock, someone else has it.
        # Throw an incomplete exception
        raise UpdateBusyCacheException("Cache directory %s is being modified" %
                                       directory)
    # We always want a valid signature for an update.
    cached_mani = Manifest.Manifest(require_signature=True)
    try:
        cached_mani.LoadFile(mani_file)
    except Exception as e:
        # If we got an exception, it's invalid.
        log.error("Could not load cached manifest file: %s" % str(e))
        raise UpdateInvalidCacheException

    # First easy thing to do:  look for the SEQUENCE file.
    try:
        cached_sequence = open(directory + "/SEQUENCE", "r").read().rstrip()
    except (IOError, Exception) as e:
        log.error("Could not sequence file in cache directory %s: %s" %
                  (directory, str(e)))
        raise UpdateIncompleteCacheException(
            "Cache directory %s does not have a sequence file" % directory)

    # Now let's see if the sequence matches us.
    if cached_sequence != mani.Sequence():
        log.error("Cached sequence, %s, does not match system sequence, %s" %
                  (cached_sequence, mani.Sequence()))
        raise UpdateInvalidCacheException(
            "Cached sequence does not match system sequence")

    # Second easy thing to do:  if there is a SERVER file, make sure it's the same server
    # name we're using
    cached_server = "default"
    try:
        cached_server = open(directory + "/SERVER", "r").read().rstrip()
    except (IOError, Exception) as e:
        log.debug("Could not open SERVER file in cache direcory %s: %s" %
                  (directory, str(e)))
        cached_server = "default"

    if cached_server != conf.UpdateServerName():
        log.error(
            "Cached server, %s, does not match system update server, %s" %
            (cached_server, conf.UpdateServerName()))
        raise UpdateInvalidCacheException(
            "Cached server name does not match system update server")

    # Next thing to do is go through the manifest, and decide which package files we need.
    diffs = Manifest.DiffManifests(mani, cached_mani)
    # This gives us an array to examine.
    # All we care about for verification is the packages
    if "Packages" in diffs:
        for (pkg, op, old) in diffs["Packages"]:
            if op == "delete":
                # Deleted package, so we don't need to do any verification here
                continue
            if op == "install":
                # New package, being installed, so we need the full package
                cur_vers = None
            if op == "upgrade":
                # Package being updated, so we can look for the delta package.
                cur_vers = old.Version()
            new_vers = pkg.Version()
            # This is slightly redundant -- if cur_vers is None, it'll check
            # the same filename twice.
            if not os.path.exists(directory + "/" + pkg.FileName())  and \
               not os.path.exists(directory + "/" + pkg.FileName(cur_vers)):
                # Neither exists, so incoplete
                log.error("Cache %s  directory missing files for package %s" %
                          (directory, pkg.Name()))
                raise UpdateIncompleteCacheException(
                    "Cache directory %s missing files for package %s" %
                    (directory, pkg.Name()))
            # Okay, at least one of them exists.
            # Let's try the full file first
            try:
                with open(directory + "/" + pkg.FileName()) as f:
                    if pkg.Checksum():
                        cksum = Configuration.ChecksumFile(f)
                        if cksum == pkg.Checksum():
                            continue
                    else:
                        continue
            except:
                pass

            if cur_vers is None:
                e = "Cache directory %s missing files for package %s" % (
                    directory, pkg.Name())
                log.error(e)
                raise UpdateIncompleteCacheException(e)

            # Now we try the delta file
            # To do that, we need to find the right dictionary in the pkg
            upd_cksum = None
            update = pkg.Update(cur_vers)
            if update and update.Checksum():
                upd_cksum = update.Checksum()
                try:
                    with open(directory + "/" + pkg.FileName(cur_vers)) as f:
                        cksum = Configuration.ChecksumFile(f)
                        if upd_cksum != cksum:
                            update = None
                except:
                    update = None
            if update is None:
                # If we got here, we are missing this file
                log_msg = "Cache directory %s is missing package %s" % (
                    directory, pkg.Name())
                log.error(log_msg)
                raise UpdateIncompleteCacheException(log_msg)
        # And end that loop
    # And if we got here, then we have found all of the packages, the manifest is fine,
    # and the sequence tag is correct.
    mani_file.seek(0)
    return mani_file
Exemple #2
0
def PendingUpdatesChanges(directory):
    """
    Return a list (a la CheckForUpdates handler right now) of
    changes between the currently installed system and the
    downloaded contents in <directory>.  If <directory>'s values
    are incomplete or invalid for whatever reason, return
    None.  "Incomplete" means a necessary file for upgrading
    from the current system is not present; "Invalid" means that
    one part of it is invalid -- manifest is not valid, signature isn't
    valid, checksum for a file is invalid, or the stashed sequence
    number does not match the current system's sequence.
    """
    mani_file = None
    conf = Configuration.Configuration()
    try:
        mani_file = VerifyUpdate(directory)
    except UpdateBusyCacheException:
        log.debug("Cache directory %s is busy, so no update available" %
                  directory)
        raise
    except (UpdateIncompleteCacheException, UpdateInvalidCacheException) as e:
        log.error(str(e))
        RemoveUpdate(directory)
        raise
    except BaseException as e:
        log.error(
            "Got exception %s while trying to determine pending updates" %
            str(e))
        raise
    if mani_file:
        new_manifest = Manifest.Manifest(require_signature=True)
        try:
            new_manifest.LoadFile(mani_file)
        except ManifestInvalidSignature as e:
            log.error("Invalid signature in cached manifest: %s" % str(e))
            raise
        # This returns a set of differences.
        # But we shouldn't rely on it until we can look at what we've
        # actually downloaded.  To do that, we need to look at any
        # package differences (diffs["Packages"]), and check the
        # updates if that's what got downloaded.
        # By definition, if there are no Packages differences, a reboot
        # isn't required.
        diffs = Manifest.DiffManifests(conf.SystemManifest(), new_manifest)
        if len(diffs) == 0:
            return None
        if "Packages" in diffs:
            reboot = False
            # Look through the install/upgrade packages
            for pkg, op, old in diffs["Packages"]:
                if op == "delete":
                    continue
                if op == "install":
                    if pkg.RequiresReboot() == True:
                        reboot = True
                if op == "upgrade":
                    # A bit trickier.
                    upd = pkg.Update(old.Version())
                    update_fname = os.path.join(directory,
                                                pkg.FileName(old.Version()))
                    if upd and os.path.exists(update_fname):
                        if upd.RequiresReboot() == True:
                            reboot = True
                    else:
                        # Have to assume the full package exists
                        if pkg.RequiresReboot() == True:
                            reboot = True
        else:
            reboot = False
        if len(diffs) == 0:
            return None
        diffs["Reboot"] = reboot
    return diffs
Exemple #3
0
def CheckForUpdates(handler=None,
                    train=None,
                    cache_dir=None,
                    diff_handler=None):
    """
    Check for an updated manifest.  If cache_dir is none, then we try
    to download just the latest manifest for the given train, and
    compare it to the current system.  If cache_dir is set, then we
    use the manifest in that directory.
    """

    conf = Configuration.Configuration()
    new_manifest = None
    if cache_dir:
        try:
            mfile = VerifyUpdate(cache_dir)
            if mfile is None:
                return None
        except UpdateBusyCacheException:
            log.debug("Cache directory %s is busy, so no update available" %
                      cache_dir)
            return None
        except (UpdateIncompleteCacheException,
                UpdateInvalidCacheException) as e:
            log.error(
                "CheckForUpdate(train = %s, cache_dir = %s):  Got exception %s, removing cache"
                % (train, cache_dir, str(e)))
            RemoveUpdate(cache_dir)
            return None
        except BaseException as e:
            log.error(
                "CheckForUpdate(train=%s, cache_dir = %s):  Got exception %s" %
                (train, cache_dir, str(e)))
            raise e
        # We always want a valid signature when doing an update
        new_manifest = Manifest.Manifest(require_signature=True)
        try:
            new_manifest.LoadFile(mfile)
        except Exception as e:
            log.error("Could not load manifest due to %s" % str(e))
            raise e
    else:
        try:
            new_manifest = conf.FindLatestManifest(train=train,
                                                   require_signature=True)
        except Exception as e:
            log.error("Could not find latest manifest due to %s" % str(e))

    if new_manifest is None:
        raise UpdateManifestNotFound("Manifest could not be found!")

    # If new_manifest is not the requested train, then we don't have an update to do
    if train and train != new_manifest.Train():
        log.debug(
            "CheckForUpdate(train = %s, cache_dir = %s):  Wrong train in caache (%s)"
            % (train, cache_dir, new_manifest.Train()))
        return None

    diffs = Manifest.DiffManifests(conf.SystemManifest(), new_manifest)
    if diffs is None or len(diffs) == 0:
        return None
    log.debug("CheckForUpdate:  diffs = %s" % diffs)
    reboot = False
    if handler and "Packages" in diffs:
        for (pkg, op, old) in diffs["Packages"]:
            handler(op, pkg, old)
        # We attempt to see if the update requires a reboot.
        # This is only tentative -- if we can't download a delta package,
        # and that's what allows the update to avoid a reboot, a reboot
        # is going to be required.
        if op == "install":
            if pkg.RequiresReboot() == True:
                reboot = True
        elif op == "upgrade":
            upd = pkg.Update(old.Version())
            if upd:
                if upd.RequiresReboot():
                    reboot = True
            elif pkg.RequiresReboot():
                reboot = True

    diffs["Reboot"] = reboot
    if diff_handler:
        diff_handler(diffs)

    return new_manifest
Exemple #4
0
def DownloadUpdate(train, directory, get_handler=None, check_handler=None):
    """
    Download, if necessary, the LATEST update for train; download
    delta packages if possible.  Checks to see if the existing content
    is the right version.  In addition to the current caching code, it
    will also stash the current sequence when it downloads; this will
    allow it to determine if a reboot into a different boot environment
    has happened.  This will remove the existing content if it decides
    it has to redownload for any reason.
    """
    import shutil
    import fcntl

    conf = Configuration.Configuration()
    mani = conf.SystemManifest()
    # First thing, let's get the latest manifest
    try:
        latest_mani = conf.FindLatestManifest(train, require_signature=True)
    except ManifestInvalidSignature as e:
        log.error("Latest manifest has invalid signature: %s" % str(e))
        return False

    if latest_mani is None:
        # This probably means we have no network.  Which means we have
        # to trust what we've already downloaded, if anything.
        log.error("Unable to find latest manifest for train %s" % train)
        try:
            VerifyUpdate(directory)
            log.debug("Possibly with no network, cached update looks good")
            return True
        except:
            log.debug(
                "Possibly with no network, either no cached update or it is bad"
            )
            return False

    cache_mani = Manifest.Manifest(require_signature=True)
    try:
        mani_file = VerifyUpdate(directory)
        if mani_file:
            cache_mani.LoadFile(mani_file)
            if cache_mani.Sequence() == latest_mani.Sequence():
                # Woohoo!
                mani_file.close()
                log.debug(
                    "DownloadUpdate:  Cache directory has latest manifest")
                return True
            # Not the latest
            mani_file.close()
        mani_file = None
    except UpdateBusyCacheException:
        log.debug("Cache directory %s is busy, so no update available" %
                  directory)
        return False
    except (UpdateIncompleteCacheException, UpdateInvalidCacheException,
            ManifestInvalidSignature) as e:
        # It's incomplete, so we need to remove it
        log.error("DownloadUpdate(%s, %s):  Got exception %s; removing cache" %
                  (train, directory, str(e)))
    except BaseException as e:
        log.error("Got exception %s while trying to prepare update cache" %
                  str(e))
        raise e
    # If we're here, then we don't have a (valid) cached update.
    log.debug("Removing invalid or incomplete cached update")
    RemoveUpdate(directory)
    try:
        os.makedirs(directory)
    except BaseException as e:
        log.error("Unable to create directory %s: %s" % (directory, str(e)))
        return False

    try:
        mani_file = open(directory + "/MANIFEST", "wxb")
    except (IOError, Exception) as e:
        log.error("Unale to create manifest file in directory %s" %
                  (directory, str(e)))
        return False
    try:
        fcntl.lockf(mani_file, fcntl.LOCK_EX | fcntl.LOCK_NB, 0, 0)
    except (IOError, Exception) as e:
        log.debug("Unable to lock manifest file: %s" % str(e))
        mani_file.close()
        return False

    # Find out what differences there are
    diffs = Manifest.DiffManifests(mani, latest_mani)
    if diffs is None or len(diffs) == 0:
        log.debug("DownloadUpdate:  No update available")
        # Remove the cache directory and empty manifest file
        RemoveUpdate(directory)
        return False
    log.debug("DownloadUpdate:  diffs = %s" % diffs)

    download_packages = []
    reboot_required = True
    if "Reboot" in diffs:
        reboot_required = diffs["Reboot"]

    if "Packages" in diffs:
        for pkg, op, old in diffs["Packages"]:
            if op == "delete":
                continue
            log.debug("DownloadUpdate:  Will %s package %s" % (op, pkg.Name()))
            download_packages.append(pkg)

    log.debug("Update does%s seem to require a reboot" %
              "" if reboot_required else " not")

    # Next steps:  download the package files.
    for indx, pkg in enumerate(download_packages):
        # This is where we find out for real if a reboot is required.
        # To do that, we may need to know which update was downloaded.
        if check_handler:
            check_handler(indx + 1, pkg=pkg, pkgList=download_packages)
        pkg_file = conf.FindPackageFile(pkg,
                                        save_dir=directory,
                                        handler=get_handler)
        if pkg_file is None:
            log.error("Could not download package file for %s" % pkg.Name())
            RemoveUpdate(directory)
            return False

    # Almost done:  get a changelog if one exists for the train
    # If we can't get it, we don't care.
    conf.GetChangeLog(train, save_dir=directory, handler=get_handler)
    # Then save the manifest file.
    latest_mani.StoreFile(mani_file)
    # Create the SEQUENCE file.
    with open(directory + "/SEQUENCE", "w") as f:
        f.write("%s" % conf.SystemManifest().Sequence())
    # And create the SERVER file.
    with open(directory + "/SERVER", "w") as f:
        f.write("%s" % conf.UpdateServerName())

    # Then return True!
    mani_file.close()
    return True
Exemple #5
0
def GetUpdateChanges(old_manifest, new_manifest, cache_dir=None):
    """
    This is used by both PendingUpdatesChanges() and CheckForUpdates().
    The difference between the two is that the latter doesn't necessarily
    have a cache directory, so if cache_dir is none, we have to assume the
    update package exists.
    This returns a dictionary that will have at least "Reboot" as a key.
    """
    def MergeServiceList(base_list, new_list):
        """
        Merge new_list into base_list.
        For each service in new_list (which is a dictionary),
        if the value is True, add it to base_list.
        If new_list is an array, simply add each item to
        base_list; if it's a dict, we check the value.
        """
        if new_list is None:
            return base_list
        if isinstance(new_list, list):
            for svc in new_list:
                if not svc in base_list:
                    base_list.append(svc)
        elif isinstance(new_list, dict):
            for svc, val in new_list.iteritems():
                if val:
                    if not svc in base_list:
                        base_list.append(svc)
        return base_list

    svcs = []
    diffs = Manifest.DiffManifests(old_manifest, new_manifest)
    if len(diffs) == 0:
        return None

    reboot = False
    if REQUIRE_REBOOT:
        reboot = True

    if "Packages" in diffs:
        # Look through the install/upgrade packages
        for pkg, op, old in diffs["Packages"]:
            if op == "delete":
                continue
            if op == "install":
                if pkg.RequiresReboot() == True:
                    reboot = True
                else:
                    pkg_services = pkg.RestartServices()
                    if pkg_services:
                        svcs = MergeServiceList(svcs, pkg_services)
            elif op == "upgrade":
                # A bit trickier.
                # If there is a list of services to restart, the update
                # path wants to look at that rather than the requires reboot.
                # However, one service could be to reboot (this is handled
                # below).
                upd = pkg.Update(old.Version())
                if cache_dir:
                    update_fname = os.path.join(cache_dir,
                                                pkg.FileName(old.Version()))
                else:
                    update_fname = None

                if upd and (update_fname is None
                            or os.path.exists(update_fname)):
                    pkg_services = upd.RestartServices()
                    if pkg_services:
                        svcs = MergeServiceList(svcs, pkg_services)
                    else:
                        if upd.RequiresReboot() == True:
                            reboot = True
                else:
                    # Have to assume the full package exists
                    if pkg.RequiresReboot() == True:
                        reboot = True
                    else:
                        pkg_services = pkg.RestartServices()
                        if pkg_services:
                            svcs = MergeServiceList(svcs, pkg_services)
    else:
        reboot = False
    if len(diffs) == 0:
        return None
    if not reboot and svcs:
        if not VerifyServices(svcs):
            reboot = True
        else:
            diffs["Restart"] = svcs
    diffs["Reboot"] = reboot
    return diffs