3
# Copyright (c) 2010 Citrix Systems, Inc.
4
# Copyright 2010 United States Government as represented by the
5
# Administrator of the National Aeronautics and Space Administration.
8
# Licensed under the Apache License, Version 2.0 (the "License"); you may
9
# not use this file except in compliance with the License. You may obtain
10
# a copy of the License at
12
# http://www.apache.org/licenses/LICENSE-2.0
14
# Unless required by applicable law or agreed to in writing, software
15
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
16
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
17
# License for the specific language governing permissions and limitations
21
# XenAPI plugin for fetching images from nova-objectstore.
35
from pluginlib_nova import *
36
configure_logging('objectstore')
39
KERNEL_DIR = '/boot/guest'
41
DOWNLOAD_CHUNK_SIZE = 2 * 1024 * 1024
44
MBR_SIZE_BYTES = MBR_SIZE_SECTORS * SECTOR_SIZE
47
def is_vdi_pv(session, args):
48
logging.debug("Checking wheter VDI has PV kernel")
49
vdi = exists(args, 'vdi-ref')
50
pv = with_vdi_in_dom0(session, vdi, False,
51
lambda dev: _is_vdi_pv('/dev/%s' % dev))
59
logging.debug("Running pygrub against %s", dest)
60
output = os.popen('pygrub -qn %s' % dest)
62
for line in output.readlines():
63
#try to find kernel string
64
m = re.search('(?<=kernel:)/.*(?:>)', line)
66
if m.group(0).find('xen') != -1:
68
logging.debug("PV:%d", pv)
72
def get_vdi(session, args):
73
src_url = exists(args, 'src_url')
74
username = exists(args, 'username')
75
password = exists(args, 'password')
76
raw_image = validate_bool(args, 'raw', 'false')
77
add_partition = validate_bool(args, 'add_partition', 'false')
78
(proto, netloc, url_path, _, _, _) = urlparse.urlparse(src_url)
81
raise Exception('Cannot find SR to write VDI to')
83
get_content_length(proto, netloc, url_path, username, password)
85
raise Exception('Cannot get VDI size')
86
vdi_size = virtual_size
89
vdi_size += MBR_SIZE_BYTES
91
vdi = create_vdi(session, sr, src_url, vdi_size, False)
92
with_vdi_in_dom0(session, vdi, False,
93
lambda dev: get_vdi_(proto, netloc, url_path,
95
add_partition, raw_image,
96
virtual_size, '/dev/%s' % dev))
97
return session.xenapi.VDI.get_uuid(vdi)
100
def get_vdi_(proto, netloc, url_path, username, password,
101
add_partition, raw_image, virtual_size, dest):
103
#vdi should not be partitioned for raw images
104
if add_partition and not raw_image:
105
write_partition(virtual_size, dest)
107
offset = (add_partition and not raw_image and MBR_SIZE_BYTES) or 0
108
get(proto, netloc, url_path, username, password, dest, offset)
111
def write_partition(virtual_size, dest):
112
mbr_last = MBR_SIZE_SECTORS - 1
113
primary_first = MBR_SIZE_SECTORS
114
primary_last = MBR_SIZE_SECTORS + (virtual_size / SECTOR_SIZE) - 1
116
logging.debug('Writing partition table %d %d to %s...',
117
primary_first, primary_last, dest)
119
result = os.system('parted --script %s mklabel msdos' % dest)
121
raise Exception('Failed to mklabel')
122
result = os.system('parted --script %s mkpart primary %ds %ds' %
123
(dest, primary_first, primary_last))
125
raise Exception('Failed to mkpart')
127
logging.debug('Writing partition table %s done.', dest)
130
def find_sr(session):
131
host = get_this_host(session)
132
srs = session.xenapi.SR.get_all()
134
sr_rec = session.xenapi.SR.get_record(sr)
135
if not ('i18n-key' in sr_rec['other_config'] and
136
sr_rec['other_config']['i18n-key'] == 'local-storage'):
138
for pbd in sr_rec['PBDs']:
139
pbd_rec = session.xenapi.PBD.get_record(pbd)
140
if pbd_rec['host'] == host:
145
def get_kernel(session, args):
146
src_url = exists(args, 'src_url')
147
username = exists(args, 'username')
148
password = exists(args, 'password')
150
(proto, netloc, url_path, _, _, _) = urlparse.urlparse(src_url)
152
dest = os.path.join(KERNEL_DIR, url_path[1:])
154
# Paranoid check against people using ../ to do rude things.
155
if os.path.commonprefix([KERNEL_DIR, dest]) != KERNEL_DIR:
156
raise Exception('Illegal destination %s %s', (url_path, dest))
158
dirname = os.path.dirname(dest)
162
if e.errno != errno.EEXIST:
164
if not os.path.isdir(dirname):
165
raise Exception('Cannot make directory %s', dirname)
172
get(proto, netloc, url_path, username, password, dest, 0)
177
def get_content_length(proto, netloc, url_path, username, password):
178
headers = make_headers('HEAD', url_path, username, password)
179
return with_http_connection(
181
lambda conn: get_content_length_(url_path, headers, conn))
184
def get_content_length_(url_path, headers, conn):
185
conn.request('HEAD', url_path, None, headers)
186
response = conn.getresponse()
187
if response.status != 200:
188
raise Exception('%d %s' % (response.status, response.reason))
190
return long(response.getheader('Content-Length', -1))
193
def get(proto, netloc, url_path, username, password, dest, offset):
194
headers = make_headers('GET', url_path, username, password)
195
download(proto, netloc, url_path, headers, dest, offset)
198
def make_headers(verb, url_path, username, password):
201
time.strftime("%a, %d %b %Y %H:%M:%S GMT", time.gmtime())
202
headers['Authorization'] = \
203
'AWS %s:%s' % (username,
204
s3_authorization(verb, url_path, password, headers))
208
def s3_authorization(verb, path, password, headers):
209
sha1 = hmac.new(password, digestmod=sha)
210
sha1.update(plaintext(verb, path, headers))
211
return base64.encodestring(sha1.digest()).strip()
214
def plaintext(verb, path, headers):
215
return '%s\n\n\n%s\n%s' % (verb,
216
"\n".join([headers[h] for h in headers]),
220
def download(proto, netloc, url_path, headers, dest, offset):
221
with_http_connection(
223
lambda conn: download_(url_path, dest, offset, headers, conn))
226
def download_(url_path, dest, offset, headers, conn):
227
conn.request('GET', url_path, None, headers)
228
response = conn.getresponse()
229
if response.status != 200:
230
raise Exception('%d %s' % (response.status, response.reason))
232
length = response.getheader('Content-Length', -1)
236
lambda dest_file: download_all(response, length, dest_file, offset))
239
def download_all(response, length, dest_file, offset):
240
dest_file.seek(offset)
243
buf = response.read(DOWNLOAD_CHUNK_SIZE)
249
if length != -1 and i >= length:
253
if __name__ == '__main__':
254
XenAPIPlugin.dispatch({'get_vdi': get_vdi,
255
'get_kernel': get_kernel,
256
'is_vdi_pv': is_vdi_pv})