~sinzui/juju-ci-tools/cloudsigma-lib

967.1.1 by Curtis Hovey
Added spike to manage the lxc cache for hosts on private networks.
1
#!/usr/bin/python
967.1.16 by Curtis Hovey
Added some docstrings.
2
"""Update the lxc 'download' template cache for hosts on closed networks."""
967.1.1 by Curtis Hovey
Added spike to manage the lxc cache for hosts on private networks.
3
4
from __future__ import print_function
5
6
from argparse import ArgumentParser
7
from collections import namedtuple
967.1.2 by Curtis Hovey
Added a working script to keep ppc64-slave's lxc cache current.
8
import errno
967.1.1 by Curtis Hovey
Added spike to manage the lxc cache for hosts on private networks.
9
import os
10
import sys
11
import traceback
12
import shutil
13
import subprocess
967.1.2 by Curtis Hovey
Added a working script to keep ppc64-slave's lxc cache current.
14
import urllib2
967.1.1 by Curtis Hovey
Added spike to manage the lxc cache for hosts on private networks.
15
16
17
SITE = 'https://images.linuxcontainers.org'
18
INDEX_PATH = 'meta/1.0'
19
INDEX = 'index-system'
20
ROOTFS = 'rootfs.tar.xz'
21
META = 'meta.tar.xz'
22
LXC_CACHE = '/var/cache/lxc/download'
23
24
25
System = namedtuple(
26
    'System', ['dist', 'release', 'arch', 'variant', 'version', 'path'])
27
28
29
PUT_SCRIPT = """\
30
scp {rootfs_path} {meta_path} {user_host}:~/
31
"""
32
33
INSTALL_SCRIPT = """\
34
ssh {user_host} bash <<"EOT"
35
sudo mkdir -p {lxc_cache}
967.1.2 by Curtis Hovey
Added a working script to keep ppc64-slave's lxc cache current.
36
sudo mv ~/{rootfs} ~/{meta} {lxc_cache}
37
sudo chown -R root:root  {lxc_cache}
967.1.1 by Curtis Hovey
Added spike to manage the lxc cache for hosts on private networks.
38
sudo tar -C {lxc_cache} -xf {lxc_cache}/meta.tar.xz
39
EOT
40
"""
41
42
43
class LxcCache:
44
    """Manage the LXC download template cache."""
45
46
    def __init__(self, workspace, verbose=False, dry_run=False):
967.1.16 by Curtis Hovey
Added some docstrings.
47
        """Set the workspace for the local cache."""
967.1.1 by Curtis Hovey
Added spike to manage the lxc cache for hosts on private networks.
48
        self.workspace = os.path.abspath(workspace)
49
        self.verbose = verbose
50
        self.dry_run = dry_run
51
        local_path = os.path.join(self.workspace, INDEX_PATH, INDEX)
967.1.2 by Curtis Hovey
Added a working script to keep ppc64-slave's lxc cache current.
52
        self.systems, ignore = self.init_systems(local_path)
967.1.1 by Curtis Hovey
Added spike to manage the lxc cache for hosts on private networks.
53
54
    def init_systems(self, location):
967.1.16 by Curtis Hovey
Added some docstrings.
55
        """Return a tuple of the dict of lxc Systems and the source data.
56
57
        A System has these attributes: 'dist', 'release', 'arch', 'variant',
58
        'version', and 'path'.  The dict keys are a tuple of
59
        (dist, release, arch, variant).
60
        """
967.1.1 by Curtis Hovey
Added spike to manage the lxc cache for hosts on private networks.
61
        systems = {}
62
        if location.startswith('http'):
967.1.2 by Curtis Hovey
Added a working script to keep ppc64-slave's lxc cache current.
63
            request = urllib2.Request(location)
64
            response = urllib2.urlopen(request)
65
            data = response.read()
967.1.1 by Curtis Hovey
Added spike to manage the lxc cache for hosts on private networks.
66
        else:
967.1.2 by Curtis Hovey
Added a working script to keep ppc64-slave's lxc cache current.
67
            try:
68
                with open(location) as f:
69
                    data = f.read()
70
            except IOError as e:
71
                if e.errno == errno.ENOENT:
72
                    if self.verbose:
73
                        print('Local cache is empty.')
74
                    return systems, None
967.1.1 by Curtis Hovey
Added spike to manage the lxc cache for hosts on private networks.
75
        for line in data.splitlines():
76
            system = System(*line.split(';'))
77
            key = (system.dist, system.release, system.arch, system.variant)
78
            systems[key] = system
967.1.2 by Curtis Hovey
Added a working script to keep ppc64-slave's lxc cache current.
79
        return systems, data
967.1.1 by Curtis Hovey
Added spike to manage the lxc cache for hosts on private networks.
80
81
    def get_updates(self, dist, release, arch, variant):
967.1.16 by Curtis Hovey
Added some docstrings.
82
        """Return a tuple of the new system and the source data that match.
83
967.1.17 by Curtis Hovey
Fix grammar and remove gremlin.
84
        The new system and source data will be None when there are
967.1.16 by Curtis Hovey
Added some docstrings.
85
        no updates. The dist, release, arch, and variant args identify the
967.1.17 by Curtis Hovey
Fix grammar and remove gremlin.
86
        system to return.
967.1.16 by Curtis Hovey
Added some docstrings.
87
        """
967.1.2 by Curtis Hovey
Added a working script to keep ppc64-slave's lxc cache current.
88
        key = (dist, release, arch, variant)
89
        old_system = self.systems.get(key)
967.1.1 by Curtis Hovey
Added spike to manage the lxc cache for hosts on private networks.
90
        url = '%s/%s/%s' % (SITE, INDEX_PATH, INDEX)
967.1.2 by Curtis Hovey
Added a working script to keep ppc64-slave's lxc cache current.
91
        new_systems, data = self.init_systems(url)
92
        new_system = new_systems[key]
93
        if not old_system or new_system.version > old_system.version:
94
            if self.verbose:
95
                print('Found new version for %s' % str(key))
96
                print(new_system.version)
97
            return new_system, data
98
        if self.verbose:
99
            print('Version is current for %s' % str(key))
100
            print(old_system.version)
101
        return None, None
967.1.1 by Curtis Hovey
Added spike to manage the lxc cache for hosts on private networks.
102
103
    def get_lxc_data(self, system):
967.1.16 by Curtis Hovey
Added some docstrings.
104
        """Download the system image and meta data.
105
106
        Return a tuple of the image and meta data paths.
107
        """
967.1.2 by Curtis Hovey
Added a working script to keep ppc64-slave's lxc cache current.
108
        image_path = os.path.join(self.workspace, system.path[1:])
109
        if not self.dry_run:
110
            if self.verbose:
111
                print('creating %s' % image_path)
112
            if not os.path.isdir(image_path):
967.1.12 by Curtis Hovey
Added test_get_lxc_data.
113
                os.makedirs(image_path)
967.1.2 by Curtis Hovey
Added a working script to keep ppc64-slave's lxc cache current.
114
        rootfs_path = os.path.join(image_path, ROOTFS)
115
        rootfs_url = '%s%s%s' % (SITE, system.path, ROOTFS)
967.1.1 by Curtis Hovey
Added spike to manage the lxc cache for hosts on private networks.
116
        self.download(rootfs_url, rootfs_path)
967.1.2 by Curtis Hovey
Added a working script to keep ppc64-slave's lxc cache current.
117
        meta_path = os.path.join(image_path, META)
118
        meta_url = '%s%s%s' % (SITE, system.path, META)
967.1.1 by Curtis Hovey
Added spike to manage the lxc cache for hosts on private networks.
119
        self.download(meta_url, meta_path)
120
        return rootfs_path, meta_path
121
967.1.2 by Curtis Hovey
Added a working script to keep ppc64-slave's lxc cache current.
122
    def download(self, location, path):
967.1.16 by Curtis Hovey
Added some docstrings.
123
        """Download a large binary from location to the specified path."""
967.1.2 by Curtis Hovey
Added a working script to keep ppc64-slave's lxc cache current.
124
        chunk = 16 * 1024
125
        if not self.dry_run:
126
            request = urllib2.Request(location)
127
            response = urllib2.urlopen(request)
128
            if response.getcode() == 200:
129
                with open(path, 'wb') as f:
130
                    shutil.copyfileobj(response, f, chunk)
131
                if self.verbose:
132
                    print('Downloaded %s' % location)
967.1.1 by Curtis Hovey
Added spike to manage the lxc cache for hosts on private networks.
133
134
    def put_lxc_data(self, user_host, system, rootfs_path, meta_path):
967.1.16 by Curtis Hovey
Added some docstrings.
135
        """Install the lxc image and meta data on the host.
136
137
        The user on the host must have password-less sudo.
138
        """
967.1.2 by Curtis Hovey
Added a working script to keep ppc64-slave's lxc cache current.
139
        lxc_cache = os.path.join(
140
            LXC_CACHE, system.dist, system.release, system.arch,
141
            system.variant)
967.1.1 by Curtis Hovey
Added spike to manage the lxc cache for hosts on private networks.
142
        put_script = PUT_SCRIPT.format(
143
            user_host=user_host, rootfs_path=rootfs_path, meta_path=meta_path)
967.1.2 by Curtis Hovey
Added a working script to keep ppc64-slave's lxc cache current.
144
        if not self.dry_run:
145
            subprocess.check_call([put_script], shell=True)
146
            if self.verbose:
147
                print("Uploaded %s and %s" % (ROOTFS, META))
967.1.1 by Curtis Hovey
Added spike to manage the lxc cache for hosts on private networks.
148
        install_script = INSTALL_SCRIPT.format(
967.1.2 by Curtis Hovey
Added a working script to keep ppc64-slave's lxc cache current.
149
            user_host=user_host, lxc_cache=lxc_cache, rootfs=ROOTFS, meta=META)
150
        if not self.dry_run:
151
            subprocess.check_call([install_script], shell=True)
152
            if self.verbose:
153
                print("Installed %s and %s" % (ROOTFS, META))
154
155
    def save_index(self, data):
967.1.16 by Curtis Hovey
Added some docstrings.
156
        "Save the (current) index data for future calls to get_updates()."
967.1.2 by Curtis Hovey
Added a working script to keep ppc64-slave's lxc cache current.
157
        index_dir = os.path.join(self.workspace, INDEX_PATH)
158
        if not os.path.isdir(index_dir):
159
            os.makedirs(index_dir)
160
        index_path = os.path.join(self.workspace, INDEX_PATH, INDEX)
161
        with open(index_path, 'w') as f:
162
            f.write(data)
163
        if self.verbose:
164
            print('saved index: %s' % INDEX)
967.1.1 by Curtis Hovey
Added spike to manage the lxc cache for hosts on private networks.
165
166
167
def parse_args(argv=None):
168
    """Return the argument parser for this program."""
169
    parser = ArgumentParser(
170
        "Update a remote host's download lxc template cache.")
171
    parser.add_argument(
172
        '-d', '--dry-run', action='store_true', default=False,
173
        help='Do not make changes.')
174
    parser.add_argument(
175
        '-v', '--verbose', action='store_true', default=False,
176
        help='Increase verbosity.')
177
    parser.add_argument(
178
        '--dist', default="ubuntu", help="The distribution to update.")
179
    parser.add_argument(
180
        '--variant', default="default", help="The variant to update.")
181
    parser.add_argument(
967.1.2 by Curtis Hovey
Added a working script to keep ppc64-slave's lxc cache current.
182
        'user_host', help='The user@host to update.')
967.1.1 by Curtis Hovey
Added spike to manage the lxc cache for hosts on private networks.
183
    parser.add_argument(
184
        'release', help='The release to update.')
185
    parser.add_argument(
967.1.16 by Curtis Hovey
Added some docstrings.
186
        'arch', help='The architecture of the remote host')
967.1.1 by Curtis Hovey
Added spike to manage the lxc cache for hosts on private networks.
187
    parser.add_argument(
188
        'workspace', help='The path to the local dir to stage the update.')
189
    args = parser.parse_args(argv)
190
    return args
191
192
193
def main(argv):
967.1.16 by Curtis Hovey
Added some docstrings.
194
    """Update the lxc download template cache for hosts on closed networks."""
967.1.1 by Curtis Hovey
Added spike to manage the lxc cache for hosts on private networks.
195
    args = parse_args(argv)
196
    try:
197
        lxc_cache = LxcCache(
198
            args.workspace, verbose=args.verbose, dry_run=args.dry_run)
967.1.2 by Curtis Hovey
Added a working script to keep ppc64-slave's lxc cache current.
199
        new_system, data = lxc_cache.get_updates(
967.1.1 by Curtis Hovey
Added spike to manage the lxc cache for hosts on private networks.
200
            args.dist, args.release, args.arch, args.variant)
201
        if new_system:
202
            rootfs_path, meta_path = lxc_cache.get_lxc_data(new_system)
203
            lxc_cache.put_lxc_data(
204
                args.user_host, new_system, rootfs_path, meta_path)
967.1.2 by Curtis Hovey
Added a working script to keep ppc64-slave's lxc cache current.
205
            lxc_cache.save_index(data)
967.1.1 by Curtis Hovey
Added spike to manage the lxc cache for hosts on private networks.
206
    except Exception as e:
207
        print(e)
208
        print(getattr(e, 'output', ''))
209
        if args.verbose:
210
            traceback.print_tb(sys.exc_info()[2])
211
        return 2
212
    if args.verbose:
213
        print("Done.")
214
    return 0
215
216
217
if __name__ == '__main__':
218
    sys.exit(main(sys.argv[1:]))