~ubuntu-branches/ubuntu/vivid/simplestreams/vivid-updates

« back to all changes in this revision

Viewing changes to tools/make-test-data

  • Committer: Package Import Robot
  • Author(s): Scott Moser
  • Date: 2013-03-26 01:10:01 UTC
  • Revision ID: package-import@ubuntu.com-20130326011001-342bcgb65worw4r8
Tags: upstream-0.1.0~bzr191
ImportĀ upstreamĀ versionĀ 0.1.0~bzr191

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
#!/usr/bin/python
 
2
 
 
3
import argparse
 
4
import json
 
5
import os
 
6
import os.path
 
7
import urllib2
 
8
import sys
 
9
from simplestreams import util
 
10
 
 
11
import toolutil
 
12
 
 
13
# could support reading from other mirrors
 
14
# for example:
 
15
#   http://cloud-images-archive.ubuntu.com/
 
16
#   file:///srv/ec2-images
 
17
#
 
18
BASE_URLS = ("http://cloud-images.ubuntu.com/",)
 
19
 
 
20
FAKE_DATA = {
 
21
    'root.tar.gz': {
 
22
        'size': 10240, 'md5': '1276481102f218c981e0324180bafd9f',
 
23
        'sha256': '84ff92691f909a05b224e1c56abb4864f01b4f8e3c854e4bb4c7baf1d3f6d652'},
 
24
    'tar.gz': {
 
25
        'size': 11264, 'md5': '820a81e0916bac82838fd7e74ab29b15',
 
26
        'sha256': '5309e677c79cffae49a65728c61b436d3cdc2a2bab4c81bf0038415f74a56880'},
 
27
    'disk1.img': {
 
28
        'size': 12288, 'md5': '4072783b8efb99a9e5817067d68f61c6',
 
29
        'sha256': 'f3cc103136423a57975750907ebc1d367e2985ac6338976d4d5a439f50323f4a'},
 
30
}
 
31
 
 
32
REAL_DATA = os.environ.get("REAL_DATA", False)
 
33
if REAL_DATA and REAL_DATA != "0":
 
34
    REAL_DATA = True
 
35
else:
 
36
    REAL_DATA = False
 
37
 
 
38
FILE_DATA = {}
 
39
 
 
40
 
 
41
def get_cache_data(path, field):
 
42
    dirname = os.path.dirname(path)
 
43
    bname = os.path.basename(path)
 
44
    return FILE_DATA.get(dirname, {}).get(bname, {}).get(field)
 
45
 
 
46
 
 
47
def store_cache_data(path, field, value):
 
48
    dirname = os.path.dirname(path)
 
49
    bname = os.path.basename(path)
 
50
    if dirname not in FILE_DATA:
 
51
        FILE_DATA[dirname] = {}
 
52
    if bname not in FILE_DATA[dirname]:
 
53
        FILE_DATA[dirname][bname] = {}
 
54
    FILE_DATA[dirname][bname][field] = value
 
55
 
 
56
 
 
57
def save_cache():
 
58
    if FILE_DATA:
 
59
        hashcache = FILE_DATA['filename']
 
60
        with open(hashcache, "w") as hfp:
 
61
            hfp.write(json.dumps(FILE_DATA, indent=1))
 
62
 
 
63
 
 
64
def get_cloud_images_file_hash(path):
 
65
    md5 = get_cache_data(path, 'md5')
 
66
    sha256 = get_cache_data(path, 'sha256')
 
67
    if md5 and sha256:
 
68
        return {'md5': md5, 'sha256': sha256}
 
69
 
 
70
    found = {}
 
71
    dirname = os.path.dirname(path)
 
72
    for cksum in ("md5", "sha256"):
 
73
        content = None
 
74
        for burl in BASE_URLS:
 
75
            dir_url = burl + dirname
 
76
 
 
77
            try:
 
78
                url = dir_url + "/%sSUMS" % cksum.upper()
 
79
                sys.stderr.write("reading %s\n" % url)
 
80
                content = util.read_url(url)
 
81
                break
 
82
            except Exception as error:
 
83
                pass
 
84
 
 
85
        if not content:
 
86
            raise error
 
87
 
 
88
        for line in content.splitlines():
 
89
            (hexsum, fname) = line.split()
 
90
            if fname.startswith("*"):
 
91
                fname = fname[1:]
 
92
            found[cksum] = hexsum
 
93
            store_cache_data(dirname + "/" + fname, cksum, hexsum)
 
94
 
 
95
    md5 = get_cache_data(path, 'md5')
 
96
    sha256 = get_cache_data(path, 'sha256')
 
97
    save_cache()
 
98
    return {'md5': md5, 'sha256': sha256}
 
99
 
 
100
 
 
101
def get_url_len(url):
 
102
    if url.startswith("file:///"):
 
103
        path = url[len("file://"):]
 
104
        return os.stat(path).st_size
 
105
    if os.path.exists(url):
 
106
        return os.stat(url).st_size
 
107
 
 
108
    # http://stackoverflow.com/questions/4421170/python-head-request-with-urllib2
 
109
    sys.stderr.write("getting size for %s\n" % url)
 
110
    request = urllib2.Request(url)
 
111
    request.get_method = lambda : 'HEAD'
 
112
    response = urllib2.urlopen(request)
 
113
    return int(response.headers.get('content-length', 0))
 
114
 
 
115
 
 
116
def get_cloud_images_file_size(path):
 
117
    size = get_cache_data(path, 'size')
 
118
    if size:
 
119
        return size
 
120
 
 
121
    for burl in BASE_URLS:
 
122
        try:
 
123
            size = int(get_url_len(burl + path))
 
124
            break
 
125
        except Exception as error:
 
126
            pass
 
127
 
 
128
    if not size:
 
129
        raise error
 
130
    store_cache_data(path, 'size', size)
 
131
    save_cache()
 
132
    return size
 
133
 
 
134
 
 
135
def create_fake_file(prefix, item):
 
136
    fpath = os.path.join(prefix, item['path'])
 
137
    path = item['path']
 
138
 
 
139
    data = FAKE_DATA[item['ftype']]
 
140
 
 
141
    util.mkdir_p(os.path.dirname(fpath))
 
142
    print "creating %s" % fpath
 
143
    with open(fpath, "w") as fp:
 
144
        fp.truncate(data['size'])
 
145
 
 
146
    item.update(data)
 
147
 
 
148
    for cksum in util.CHECKSUMS:
 
149
        if cksum in item and not cksum in data:
 
150
            del item[data]
 
151
 
 
152
    return
 
153
 
 
154
 
 
155
def dl_load_query(path):
 
156
    tree = {}
 
157
    for rline in toolutil.load_query_download(path):
 
158
        (stream, rel, build, label, serial, arch, filepath, fname) = rline
 
159
 
 
160
        if stream not in tree:
 
161
            tree[stream] = {'products': {}}
 
162
        products = tree[stream]['products']
 
163
 
 
164
        prodname = "%s:%s:%s" % (build, rel, arch)
 
165
 
 
166
        if prodname not in products:
 
167
            products[prodname] = {
 
168
                "release": rel,
 
169
                "version": toolutil.REL2VER[rel]['version'],
 
170
                "arch": arch,
 
171
                "versions": {}
 
172
            }
 
173
 
 
174
        product = products[prodname]
 
175
 
 
176
        if serial not in product['versions']:
 
177
            product['versions'][serial] = {'items': {}, "label": label}
 
178
 
 
179
        name = pubname(label, rel, arch, serial, build)
 
180
        product['versions'][serial]['pubname'] = name
 
181
 
 
182
        items = product['versions'][serial]['items']
 
183
 
 
184
        # ftype: finding the unique extension is not-trivial
 
185
        # take basename of the filename, and remove up to "-<arch>?"
 
186
        # so ubuntu-12.04-server-cloudimg-armhf.tar.gz becomes
 
187
        # 'tar.gz' and 'ubuntu-12.04-server-cloudimg-armhf-disk1.img'
 
188
        # becomse 'disk1.img'
 
189
        dash_arch = "-" + arch
 
190
        ftype = filepath[filepath.rindex(dash_arch) + len(dash_arch) + 1:]
 
191
        items[ftype] = {
 
192
            'path': filepath,
 
193
            'ftype': ftype
 
194
        }
 
195
 
 
196
    return tree
 
197
 
 
198
 
 
199
def pubname(label, rel, arch, serial, build='server'):
 
200
    version = toolutil.REL2VER[rel]['version']
 
201
 
 
202
    if label == "daily":
 
203
        rv_label = rel + "-daily"
 
204
    elif label == "release":
 
205
        rv_label = "%s-%s" % (rel, version)
 
206
    elif label.startswith("beta"):
 
207
        rv_label = "%s-%s-%s" % (rel, version, label)
 
208
    else:
 
209
        rv_label = "%s-%s" % (rel, label)
 
210
    return "ubuntu-%s-%s-%s-%s" % (rv_label, arch, build, serial)
 
211
 
 
212
 
 
213
def ec2_load_query(path):
 
214
    tree = {}
 
215
 
 
216
    dmap = {
 
217
        "north": "nn",
 
218
        "northeast": "ne",
 
219
        "east": "ee",
 
220
        "southeast": "se",
 
221
        "south": "ss",
 
222
        "southwest": "sw",
 
223
        "west": "ww",
 
224
        "northwest": "nw",
 
225
    }
 
226
    itmap = {
 
227
        'pv': {'instance': "pi", "ebs": "pe"},
 
228
        'hvm': {'instance': "hi", "ebs": "he"}
 
229
    }
 
230
 
 
231
    for rline in toolutil.load_query_ec2(path):
 
232
        (stream, rel, build, label, serial, store, arch, region,
 
233
         iid, _kern, _rmd, vtype) = rline
 
234
 
 
235
        if stream not in tree:
 
236
            tree[stream] = {'products': {}}
 
237
        products = tree[stream]['products']
 
238
 
 
239
        prodname = "%s:%s:%s" % (build, rel, arch)
 
240
 
 
241
        if prodname not in products:
 
242
            products[prodname] = {
 
243
                "release": rel,
 
244
                "version": toolutil.REL2VER[rel]['version'],
 
245
                "arch": arch,
 
246
                "versions": {}
 
247
            }
 
248
 
 
249
        product = products[prodname]
 
250
 
 
251
        if serial not in product['versions']:
 
252
            product['versions'][serial] = {'items': {}, "label": label}
 
253
        items = product['versions'][serial]['items']
 
254
 
 
255
        name = pubname(label, rel, arch, serial, build)
 
256
        product['versions'][serial]['pubname'] = name
 
257
 
 
258
        if store == "instance-store":
 
259
            store = 'instance'
 
260
        if vtype == "paravirtual":
 
261
            vtype = "pv"
 
262
 
 
263
        # create the item key:
 
264
        #  - 2 letter country code (us)
 
265
        #  - 2 letter direction ('nn' for north, 'nw' for northwest)
 
266
        #  - 1 digit number
 
267
        #  - 1 char for virt type
 
268
        #  - 1 char for root-store type
 
269
        (cc, direction, num) = region.split("-")
 
270
        ikey = cc + dmap[direction] + num + itmap[vtype][store]
 
271
 
 
272
        items[ikey] = {
 
273
            'id': iid,
 
274
            'root_store': store,
 
275
            'virt': vtype,
 
276
            'crsn': region,
 
277
        }
 
278
    return tree
 
279
 
 
280
 
 
281
def printitem(item, exdata):
 
282
    full = exdata.copy()
 
283
    full.update(item)
 
284
    print full
 
285
 
 
286
 
 
287
def create_image_data(query_tree, out_d, streamdir):
 
288
    hashcache = os.path.join(query_tree, "FILE_DATA_CACHE")
 
289
    FILE_DATA['filename'] = hashcache
 
290
    if os.path.isfile(hashcache):
 
291
        FILE_DATA.update(json.loads(open(hashcache).read()))
 
292
 
 
293
    ts = util.timestamp()
 
294
    tree = dl_load_query(query_tree)
 
295
 
 
296
    def update_hashes(item, tree, pedigree):
 
297
        item.update(get_cloud_images_file_hash(item['path']))
 
298
 
 
299
    def update_sizes(item, tree, pedigree):
 
300
        item.update({'size': get_cloud_images_file_size(item['path'])})
 
301
 
 
302
    cid_fmt = "com.ubuntu.cloud:%s:download"
 
303
    for stream in tree:
 
304
        def create_file(item, tree, pedigree):
 
305
            create_fake_file(os.path.join(out_d, stream), item)
 
306
 
 
307
        cid = cid_fmt % stream
 
308
        if REAL_DATA:
 
309
            util.walk_products(tree[stream], cb_item=update_hashes)
 
310
            util.walk_products(tree[stream], cb_item=update_sizes)
 
311
        else:
 
312
            util.walk_products(tree[stream], cb_item=create_file)
 
313
 
 
314
        tree[stream]['format'] = "products:1.0"
 
315
        tree[stream]['updated'] = ts
 
316
        tree[stream]['content_id'] = cid
 
317
 
 
318
        outfile = os.path.join(out_d, stream, streamdir, cid + ".js")
 
319
        util.mkdir_p(os.path.dirname(outfile))
 
320
        with open(outfile, "w") as fp:
 
321
            sys.stderr.write("writing %s\n" % outfile)
 
322
            fp.write(json.dumps(tree[stream], indent=1) + "\n")
 
323
 
 
324
    # save hashes data
 
325
    save_cache()
 
326
    return tree
 
327
 
 
328
 
 
329
def create_aws_data(query_tree, out_d, streamdir):
 
330
    tree = ec2_load_query(query_tree)
 
331
    ts = util.timestamp()
 
332
    cid_fmt = "com.ubuntu.cloud:%s:aws"
 
333
    for stream in tree:
 
334
        cid = cid_fmt % stream
 
335
        # now add the '_alias' data
 
336
        regions = set()
 
337
 
 
338
        def findregions(item, tree, pedigree):
 
339
            regions.add(item['crsn'])
 
340
 
 
341
        util.walk_products(tree[stream], cb_item=findregions)
 
342
 
 
343
        tree[stream]['_aliases'] = {'crsn': {}}
 
344
        for region in regions:
 
345
            tree[stream]['_aliases']['crsn'][region] = {
 
346
                'endpoint': 'http://ec2.%s.amazonaws.com' % region,
 
347
                'region': region}
 
348
 
 
349
        tree[stream]['format'] = "products:1.0"
 
350
        tree[stream]['updated'] = ts
 
351
        tree[stream]['content_id'] = cid
 
352
        outfile = os.path.join(out_d, stream, streamdir, cid + ".js")
 
353
        util.mkdir_p(os.path.dirname(outfile))
 
354
        with open(outfile, "w") as fp:
 
355
            sys.stderr.write("writing %s\n" % outfile)
 
356
            fp.write(json.dumps(tree[stream], indent=1) + "\n")
 
357
 
 
358
    return tree
 
359
 
 
360
 
 
361
def main():
 
362
    parser = argparse.ArgumentParser(description="create example content tree")
 
363
 
 
364
    parser.add_argument("query_tree", metavar='query_tree',
 
365
                        help=('read in content from /query tree. Hint: ' +
 
366
                              'make exdata-query'))
 
367
 
 
368
    parser.add_argument("out_d", metavar='out_d',
 
369
                        help=('create content under output_dir'))
 
370
 
 
371
    parser.add_argument('--sign', action='store_true', default=False,
 
372
                        help='sign all generated files')
 
373
 
 
374
    args = parser.parse_args()
 
375
    streamdir = "streams/v1"
 
376
 
 
377
    dltree = create_image_data(args.query_tree, args.out_d, streamdir)
 
378
 
 
379
    aws_tree = create_aws_data(args.query_tree, args.out_d, streamdir)
 
380
 
 
381
    for streamname in aws_tree:
 
382
        index = {"index": {}, 'format': 'index:1.0',
 
383
                 'updated': util.timestamp()}
 
384
 
 
385
        clouds = aws_tree[streamname]['_aliases']['crsn'].values()
 
386
        index['index'][aws_tree[streamname]['content_id']] = {
 
387
            'updated': aws_tree[streamname]['updated'],
 
388
            'datatype': 'image-ids',
 
389
            'clouds': clouds,
 
390
            'cloudname': "aws",
 
391
            'path': '/'.join((streamdir,
 
392
                             "%s.js" % aws_tree[streamname]['content_id'],)),
 
393
            'products': aws_tree[streamname]['products'].keys(),
 
394
            'format': aws_tree[streamname]['format'],
 
395
        }
 
396
        index['index'][dltree[streamname]['content_id']] = {
 
397
            'updated': dltree[streamname]['updated'],
 
398
            'datatype': 'image-downloads',
 
399
            'path': '/'.join((streamdir,
 
400
                             "%s.js" % dltree[streamname]['content_id'],)),
 
401
            'products': dltree[streamname]['products'].keys(),
 
402
            'format': dltree[streamname]['format']
 
403
        }
 
404
 
 
405
        outfile = os.path.join(args.out_d, streamname, streamdir, 'index.js')
 
406
        util.mkdir_p(os.path.dirname(outfile))
 
407
        with open(outfile, "w") as fp:
 
408
            sys.stderr.write("writing %s\n" % outfile)
 
409
            fp.write(json.dumps(index, indent=1) + "\n")
 
410
 
 
411
    if args.sign:
 
412
        def printstatus(name, fmt):
 
413
            sys.stderr.write("signing %s: %s\n" % (name, fmt))
 
414
        for root, dirs, files in os.walk(args.out_d):
 
415
            for f in [f for f in files if f.endswith(".js")]:
 
416
                toolutil.signjs_file(os.path.join(root, f),
 
417
                                     status_cb=printstatus)
 
418
 
 
419
    return
 
420
 
 
421
if __name__ == '__main__':
 
422
    sys.exit(main())
 
423
 
 
424
# vi: ts=4 expandtab