| ######### |
| # Copyright (c) 2015 GigaSpaces Technologies Ltd. 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 httplib |
| from urlparse import urlparse |
| |
| from cloudify import ctx |
| from cloudify.decorators import operation |
| from cloudify.exceptions import NonRecoverableError |
| |
| from openstack_plugin_common import ( |
| with_glance_client, |
| get_resource_id, |
| use_external_resource, |
| get_openstack_ids_of_connected_nodes_by_openstack_type, |
| delete_resource_and_runtime_properties, |
| validate_resource, |
| COMMON_RUNTIME_PROPERTIES_KEYS, |
| OPENSTACK_ID_PROPERTY, |
| OPENSTACK_TYPE_PROPERTY, |
| OPENSTACK_NAME_PROPERTY) |
| |
| |
| IMAGE_OPENSTACK_TYPE = 'image' |
| IMAGE_STATUS_ACTIVE = 'active' |
| |
| RUNTIME_PROPERTIES_KEYS = COMMON_RUNTIME_PROPERTIES_KEYS |
| REQUIRED_PROPERTIES = ['container_format', 'disk_format'] |
| |
| |
| @operation |
| @with_glance_client |
| def create(glance_client, **kwargs): |
| if use_external_resource(ctx, glance_client, IMAGE_OPENSTACK_TYPE): |
| return |
| |
| img_dict = { |
| 'name': get_resource_id(ctx, IMAGE_OPENSTACK_TYPE) |
| } |
| _validate_image_dictionary() |
| img_properties = ctx.node.properties['image'] |
| img_dict.update({key: value for key, value in img_properties.iteritems() |
| if key != 'data'}) |
| img = glance_client.images.create(**img_dict) |
| img_path = img_properties.get('data', '') |
| img_url = ctx.node.properties.get('image_url') |
| try: |
| _validate_image() |
| if img_path: |
| with open(img_path, 'rb') as image_file: |
| glance_client.images.upload( |
| image_id=img.id, |
| image_data=image_file) |
| elif img_url: |
| img = glance_client.images.add_location(img.id, img_url, {}) |
| |
| except: |
| _remove_protected(glance_client) |
| glance_client.images.delete(image_id=img.id) |
| raise |
| |
| ctx.instance.runtime_properties[OPENSTACK_ID_PROPERTY] = img.id |
| ctx.instance.runtime_properties[OPENSTACK_TYPE_PROPERTY] = \ |
| IMAGE_OPENSTACK_TYPE |
| ctx.instance.runtime_properties[OPENSTACK_NAME_PROPERTY] = img.name |
| |
| |
| def _get_image_by_ctx(glance_client, ctx): |
| return glance_client.images.get( |
| image_id=ctx.instance.runtime_properties[OPENSTACK_ID_PROPERTY]) |
| |
| |
| @operation |
| @with_glance_client |
| def start(glance_client, start_retry_interval, **kwargs): |
| img = _get_image_by_ctx(glance_client, ctx) |
| if img.status != IMAGE_STATUS_ACTIVE: |
| return ctx.operation.retry( |
| message='Waiting for image to get uploaded', |
| retry_after=start_retry_interval) |
| |
| |
| @operation |
| @with_glance_client |
| def delete(glance_client, **kwargs): |
| _remove_protected(glance_client) |
| delete_resource_and_runtime_properties(ctx, glance_client, |
| RUNTIME_PROPERTIES_KEYS) |
| |
| |
| @operation |
| @with_glance_client |
| def creation_validation(glance_client, **kwargs): |
| validate_resource(ctx, glance_client, IMAGE_OPENSTACK_TYPE) |
| _validate_image_dictionary() |
| _validate_image() |
| |
| |
| def _validate_image_dictionary(): |
| img = ctx.node.properties['image'] |
| missing = '' |
| try: |
| for prop in REQUIRED_PROPERTIES: |
| if prop not in img: |
| missing += '{0} '.format(prop) |
| except TypeError: |
| missing = ' '.join(REQUIRED_PROPERTIES) |
| if missing: |
| raise NonRecoverableError('Required properties are missing: {' |
| '0}. Please update your image ' |
| 'dictionary.'.format(missing)) |
| |
| |
| def _validate_image(): |
| img = ctx.node.properties['image'] |
| img_path = img.get('data') |
| img_url = ctx.node.properties.get('image_url') |
| if not img_url and not img_path: |
| raise NonRecoverableError('Neither image url nor image path was ' |
| 'provided') |
| if img_url and img_path: |
| raise NonRecoverableError('Multiple image sources provided') |
| if img_url: |
| _check_url(img_url) |
| if img_path: |
| _check_path() |
| |
| |
| def _check_url(url): |
| p = urlparse(url) |
| conn = httplib.HTTPConnection(p.netloc) |
| conn.request('HEAD', p.path) |
| resp = conn.getresponse() |
| if resp.status >= 400: |
| raise NonRecoverableError('Invalid image URL') |
| |
| |
| def _check_path(): |
| img = ctx.node.properties['image'] |
| img_path = img.get('data') |
| try: |
| with open(img_path, 'rb'): |
| pass |
| except TypeError: |
| if not img.get('url'): |
| raise NonRecoverableError('No path or url provided') |
| except IOError: |
| raise NonRecoverableError( |
| 'Unable to open image file with path: "{}"'.format(img_path)) |
| |
| |
| def _remove_protected(glance_client): |
| if use_external_resource(ctx, glance_client, IMAGE_OPENSTACK_TYPE): |
| return |
| |
| is_protected = ctx.node.properties['image'].get('protected', False) |
| if is_protected: |
| img_id = ctx.instance.runtime_properties[OPENSTACK_ID_PROPERTY] |
| glance_client.images.update(img_id, protected=False) |
| |
| |
| def handle_image_from_relationship(obj_dict, property_name_to_put, ctx): |
| images = get_openstack_ids_of_connected_nodes_by_openstack_type( |
| ctx, IMAGE_OPENSTACK_TYPE) |
| if images: |
| obj_dict.update({property_name_to_put: images[0]}) |