1
# vim: tabstop=4 shiftwidth=4 softtabstop=4
2
# Copyright [2010] [Anso Labs, LLC]
4
# Licensed under the Apache License, Version 2.0 (the "License");
5
# you may not use this file except in compliance with the License.
6
# You may obtain a copy of the License at
8
# http://www.apache.org/licenses/LICENSE-2.0
10
# Unless required by applicable law or agreed to in writing, software
11
# distributed under the License is distributed on an "AS IS" BASIS,
12
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
# See the License for the specific language governing permissions and
14
# limitations under the License.
17
Take uploaded bucket contents and register them as disk images (AMIs).
18
Requires decryption using keys in the manifest.
21
# TODO(jesse): Got these from Euca2ools, will need to revisit them
30
from xml.etree import ElementTree
32
from nova import exception
33
from nova import flags
34
from nova import utils
35
from nova.objectstore import bucket
39
flags.DEFINE_string('images_path', utils.abspath('../images'),
40
'path to decrypted images')
43
def __init__(self, image_id):
44
self.image_id = image_id
45
self.path = os.path.abspath(os.path.join(FLAGS.images_path, image_id))
46
if not self.path.startswith(os.path.abspath(FLAGS.images_path)) or \
47
not os.path.isdir(self.path):
48
raise exception.NotFound
51
for fn in ['info.json', 'image']:
53
os.unlink(os.path.join(self.path, fn))
61
def is_authorized(self, user):
63
return self.metadata['isPublic'] or self.metadata['imageOwnerId'] == user.id
67
def set_public(self, state):
69
md['isPublic'] = state
70
with open(os.path.join(self.path, 'info.json'), 'w') as f:
76
for fn in glob.glob("%s/*/info.json" % FLAGS.images_path):
78
image_id = fn.split('/')[-2]
79
images.append(Image(image_id))
86
return self.metadata['imageOwnerId']
90
with open(os.path.join(self.path, 'info.json')) as f:
94
def create(image_id, image_location, user):
95
image_path = os.path.join(FLAGS.images_path, image_id)
96
os.makedirs(image_path)
98
bucket_name = image_location.split("/")[0]
99
manifest_path = image_location[len(bucket_name)+1:]
100
bucket_object = bucket.Bucket(bucket_name)
102
manifest = ElementTree.fromstring(bucket_object[manifest_path].read())
103
image_type = 'machine'
106
kernel_id = manifest.find("machine_configuration/kernel_id").text
107
if kernel_id == 'true':
108
image_type = 'kernel'
113
ramdisk_id = manifest.find("machine_configuration/ramdisk_id").text
114
if ramdisk_id == 'true':
115
image_type = 'ramdisk'
121
'imageLocation': image_location,
122
'imageOwnerId': user.id,
123
'isPublic': False, # FIXME: grab public from manifest
124
'architecture': 'x86_64', # FIXME: grab architecture from manifest
128
def write_state(state):
129
info['imageState'] = state
130
with open(os.path.join(image_path, 'info.json'), "w") as f:
133
write_state('pending')
135
encrypted_filename = os.path.join(image_path, 'image.encrypted')
136
with open(encrypted_filename, 'w') as f:
137
for filename in manifest.find("image").getiterator("filename"):
138
shutil.copyfileobj(bucket_object[filename.text].file, f)
140
write_state('decrypting')
142
# FIXME: grab kernelId and ramdiskId from bundle manifest
143
encrypted_key = binascii.a2b_hex(manifest.find("image/ec2_encrypted_key").text)
144
encrypted_iv = binascii.a2b_hex(manifest.find("image/ec2_encrypted_iv").text)
145
cloud_private_key = os.path.join(FLAGS.ca_path, "private/cakey.pem")
147
decrypted_filename = os.path.join(image_path, 'image.tar.gz')
148
Image.decrypt_image(encrypted_filename, encrypted_key, encrypted_iv, cloud_private_key, decrypted_filename)
150
write_state('untarring')
152
image_file = Image.untarzip_image(image_path, decrypted_filename)
153
shutil.move(os.path.join(image_path, image_file), os.path.join(image_path, 'image'))
155
write_state('available')
156
os.unlink(decrypted_filename)
157
os.unlink(encrypted_filename)
160
def decrypt_image(encrypted_filename, encrypted_key, encrypted_iv, cloud_private_key, decrypted_filename):
161
key, err = utils.execute('openssl rsautl -decrypt -inkey %s' % cloud_private_key, encrypted_key)
163
raise exception.Error("Failed to decrypt private key: %s" % err)
164
iv, err = utils.execute('openssl rsautl -decrypt -inkey %s' % cloud_private_key, encrypted_iv)
166
raise exception.Error("Failed to decrypt initialization vector: %s" % err)
167
out, err = utils.execute('openssl enc -d -aes-128-cbc -in %s -K %s -iv %s -out %s' % (encrypted_filename, key, iv, decrypted_filename))
169
raise exception.Error("Failed to decrypt image file %s : %s" % (encrypted_filename, err))
172
def untarzip_image(path, filename):
173
tar_file = tarfile.open(filename, "r|gz")
174
tar_file.extractall(path)
175
image_file = tar_file.getnames()[0]