
Initial PoC source code for Tricircle, the project for OpenStack cascading solution. Change-Id: I8abc93839a26446cb61c8d9004dfd812bd91de6e
216 lines
6.1 KiB
Python
216 lines
6.1 KiB
Python
# Copyright (c) 2014 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.
|
|
#
|
|
# @author: Jia Dong, HuaWei
|
|
|
|
import re
|
|
|
|
from oslo.config import cfg
|
|
import six.moves.urllib.parse as urlparse
|
|
|
|
from glance.sync.clients import Clients as clients
|
|
|
|
CONF = cfg.CONF
|
|
CONF.import_opt('cascading_endpoint_url', 'glance.common.config', group='sync')
|
|
CONF.import_opt('sync_strategy', 'glance.common.config', group='sync')
|
|
|
|
|
|
def create_glance_client(auth_token, url):
|
|
"""
|
|
create glance clients
|
|
"""
|
|
return clients(auth_token).glance(url=url)
|
|
|
|
|
|
def create_self_glance_client(auth_token):
|
|
return create_glance_client(auth_token, get_cascading_endpoint_url())
|
|
|
|
|
|
def get_mappings_from_image(auth_token, image_id):
|
|
"""
|
|
get image's patched glance-locations
|
|
"""
|
|
client = create_self_glance_client(auth_token)
|
|
image = client.images.get(image_id)
|
|
locations = image.locations
|
|
if not locations:
|
|
return {}
|
|
return get_mappings_from_locations(locations)
|
|
|
|
|
|
def get_mappings_from_locations(locations):
|
|
mappings = {}
|
|
for loc in locations:
|
|
if is_glance_location(loc['url']):
|
|
id = loc['metadata'].get('image_id')
|
|
if not id:
|
|
continue
|
|
ep_url = create_ep_by_loc(loc)
|
|
mappings[ep_url] = id
|
|
return mappings
|
|
|
|
|
|
def get_cascading_endpoint_url():
|
|
return CONF.sync.cascading_endpoint_url
|
|
|
|
|
|
def get_host_from_ep(ep_url):
|
|
if not ep_url:
|
|
return None
|
|
pieces = urlparse.urlparse(ep_url)
|
|
return pieces.netloc.split(':')[0]
|
|
|
|
pattern = re.compile(r'^https?://\S+/v2/images/\S+$')
|
|
|
|
|
|
def get_default_location(locations):
|
|
for location in locations:
|
|
if is_default_location(location):
|
|
return location
|
|
return None
|
|
|
|
|
|
def is_glance_location(loc_url):
|
|
return pattern.match(loc_url)
|
|
|
|
|
|
def is_snapshot_location(location):
|
|
l_meta = location['metadata']
|
|
return l_meta and l_meta.get('image_from', None) in['snapshot', 'volume']
|
|
|
|
|
|
def get_id_from_glance_loc(location):
|
|
if not is_glance_location(location['url']):
|
|
return None
|
|
loc_meta = location['metadata']
|
|
if not loc_meta:
|
|
return None
|
|
return loc_meta.get('image_id', None)
|
|
|
|
|
|
def is_default_location(location):
|
|
try:
|
|
return not is_glance_location(location['url']) \
|
|
and location['metadata']['is_default'] == 'true'
|
|
except:
|
|
return False
|
|
|
|
|
|
def get_snapshot_glance_loc(locations):
|
|
for location in locations:
|
|
if is_snapshot_location(location):
|
|
return location
|
|
return None
|
|
|
|
|
|
def create_ep_by_loc(location):
|
|
loc_url = location['url']
|
|
if not is_glance_location(loc_url):
|
|
return None
|
|
piece = urlparse.urlparse(loc_url)
|
|
return piece.scheme + '://' + piece.netloc + '/'
|
|
|
|
|
|
def generate_glance_location(ep, image_id, port=None):
|
|
default_port = port or '9292'
|
|
piece = urlparse.urlparse(ep)
|
|
paths = []
|
|
paths.append(piece.scheme)
|
|
paths.append('://')
|
|
paths.append(piece.netloc.split(':')[0])
|
|
paths.append(':')
|
|
paths.append(default_port)
|
|
paths.append('/v2/images/')
|
|
paths.append(image_id)
|
|
return ''.join(paths)
|
|
|
|
|
|
def get_endpoints(auth_token=None, tenant_id=None, **kwargs):
|
|
"""
|
|
find which glance should be sync by strategy config
|
|
"""
|
|
strategy = CONF.sync.sync_strategy
|
|
if strategy not in ['All', 'User']:
|
|
return None
|
|
|
|
openstack_clients = clients(auth_token, tenant_id)
|
|
ksclient = openstack_clients.keystone()
|
|
|
|
'''
|
|
suppose that the cascading glance is 'public' endpoint type, and the
|
|
cascaded glacne endpoints are 'internal'
|
|
'''
|
|
regions = kwargs.pop('region_names', [])
|
|
if strategy == 'All' and not regions:
|
|
urls = ksclient.service_catalog.get_urls(service_type='image',
|
|
endpoint_type='publicURL')
|
|
if urls:
|
|
result = [u for u in urls if u != get_cascading_endpoint_url()]
|
|
else:
|
|
result = []
|
|
return result
|
|
else:
|
|
user_urls = []
|
|
for region_name in regions:
|
|
urls = ksclient.service_catalog.get_urls(service_type='image',
|
|
endpoint_type='publicURL',
|
|
region_name=region_name)
|
|
if urls:
|
|
user_urls.extend(urls)
|
|
result = [u for u in set(user_urls) if u !=
|
|
get_cascading_endpoint_url()]
|
|
return result
|
|
|
|
|
|
_V2_IMAGE_CREATE_PROPERTIES = ['container_format',
|
|
'disk_format', 'min_disk', 'min_ram', 'name',
|
|
'virtual_size', 'visibility', 'protected']
|
|
|
|
|
|
def get_core_properties(image):
|
|
"""
|
|
when sync, create image object, get the sync info
|
|
"""
|
|
_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 calculate_lack_endpoints(all_ep_urls, glance_urls):
|
|
"""
|
|
calculate endpoints which exists in all_eps but not in glance_eps
|
|
"""
|
|
if not glance_urls:
|
|
return all_ep_urls
|
|
|
|
def _contain(ep):
|
|
_hosts = [urlparse.urlparse(_ep).netloc for _ep in glance_urls]
|
|
return not urlparse.urlparse(ep).netloc in _hosts
|
|
return filter(_contain, all_ep_urls)
|
|
|
|
|
|
def is_ep_contains(ep_url, glance_urls):
|
|
_hosts = [urlparse.urlparse(_ep).netloc for _ep in glance_urls]
|
|
return urlparse.urlparse(ep_url) in _hosts
|