kingbird/kingbird/engine/image_sync_manager.py

210 lines
9.3 KiB
Python

# Copyright 2017 Ericsson AB.
#
# 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 threading
from oslo_log import log as logging
from kingbird.common import consts
from kingbird.common import exceptions
from kingbird.db.sqlalchemy import api as db_api
from kingbird.drivers.openstack import glance_adapter
from kingbird.drivers.openstack.glance_v2 import GlanceClient
from kingbird.drivers.openstack.glance_v2 import GlanceUpload
LOG = logging.getLogger(__name__)
class ImageSyncManager(object):
"""Manages tasks related to resource management."""
def __init__(self, *args, **kwargs):
super(ImageSyncManager, self).__init__()
def create_resources_in_region(self, job_id, target_regions,
source_region, context, resource, force):
"""Create Region Specific threads."""
regions_thread = list()
for region in target_regions:
thread = threading.Thread(target=self.create_resources,
args=(job_id, region, source_region,
context, resource, force))
regions_thread.append(thread)
thread.start()
for region_thread in regions_thread:
region_thread.join()
def create_resources(self, job_id, region, source_region, context,
resource, force):
"""Check dependent images and create resources in target regions."""
source_glance_client = GlanceClient(source_region, context)
target_glance_client = GlanceClient(region, context)
dependent_images = glance_adapter.check_dependent_images(
context, source_region, resource)
if dependent_images is not None:
result = self.create_dependent_image(
resource, dependent_images, target_glance_client,
source_glance_client, region, force)
self.update_result_in_database(context, job_id, region, resource,
result)
else:
result = self.create_independent_image(
resource, target_glance_client, source_glance_client,
region, force)
self.update_result_in_database(context, job_id, region, resource,
result)
def update_result_in_database(self, context, job_id, region, resource,
result):
"""Update result in database based on the sync operation."""
job_result = consts.JOB_SUCCESS if result else consts.JOB_FAILURE
try:
db_api.resource_sync_update(context, job_id, region,
resource, job_result)
except exceptions.JobNotFound():
raise
pass
def create_dependent_image(self, resource, dependent_images,
target_client, source_client, region, force):
"""Create dependent images along with base image.
Base image here is Amazon Machine Image(AMI) and
Dependent images are Amazon Kernel Image(AKI),
Amazon Ramdisk Image(ARI).
:param resource: Resource to be synced.
:param dependent_images: Dependent images for the base image.
:param target_client: Glance client object for the target_region.
:param source_client: Glance client object for source_region.
:param region: Target region in which resource has to be synced.
:param force: Default force option is False. If '--force'
is given then force is set to True.
"""
try:
kernel_image = dependent_images['kernel_image']
ramdisk_image = dependent_images['ramdisk_image']
source_image = source_client.get_image(resource)
# Create images in target regions.
target_kernel_image = target_client.\
create_image(kernel_image, force)
target_ramdisk_image = target_client.\
create_image(ramdisk_image, force)
target_source_image = target_client.\
create_image(source_image, force, target_kernel_image.id,
target_ramdisk_image.id)
# Fetch and Upload image into glance.
# Kernel Image upload.
kernel_image_data = source_client.\
get_image_data(kernel_image.id)
upload_kernel_image = GlanceUpload(kernel_image_data)
target_client.image_upload(target_kernel_image.id,
upload_kernel_image)
LOG.info('Kernel_image %(image)s uploaded in %(region)s'
% {'image': kernel_image.id, 'region': region})
# Ramdisk image upload.
ramdisk_image_data = source_client.\
get_image_data(ramdisk_image.id)
upload_ram_disk_image = GlanceUpload(ramdisk_image_data)
target_client.image_upload(target_ramdisk_image.id,
upload_ram_disk_image)
LOG.info('ramdisk_image %(image)s uploaded in %(region)s'
% {'image': ramdisk_image.id, 'region': region})
# Base 'AMI' image upload.
source_image_data = source_client.get_image_data(source_image.id)
upload_source_image = GlanceUpload(source_image_data)
target_client.image_upload(target_source_image.id,
upload_source_image)
LOG.info('source_image %(image)s uploaded in %(region)s'
% {'image': source_image.id, 'region': region})
return True
except Exception as exc:
LOG.error('Exception Occurred: %(msg)s in %(region)s'
% {'msg': exc.message, 'region': region})
return False
def create_independent_image(self, resource, target_client,
source_client, region, force):
"""Create independent images.
Base image here is Qcow2.
:param resource: Resource to be synced.
:param target_client: Glance client object for the target_region.
:param source_client: Glance client object for source_region.
:param region: Target region in which resource has to be synced.
:param force: Default force option is False. If '--force'
is given then force is set to True.
"""
try:
source_image = source_client.get_image(resource)
target_source_image = target_client.create_image(source_image,
force)
source_image_data = source_client.get_image_data(source_image.id)
upload_source_image = GlanceUpload(source_image_data)
target_client.image_upload(target_source_image.id,
upload_source_image)
LOG.info('source_image %(image)s uploaded in %(region)s'
% {'image': source_image.id, 'region': region})
return True
except Exception as exc:
LOG.error('Exception Occurred: %(msg)s in %(region)s'
% {'msg': exc.message, 'region': region})
return False
def resource_sync(self, context, job_id, payload):
"""Create resources in target regions.
Image with same id is created in target_regions and therefore
if a user wants to syncs the same resource as the ID is already
used glance throws 409 error in order to avoid that we use --force
and set force flag to true and there by creates resource without
fail.
:param context: request context object.
:param job_id: ID of the job which triggered image_sync.
:payload: request payload.
"""
LOG.info('Triggered image sync.')
images_thread = list()
target_regions = payload['target']
force = eval(str(payload.get('force', False)))
resource_ids = payload.get('resources')
source_region = payload['source']
for resource in resource_ids:
thread = threading.Thread(target=self.create_resources_in_region,
args=(job_id, target_regions,
source_region, context,
resource, force))
images_thread.append(thread)
thread.start()
for image_thread in images_thread:
image_thread.join()
try:
resource_sync_details = db_api.\
resource_sync_status(context, job_id)
except exceptions.JobNotFound:
raise
result = consts.JOB_SUCCESS
if consts.JOB_FAILURE in resource_sync_details:
result = consts.JOB_FAILURE
try:
db_api.sync_job_update(context, job_id, result)
except exceptions.JobNotFound:
raise