Files
nova/nova/virt/xenapi/image/utils.py
jianghua wang f7593ded8f XenAPI: define a new image handler to use vdi streaming
With the new image handler, it creates an image proxy which
will use the vdi streaming function from os-xenapi to
remotely export VHD from XenServer(image upload) or import
VHD to Xenerver(image download).
The existing GlanceStore uses custom functionality to directly
manipulate files on-disk, so it has the restriction that SR's
type must be file system based: e.g. ext or nfs. The new
image handler invokes APIs formally supported by XenServer
to export/import VDI remotely, it can support other SR
types also e.g. lvm, iscsi, etc.

Note:
vdi streaming would be supported by XenServer 6.5 or above.
The function of image handler depends on os-xenapi 0.3.3 or
above, so bump os-xenapi's version to 0.3.3 and also declare
depends on the patch which bump version in openstack/requirements.

Blueprint: xenapi-image-handler-option-improvement
Change-Id: I0ad8e34808401ace9b85e1b937a542f4c4e61690
Depends-On: Ib8bc0f837c55839dc85df1d1f0c76b320b9d97b8
2018-06-13 01:59:07 +00:00

122 lines
3.8 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 shutil
import tarfile
from oslo_utils import importutils
from nova import exception
from nova import image
_VDI_FORMAT_RAW = 1
IMAGE_API = image.API()
IMAGE_HANDLERS = {'direct_vhd': 'glance.GlanceStore',
'vdi_local_dev': 'vdi_through_dev.VdiThroughDevStore',
'vdi_remote_stream': 'vdi_stream.VdiStreamStore'}
def get_image_handler(handler_name):
if handler_name not in IMAGE_HANDLERS:
raise exception.ImageHandlerUnsupported(image_handler=handler_name)
return importutils.import_object('nova.virt.xenapi.image.'
'%s' % IMAGE_HANDLERS[handler_name])
class GlanceImage(object):
def __init__(self, context, image_href_or_id):
self._context = context
self._image_id = image_href_or_id
self._cached_meta = None
@property
def meta(self):
if self._cached_meta is None:
self._cached_meta = IMAGE_API.get(self._context, self._image_id)
return self._cached_meta
def download_to(self, fileobj):
return IMAGE_API.download(self._context, self._image_id, fileobj)
def is_raw_tgz(self):
return ['raw', 'tgz'] == [
self.meta.get(key) for key in ('disk_format', 'container_format')]
def data(self):
return IMAGE_API.download(self._context, self._image_id)
class RawImage(object):
def __init__(self, glance_image):
self.glance_image = glance_image
def get_size(self):
return int(self.glance_image.meta['size'])
def stream_to(self, fileobj):
return self.glance_image.download_to(fileobj)
class IterableToFileAdapter(object):
"""A degenerate file-like so that an iterable could be read like a file.
As Glance client returns an iterable, but tarfile requires a file like,
this is the adapter between the two. This allows tarfile to access the
glance stream.
"""
def __init__(self, iterable):
self.iterator = iterable.__iter__()
self.remaining_data = ''
def read(self, size):
chunk = self.remaining_data
try:
while not chunk:
chunk = next(self.iterator)
except StopIteration:
return ''
return_value = chunk[0:size]
self.remaining_data = chunk[size:]
return return_value
class RawTGZImage(object):
def __init__(self, glance_image):
self.glance_image = glance_image
self._tar_info = None
self._tar_file = None
def _as_file(self):
return IterableToFileAdapter(self.glance_image.data())
def _as_tarfile(self):
return tarfile.open(mode='r|gz', fileobj=self._as_file())
def get_size(self):
if self._tar_file is None:
self._tar_file = self._as_tarfile()
self._tar_info = self._tar_file.next()
return self._tar_info.size
def stream_to(self, target_file):
if self._tar_file is None:
self._tar_file = self._as_tarfile()
self._tar_info = self._tar_file.next()
source_file = self._tar_file.extractfile(self._tar_info)
shutil.copyfileobj(source_file, target_file)
self._tar_file.close()