diff options
author | Joseph Hunkeler <jhunkeler@gmail.com> | 2014-08-06 17:09:29 -0400 |
---|---|---|
committer | Joseph Hunkeler <jhunkeler@gmail.com> | 2014-08-06 17:09:29 -0400 |
commit | fd2b72e3d729cd7768db88531833c4fbcd9f2b7a (patch) | |
tree | 20f585a09d221dc8c914a0a28a02682ec6080863 /ur_upgrade.py | |
parent | 6e3423392ecba1392ad1ae090b81426f0a0954f8 (diff) | |
download | ur_upgrade-fd2b72e3d729cd7768db88531833c4fbcd9f2b7a.tar.gz |
Add BSD license. Code consolidation in progress.
Diffstat (limited to 'ur_upgrade.py')
-rwxr-xr-x | ur_upgrade.py | 209 |
1 files changed, 95 insertions, 114 deletions
diff --git a/ur_upgrade.py b/ur_upgrade.py index a22ae95..f0e3879 100755 --- a/ur_upgrade.py +++ b/ur_upgrade.py @@ -1,81 +1,43 @@ #!/usr/bin/env python +#Copyright (c) 2014, Joseph Hunkeler <jhunkeler at gmail.com> +#All rights reserved. +# +#Redistribution and use in source and binary forms, with or without +#modification, are permitted provided that the following conditions are met: +# +#1. Redistributions of source code must retain the above copyright notice, this +# list of conditions and the following disclaimer. +#2. Redistributions in binary form must reproduce the above copyright notice, +# this list of conditions and the following disclaimer in the documentation +# and/or other materials provided with the distribution. +# +#THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +#ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +#WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +#DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR +#ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +#(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +#LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +#ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +#(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +#SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. from __future__ import division import argparse import os import shutil -import sys import subprocess import tempfile import urllib2 import signal import tarfile from distutils.version import StrictVersion +from distutils.spawn import find_executable as which from collections import namedtuple from string import Template from itertools import chain -def which(cmd, mode=os.F_OK | os.X_OK, path=None): - """Given a command, mode, and a PATH string, return the path which - conforms to the given mode on the PATH, or None if there is no such - file. - - `mode` defaults to os.F_OK | os.X_OK. `path` defaults to the result - of os.environ.get("PATH"), or can be overridden with a custom search - path. - - """ - # Check that a given file can be accessed with the correct mode. - # Additionally check that `file` is not a directory, as on Windows - # directories pass the os.access check. - def _access_check(fn, mode): - return (os.path.exists(fn) and os.access(fn, mode) - and not os.path.isdir(fn)) - - # If we're given a path with a directory part, look it up directly rather - # than referring to PATH directories. This includes checking relative to the - # current directory, e.g. ./script - if os.path.dirname(cmd): - if _access_check(cmd, mode): - return cmd - return None - - if path is None: - path = os.environ.get("PATH", os.defpath) - if not path: - return None - path = path.split(os.pathsep) - - if sys.platform == "win32": - # The current directory takes precedence on Windows. - if not os.curdir in path: - path.insert(0, os.curdir) - - # PATHEXT is necessary to check on Windows. - pathext = os.environ.get("PATHEXT", "").split(os.pathsep) - # See if the given file matches any of the expected path extensions. - # This will allow us to short circuit when given "python.exe". - # If it does match, only test that one, otherwise we have to try - # others. - if any(cmd.lower().endswith(ext.lower()) for ext in pathext): - files = [cmd] - else: - files = [cmd + ext for ext in pathext] - else: - # On other platforms you don't have things like PATHEXT to tell you - # what file suffixes are executable, so just pass on cmd as-is. - files = [cmd] - - seen = set() - for di in path: - normdir = os.path.normcase(di) - if not normdir in seen: - seen.add(normdir) - for thefile in files: - name = os.path.join(di, thefile) - if _access_check(name, mode): - return name - return None +DEFAULT_MIRROR = "http://ssb.stsci.edu/ureka" class Ureka(object): ''' @@ -156,6 +118,53 @@ def ur_check_version(urobj, vers): return False return True +def get_archive(url): + print("Downloading archive... {}".format(url)) + try: + req_data = urllib2.urlopen(url) + except Exception as ex: + print("Error {}, {}: {}".format(ex.code, ex.msg, url)) + exit(1) + + remote_size = float(req_data.headers['content-length']) + total = 0 + with open(os.path.join(UPGRADE_TEMP, UREKA_TARBALL), 'wb') as installer: + for chunk in iter(lambda: req_data.read(16*1024), ''): + print("\r{:.2f}% [{:.2f}/{:.2f} MB]".format((total / remote_size * 100), (total / 1024 ** 2), (remote_size / 1024 ** 2))), + total += float(len(chunk)) + installer.write(chunk) + print("") + +def upgrade(srcobj, destobj): + print("Upgrading distribution from {} to {}...".format(srcobj['version'], destobj['version'])) + proc = subprocess.Popen('rsync -a -ureka_dist_original {} {}'.format(destobj.path, os.path.dirname(srcobj.path)).split()) + proc.wait() + +def unpack_archive(): + installer = tarfile.open(os.path.join(UPGRADE_TEMP, UREKA_TARBALL), 'r') + print("Preparing to unpack upgrade... (please wait)") + files_total = len(installer.getmembers()) + + print("Unpacking upgrade...") + for index, member in enumerate(installer, start=1): + print("\r{:.2f}% [{}/{}]".format((index / files_total * 100), index, files_total)), + installer.extract(member, path=TEMP_INSTALL_PATH) + print("") + +def pre_upgrade(): + with open(os.path.join(TEMP_INSTALL_PATH, 'Ureka', 'misc', 'name'), 'w+') as fp: + fp.write(ureka_dist_original['name'] + os.linesep) + +def post_upgrade(urobj): + print("Performing upgrade maintenance tasks...") + ur_env = ur_getenv(urobj.path) + proc = subprocess.Popen('ur_normalize -n -i -x'.split(), env=ur_env) + proc.wait() + + print("Purging temporary data...") + shutil.rmtree(UPGRADE_TEMP) + + def cleanup(sig, stack): print('') print('Received signal {}!'.format(sig)) @@ -177,6 +186,8 @@ if __name__ == "__main__": exit(1) if args.latest: + # Need to work out a deal with SSB. + # "LATEST-STABLE" and "LATEST-DEVEL" links could be useful print("--latest is not implemented yet.") exit(1) @@ -185,7 +196,7 @@ if __name__ == "__main__": exit(1) UPGRADE_TEMP = tempfile.mkdtemp(prefix="upgrade") - ORIGINAL = args.UR_DIR + CURRENT = args.UR_DIR # Register signal handlers AFTER temporary directory is created signal.signal(signal.SIGINT, cleanup) @@ -195,67 +206,37 @@ if __name__ == "__main__": print('rsync not found in PATH. Please install it.') exit(1) - u = Ureka(ORIGINAL) + ureka_dist_original = Ureka(CURRENT) - if not ur_check_version(u, args.request): - print("Refusing to downgrade from {} to {}".format(u['version'], args.request)) + if not ur_check_version(ureka_dist_original, args.request): + print("Refusing to downgrade from {} to {}".format(ureka_dist_original['version'], args.request)) exit(1) - INSTALL_PATH = os.path.join(UPGRADE_TEMP, args.request) - UREKA_TARBALL = "Ureka_{}_{}_{}.tar.gz".format(u['os'], u['bits'], args.request) - ''' http://ssb.stsci.edu/ureka/1.0/Ureka_linux-rhe5_64_1.4.1.tar.gz ''' + TEMP_INSTALL_PATH = os.path.join(UPGRADE_TEMP, args.request) + UREKA_TARBALL = "Ureka_{}_{}_{}.tar.gz".format(ureka_dist_original['os'], ureka_dist_original['bits'], args.request) - #url = "http://ssb.stsci.edu/ureka/{}/{}".format(args.version, UREKA_TARBALL) - url = "http://localhost/ureka/{}/{}".format(args.request, UREKA_TARBALL) - + url = "{}/{}/{}".format(DEFAULT_MIRROR, args.request, UREKA_TARBALL) if args.mirror: url = "{}/{}/{}".format(args.mirror, args.request, UREKA_TARBALL) + + # Download requested archive + get_archive(url) - print("Downloading archive... {}".format(url)) - try: - req_data = urllib2.urlopen(url) - except Exception as ex: - print("Error {}, {}: {}".format(ex.code, ex.msg, url)) - exit(1) - - remote_size = float(req_data.headers['content-length']) - total = 0 - with open(os.path.join(UPGRADE_TEMP, UREKA_TARBALL), 'wb') as installer: - for chunk in iter(lambda: req_data.read(16*1024), ''): - print("\r{:.2f}% [{:.2f}/{:.2f} MB]".format((total / remote_size * 100), (total / 1024 ** 2), (remote_size / 1024 ** 2))), - total += float(len(chunk)) - installer.write(chunk) - print("") - - os.mkdir(INSTALL_PATH) - tarball = tarfile.open(os.path.join(UPGRADE_TEMP, UREKA_TARBALL), 'r') - print("Preparing to unpack upgrade... (please wait)") - files_total = len(tarball.getmembers()) + # Unpack archive to temporary storage + unpack_archive() - print("Unpacking upgrade...") - for index, member in enumerate(tarball, start=1): - print("\r{:.2f}% [{}/{}]".format((index / files_total * 100), index, files_total)), - tarball.extract(member, path=INSTALL_PATH) - print("") + # Generate upgrade Ureka object + ureka_dist_new = Ureka(os.path.join(TEMP_INSTALL_PATH, 'Ureka')) - open(os.path.join(INSTALL_PATH, 'Ureka', 'misc', 'name'), 'w+').write(u['name'] + os.linesep) - n = Ureka(os.path.join(INSTALL_PATH, 'Ureka')) + # Perform tasks required before upgrading + pre_upgrade() - print("Upgrading distribution {} to {}...".format(u['version'], n['version'])) - proc = subprocess.Popen('rsync -a -u {} {}'.format(n.path, os.path.dirname(u.path)).split()) - proc.wait() + # Perform upgrade + upgrade(ureka_dist_original, ureka_dist_new) - # Regenerate upgraded ureka installation object - u = Ureka(ORIGINAL) + # Regenerate upgraded ureka installation target + ureka_dist_original = Ureka(CURRENT) - print("Performing upgrade maintenance tasks...") - ur_env = ur_getenv(u.path) - - proc = subprocess.Popen('ur_normalize -n -i -x'.split(), env=ur_env) - proc.wait() + # Perform tasks required after upgrade is completed + post_upgrade(ureka_dist_original) - print("Purging temporary data...") - shutil.rmtree(UPGRADE_TEMP) - - print("Upgrade complete!") - |