3
import simplestreams.mirrors as mirrors
4
import simplestreams.util as util
5
import simplestreams.openstack as openstack
6
from simplestreams.log import LOG
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)
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
28
def empty_iid_products(content_id):
29
return {'content_id': content_id, 'products': {},
30
'datatype': 'image-ids', 'format': 'products:1.0'}
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,
38
super(GlanceMirror, self).__init__(config=config)
40
self.loaded_content = {}
41
self.store = objectstore
43
self.keystone_creds = openstack.load_keystone_creds()
45
self.name_prefix = name_prefix or ""
46
if region is not None:
47
self.keystone_creds['region_name'] = region
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']
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']
59
def _cidpath(self, content_id):
60
return "streams/v1/%s.js" % content_id
62
def load_products(self, path=None, content_id=None):
63
my_cid = translate_dl_content_id(content_id, self.crsn)
65
# glance is the definitive store. Any data loaded from the store
70
path = self._cidpath(my_cid)
71
store_t = util.load_content(self.store.reader(path).read())
73
if e.errno != errno.ENOENT:
76
store_t = empty_iid_products(my_cid)
78
glance_t = empty_iid_products(my_cid)
80
images = self.gclient.images.list()
82
image = image.to_dict()
84
if image['owner'] != self.tenant_id:
87
props = image['properties']
88
if props.get('content_id') != my_cid:
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):
97
# get data from the datastore for this item, if it exists
98
# and then update that with glance data (just in case different)
100
item_data = util.products_exdata(store_t,
101
(product, version, item,),
103
insert_fieldnames=False)
107
item_data.update({'name': image['name'], 'id': image['id']})
109
util.products_set(glance_t, item_data,
110
(product, version, item,))
112
for product in glance_t['products']:
113
glance_t['products'][product]['region'] = self.region
114
glance_t['products'][product]['endpoint'] = self.auth_url
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'))
123
def insert_item(self, data, src, target, pedigree, contentsource):
124
flat = util.products_exdata(src, pedigree, include_top=False)
129
name = flat.get('pubname', flat.get('name'))
130
if not name.endswith(flat['item_name']):
131
name += "-%s" % (flat['item_name'])
137
props = {'content_id': target['content_id']}
138
for n in ('product_name', 'version_name', 'item_name'):
142
arch = flat.get('arch')
146
props['architecture'] = arch
149
'name': self.name_prefix + name,
151
'disk_format': 'qcow2',
152
'container_format': 'bare',
156
create_kwargs['size'] = data.get('size')
159
create_kwargs['checksum'] = data.get('md5')
163
(tmp_path, tmp_del) = util.get_local_copy(contentsource)
165
contentsource.close()
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)
173
if tmp_del and os.path.exists(tmp_path):
176
util.products_set(target, t_item, pedigree)
178
def remove_item(self, data, src, target, pedigree):
179
util.products_del(target, pedigree)
181
print "removing %s: %s" % (data['id'], data['name'])
182
self.gclient.images.delete(data['id'])
184
def filter_index_entry(self, data, src, pedigree):
185
return data.get('datatype') in ("image-downloads", None)
187
def insert_products(self, path, target, content):
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)
197
tsnow = util.timestamp()
198
tree['updated'] = tsnow
200
dpath = self._cidpath(tree['content_id'])
201
LOG.info("writing data: %s", dpath)
202
self.store.insert_content(dpath, util.dump_data(tree))
204
# now insert or update an index
205
ipath = "streams/v1/index.js"
207
index = util.load_content(self.store.reader(ipath).read())
208
except IOError as exc:
209
if exc.errno != errno.ENOENT:
211
index = {"index": {}, 'format': 'index:1.0',
212
'updated': util.timestamp()}
214
index['index'][tree['content_id']] = {
216
'datatype': 'image-ids',
217
'clouds': [{'region': self.region, 'endpoint': self.auth_url}],
218
'cloudname': self.cloudname,
220
'products': tree['products'].keys(),
221
'format': tree['format'],
223
LOG.info("writing data: %s", ipath)
224
self.store.insert_content(ipath, util.dump_data(index))
226
# vi: ts=4 expandtab syntax=python