32
33
LOG = logging.getLogger('nova.image.glance')
34
36
FLAGS = flags.FLAGS
36
39
GlanceClient = utils.import_class('glance.client.Client')
39
42
class GlanceImageService(service.BaseImageService):
40
43
"""Provides storage and retrieval of disk image objects within Glance."""
42
GLANCE_ONLY_ATTRS = ["size", "location", "disk_format",
45
GLANCE_ONLY_ATTRS = ['size', 'location', 'disk_format',
45
48
# NOTE(sirp): Overriding to use _translate_to_service provided by
56
59
self.client = client
58
61
def index(self, context):
60
Calls out to Glance for a list of images available
62
"""Calls out to Glance for a list of images available."""
62
63
# NOTE(sirp): We need to use `get_images_detailed` and not
63
64
# `get_images` here because we need `is_public` and `properties`
64
65
# included so we can filter by user
73
74
def detail(self, context):
75
Calls out to Glance for a list of detailed image information
75
"""Calls out to Glance for a list of detailed image information."""
78
77
image_metas = self.client.get_images_detailed()
79
78
for image_meta in image_metas:
85
84
def show(self, context, image_id):
87
Returns a dict containing image data for the given opaque image id.
85
"""Returns a dict with image data for the given opaque image id."""
90
87
image_meta = self.client.get_image_meta(image_id)
91
88
except glance_exception.NotFound:
92
raise exception.NotFound
89
raise exception.ImageNotFound(image_id=image_id)
94
91
if not self._is_image_available(context, image_meta):
95
raise exception.NotFound
92
raise exception.ImageNotFound(image_id=image_id)
97
94
base_image_meta = self._translate_to_base(image_meta)
98
95
return base_image_meta
100
97
def show_by_name(self, context, name):
102
Returns a dict containing image data for the given name.
98
"""Returns a dict containing image data for the given name."""
104
99
# TODO(vish): replace this with more efficient call when glance
106
101
image_metas = self.detail(context)
107
102
for image_meta in image_metas:
108
103
if name == image_meta.get('name'):
109
104
return image_meta
110
raise exception.NotFound
105
raise exception.ImageNotFound(image_id=name)
112
107
def get(self, context, image_id, data):
114
Calls out to Glance for metadata and data and writes data.
108
"""Calls out to Glance for metadata and data and writes data."""
117
110
image_meta, image_chunks = self.client.get_image(image_id)
118
111
except glance_exception.NotFound:
119
raise exception.NotFound
112
raise exception.ImageNotFound(image_id=image_id)
121
114
for chunk in image_chunks:
122
115
data.write(chunk)
125
118
return base_image_meta
127
120
def create(self, context, image_meta, data=None):
129
Store the image data and return the new image id.
131
:raises AlreadyExists if the image already exist.
121
"""Store the image data and return the new image id.
123
:raises: AlreadyExists if the image already exist.
133
126
# Translate Base -> Service
134
LOG.debug(_("Creating image in Glance. Metadata passed in %s"),
127
LOG.debug(_('Creating image in Glance. Metadata passed in %s'),
136
129
sent_service_image_meta = self._translate_to_service(image_meta)
137
LOG.debug(_("Metadata after formatting for Glance %s"),
130
LOG.debug(_('Metadata after formatting for Glance %s'),
138
131
sent_service_image_meta)
140
133
recv_service_image_meta = self.client.add_image(
143
136
# Translate Service -> Base
144
137
base_image_meta = self._translate_to_base(recv_service_image_meta)
145
LOG.debug(_("Metadata returned from Glance formatted for Base %s"),
138
LOG.debug(_('Metadata returned from Glance formatted for Base %s'),
147
140
return base_image_meta
149
142
def update(self, context, image_id, image_meta, data=None):
150
143
"""Replace the contents of the given image with the new data.
152
:raises NotFound if the image does not exist.
145
:raises: ImageNotFound if the image does not exist.
154
148
# NOTE(vish): show is to check if image is available
155
149
self.show(context, image_id)
157
151
image_meta = self.client.update_image(image_id, image_meta, data)
158
152
except glance_exception.NotFound:
159
raise exception.NotFound
153
raise exception.ImageNotFound(image_id=image_id)
161
155
base_image_meta = self._translate_to_base(image_meta)
162
156
return base_image_meta
164
158
def delete(self, context, image_id):
166
Delete the given image.
168
:raises NotFound if the image does not exist.
159
"""Delete the given image.
161
:raises: ImageNotFound if the image does not exist.
170
164
# NOTE(vish): show is to check if image is available
171
165
self.show(context, image_id)
173
167
result = self.client.delete_image(image_id)
174
168
except glance_exception.NotFound:
175
raise exception.NotFound
169
raise exception.ImageNotFound(image_id=image_id)
178
172
def delete_all(self):
180
Clears out all images
173
"""Clears out all images."""
185
177
def _translate_to_base(cls, image_meta):
186
"""Overriding the base translation to handle conversion to datetime
189
image_meta = service.BaseImageService._translate_to_base(image_meta)
178
"""Override translation to handle conversion to datetime objects."""
179
image_meta = service.BaseImageService._propertify_metadata(
180
image_meta, cls.SERVICE_IMAGE_ATTRS)
190
181
image_meta = _convert_timestamps_to_datetimes(image_meta)
191
182
return image_meta
194
185
# utility functions
195
186
def _convert_timestamps_to_datetimes(image_meta):
197
Returns image with known timestamp fields converted to datetime objects
187
"""Returns image with timestamp fields converted to datetime objects."""
199
188
for attr in ['created_at', 'updated_at', 'deleted_at']:
200
189
if image_meta.get(attr):
201
190
image_meta[attr] = _parse_glance_iso8601_timestamp(
206
195
def _parse_glance_iso8601_timestamp(timestamp):
208
Parse a subset of iso8601 timestamps into datetime objects
210
iso_formats = ["%Y-%m-%dT%H:%M:%S.%f", "%Y-%m-%dT%H:%M:%S"]
196
"""Parse a subset of iso8601 timestamps into datetime objects."""
197
iso_formats = ['%Y-%m-%dT%H:%M:%S.%f', '%Y-%m-%dT%H:%M:%S']
212
199
for iso_format in iso_formats: