**** CubicPower OpenStack Study ****
# Copyright 2012 OpenStack Foundation
# Copyright 2013 IBM Corp.
# 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 collections
import datetime
import uuid
from oslo.config import cfg
from glance.common import exception
import glance.openstack.common.log as logging
from glance.openstack.common import timeutils
CONF = cfg.CONF
LOG = logging.getLogger(__name__)
_delayed_delete_imported = False
**** CubicPower OpenStack Study ****
def _import_delayed_delete():
# glance.store (indirectly) imports glance.domain therefore we can't put
# the CONF.import_opt outside - we have to do it in a convoluted/indirect
# way!
global _delayed_delete_imported
if not _delayed_delete_imported:
CONF.import_opt('delayed_delete', 'glance.store')
_delayed_delete_imported = True
**** CubicPower OpenStack Study ****
class ImageFactory(object):
_readonly_properties = ['created_at', 'updated_at', 'status', 'checksum',
'size', 'virtual_size']
_reserved_properties = ['owner', 'is_public', 'locations',
'deleted', 'deleted_at', 'direct_url', 'self',
'file', 'schema']
**** CubicPower OpenStack Study ****
def _check_readonly(self, kwargs):
for key in self._readonly_properties:
if key in kwargs:
raise exception.ReadonlyProperty(property=key)
**** CubicPower OpenStack Study ****
def _check_unexpected(self, kwargs):
if kwargs:
msg = _('new_image() got unexpected keywords %s')
raise TypeError(msg % kwargs.keys())
**** CubicPower OpenStack Study ****
def _check_reserved(self, properties):
if properties is not None:
for key in self._reserved_properties:
if key in properties:
raise exception.ReservedProperty(property=key)
**** CubicPower OpenStack Study ****
def new_image(self, image_id=None, name=None, visibility='private',
min_disk=0, min_ram=0, protected=False, owner=None,
disk_format=None, container_format=None,
extra_properties=None, tags=None, **other_args):
self._check_readonly(other_args)
self._check_unexpected(other_args)
self._check_reserved(extra_properties)
if image_id is None:
image_id = str(uuid.uuid4())
created_at = timeutils.utcnow()
updated_at = created_at
status = 'queued'
return Image(image_id=image_id, name=name, status=status,
created_at=created_at, updated_at=updated_at,
visibility=visibility, min_disk=min_disk,
min_ram=min_ram, protected=protected,
owner=owner, disk_format=disk_format,
container_format=container_format,
extra_properties=extra_properties, tags=tags)
**** CubicPower OpenStack Study ****
class Image(object):
valid_state_targets = {
# Each key denotes a "current" state for the image. Corresponding
# values list the valid states to which we can jump from that "current"
# state.
# NOTE(flwang): In v2, we are deprecating the 'killed' status, so it's
# allowed to restore image from 'saving' to 'queued' so that upload
# can be retried.
'queued': ('saving', 'active', 'deleted'),
'saving': ('active', 'killed', 'deleted', 'queued'),
'active': ('queued', 'pending_delete', 'deleted'),
'killed': ('deleted'),
'pending_delete': ('deleted'),
'deleted': (),
}
**** CubicPower OpenStack Study ****
def __init__(self, image_id, status, created_at, updated_at, **kwargs):
self.image_id = image_id
self.status = status
self.created_at = created_at
self.updated_at = updated_at
self.name = kwargs.pop('name', None)
self.visibility = kwargs.pop('visibility', 'private')
self.min_disk = kwargs.pop('min_disk', 0)
self.min_ram = kwargs.pop('min_ram', 0)
self.protected = kwargs.pop('protected', False)
self.locations = kwargs.pop('locations', [])
self.checksum = kwargs.pop('checksum', None)
self.owner = kwargs.pop('owner', None)
self._disk_format = kwargs.pop('disk_format', None)
self._container_format = kwargs.pop('container_format', None)
self.size = kwargs.pop('size', None)
self.virtual_size = kwargs.pop('virtual_size', None)
extra_properties = kwargs.pop('extra_properties', None) or {}
self.extra_properties = ExtraProperties(extra_properties)
self.tags = kwargs.pop('tags', None) or []
if kwargs:
message = _("__init__() got unexpected keyword argument '%s'")
raise TypeError(message % kwargs.keys()[0])
@property
**** CubicPower OpenStack Study ****
def status(self):
return self._status
@status.setter
**** CubicPower OpenStack Study ****
def status(self, status):
has_status = hasattr(self, '_status')
if has_status:
if status not in self.valid_state_targets[self._status]:
kw = {'cur_status': self._status, 'new_status': status}
e = exception.InvalidImageStatusTransition(**kw)
LOG.debug(e)
raise e
if self._status == 'queued' and status in ('saving', 'active'):
missing = [k for k in ['disk_format', 'container_format']
if not getattr(self, k)]
if len(missing) > 0:
if len(missing) == 1:
msg = _('Property %s must be set prior to '
'saving data.')
else:
msg = _('Properties %s must be set prior to '
'saving data.')
raise ValueError(msg % ', '.join(missing))
# NOTE(flwang): Image size should be cleared as long as the image
# status is updated to 'queued'
if status == 'queued':
self.size = None
self.virtual_size = None
self._status = status
@property
**** CubicPower OpenStack Study ****
def visibility(self):
return self._visibility
@visibility.setter
**** CubicPower OpenStack Study ****
def visibility(self, visibility):
if visibility not in ('public', 'private'):
raise ValueError(_('Visibility must be either "public" '
'or "private"'))
self._visibility = visibility
@property
**** CubicPower OpenStack Study ****
def tags(self):
return self._tags
@tags.setter
**** CubicPower OpenStack Study ****
def tags(self, value):
self._tags = set(value)
@property
**** CubicPower OpenStack Study ****
def container_format(self):
return self._container_format
@container_format.setter
**** CubicPower OpenStack Study ****
def container_format(self, value):
if hasattr(self, '_container_format') and self.status != 'queued':
msg = _("Attribute container_format can be only replaced "
"for a queued image.")
raise exception.Forbidden(message=msg)
self._container_format = value
@property
**** CubicPower OpenStack Study ****
def disk_format(self):
return self._disk_format
@disk_format.setter
**** CubicPower OpenStack Study ****
def disk_format(self, value):
if hasattr(self, '_disk_format') and self.status != 'queued':
msg = _("Attribute disk_format can be only replaced "
"for a queued image.")
raise exception.Forbidden(message=msg)
self._disk_format = value
@property
**** CubicPower OpenStack Study ****
def min_disk(self):
return self._min_disk
@min_disk.setter
**** CubicPower OpenStack Study ****
def min_disk(self, value):
if value and value < 0:
extra_msg = _('Cannot be a negative value')
raise exception.InvalidParameterValue(value=value,
param='min_disk',
extra_msg=extra_msg)
self._min_disk = value
@property
**** CubicPower OpenStack Study ****
def min_ram(self):
return self._min_ram
@min_ram.setter
**** CubicPower OpenStack Study ****
def min_ram(self, value):
if value and value < 0:
extra_msg = _('Cannot be a negative value')
raise exception.InvalidParameterValue(value=value,
param='min_ram',
extra_msg=extra_msg)
self._min_ram = value
**** CubicPower OpenStack Study ****
def delete(self):
if self.protected:
raise exception.ProtectedImageDelete(image_id=self.image_id)
if CONF.delayed_delete and self.locations:
self.status = 'pending_delete'
else:
self.status = 'deleted'
**** CubicPower OpenStack Study ****
def get_data(self):
raise NotImplementedError()
**** CubicPower OpenStack Study ****
def set_data(self, data, size=None):
raise NotImplementedError()
**** CubicPower OpenStack Study ****
class ExtraProperties(collections.MutableMapping, dict):
**** CubicPower OpenStack Study ****
def __getitem__(self, key):
return dict.__getitem__(self, key)
**** CubicPower OpenStack Study ****
def __setitem__(self, key, value):
return dict.__setitem__(self, key, value)
**** CubicPower OpenStack Study ****
def __delitem__(self, key):
return dict.__delitem__(self, key)
**** CubicPower OpenStack Study ****
def __eq__(self, other):
if isinstance(other, ExtraProperties):
return dict(self).__eq__(dict(other))
elif isinstance(other, dict):
return dict(self).__eq__(other)
else:
return False
**** CubicPower OpenStack Study ****
def __len__(self):
return dict(self).__len__()
**** CubicPower OpenStack Study ****
def keys(self):
return dict(self).keys()
**** CubicPower OpenStack Study ****
class ImageMembership(object):
**** CubicPower OpenStack Study ****
def __init__(self, image_id, member_id, created_at, updated_at,
id=None, status=None):
self.id = id
self.image_id = image_id
self.member_id = member_id
self.created_at = created_at
self.updated_at = updated_at
self.status = status
@property
**** CubicPower OpenStack Study ****
def status(self):
return self._status
@status.setter
**** CubicPower OpenStack Study ****
def status(self, status):
if status not in ('pending', 'accepted', 'rejected'):
msg = _('Status must be "pending", "accepted" or "rejected".')
raise ValueError(msg)
self._status = status
**** CubicPower OpenStack Study ****
class ImageMemberFactory(object):
**** CubicPower OpenStack Study ****
def new_image_member(self, image, member_id):
created_at = timeutils.utcnow()
updated_at = created_at
return ImageMembership(image_id=image.image_id, member_id=member_id,
created_at=created_at, updated_at=updated_at,
status='pending')
**** CubicPower OpenStack Study ****
class Task(object):
_supported_task_type = ('import',)
_supported_task_status = ('pending', 'processing', 'success', 'failure')
**** CubicPower OpenStack Study ****
def __init__(self, task_id, task_type, status, owner,
expires_at, created_at, updated_at, task_time_to_live=48):
if task_type not in self._supported_task_type:
raise exception.InvalidTaskType(task_type)
if status not in self._supported_task_status:
raise exception.InvalidTaskStatus(status)
self.task_id = task_id
self._status = status
self.type = task_type
self.owner = owner
self.expires_at = expires_at
# NOTE(nikhil): We use '_time_to_live' to determine how long a
# task should live from the time it succeeds or fails.
self._time_to_live = datetime.timedelta(hours=task_time_to_live)
self.created_at = created_at
self.updated_at = updated_at
@property
**** CubicPower OpenStack Study ****
def status(self):
return self._status
**** CubicPower OpenStack Study ****
def run(self, executor):
pass
**** CubicPower OpenStack Study ****
def _validate_task_status_transition(self, cur_status, new_status):
valid_transitions = {
'pending': ['processing', 'failure'],
'processing': ['success', 'failure'],
'success': [],
'failure': [],
}
if new_status in valid_transitions[cur_status]:
return True
else:
return False
**** CubicPower OpenStack Study ****
def _set_task_status(self, new_status):
if self._validate_task_status_transition(self.status, new_status):
self._status = new_status
log_msg = (_("Task status changed from %(cur_status)s to "
"%(new_status)s") % {'cur_status': self.status,
'new_status': new_status})
LOG.info(log_msg)
else:
log_msg = (_("Task status failed to change from %(cur_status)s "
"to %(new_status)s") % {'cur_status': self.status,
'new_status': new_status})
LOG.error(log_msg)
raise exception.InvalidTaskStatusTransition(
cur_status=self.status,
new_status=new_status
)
**** CubicPower OpenStack Study ****
def begin_processing(self):
new_status = 'processing'
self._set_task_status(new_status)
**** CubicPower OpenStack Study ****
def succeed(self, result):
new_status = 'success'
self.result = result
self._set_task_status(new_status)
self.expires_at = timeutils.utcnow() + self._time_to_live
**** CubicPower OpenStack Study ****
def fail(self, message):
new_status = 'failure'
self.message = message
self._set_task_status(new_status)
self.expires_at = timeutils.utcnow() + self._time_to_live
**** CubicPower OpenStack Study ****
class TaskDetails(object):
**** CubicPower OpenStack Study ****
def __init__(self, task_id, task_input, message, result):
if task_id is None:
raise exception.TaskException(_('task_id is required to create '
'a new TaskDetails object'))
self.task_id = task_id
self.input = task_input
self.message = message
self.result = result
**** CubicPower OpenStack Study ****
class TaskFactory(object):
**** CubicPower OpenStack Study ****
def new_task(self, task_type, owner, task_time_to_live=48):
task_id = str(uuid.uuid4())
status = 'pending'
# Note(nikhil): expires_at would be set on the task, only when it
# succeeds or fails.
expires_at = None
created_at = timeutils.utcnow()
updated_at = created_at
return Task(
task_id,
task_type,
status,
owner,
expires_at,
created_at,
updated_at,
task_time_to_live
)
**** CubicPower OpenStack Study ****
def new_task_details(self, task_id, task_input, message=None, result=None):
return TaskDetails(task_id, task_input, message, result)