async def update(self, job, attrs=None): """ Downloads (if not already in cache) and apply an update. """ attrs = attrs or {} train = attrs.get('train') or (await self.get_trains())['selected'] location = await self.middleware.call('notifier.get_update_location') job.set_progress(0, 'Retrieving update manifest') handler = UpdateHandler(self, job) update = Update.DownloadUpdate( train, location, check_handler=handler.check_handler, get_handler=handler.get_handler, ) if update is False: raise ValueError('No update available') new_manifest = Manifest.Manifest(require_signature=True) new_manifest.LoadPath('{}/MANIFEST'.format(location)) Update.ApplyUpdate( location, install_handler=handler.install_handler, ) await self.middleware.call('cache.put', 'update.applied', True) if attrs.get('reboot'): await self.middleware.call('system.reboot', {'delay': 10}) return True
def download(self, job): train = self.middleware.call_sync('update.get_trains')['selected'] location = self.middleware.call_sync('notifier.get_update_location') job.set_progress(0, 'Retrieving update manifest') handler = UpdateHandler(self, job, 100) Update.DownloadUpdate( train, location, check_handler=handler.check_handler, get_handler=handler.get_handler, ) update = Update.CheckForUpdates(train=train, cache_dir=location) if not update: return False notified = False try: if self.middleware.call_sync('cache.has_key', 'update.notified'): notified = self.middleware.call_sync('cache.get', 'update.notified') except Exception: pass if not notified: self.middleware.call_sync('cache.put', 'update.notified', True) conf = Configuration.Configuration() sys_mani = conf.SystemManifest() if sys_mani: sequence = sys_mani.Sequence() else: sequence = '' changelog = get_changelog(train, start=sequence, end=update.Sequence()) try: # FIXME: Translation self.middleware.call_sync( 'mail.send', { 'subject': 'Update Available', 'text': '''A new update is available for the %(train)s train. Version: %(version)s Changelog: %(changelog)s ''' % { 'train': train, 'version': update.Version(), 'changelog': changelog, }, }).wait_sync() except Exception: self.logger.warn('Failed to send email about new update', exc_info=True) return True
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 set_attribute(self, oid, attrs): """ Sets attributes boot environment `id`. Currently only `keep` attribute is allowed. """ clone = Update.FindClone(oid) return Update.CloneSetAttr(clone, **attrs)
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
def main(): try: updateobj = mUpdate.objects.order_by('-id')[0] except IndexError: updateobj = mUpdate.objects.create() if updateobj.upd_autocheck is False: return location = notifier().get_update_location() Update.DownloadUpdate(updateobj.get_train(), location) update = Update.CheckForUpdates( train=updateobj.get_train(), cache_dir=location, ) if not update: return conf = Configuration.Configuration() sys_mani = conf.SystemManifest() if sys_mani: sequence = sys_mani.Sequence() else: sequence = '' changelog = get_changelog( updateobj.get_train(), start=sequence, end=update.Sequence(), ) hostname = socket.gethostname() send_mail( subject='%s: %s' % ( hostname, _('Update Available'), ), extra_headers={ 'X-Mailer': get_sw_name(), 'X-%s-Host' % get_sw_name(): socket.gethostname() }, text=_('''A new update is available for the %(train)s train. Version: %(version)s Changelog: %(changelog)s ''') % { 'train': updateobj.get_train(), 'version': update.Version(), 'changelog': changelog, }, )
async def download(self, job): train = (await self.get_trains())['selected'] location = await self.middleware.call('notifier.get_update_location') Update.DownloadUpdate( train, location, ) update = Update.CheckForUpdates(train=train, cache_dir=location) if not update: return False notified = False try: if await self.middleware.call('cache.has_key', 'update.notified'): notified = await self.middleware.call('cache.get', 'update.notified') except Exception: pass if not notified: await self.middleware.call('cache.put', 'update.notified', True) conf = Configuration.Configuration() sys_mani = conf.SystemManifest() if sys_mani: sequence = sys_mani.Sequence() else: sequence = '' changelog = get_changelog(train, start=sequence, end=update.Sequence()) hostname = socket.gethostname() try: # FIXME: Translation await self.middleware.call( 'mail.send', { 'subject': '{}: {}'.format(hostname, 'Update Available'), 'text': '''A new update is available for the %(train)s train. Version: %(version)s Changelog: %(changelog)s ''' % { 'train': train, 'version': update.Version(), 'changelog': changelog, }, }) except Exception: self.logger.warn('Failed to send email about new update', exc_info=True) return True
def DoDownload(train, cache_dir, pkg_type, verbose, ignore_space=False): try: if not verbose: with ProgressBar() as progress_bar: handler = UpdateHandler(progress_bar.update) rv = Update.DownloadUpdate( train, cache_dir, get_handler=handler.get_handler, check_handler=handler.check_handler, pkg_type=pkg_type, ) if rv is False: progress_bar.update(message="No updates available") else: rv = Update.DownloadUpdate(train, cache_dir, pkg_type=pkg_type, ignore_space=ignore_space) except Exceptions.ManifestInvalidSignature: log.error("Manifest has invalid signature") print("Manifest has invalid signature", file=sys.stderr) sys.exit(1) except Exceptions.UpdateBusyCacheException as e: log.error(str(e)) print("Download cache directory is busy", file=sys.stderr) sys.exit(1) except Exceptions.UpdateIncompleteCacheException: log.error(str(e)) print("Incomplete download cache, cannot update", file=sys.stderr) sys.exit(1) except Exceptions.ChecksumFailException as e: log.error(str(e)) print("Checksum error, cannot update", file=sys.stderr) sys.exit(1) except Exceptions.UpdateInvalidUpdateException as e: log.error(str(e)) print("Update not permitted:\n{0}".format(e.value), file=sys.stderr) sys.exit(1) except Exceptions.UpdateInsufficientSpace as e: log.error(str(e), exc_info=True) print(e.value if e.value else "Insufficient space for download") sys.exit(1) except BaseException as e: log.error(str(e), exc_info=True) print("Received exception during download phase, cannot update", file=sys.stderr) sys.exit(1) return rv
def download_impl(self, job, train, location, progress_proportion): job.set_progress(0, 'Retrieving update manifest') handler = UpdateHandler(self, job, progress_proportion) Update.DownloadUpdate( train, location, check_handler=handler.check_handler, get_handler=handler.get_handler, ) update = Update.CheckForUpdates(train=train, cache_dir=location) return bool(update)
async def update(self, job, attrs=None): """ Downloads (if not already in cache) and apply an update. """ attrs = attrs or {} trains = await self.middleware.call('update.get_trains') train = attrs.get('train') or trains['selected'] if attrs.get('train'): await self.middleware.run_in_thread(self.__set_train, attrs.get('train'), trains) location = await self.middleware.call('update.get_update_location') job.set_progress(0, 'Retrieving update manifest') handler = UpdateHandler(self, job) update = Update.DownloadUpdate( train, location, check_handler=handler.check_handler, get_handler=handler.get_handler, ) if update is False: raise ValueError('No update available') new_manifest = Manifest.Manifest(require_signature=True) new_manifest.LoadPath('{}/MANIFEST'.format(location)) Update.ApplyUpdate( location, install_handler=handler.install_handler, ) await self.middleware.call('cache.put', 'update.applied', True) if ( await self.middleware.call('system.is_freenas') or ( await self.middleware.call('failover.licensed') and await self.middleware.call('failover.status') != 'BACKUP' ) ): await self.middleware.call('update.take_systemdataset_samba4_snapshot') if attrs.get('reboot'): await self.middleware.call('system.reboot', {'delay': 10}) return True
def update(self, job, attrs=None): """ Downloads (if not already in cache) and apply an update. """ train = (attrs or {}).get('train') or self.get_train() location = self.middleware.call('notifier.get_update_location') handler = UpdateHandler(self, job) update = Update.DownloadUpdate( train, location, check_handler=handler.check_handler, get_handler=handler.get_handler, ) if update is False: raise ValueError('No update available') new_manifest = Manifest.Manifest(require_signature=True) new_manifest.LoadPath('{}/MANIFEST'.format(location)) self.middleware.thread_run( Update.ApplyUpdate, location, install_handler=handler.install_handler, ) self.middleware.call('cache.put', 'update.applied', True) return True
def install_manual_impl(self, job, path, dest_extracted): job.set_progress(30, 'Extracting file') Update.ExtractFrozenUpdate(path, dest_extracted, verbose=True) job.set_progress(50, 'Applying update') if self.install_impl(job, dest_extracted) is None: raise CallError('Uploaded file is not a manual update file')
def DoDownload(train, cache_dir, pkg_type): import freenasOS.Update as Update import freenasOS.Exceptions as Exceptions try: rv = Update.DownloadUpdate(train, cache_dir, pkg_type=pkg_type) except Exceptions.ManifestInvalidSignature: log.error("Manifest has invalid signature") print("Manifest has invalid signature", file=sys.stderr) sys.exit(1) except Exceptions.UpdateBusyCacheException as e: log.error(str(e)) print("Download cache directory is busy", file=sys.stderr) sys.exit(1) except Exceptions.UpdateIncompleteCacheException: log.error(str(e)) print("Incomplete download cache, cannot update", file=sys.stderr) sys.exit(1) except BaseException as e: log.error(str(e)) print("Received exception during download phase, cannot update", file=sys.stderr) sys.exit(1) return rv
def PrintDifferences(diffs): for type in diffs: if type == "Packages": pkg_diffs = diffs[type] for (pkg, op, old) in pkg_diffs: if op == "delete": print("Delete package {0}".format(pkg.Name()), file=sys.stderr) elif op == "install": print("Install package {0}-{1}".format(pkg.Name(), pkg.Version()), file=sys.stderr) elif op == "upgrade": print("Upgrade package {0} {1}->{2}".format(pkg.Name(), old.Version(), pkg.Version()), file=sys.stderr) else: print("Unknown package operation {0} for packge {1}-{2}".format(op, pkg.Name(), pkg.Version()), file=sys.stderr) elif type == "Restart": for svc in diffs[type]: desc = Update.GetServiceDescription(svc) if desc: print(str(desc), file=sys.stderr) else: print("Unknown service restart {0}?!".format(svc), file=sys.stderr) elif type in ("Train", "Sequence"): # Train and Sequence are a single tuple, (old, new) old, new = diffs[type] print("{0} {1} -> {2}".format(type, old, new), file=sys.stderr) elif type == "Reboot": rr = diffs[type] print("Reboot is (conditionally) {0}required".format("" if rr else "not "), file=sys.stderr) else: print("*** Unknown key {0} (value {1})".format(type, str(diffs[type])), file=sys.stderrr)
def install_impl_job(self, job, job_id, location): job = FakeJob(job_id, self.middleware.client) handler = UpdateHandler(self, job) return Update.ApplyUpdate( location, install_handler=handler.install_handler, )
def query(self, filters=None, options=None): """ Query all Boot Environments with `query-filters` and `query-options`. """ results = [] for clone in Update.ListClones(): clone['id'] = clone['name'] results.append(clone) return filter_list(results, filters, options)
def do_update(self, oid, data): verrors = ValidationErrors() self._clean_be_name(verrors, 'bootenv_update', data['name']) if verrors: raise verrors if not Update.RenameClone(oid, data['name']): raise CallError('Failed to update boot environment') return data['name']
def download(self, job): train = self.middleware.call_sync('update.get_trains')['selected'] location = self.middleware.call_sync('update.get_update_location') job.set_progress(0, 'Retrieving update manifest') handler = UpdateHandler(self, job, 100) Update.DownloadUpdate( train, location, check_handler=handler.check_handler, get_handler=handler.get_handler, ) update = Update.CheckForUpdates(train=train, cache_dir=location) self.middleware.call_sync('alert.alert_source_clear_run', 'HasUpdate') return bool(update)
def install_manual_impl(self, job, path, dest_extracted): p = subprocess.run(['file', path], stdout=subprocess.PIPE, stderr=subprocess.DEVNULL, encoding='ascii', errors='ignore') if 'Squashfs filesystem' in p.stdout: return self._install_scale(job, path) job.set_progress(30, 'Extracting file') Update.ExtractFrozenUpdate(path, dest_extracted, verbose=True) job.set_progress(50, 'Applying update') if self.install_impl(job, dest_extracted) is None: raise CallError('Uploaded file is not a manual update file')
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
async def get_pending(self, path=None): """ Gets a list of packages already downloaded and ready to be applied. Each entry of the lists consists of type of operation and name of it, e.g. { "operation": "upgrade", "name": "baseos-11.0 -> baseos-11.1" } """ if path is None: path = await self.middleware.call('notifier.get_update_location') data = [] try: changes = await self.middleware.run_in_thread( 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
def do_update(self, oid, data): """ Update `id` boot environment name with a new provided valid `name`. """ verrors = ValidationErrors() self._clean_be_name(verrors, 'bootenv_update', data['name']) if verrors: raise verrors if not Update.RenameClone(oid, data['name']): raise CallError('Failed to update boot environment') return data['name']
def is_update_applied(update_version, create_alert=True): active_be_msg = 'Please reboot the system to activate this update.' # TODO: The below boot env name should really be obtained from the update code # for now we just duplicate that code here if update_version.startswith(Update.Avatar() + "-"): update_boot_env = update_version[len(Update.Avatar() + "-"):] else: update_boot_env = "%s-%s" % (Update.Avatar(), update_version) found = False msg = '' for clone in Update.ListClones(): if clone['realname'] == update_boot_env: if clone['active'] != 'R': active_be_msg = 'Please activate {0} via'.format(update_boot_env) + \ ' the Boot Environment Tab and Reboot to use this updated version.' msg = 'Update: {0} has already been applied. {1}'.format(update_version, active_be_msg) found = True if create_alert: create_update_alert(update_version) break return (found, msg)
def DoDownload(train, cache_dir, pkg_type, verbose): try: if not verbose: progress_bar = ProgressBar() handler = UpdateHandler(progress_bar.update) rv = Update.DownloadUpdate( train, cache_dir, get_handler=handler.get_handler, check_handler=handler.check_handler, pkg_type=pkg_type, ) if rv is False: progress_bar.update(message="No updates available") progress_bar.finish() else: rv = Update.DownloadUpdate(train, cache_dir, pkg_type=pkg_type) except Exceptions.ManifestInvalidSignature: log.error("Manifest has invalid signature") print("Manifest has invalid signature", file=sys.stderr) sys.exit(1) except Exceptions.UpdateBusyCacheException as e: log.error(str(e)) print("Download cache directory is busy", file=sys.stderr) sys.exit(1) except Exceptions.UpdateIncompleteCacheException: log.error(str(e)) print("Incomplete download cache, cannot update", file=sys.stderr) sys.exit(1) except BaseException as e: log.error(str(e)) print("Received exception during download phase, cannot update", file=sys.stderr) sys.exit(1) return rv
def main(handler, args): set_proc_name('updated') handler.pid = os.getpid() handler.dump() if args.download: log.debug('Starting DownloadUpdate') Update.DownloadUpdate( args.train, args.cache, check_handler=handler.get_handler, get_handler=handler.get_file_handler, ) log.debug('DownloadUpdate finished') if args.apply: log.debug('Starting ApplyUpdate') Update.ApplyUpdate( args.cache, install_handler=handler.install_handler, ) log.debug('ApplyUpdate finished')
def do_create(self, data): verrors = ValidationErrors() self._clean_be_name(verrors, 'bootenv_create', data['name']) if verrors: raise verrors kwargs = {} source = data.get('source') if source: kwargs['bename'] = source clone = Update.CreateClone(data['name'], **kwargs) if clone is False: raise CallError('Failed to create boot environment') return data['name']
def do_create(self, data): """ Create a new boot environment using `name`. If a new boot environment is desired which is a clone of another boot environment, `source` can be passed. Then, a new boot environment of `name` is created using boot environment `source` by cloning it. Ensure that `name` and `source` are valid boot environment names. """ verrors = ValidationErrors() self._clean_be_name(verrors, 'bootenv_create', data['name']) if verrors: raise verrors kwargs = {} source = data.get('source') if source: kwargs['bename'] = source clone = Update.CreateClone(data['name'], **kwargs) if clone is False: raise CallError('Failed to create boot environment') return data['name']
def PrintDifferences(diffs): for type in diffs: if type == "Packages": pkg_diffs = diffs[type] for (pkg, op, old) in pkg_diffs: if op == "delete": print("Delete package %s" % pkg.Name(), file=sys.stderr) elif op == "install": print("Install package %s-%s" % (pkg.Name(), pkg.Version()), file=sys.stderr) elif op == "upgrade": print("Upgrade package %s %s->%s" % (pkg.Name(), old.Version(), pkg.Version()), file=sys.stderr) else: print("Unknown package operation %s for packge %s-%s" % (op, pkg.Name(), pkg.Version()), file=sys.stderr) elif type == "Restart": for svc in diffs[type]: desc = Update.GetServiceDescription(svc) if desc: print("%s" % desc) else: print("Unknown service restart %s?!" % svc) elif type in ("Train", "Sequence"): # Train and Sequence are a single tuple, (old, new) old, new = diffs[type] print("%s %s -> %s" % (type, old, new), file=sys.stderr) elif type == "Reboot": rr = diffs[type] print("Reboot is (conditionally) %srequired" % ("" if rr else "not "), file=sys.stderr) else: print("*** Unknown key %s (value %s)" % (type, str(diffs[type])), file=sys.stderrr)
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 >> 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.stdout', }, }, '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: if verbose: diffs = Update.PendingUpdates(download_dir) for (pkg, op, old) in diffs: if op == "delete": print >> sys.stderr, "Delete package %s" % pkg.Name() elif op == "install": print >> sys.stderr, "Install package %s-%s" % ( pkg.Name(), pkg.Version()) elif op == "upgrade": print >> sys.stderr, "Upgrade package %s %s->%s" % ( pkg.Name(), old.Version(), pkg.Version()) 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. 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.PendingUpdates(download_dir) if diffs is None or diffs == {}: if verbose: print >> sys.stderr, "No updates to apply" else: if verbose: for (pkg, op, old) in diffs: if op == "delete": print >> sys.stderr, "Delete package %s" % pkg.Name() elif op == "install": print >> sys.stderr, "Install package %s-%s" % ( pkg.Name(), pkg.Version()) elif op == "upgrade": print >> sys.stderr, "Upgrade package %s %s -> %s" % ( pkg.Name(), old.Version(), pkg.Version()) rv = Update.ApplyUpdate(download_dir) if rv is False: if verbose: print >> sys.stderr, "ApplyUpdates failed" if cache_dir is None: Update.RemoveUpdate(download_dir) sys.exit(1) Update.RemoveUpdate(download_dir) # Change this if/when we can do an update without a reboot. print >> sys.stderr, "System should be rebooted now" sys.exit(0) else: usage()