109 lines
3.6 KiB
Python
109 lines
3.6 KiB
Python
# Copyright 2013 OpenStack Foundation
|
|
# All Rights Reserved.
|
|
#
|
|
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
|
# not use this file except in compliance with the License. You may obtain
|
|
# a copy of the License at
|
|
#
|
|
# http://www.apache.org/licenses/LICENSE-2.0
|
|
#
|
|
# Unless required by applicable law or agreed to in writing, software
|
|
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
|
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
|
# License for the specific language governing permissions and limitations
|
|
# under the License.
|
|
|
|
import contextlib
|
|
import os
|
|
import tarfile
|
|
|
|
import eventlet
|
|
from eventlet import greenio
|
|
|
|
from nova.image import glance
|
|
from nova import utils
|
|
from nova.virt.xenapi import vm_utils
|
|
|
|
|
|
class VdiThroughDevStore(object):
|
|
"""Deal with virtual disks by attaching them to the OS domU.
|
|
|
|
At the moment it supports upload to Glance, and the upload format is a raw
|
|
disk inside a tgz.
|
|
"""
|
|
|
|
def upload_image(self, context, session, instance, vdi_uuids, image_id):
|
|
command = UploadToGlanceAsRawTgz(
|
|
context, session, instance, vdi_uuids, image_id)
|
|
return command.upload_image()
|
|
|
|
def download_image(self, context, session, image_id):
|
|
# TODO(matelakat) Move through-dev image download functionality to this
|
|
# method.
|
|
raise NotImplementedError()
|
|
|
|
|
|
class UploadToGlanceAsRawTgz(object):
|
|
def __init__(self, context, session, instance, vdi_uuids, image_id):
|
|
self.context = context
|
|
self.image_id = image_id
|
|
self.session = session
|
|
self.vdi_uuids = vdi_uuids
|
|
|
|
def _get_virtual_size(self):
|
|
return self.session.call_xenapi(
|
|
'VDI.get_virtual_size', self._get_vdi_ref())
|
|
|
|
def _get_vdi_ref(self):
|
|
return self.session.call_xenapi('VDI.get_by_uuid', self.vdi_uuids[0])
|
|
|
|
def _perform_upload(self, devpath):
|
|
readfile, writefile = self._create_pipe()
|
|
size = self._get_virtual_size()
|
|
producer = TarGzProducer(devpath, writefile, size, 'disk.raw')
|
|
consumer = glance.UpdateGlanceImage(
|
|
self.context, self.image_id, producer.get_metadata(), readfile)
|
|
pool = eventlet.GreenPool()
|
|
pool.spawn(producer.start)
|
|
pool.spawn(consumer.start)
|
|
pool.waitall()
|
|
|
|
def _create_pipe(self):
|
|
rpipe, wpipe = os.pipe()
|
|
rfile = greenio.GreenPipe(rpipe, 'rb', 0)
|
|
wfile = greenio.GreenPipe(wpipe, 'wb', 0)
|
|
return rfile, wfile
|
|
|
|
def upload_image(self):
|
|
vdi_ref = self._get_vdi_ref()
|
|
with vm_utils.vdi_attached_here(self.session, vdi_ref,
|
|
read_only=True) as dev:
|
|
devpath = utils.make_dev_path(dev)
|
|
with utils.temporary_chown(devpath):
|
|
self._perform_upload(devpath)
|
|
|
|
|
|
class TarGzProducer(object):
|
|
def __init__(self, devpath, writefile, size, fname):
|
|
self.fpath = devpath
|
|
self.output = writefile
|
|
self.size = size
|
|
self.fname = fname
|
|
|
|
def get_metadata(self):
|
|
return {
|
|
'disk_format': 'raw',
|
|
'container_format': 'tgz'
|
|
}
|
|
|
|
def start(self):
|
|
with contextlib.closing(self.output):
|
|
tinfo = tarfile.TarInfo(name=self.fname)
|
|
tinfo.size = int(self.size)
|
|
with tarfile.open(fileobj=self.output, mode='w|gz') as tfile:
|
|
with self._open_file(self.fpath, 'rb') as input_file:
|
|
tfile.addfile(tinfo, fileobj=input_file)
|
|
|
|
def _open_file(self, *args):
|
|
return open(*args)
|