nova/nova/virt/xenapi/image/vdi_through_dev.py

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)