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

« back to all changes in this revision

Viewing changes to simplestreams/mirrors/glance.py

  • Committer: Package Import Robot
  • Author(s): Scott Moser
  • Date: 2013-04-11 13:05:52 UTC
  • mfrom: (1.1.1)
  • Revision ID: package-import@ubuntu.com-20130411130552-ow8n8lvb3yp75az8
Tags: 0.1.0~bzr223-0ubuntu1
* New upstream snapshot.
  * some fixes to resolvework
  * include upstream work on glance mirror with support for
    writing simplestreams output to swift

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
#!/usr/bin/python
 
2
 
 
3
import simplestreams.mirrors as mirrors
 
4
import simplestreams.util as util
 
5
import simplestreams.openstack as openstack
 
6
from simplestreams.log import LOG
 
7
 
 
8
import copy
 
9
import errno
 
10
import glanceclient
 
11
import os
 
12
import re
 
13
 
 
14
 
 
15
def get_glanceclient(version='1', **kwargs):
 
16
    pt = ('endpoint', 'token', 'insecure', 'cacert')
 
17
    kskw = {k: kwargs.get(k) for k in pt if k in kwargs}
 
18
    return glanceclient.Client(version, **kskw)
 
19
 
 
20
 
 
21
def translate_dl_content_id(content_id, cloudname):
 
22
    # given content_id=com.ubuntu.cloud:released:download
 
23
    # return "com.ubuntu.cloud:released:%s" % cloudname
 
24
    toks = content_id.split(":")
 
25
    return ":".join(toks[:-1]) + ":" + cloudname
 
26
 
 
27
 
 
28
def empty_iid_products(content_id):
 
29
    return {'content_id': content_id, 'products': {},
 
30
            'datatype': 'image-ids', 'format': 'products:1.0'}
 
31
 
 
32
 
 
33
# glance mirror 'image-downloads' content into glance
 
34
# if provided an object store, it will produce a 'image-ids' mirror
 
35
class GlanceMirror(mirrors.BasicMirrorWriter):
 
36
    def __init__(self, config, objectstore=None, region=None,
 
37
                 name_prefix=None):
 
38
        super(GlanceMirror, self).__init__(config=config)
 
39
 
 
40
        self.loaded_content = {}
 
41
        self.store = objectstore
 
42
 
 
43
        self.keystone_creds = openstack.load_keystone_creds()
 
44
 
 
45
        self.name_prefix = name_prefix or ""
 
46
        if region is not None:
 
47
            self.keystone_creds['region_name'] = region
 
48
 
 
49
        conn_info = openstack.get_service_conn_info('image',
 
50
                                                    **self.keystone_creds)
 
51
        self.gclient = get_glanceclient(**conn_info)
 
52
        self.tenant_id = conn_info['tenant_id']
 
53
 
 
54
        self.region = self.keystone_creds.get('region_name', 'nullregion')
 
55
        self.cloudname = config.get("cloud_name", 'nullcloud')
 
56
        self.crsn = '-'.join((self.cloudname, self.region,))
 
57
        self.auth_url = self.keystone_creds['auth_url']
 
58
 
 
59
    def _cidpath(self, content_id):
 
60
        return "streams/v1/%s.js" % content_id
 
61
 
 
62
    def load_products(self, path=None, content_id=None):
 
63
        my_cid = translate_dl_content_id(content_id, self.crsn)
 
64
 
 
65
        # glance is the definitive store.  Any data loaded from the store
 
66
        # is secondary.
 
67
        store_t = None
 
68
        if self.store:
 
69
            try:
 
70
                path = self._cidpath(my_cid)
 
71
                store_t = util.load_content(self.store.reader(path).read())
 
72
            except IOError as e:
 
73
                if e.errno != errno.ENOENT:
 
74
                    raise
 
75
        if not store_t:
 
76
            store_t = empty_iid_products(my_cid)
 
77
 
 
78
        glance_t = empty_iid_products(my_cid)
 
79
 
 
80
        images = self.gclient.images.list()
 
81
        for image in images:
 
82
            image = image.to_dict()
 
83
 
 
84
            if image['owner'] != self.tenant_id:
 
85
                continue
 
86
 
 
87
            props = image['properties']
 
88
            if props.get('content_id') != my_cid:
 
89
                continue
 
90
 
 
91
            product = props.get('product_name')
 
92
            version = props.get('version_name')
 
93
            item = props.get('item_name')
 
94
            if not (version and product and item):
 
95
                continue
 
96
 
 
97
            # get data from the datastore for this item, if it exists
 
98
            # and then update that with glance data (just in case different)
 
99
            try:
 
100
                item_data = util.products_exdata(store_t,
 
101
                                                 (product, version, item,),
 
102
                                                 include_top=False,
 
103
                                                 insert_fieldnames=False)
 
104
            except KeyError:
 
105
                item_data = {}
 
106
 
 
107
            item_data.update({'name': image['name'], 'id': image['id']})
 
108
 
 
109
            util.products_set(glance_t, item_data,
 
110
                (product, version, item,))
 
111
 
 
112
        for product in glance_t['products']:
 
113
            glance_t['products'][product]['region'] = self.region
 
114
            glance_t['products'][product]['endpoint'] = self.auth_url
 
115
 
 
116
        return glance_t
 
117
 
 
118
    def filter_item(self, data, src, target, pedigree):
 
119
        flat = util.products_exdata(src, pedigree, include_top=False)
 
120
        return (flat.get('ftype') in ('disk1.img', 'disk.img') and
 
121
                flat.get('arch') in ('x86_64', 'amd64', 'i386'))
 
122
 
 
123
    def insert_item(self, data, src, target, pedigree, contentsource):
 
124
        flat = util.products_exdata(src, pedigree, include_top=False)
 
125
 
 
126
        tmp_path = None
 
127
        tmp_del = None
 
128
 
 
129
        name = flat.get('pubname', flat.get('name'))
 
130
        if not name.endswith(flat['item_name']):
 
131
            name += "-%s" % (flat['item_name'])
 
132
 
 
133
        t_item = flat.copy()
 
134
        if 'path' in t_item:
 
135
            del t_item['path']
 
136
 
 
137
        props = {'content_id': target['content_id']}
 
138
        for n in ('product_name', 'version_name', 'item_name'):
 
139
            props[n] = flat[n]
 
140
            del t_item[n]
 
141
 
 
142
        arch = flat.get('arch')
 
143
        if arch == "amd64":
 
144
            arch = "x86_64"
 
145
        if arch:
 
146
            props['architecture'] = arch
 
147
 
 
148
        create_kwargs = {
 
149
            'name': self.name_prefix + name,
 
150
            'properties': props,
 
151
            'disk_format': 'qcow2',
 
152
            'container_format': 'bare',
 
153
            'is_public': True,
 
154
        }
 
155
        if 'size' in data:
 
156
            create_kwargs['size'] = data.get('size')
 
157
 
 
158
        if 'md5' in data:
 
159
            create_kwargs['checksum'] = data.get('md5')
 
160
 
 
161
        try:
 
162
            try:
 
163
                (tmp_path, tmp_del) = util.get_local_copy(contentsource)
 
164
            finally:
 
165
                contentsource.close()
 
166
 
 
167
            create_kwargs['data'] = open(tmp_path, 'rb')
 
168
            ret = self.gclient.images.create(**create_kwargs)
 
169
            t_item['id'] = ret.id
 
170
            print "created %s: %s" % (ret.id, name)
 
171
 
 
172
        finally:
 
173
            if tmp_del and os.path.exists(tmp_path):
 
174
                os.unlink(tmp_path)
 
175
 
 
176
        util.products_set(target, t_item, pedigree)
 
177
 
 
178
    def remove_item(self, data, src, target, pedigree):
 
179
        util.products_del(target, pedigree)
 
180
        if 'id' in data:
 
181
            print "removing %s: %s" % (data['id'], data['name'])
 
182
            self.gclient.images.delete(data['id'])
 
183
 
 
184
    def filter_index_entry(self, data, src, pedigree):
 
185
        return data.get('datatype') in ("image-downloads", None)
 
186
 
 
187
    def insert_products(self, path, target, content):
 
188
        if not self.store:
 
189
            return
 
190
 
 
191
        tree = copy.deepcopy(target)
 
192
        util.products_prune(tree)
 
193
        # stop these items from copying up when we call condense
 
194
        sticky = ['ftype', 'md5', 'sha256', 'size', 'name', 'id']
 
195
        util.products_condense(tree, sticky=sticky)
 
196
 
 
197
        tsnow = util.timestamp()
 
198
        tree['updated'] = tsnow
 
199
 
 
200
        dpath = self._cidpath(tree['content_id'])
 
201
        LOG.info("writing data: %s", dpath)
 
202
        self.store.insert_content(dpath, util.dump_data(tree))
 
203
 
 
204
        # now insert or update an index
 
205
        ipath = "streams/v1/index.js"
 
206
        try:
 
207
            index = util.load_content(self.store.reader(ipath).read())
 
208
        except IOError as exc:
 
209
            if exc.errno != errno.ENOENT:
 
210
                raise
 
211
            index = {"index": {}, 'format': 'index:1.0',
 
212
                     'updated': util.timestamp()}
 
213
 
 
214
        index['index'][tree['content_id']] = {
 
215
            'updated': tsnow,
 
216
            'datatype': 'image-ids',
 
217
            'clouds': [{'region': self.region, 'endpoint': self.auth_url}],
 
218
            'cloudname': self.cloudname,
 
219
            'path': dpath,
 
220
            'products': tree['products'].keys(),
 
221
            'format': tree['format'],
 
222
        }
 
223
        LOG.info("writing data: %s", ipath)
 
224
        self.store.insert_content(ipath, util.dump_data(index))
 
225
 
 
226
# vi: ts=4 expandtab syntax=python