Beispiel #1
0
def DoUpdate(cache_dir, verbose):
    """
    Common code to apply an update once it's been downloaded.
    This will handle all of the exceptions in a common fashion.
    Raises an exception on error.
    """
    global log

    try:
        diffs = Update.PendingUpdatesChanges(cache_dir)
    except Exceptions.UpdateBusyCacheException:
        log.error("Cache directory busy, cannot update")
        raise
    except Exceptions.UpdateInvalidUpdateException as e:
        log.error("Unable not permitted: {0}".format(e.value))
        raise
    except BaseException as e:
        log.error("Unable to update: {0}".format(str(e)))
        raise
    if verbose:
        PrintDifferences(diffs)

    if not diffs:
        log.debug("No updates to apply")
        return

    try:
        rv = Update.ApplyUpdate(cache_dir)
    except BaseException as e:
        log.error("Unable to apply update: {0}".format(str(e)))
        raise
    if rv and verbose:
        print("System should be rebooted now", file=sys.stderr)

    return
Beispiel #2
0
    def get_pending(self, path=None):
        if path is None:
            path = self.middleware.call('notifier.get_update_location')
        data = []
        changes = Update.PendingUpdatesChanges(path)
        if changes:
            if changes.get("Reboot", True) is False:
                for svc in changes.get("Restart", []):
                    data.append({
                        'operation': svc,
                        'name': Update.GetServiceDescription(svc),
                    })
            for new, op, old in changes['Packages']:
                if op == 'upgrade':
                    name = '%s-%s -> %s-%s' % (
                        old.Name(),
                        old.Version(),
                        new.Name(),
                        new.Version(),
                    )
                elif op == 'install':
                    name = '%s-%s' % (new.Name(), new.Version())
                else:
                    # Its unclear why "delete" would feel out new
                    # instead of old, sounds like a pkgtools bug?
                    if old:
                        name = '%s-%s' % (old.Name(), old.Version())
                    else:
                        name = '%s-%s' % (new.Name(), new.Version())

                data.append({
                    'operation': op,
                    'name': name,
                })
        return data
Beispiel #3
0
def get_pending_updates(path):
    data = []
    changes = Update.PendingUpdatesChanges(path)
    if changes:
        if changes.get("Reboot", True) is False:
            for svc in changes.get("Restart", []):
                data.append({
                    'operation': svc,
                    'name': Update.GetServiceDescription(svc),
                })
        for new, op, old in changes['Packages']:
            if op == 'upgrade':
                name = '%s-%s -> %s-%s' % (
                    old.Name(),
                    old.Version(),
                    new.Name(),
                    new.Version(),
                )
            elif op == 'install':
                name = '%s-%s' % (new.Name(), new.Version())
            else:
                name = '%s-%s' % (old.Name(), old.Version())

            data.append({
                'operation': op,
                'name': name,
            })
    return data
def DoUpdate(cache_dir, verbose, ignore_space=False, force_trampoline=None):
    """
    Common code to apply an update once it's been downloaded.
    This will handle all of the exceptions in a common fashion.
    Raises an exception on error.
    """
    global log

    try:
        diffs = Update.PendingUpdatesChanges(cache_dir)
    except Exceptions.UpdateBusyCacheException:
        log.error("Cache directory busy, cannot update")
        raise
    except Exceptions.UpdateInvalidUpdateException as e:
        log.error("Unable not permitted: {0}".format(e.value))
        raise
    except BaseException as e:
        log.error("Unable to update: {0}".format(str(e)))
        raise
    if verbose:
        PrintDifferences(diffs)

    if not diffs:
        log.debug("No updates to apply")
        return False

    try:
        if not verbose:
            with ProgressBar() as progress_bar:
                handler = UpdateHandler(progress_bar.update)
                rv = Update.ApplyUpdate(
                    cache_dir,
                    install_handler=handler.install_handler,
                    ignore_space=ignore_space,
                    force_trampoline=force_trampoline,
                )
                if rv is False:
                    progress_bar.update(message="Updates were not applied")
        else:
            with ProgressHandler() as pf:
                rv = Update.ApplyUpdate(
                    cache_dir,
                    progressFunc=pf.update,
                    ignore_space=ignore_space,
                    force_trampoline=force_trampoline,
                )

    except Exceptions.UpdateInsufficientSpace as e:
        log.error(str(e))
        print(e.value if e.value else "Insufficient space for update")
        sys.exit(1)
    except BaseException as e:
        log.error("Unable to apply update: {0}".format(str(e)))
        raise
    if rv and verbose:
        print("System should be rebooted now", file=sys.stderr)

    return rv
Beispiel #5
0
    def get_pending_in_path(self, path):
        data = []
        try:
            changes = Update.PendingUpdatesChanges(path)
        except (
                UpdateIncompleteCacheException,
                UpdateInvalidCacheException,
                UpdateBusyCacheException,
        ):
            changes = []
        if changes:
            if changes.get("Reboot", True) is False:
                for svc in changes.get("Restart", []):
                    data.append({
                        'operation': svc,
                        'name': Update.GetServiceDescription(svc),
                    })
            for new, op, old in changes['Packages']:
                if op == 'upgrade':
                    name = '%s-%s -> %s-%s' % (
                        old.Name(),
                        old.Version(),
                        new.Name(),
                        new.Version(),
                    )
                elif op == 'install':
                    name = '%s-%s' % (new.Name(), new.Version())
                else:
                    # Its unclear why "delete" would feel out new
                    # instead of old, sounds like a pkgtools bug?
                    if old:
                        name = '%s-%s' % (old.Name(), old.Version())
                    else:
                        name = '%s-%s' % (new.Name(), new.Version())

                data.append({
                    'operation': op,
                    'name': name,
                })
        return data
Beispiel #6
0
def main():
    global log

    log_config_dict = {
        'version': 1,
        'disable_existing_loggers': False,
        'formatters': {
            'simple': {
                'format': '[%(name)s:%(lineno)s] %(message)s',
            },
        },
        'filters': {
            'cleandownload': {
                '()': StartsWithFilter,
                'params': ['TryGetNetworkFile', 'Searching']
            }
        },
        'handlers': {
            'std': {
                'class': 'logging.StreamHandler',
                'level': 'DEBUG',
                'stream': 'ext://sys.stderr',
            },
        },
        'loggers': {
            '': {
                'handlers': ['std'],
                'level': 'DEBUG',
                'propagate': True,
            },
        },
    }

    def usage():
        print(
            """Usage: {0} [-C cache_dir] [-d] [-T train] [--no-delta] [-v] <cmd>
or	{0} <update_tar_file>
where cmd is one of:
        check\tCheck for updates
        update\tDo an update""".format(sys.argv[0]),
            file=sys.stderr)
        sys.exit(1)

    try:
        short_opts = "C:dT:v"
        long_opts = ["cache=", "debug", "train=", "verbose", "no-delta", "snl"]
        opts, args = getopt.getopt(sys.argv[1:], short_opts, long_opts)
    except getopt.GetoptError as err:
        print(str(err), file=sys.stderr)
        usage()

    verbose = False
    debug = 0
    config = None
    # Should I get this from a configuration file somewhere?
    cache_dir = "/var/db/system/update"
    train = None
    pkg_type = None
    snl = False

    for o, a in opts:
        if o in ("-v", "--verbose"):
            verbose = True
        elif o in ("-d", "--debug"):
            debug += 1
        elif o in ('-C', "--cache"):
            cache_dir = a
        elif o in ("-T", "--train"):
            train = a
        elif o in ("--no-delta"):
            pkg_type = Update.PkgFileFullOnly
        elif o in ("--snl"):
            snl = True
        else:
            assert False, "unhandled option {0}".format(o)

    if not verbose:
        log_config_dict['handlers']['std']['filters'] = ['cleandownload']
    logging.config.dictConfig(log_config_dict)
    log = logging.getLogger('freenas-update')

    config = Configuration.Configuration()
    if train is None:
        train = config.SystemManifest().Train()

    if len(args) != 1:
        usage()

    if args[0] == "check":
        # To see if we have an update available, we
        # call Update.DownloadUpdate.  If we have been
        # given a cache directory, we pass that in; otherwise,
        # we make a temporary directory and use that.  We
        # have to clean up afterwards in that case.

        rv = DoDownload(train, cache_dir, pkg_type, verbose)
        if rv is False:
            if verbose:
                print("No updates available")
            Update.RemoveUpdate(cache_dir)
            sys.exit(1)
        else:
            diffs = Update.PendingUpdatesChanges(cache_dir)
            if diffs is None or len(diffs) == 0:
                print(
                    "Strangely, DownloadUpdate says there updates, but PendingUpdates says otherwise",
                    file=sys.stderr)
                sys.exit(1)
            PrintDifferences(diffs)
            if snl:
                print(
                    "I've got a fever, and the only prescription is applying the pending update."
                )
            sys.exit(0)

    elif args[0] == "update":
        # This will attempt to apply an update.
        # If cache_dir is given, then we will only check that directory,
        # not force a download if it is already there.  If cache_dir is not
        # given, however, then it downloads.  (The reason is that you would
        # want to run "freenas-update -c /foo check" to look for an update,
        # and it will download the latest one as necessary, and then run
        # "freenas-update -c /foo update" if it said there was an update.
        try:
            update_opts, update_args = getopt.getopt(args[1:], "R", "--reboot")
        except getopt.GetoptError as err:
            print(str(err), file=sys.stderr)
            usage()

        force_reboot = False
        for o, a in update_opts:
            if o in ("-R", "--reboot"):
                force_reboot = True
            else:
                assert False, "Unhandled option {0}".format(o)

        # See if the cache directory has an update downloaded already
        do_download = True
        try:
            f = Update.VerifyUpdate(cache_dir)
            if f:
                f.close()
                do_download = False
        except Exceptions.UpdateBusyCacheException:
            print("Cache directory busy, cannot update", file=sys.stderr)
            sys.exit(0)
        except (Exceptions.UpdateInvalidCacheException,
                Exceptions.UpdateIncompleteCacheException):
            pass
        except:
            raise

        if do_download:
            rv = DoDownload(train, cache_dir, pkg_type, verbose)

        try:
            DoUpdate(cache_dir, verbose)
        except:
            sys.exit(1)
        else:
            sys.exit(0)
    else:
        # If it's not a tarfile (possibly because it doesn't exist),
        # print usage and exit.
        try:
            if len(args) > 1:
                usage()
            if not tarfile.is_tarfile(args[0]):
                usage()
        except:
            usage()

        # Frozen tarball.  We'll extract it into the cache directory, and
        # then add a couple of things to make it pass sanity, and then apply it.
        # For now we just copy the code above.
        # First, remove the cache directory
        # Hrm, could overstep a locked file.
        shutil.rmtree(cache_dir, ignore_errors=True)
        try:
            os.makedirs(cache_dir)
        except BaseException as e:
            print("Unable to create cache directory {0}: {1}".format(
                cache_dir, str(e)))
            sys.exit(1)

        try:
            ExtractFrozenUpdate(args[0], cache_dir, verbose=verbose)
        except BaseException as e:
            print("Unable to extract frozen update {0}: {1}".format(
                args[0], str(e)))
            sys.exit(1)
        # Exciting!  Now we need to have a SEQUENCE file, or it will fail verification.
        with open(os.path.join(cache_dir, "SEQUENCE"), "w") as s:
            s.write(config.SystemManifest().Sequence())
        # And now the SERVER file
        with open(os.path.join(cache_dir, "SERVER"), "w") as s:
            s.write(config.UpdateServerName())

        try:
            DoUpdate(cache_dir, verbose)
        except:
            sys.exit(1)
        else:
            sys.exit(0)
def main():
    global log

    def usage():
        print(
            """Usage: {0} [-C cache_dir] [-d] [-T train] [--no-delta] [--reboot|-R] [--server|-S server][-B|--trampline yes|no] [--force|-F] [-v] <cmd>
or	{0} <update_tar_file>
where cmd is one of:
        check\tCheck for updates
        update\tDo an update""".format(sys.argv[0]),
            file=sys.stderr)
        sys.exit(1)

    try:
        short_opts = "B:C:dFRS:T:v"
        long_opts = [
            "cache=", "debug", "reboot", "train=", "verbose", "no-delta",
            "force", "server=", "trampoline=", "snl"
        ]
        opts, args = getopt.getopt(sys.argv[1:], short_opts, long_opts)
    except getopt.GetoptError as err:
        print(str(err), file=sys.stderr)
        usage()

    do_reboot = False
    verbose = False
    debug = 0
    config = None
    # Should I get this from a configuration file somewhere?
    cache_dir = "/var/db/system/update"
    train = None
    pkg_type = None
    snl = False
    force = False
    server = None
    force_trampoline = None

    for o, a in opts:
        if o in ("-v", "--verbose"):
            verbose = True
        elif o in ("-d", "--debug"):
            debug += 1
        elif o in ('-C', "--cache"):
            cache_dir = a
        elif o in ("-R", "--reboot"):
            do_reboot = True
        elif o in ("-S", "--server"):
            server = a
        elif o in ("-T", "--train"):
            train = a
        elif o in ("--no-delta"):
            pkg_type = Update.PkgFileFullOnly
        elif o in ("-B", "--trampoline"):
            if a in ("true", "True", "yes", "YES", "Yes"):
                force_trampoline = True
            elif a in ("false", "False", "no", "NO", "No"):
                force_trampoline = False
            else:
                print("Trampoline option must be boolean [yes/no]",
                      file=sys.stderr)
                usage()
        elif o in ("--snl"):
            snl = True
        elif o in ("-F", "--force"):
            force = True
        else:
            assert False, "unhandled option {0}".format(o)

    if verbose:
        log_to_handler('stderr')
    log = logging.getLogger('freenas-update')

    config = Configuration.SystemConfiguration()
    if server:
        assert server in config.ListUpdateServers(
        ), "Unknown update server {}".format(server)
        config.SetUpdateServer(server, save=False)

    if train is None:
        train = config.SystemManifest().Train()

    if len(args) != 1:
        usage()

    if args[0] == "check":
        # To see if we have an update available, we
        # call Update.DownloadUpdate.  If we have been
        # given a cache directory, we pass that in; otherwise,
        # we make a temporary directory and use that.  We
        # have to clean up afterwards in that case.

        rv = DoDownload(train,
                        cache_dir,
                        pkg_type,
                        verbose,
                        ignore_space=force)
        if rv is False:
            if verbose:
                print("No updates available")
            Update.RemoveUpdate(cache_dir)
            sys.exit(1)
        else:
            diffs = Update.PendingUpdatesChanges(cache_dir)
            if diffs is None or len(diffs) == 0:
                print(
                    "Strangely, DownloadUpdate says there updates, but PendingUpdates says otherwise",
                    file=sys.stderr)
                sys.exit(1)
            PrintDifferences(diffs)
            if snl:
                print(
                    "I've got a fever, and the only prescription is applying the pending update."
                )
            sys.exit(0)

    elif args[0] == "update":
        # This will attempt to apply an update.
        # If cache_dir is given, then we will only check that directory,
        # not force a download if it is already there.  If cache_dir is not
        # given, however, then it downloads.  (The reason is that you would
        # want to run "freenas-update -c /foo check" to look for an update,
        # and it will download the latest one as necessary, and then run
        # "freenas-update -c /foo update" if it said there was an update.

        # See if the cache directory has an update downloaded already
        do_download = True
        try:
            f = Update.VerifyUpdate(cache_dir)
            if f:
                f.close()
                do_download = False
        except Exceptions.UpdateBusyCacheException:
            print("Cache directory busy, cannot update", file=sys.stderr)
            sys.exit(0)
        except (Exceptions.UpdateInvalidCacheException,
                Exceptions.UpdateIncompleteCacheException):
            pass
        except:
            raise

        if do_download:
            rv = DoDownload(train,
                            cache_dir,
                            pkg_type,
                            verbose,
                            ignore_space=force)
            if rv is False:
                if verbose:
                    print("No updates available")
                Update.RemoveUpdate(cache_dir)
                sys.exit(1)

        try:
            rv = DoUpdate(cache_dir,
                          verbose,
                          ignore_space=force,
                          force_trampoline=force_trampoline)
        except:
            sys.exit(1)
        else:
            if rv:
                if do_reboot:
                    os.system("/sbin/shutdown -r now")
                sys.exit(0)
            else:
                sys.exit(1)
    else:
        # If it's not a tarfile (possibly because it doesn't exist),
        # print usage and exit.
        try:
            if len(args) > 1:
                usage()
            if not tarfile.is_tarfile(args[0]):
                usage()
        except:
            usage()

        # Frozen tarball.  We'll extract it into the cache directory, and
        # then add a couple of things to make it pass sanity, and then apply it.
        # For now we just copy the code above.
        # First, remove the cache directory
        # Hrm, could overstep a locked file.
        shutil.rmtree(cache_dir, ignore_errors=True)
        try:
            os.makedirs(cache_dir)
        except BaseException as e:
            print("Unable to create cache directory {0}: {1}".format(
                cache_dir, str(e)))
            sys.exit(1)

        try:
            Update.ExtractFrozenUpdate(args[0], cache_dir, verbose=verbose)
        except BaseException as e:
            print("Unable to extract frozen update {0}: {1}".format(
                args[0], str(e)))
            sys.exit(1)
        try:
            rv = DoUpdate(cache_dir,
                          verbose,
                          ignore_space=force,
                          force_trampoline=force_trampoline)
        except:
            sys.exit(1)
        else:
            if rv:
                if do_reboot:
                    os.system("/sbin/shutdown -r now")
                sys.exit(0)
            else:
                sys.exit(1)
Beispiel #8
0
def main():
    global log

    def usage():
        print >> sys.stderr, """Usage: %s [-C cache_dir] [-d] [-T train] [-v] <cmd>, where cmd is one of:
        check\tCheck for updates
        update\tDo an update""" % sys.argv[0]
        sys.exit(1)

    try:
        opts, args = getopt.getopt(sys.argv[1:], "C:dT:v")
    except getopt.GetoptError as err:
        print str(err)
        usage()

    verbose = False
    debug = 0
    config = None
    cache_dir = None
    train = None

    for o, a in opts:
        if o == "-v":
            verbose = True
        elif o == "-d":
            debug += 1
        elif o == '-C':
            cache_dir = a
        elif o == "-T":
            train = a
        elif o == '-c':
            cachedir = a
        else:
            assert False, "unhandled option %s" % o

    logging.config.dictConfig({
        'version': 1,
        'disable_existing_loggers': True,
        'formatters': {
            'simple': {
                'format': '[%(name)s:%(lineno)s] %(message)s',
            },
        },
        'handlers': {
            'std': {
                'class': 'logging.StreamHandler',
                'level': 'DEBUG',
                'stream': 'ext://sys.stderr',
            },
        },
        'loggers': {
            '': {
                'handlers': ['std'],
                'level': 'DEBUG',
                'propagate': True,
            },
        },
    })

    log = logging.getLogger('freenas-update')

    sys.path.append("/usr/local/lib")

    import freenasOS.Configuration as Configuration
    import freenasOS.Manifest as Manifest
    import freenasOS.Update as Update

    config = Configuration.Configuration()
    if train is None:
        train = config.SystemManifest().Train()

    if len(args) != 1:
        usage()

    if args[0] == "check":
        # To see if we have an update available, we
        # call Update.DownloadUpdate.  If we have been
        # given a cache directory, we pass that in; otherwise,
        # we make a temporary directory and use that.  We
        # have to clean up afterwards in that case.

        if cache_dir is None:
            download_dir = tempfile.mkdtemp(prefix="UpdateCheck-",
                                            dir=config.TemporaryDirectory())
            if download_dir is None:
                print >> sys.stderr, "Unable to create temporary directory"
                sys.exit(1)
        else:
            download_dir = cache_dir

        rv = Update.DownloadUpdate(train, download_dir)
        if rv is False:
            if verbose:
                print "No updates available"
            if cache_dir is None:
                Update.RemoveUpdate(download_dir)
            sys.exit(1)
        else:
            diffs = Update.PendingUpdatesChanges(download_dir)
            if diffs is None or len(diffs) == 0:
                print >> sys.stderr, "Strangely, DownloadUpdate says there updates, but PendingUpdates says otherwise"
                sys.exit(1)
            PrintDifferences(diffs)
            if cache_dir is None:
                Update.RemoveUpdate(download_dir)
            sys.exit(0)

    elif args[0] == "update":
        # This will attempt to apply an update.
        # If cache_dir is given, then we will only check that directory,
        # not force a download if it is already there.  If cache_dir is not
        # given, however, then it downloads.  (The reason is that you would
        # want to run "freenas-update -c /foo check" to look for an update,
        # and it will download the latest one as necessary, and then run
        # "freenas-update -c /foo update" if it said there was an update.
        try:
            update_opts, update_args = getopt.getopt(args[1:], "R", "--reboot")
        except getopt.GetoptError as err:
            print str(err)
            usage()

        force_reboot = False
        for o, a in update_opts:
            if o in ("-R", "--reboot"):
                force_reboot = True
            else:
                assert False, "Unhandled option %s" % o

        if cache_dir is None:
            download_dir = tempfile.mkdtemp(prefix="UpdateUpdate-",
                                            dir=config.TemporaryDirectory())
            if download_dir is None:
                print >> sys.stderr, "Unable to create temporary directory"
                sys.exit(1)
            rv = Update.DownloadUpdate(train, download_dir)
            if rv is False:
                if verbose or debug:
                    print >> sys.stderr, "DownloadUpdate returned False"
                sys.exit(1)
        else:
            download_dir = cache_dir

        diffs = Update.PendingUpdatesChanges(download_dir)
        if diffs is None or diffs == {}:
            if verbose:
                print >> sys.stderr, "No updates to apply"
        else:
            if verbose:
                PrintDifferences(diffs)
            try:
                rv = Update.ApplyUpdate(download_dir,
                                        force_reboot=force_reboot)
            except BaseException as e:
                print >> sys.stderr, "Unable to apply update: %s" % str(e)
                sys.exit(1)
            if cache_dir is None:
                Update.RemoveUpdate(download_dir)
            if rv:
                print >> sys.stderr, "System should be rebooted now"
            sys.exit(0)
    else:
        usage()
Beispiel #9
0
def main():
    global log

    logging.config.dictConfig({
        'version': 1,
        'disable_existing_loggers': True,
        'formatters': {
            'simple': {
                'format': '[%(name)s:%(lineno)s] %(message)s',
            },
        },
        'handlers': {
            'std': {
                'class': 'logging.StreamHandler',
                'level': 'DEBUG',
                'stream': 'ext://sys.stderr',
            },
        },
        'loggers': {
            '': {
                'handlers': ['std'],
                'level': 'DEBUG',
                'propagate': True,
            },
        },
    })

    log = logging.getLogger('freenas-update')

    sys.path.append("/usr/local/lib")

    import freenasOS.Configuration as Configuration
    import freenasOS.Manifest as Manifest
    import freenasOS.Update as Update
    import freenasOS.Exceptions as Exceptions

    def usage():
        print(
            """Usage: %s [-C cache_dir] [-d] [-T train] [--no-delta] [-v] <cmd>, where cmd is one of:
        check\tCheck for updates
        update\tDo an update""" % sys.argv[0],
            file=sys.stderr)
        sys.exit(1)

    try:
        short_opts = "C:dT:v"
        long_opts = ["cache=", "debug", "train=", "verbose", "no-delta", "snl"]
        opts, args = getopt.getopt(sys.argv[1:], short_opts, long_opts)
    except getopt.GetoptError as err:
        print(str(err))
        usage()

    verbose = False
    debug = 0
    config = None
    # Should I get this from a configuration file somewhere?
    cache_dir = "/var/db/system/update"
    train = None
    pkg_type = None
    snl = False

    for o, a in opts:
        if o in ("-v", "--verbose"):
            verbose = True
        elif o in ("-d", "--debug"):
            debug += 1
        elif o in ('-C', "--cache"):
            cache_dir = a
        elif o in ("-T", "--train"):
            train = a
        elif o in ("--no-delta"):
            pkg_type = Update.PkgFileFullOnly
        elif o in ("--snl"):
            snl = True
        else:
            assert False, "unhandled option %s" % o

    config = Configuration.Configuration()
    if train is None:
        train = config.SystemManifest().Train()

    if len(args) != 1:
        usage()

    if args[0] == "check":
        # To see if we have an update available, we
        # call Update.DownloadUpdate.  If we have been
        # given a cache directory, we pass that in; otherwise,
        # we make a temporary directory and use that.  We
        # have to clean up afterwards in that case.

        rv = DoDownload(train, cache_dir, pkg_type)
        if rv is False:
            if verbose:
                print("No updates available")
            if cache_dir is None:
                Update.RemoveUpdate(cache_dir)
            sys.exit(1)
        else:
            diffs = Update.PendingUpdatesChanges(cache_dir)
            if diffs is None or len(diffs) == 0:
                print(
                    "Strangely, DownloadUpdate says there updates, but PendingUpdates says otherwise",
                    file=sys.stderr)
                sys.exit(1)
            PrintDifferences(diffs)
            if snl:
                print(
                    "I've got a fever, and the only prescription is applying the pending update."
                )
            sys.exit(0)

    elif args[0] == "update":
        # This will attempt to apply an update.
        # If cache_dir is given, then we will only check that directory,
        # not force a download if it is already there.  If cache_dir is not
        # given, however, then it downloads.  (The reason is that you would
        # want to run "freenas-update -c /foo check" to look for an update,
        # and it will download the latest one as necessary, and then run
        # "freenas-update -c /foo update" if it said there was an update.
        try:
            update_opts, update_args = getopt.getopt(args[1:], "R", "--reboot")
        except getopt.GetoptError as err:
            print(str(err))
            usage()

        force_reboot = False
        for o, a in update_opts:
            if o in ("-R", "--reboot"):
                force_reboot = True
            else:
                assert False, "Unhandled option %s" % o

        # See if the cache directory has an update downloaded already
        do_download = True
        try:
            f = Update.VerifyUpdate(cache_dir)
            if f:
                f.close()
                do_download = False
        except Exceptions.UpdateBusyCacheException:
            printf("Cache directory busy, cannot update")
            sys.exit(0)
        except (Exceptions.UpdateInvalidCacheException,
                Exceptions.UpdateIncompleteCacheException):
            pass
        except:
            raise

        if do_download:
            rv = DoDownload(train, cache_dir, pkg_type)

        diffs = Update.PendingUpdatesChanges(cache_dir)
        if diffs is None or diffs == {}:
            if verbose:
                print("No updates to apply", file=sys.stderr)
        else:
            if verbose:
                PrintDifferences(diffs)
            try:
                rv = Update.ApplyUpdate(cache_dir, force_reboot=force_reboot)
            except BaseException as e:
                print("Unable to apply update: %s" % str(e), file=sys.stderr)
                sys.exit(1)
            if rv:
                print("System should be rebooted now", file=sys.stderr)
                if snl:
                    print("Really explore the space.")
            sys.exit(0)
    else:
        usage()