187 lines
7.5 KiB
Python
187 lines
7.5 KiB
Python
"""Implementation of an cascading image service that uses to sync the image
|
|
from cascading glance to the special cascaded glance.
|
|
"""
|
|
import logging
|
|
import os
|
|
import urlparse
|
|
|
|
from oslo.config import cfg
|
|
|
|
from nova.openstack.common.gettextutils import _
|
|
from nova.image import glance
|
|
from nova.image.sync import drivers as drivermgr
|
|
|
|
|
|
CONF = cfg.CONF
|
|
LOG = logging.getLogger(__name__)
|
|
|
|
glance_cascading_opt = [
|
|
cfg.StrOpt('image_copy_dest_location_url',
|
|
default='file:///var/lib/glance/images',
|
|
help=_("The path cascaded image_data copy to."),
|
|
deprecated_opts=[cfg.DeprecatedOpt('dest_location_url',
|
|
group='DEFAULT')]),
|
|
cfg.StrOpt('image_copy_dest_host',
|
|
default='127.0.0.1',
|
|
help=_("The host name where image_data copy to."),
|
|
deprecated_opts=[cfg.DeprecatedOpt('dest_host',
|
|
group='DEFAULT')]),
|
|
cfg.StrOpt('image_copy_dest_user',
|
|
default='glance',
|
|
help=_("The user name of cascaded glance for copy."),
|
|
deprecated_opts=[cfg.DeprecatedOpt('dest_user',
|
|
group='DEFAULT')]),
|
|
cfg.StrOpt('image_copy_dest_password',
|
|
default='openstack',
|
|
help=_("The passowrd of cascaded glance for copy."),
|
|
deprecated_opts=[cfg.DeprecatedOpt('dest_password',
|
|
group='DEFAULT')]),
|
|
cfg.StrOpt('image_copy_source_location_url',
|
|
default='file:///var/lib/glance/images',
|
|
help=_("where the cascaded image data from"),
|
|
deprecated_opts=[cfg.DeprecatedOpt('source_location_url',
|
|
group='DEFAULT')]),
|
|
cfg.StrOpt('image_copy_source_host',
|
|
default='0.0.0.1',
|
|
help=_("The host name where image_data copy from."),
|
|
deprecated_opts=[cfg.DeprecatedOpt('source_host',
|
|
group='DEFAULT')]),
|
|
cfg.StrOpt('image_copy_source_user',
|
|
default='glance',
|
|
help=_("The user name of glance for copy."),
|
|
deprecated_opts=[cfg.DeprecatedOpt('source_user',
|
|
group='DEFAULT')]),
|
|
cfg.StrOpt('image_copy_source_password',
|
|
default='openstack',
|
|
help=_("The passowrd of glance for copy."),
|
|
deprecated_opts=[cfg.DeprecatedOpt('source_password',
|
|
group='DEFAULT')]),
|
|
]
|
|
|
|
CONF.register_opts(glance_cascading_opt)
|
|
|
|
_V2_IMAGE_CREATE_PROPERTIES = ['container_format', 'disk_format', 'min_disk',
|
|
'min_ram', 'name', 'protected']
|
|
|
|
|
|
def get_adding_image_properties(image):
|
|
_tags = list(image.tags) or []
|
|
kwargs = {}
|
|
for key in _V2_IMAGE_CREATE_PROPERTIES:
|
|
try:
|
|
value = getattr(image, key, None)
|
|
if value and value != 'None':
|
|
kwargs[key] = value
|
|
except KeyError:
|
|
pass
|
|
if _tags:
|
|
kwargs['tags'] = _tags
|
|
return kwargs
|
|
|
|
|
|
def get_candidate_path(image, scheme='file'):
|
|
locations = image.locations or []
|
|
for loc in locations:
|
|
if loc['url'].startswith(scheme):
|
|
return loc['url'] if scheme != 'file' \
|
|
else loc['url'][len('file://'):]
|
|
return None
|
|
|
|
|
|
def get_copy_driver(scheme_key):
|
|
return drivermgr.get_store_driver(scheme_key)
|
|
|
|
|
|
def get_host_port(url):
|
|
if not url:
|
|
return None, None
|
|
pieces = urlparse.urlparse(url)
|
|
return pieces.netloc.split(":")[0], pieces.netloc.split(":")[1]
|
|
|
|
|
|
class GlanceCascadingService(object):
|
|
|
|
def __init__(self, cascading_client=None):
|
|
self._client = cascading_client or glance.GlanceClientWrapper()
|
|
|
|
def sync_image(self, context, cascaded_url, cascading_image):
|
|
cascaded_glance_url = cascaded_url
|
|
_host, _port = get_host_port(cascaded_glance_url)
|
|
_cascaded_client = glance.GlanceClientWrapper(context=context,
|
|
host=_host,
|
|
port=_port,
|
|
version=2)
|
|
|
|
image_meta = get_adding_image_properties(cascading_image)
|
|
cascaded_image = _cascaded_client.call(context, 2, 'create',
|
|
**image_meta)
|
|
image_id = cascading_image.id
|
|
cascaded_id = cascaded_image.id
|
|
candidate_path = get_candidate_path(cascading_image)
|
|
LOG.debug("the candidate path is %s." % (candidate_path))
|
|
# copy image
|
|
try:
|
|
image_loc = self._copy_data(image_id, cascaded_id, candidate_path)
|
|
except Exception as e:
|
|
LOG.exception(_("copy image failed, reason=%s") % e)
|
|
raise
|
|
else:
|
|
if not image_loc:
|
|
LOG.exception(_("copy image Exception, no cascaded_loc"))
|
|
try:
|
|
# patch loc to the cascaded image
|
|
csd_locs = [{'url': image_loc,
|
|
'metadata': {}
|
|
}]
|
|
_cascaded_client.call(context, 2, 'update', cascaded_id,
|
|
remove_props=None,
|
|
locations=csd_locs)
|
|
except Exception as e:
|
|
LOG.exception(_("patch loc to cascaded image Exception, reason: %s"
|
|
% e))
|
|
raise
|
|
|
|
try:
|
|
# patch glance-loc to cascading image
|
|
csg_locs = cascading_image.locations
|
|
glance_loc = '%s/v2/images/%s' % (cascaded_glance_url,
|
|
cascaded_id)
|
|
csg_locs.append({'url': glance_loc,
|
|
'metadata': {'image_id': str(cascaded_id),
|
|
'action': 'upload'
|
|
}
|
|
})
|
|
self._client.call(context, 2, 'update', image_id,
|
|
remove_props=None, locations=csg_locs)
|
|
except Exception as e:
|
|
LOG.exception(_("patch loc to cascading image Exception, reason: %s"
|
|
% e))
|
|
raise
|
|
|
|
return cascaded_id
|
|
|
|
@staticmethod
|
|
def _copy_data(cascading_id, cascaded_id, candidate_path):
|
|
source_pieces = urlparse.urlparse(CONF.image_copy_source_location_url)
|
|
dest_pieces = urlparse.urlparse(CONF.image_copy_dest_location_url)
|
|
source_scheme = source_pieces.scheme
|
|
dest_scheme = dest_pieces.scheme
|
|
_key = ('%s:%s' % (source_scheme, dest_scheme))
|
|
copy_driver = get_copy_driver(_key)
|
|
source_path = os.path.join(source_pieces.path, cascading_id)
|
|
dest_path = os.path.join(dest_pieces.path, cascaded_id)
|
|
|
|
source_location = {'host': CONF.image_copy_source_host,
|
|
'login_user': CONF.image_copy_source_user,
|
|
'login_password': CONF.image_copy_source_password,
|
|
'path': source_path
|
|
}
|
|
dest_location = {'host': CONF.image_copy_dest_host,
|
|
'login_user': CONF.image_copy_dest_user,
|
|
'login_password': CONF.image_copy_dest_password,
|
|
'path': dest_path
|
|
}
|
|
return copy_driver.copy_to(source_location,
|
|
dest_location,
|
|
candidate_path=candidate_path)
|