dfilppi | 9981f55 | 2017-08-07 20:10:53 +0000 | [diff] [blame^] | 1 | ######### |
| 2 | # Copyright (c) 2015 GigaSpaces Technologies Ltd. All rights reserved |
| 3 | # |
| 4 | # Licensed under the Apache License, Version 2.0 (the "License"); |
| 5 | # you may not use this file except in compliance with the License. |
| 6 | # You may obtain a copy of the License at |
| 7 | # |
| 8 | # http://www.apache.org/licenses/LICENSE-2.0 |
| 9 | # |
| 10 | # Unless required by applicable law or agreed to in writing, software |
| 11 | # distributed under the License is distributed on an "AS IS" BASIS, |
| 12 | # * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| 13 | # * See the License for the specific language governing permissions and |
| 14 | # * limitations under the License. |
| 15 | import httplib |
| 16 | from urlparse import urlparse |
| 17 | |
| 18 | from cloudify import ctx |
| 19 | from cloudify.decorators import operation |
| 20 | from cloudify.exceptions import NonRecoverableError |
| 21 | |
| 22 | from openstack_plugin_common import ( |
| 23 | with_glance_client, |
| 24 | get_resource_id, |
| 25 | use_external_resource, |
| 26 | get_openstack_ids_of_connected_nodes_by_openstack_type, |
| 27 | delete_resource_and_runtime_properties, |
| 28 | validate_resource, |
| 29 | COMMON_RUNTIME_PROPERTIES_KEYS, |
| 30 | OPENSTACK_ID_PROPERTY, |
| 31 | OPENSTACK_TYPE_PROPERTY, |
| 32 | OPENSTACK_NAME_PROPERTY) |
| 33 | |
| 34 | |
| 35 | IMAGE_OPENSTACK_TYPE = 'image' |
| 36 | IMAGE_STATUS_ACTIVE = 'active' |
| 37 | |
| 38 | RUNTIME_PROPERTIES_KEYS = COMMON_RUNTIME_PROPERTIES_KEYS |
| 39 | REQUIRED_PROPERTIES = ['container_format', 'disk_format'] |
| 40 | |
| 41 | |
| 42 | @operation |
| 43 | @with_glance_client |
| 44 | def create(glance_client, **kwargs): |
| 45 | if use_external_resource(ctx, glance_client, IMAGE_OPENSTACK_TYPE): |
| 46 | return |
| 47 | |
| 48 | img_dict = { |
| 49 | 'name': get_resource_id(ctx, IMAGE_OPENSTACK_TYPE) |
| 50 | } |
| 51 | _validate_image_dictionary() |
| 52 | img_properties = ctx.node.properties['image'] |
| 53 | img_dict.update({key: value for key, value in img_properties.iteritems() |
| 54 | if key != 'data'}) |
| 55 | img = glance_client.images.create(**img_dict) |
| 56 | img_path = img_properties.get('data', '') |
| 57 | img_url = ctx.node.properties.get('image_url') |
| 58 | try: |
| 59 | _validate_image() |
| 60 | if img_path: |
| 61 | with open(img_path, 'rb') as image_file: |
| 62 | glance_client.images.upload( |
| 63 | image_id=img.id, |
| 64 | image_data=image_file) |
| 65 | elif img_url: |
| 66 | img = glance_client.images.add_location(img.id, img_url, {}) |
| 67 | |
| 68 | except: |
| 69 | _remove_protected(glance_client) |
| 70 | glance_client.images.delete(image_id=img.id) |
| 71 | raise |
| 72 | |
| 73 | ctx.instance.runtime_properties[OPENSTACK_ID_PROPERTY] = img.id |
| 74 | ctx.instance.runtime_properties[OPENSTACK_TYPE_PROPERTY] = \ |
| 75 | IMAGE_OPENSTACK_TYPE |
| 76 | ctx.instance.runtime_properties[OPENSTACK_NAME_PROPERTY] = img.name |
| 77 | |
| 78 | |
| 79 | def _get_image_by_ctx(glance_client, ctx): |
| 80 | return glance_client.images.get( |
| 81 | image_id=ctx.instance.runtime_properties[OPENSTACK_ID_PROPERTY]) |
| 82 | |
| 83 | |
| 84 | @operation |
| 85 | @with_glance_client |
| 86 | def start(glance_client, start_retry_interval, **kwargs): |
| 87 | img = _get_image_by_ctx(glance_client, ctx) |
| 88 | if img.status != IMAGE_STATUS_ACTIVE: |
| 89 | return ctx.operation.retry( |
| 90 | message='Waiting for image to get uploaded', |
| 91 | retry_after=start_retry_interval) |
| 92 | |
| 93 | |
| 94 | @operation |
| 95 | @with_glance_client |
| 96 | def delete(glance_client, **kwargs): |
| 97 | _remove_protected(glance_client) |
| 98 | delete_resource_and_runtime_properties(ctx, glance_client, |
| 99 | RUNTIME_PROPERTIES_KEYS) |
| 100 | |
| 101 | |
| 102 | @operation |
| 103 | @with_glance_client |
| 104 | def creation_validation(glance_client, **kwargs): |
| 105 | validate_resource(ctx, glance_client, IMAGE_OPENSTACK_TYPE) |
| 106 | _validate_image_dictionary() |
| 107 | _validate_image() |
| 108 | |
| 109 | |
| 110 | def _validate_image_dictionary(): |
| 111 | img = ctx.node.properties['image'] |
| 112 | missing = '' |
| 113 | try: |
| 114 | for prop in REQUIRED_PROPERTIES: |
| 115 | if prop not in img: |
| 116 | missing += '{0} '.format(prop) |
| 117 | except TypeError: |
| 118 | missing = ' '.join(REQUIRED_PROPERTIES) |
| 119 | if missing: |
| 120 | raise NonRecoverableError('Required properties are missing: {' |
| 121 | '0}. Please update your image ' |
| 122 | 'dictionary.'.format(missing)) |
| 123 | |
| 124 | |
| 125 | def _validate_image(): |
| 126 | img = ctx.node.properties['image'] |
| 127 | img_path = img.get('data') |
| 128 | img_url = ctx.node.properties.get('image_url') |
| 129 | if not img_url and not img_path: |
| 130 | raise NonRecoverableError('Neither image url nor image path was ' |
| 131 | 'provided') |
| 132 | if img_url and img_path: |
| 133 | raise NonRecoverableError('Multiple image sources provided') |
| 134 | if img_url: |
| 135 | _check_url(img_url) |
| 136 | if img_path: |
| 137 | _check_path() |
| 138 | |
| 139 | |
| 140 | def _check_url(url): |
| 141 | p = urlparse(url) |
| 142 | conn = httplib.HTTPConnection(p.netloc) |
| 143 | conn.request('HEAD', p.path) |
| 144 | resp = conn.getresponse() |
| 145 | if resp.status >= 400: |
| 146 | raise NonRecoverableError('Invalid image URL') |
| 147 | |
| 148 | |
| 149 | def _check_path(): |
| 150 | img = ctx.node.properties['image'] |
| 151 | img_path = img.get('data') |
| 152 | try: |
| 153 | with open(img_path, 'rb'): |
| 154 | pass |
| 155 | except TypeError: |
| 156 | if not img.get('url'): |
| 157 | raise NonRecoverableError('No path or url provided') |
| 158 | except IOError: |
| 159 | raise NonRecoverableError( |
| 160 | 'Unable to open image file with path: "{}"'.format(img_path)) |
| 161 | |
| 162 | |
| 163 | def _remove_protected(glance_client): |
| 164 | if use_external_resource(ctx, glance_client, IMAGE_OPENSTACK_TYPE): |
| 165 | return |
| 166 | |
| 167 | is_protected = ctx.node.properties['image'].get('protected', False) |
| 168 | if is_protected: |
| 169 | img_id = ctx.instance.runtime_properties[OPENSTACK_ID_PROPERTY] |
| 170 | glance_client.images.update(img_id, protected=False) |
| 171 | |
| 172 | |
| 173 | def handle_image_from_relationship(obj_dict, property_name_to_put, ctx): |
| 174 | images = get_openstack_ids_of_connected_nodes_by_openstack_type( |
| 175 | ctx, IMAGE_OPENSTACK_TYPE) |
| 176 | if images: |
| 177 | obj_dict.update({property_name_to_put: images[0]}) |