234 lines
		
	
	
		
			8.6 KiB
		
	
	
	
		
			Python
		
	
	
	
	
	
			
		
		
	
	
			234 lines
		
	
	
		
			8.6 KiB
		
	
	
	
		
			Python
		
	
	
	
	
	
# coding=utf-8
 | 
						|
# vim: tabstop=4 shiftwidth=4 softtabstop=4
 | 
						|
 | 
						|
#
 | 
						|
#    Copyright (c) 2012, Intel Performance Learning Solutions Ltd.
 | 
						|
#
 | 
						|
#    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.
 | 
						|
 | 
						|
"""
 | 
						|
Backends for the storage resource.
 | 
						|
"""
 | 
						|
 | 
						|
#pylint: disable=R0201,W0232,W0613
 | 
						|
import uuid
 | 
						|
 | 
						|
from occi import backend
 | 
						|
from occi import exceptions
 | 
						|
from occi.extensions import infrastructure
 | 
						|
from nova_glue import storage, vm
 | 
						|
 | 
						|
 | 
						|
class StorageBackend(backend.KindBackend, backend.ActionBackend):
 | 
						|
    """
 | 
						|
    Backend to handle storage resources.
 | 
						|
    """
 | 
						|
 | 
						|
    def create(self, entity, extras):
 | 
						|
        """
 | 
						|
        Creates a new volume.
 | 
						|
        """
 | 
						|
        context = extras['nova_ctx']
 | 
						|
        if 'occi.storage.size' not in entity.attributes:
 | 
						|
            raise AttributeError('size attribute not found!')
 | 
						|
 | 
						|
        new_volume = storage.create_storage(entity.attributes['occi.storage' \
 | 
						|
                                                          '.size'], context)
 | 
						|
        vol_id = new_volume['id']
 | 
						|
 | 
						|
        # Work around problem that instance is lazy-loaded...
 | 
						|
        new_volume = storage.get_storage(vol_id, context)
 | 
						|
 | 
						|
        if new_volume['status'] == 'error':
 | 
						|
            raise exceptions.HTTPError(500, 'There was an error creating the '
 | 
						|
                                       'volume')
 | 
						|
        entity.attributes['occi.core.id'] = str(vol_id)
 | 
						|
 | 
						|
        if new_volume['status'] == 'available':
 | 
						|
            entity.attributes['occi.storage.state'] = 'online'
 | 
						|
 | 
						|
        entity.actions = [infrastructure.OFFLINE, infrastructure.BACKUP,
 | 
						|
                            infrastructure.SNAPSHOT, infrastructure.RESIZE]
 | 
						|
 | 
						|
    def retrieve(self, entity, extras):
 | 
						|
        """
 | 
						|
        Gets a representation of the storage volume and presents it ready for
 | 
						|
        rendering by pyssf.
 | 
						|
        """
 | 
						|
        v_id = entity.attributes['occi.core.id']
 | 
						|
 | 
						|
        volume = storage.get_storage(v_id, extras['nova_ctx'])
 | 
						|
 | 
						|
        entity.attributes['occi.storage.size'] = str(float(volume['size']))
 | 
						|
 | 
						|
        # OS volume states:
 | 
						|
        #       available, creating, deleting, in-use, error, error_deleting
 | 
						|
        if volume['status'] == 'available' or volume['status'] == 'in-use':
 | 
						|
            entity.attributes['occi.storage.state'] = 'online'
 | 
						|
            entity.actions = [infrastructure.OFFLINE, infrastructure.BACKUP,
 | 
						|
                              infrastructure.SNAPSHOT, infrastructure.RESIZE]
 | 
						|
        else:
 | 
						|
            entity.attributes['occi.storage.state'] = 'offline'
 | 
						|
 | 
						|
    def update(self, old, new, extras):
 | 
						|
        """
 | 
						|
        Updates simple attributes of a storage resource:
 | 
						|
        occi.core.title, occi.core.summary
 | 
						|
        """
 | 
						|
        # TODO: proper set the state of an storage instance!
 | 
						|
 | 
						|
        # update attributes.
 | 
						|
        if len(new.attributes) > 0:
 | 
						|
            # support only title and summary changes now.
 | 
						|
            if (('occi.core.title' in new.attributes)
 | 
						|
                or ('occi.core.title' in new.attributes)):
 | 
						|
                if len(new.attributes['occi.core.title']) > 0:
 | 
						|
                    old.attributes['occi.core.title'] = \
 | 
						|
                    new.attributes['occi.core.title']
 | 
						|
 | 
						|
                if len(new.attributes['occi.core.summary']) > 0:
 | 
						|
                    old.attributes['occi.core.summary'] = \
 | 
						|
                    new.attributes['occi.core.summary']
 | 
						|
            else:
 | 
						|
                raise AttributeError('Cannot update the supplied attributes.')
 | 
						|
 | 
						|
    def replace(self, old, new, extras):
 | 
						|
        """
 | 
						|
        Ignored.
 | 
						|
        """
 | 
						|
        pass
 | 
						|
 | 
						|
    def delete(self, entity, extras):
 | 
						|
        """
 | 
						|
        Deletes the storage resource
 | 
						|
        """
 | 
						|
        context = extras['nova_ctx']
 | 
						|
        volume_id = entity.attributes['occi.core.id']
 | 
						|
 | 
						|
        storage.delete_storage_instance(volume_id, context)
 | 
						|
 | 
						|
    def action(self, entity, action, attributes, extras):
 | 
						|
        """
 | 
						|
        Executes actions against the target storage resource.
 | 
						|
        """
 | 
						|
        if action not in entity.actions:
 | 
						|
            raise AttributeError("This action is currently no applicable.")
 | 
						|
 | 
						|
        elif action == infrastructure.ONLINE:
 | 
						|
            # ONLINE, ready for service, default state of a created volume.
 | 
						|
            # could this cover the attach functionality in storage link?
 | 
						|
            # The following is not an approach to use:
 | 
						|
            # self.volume_api.initialize_connection(context, volume, connector)
 | 
						|
 | 
						|
            # By default storage is ONLINE and can not be brought OFFLINE
 | 
						|
 | 
						|
            msg = 'Online storage action requested resource with id: %s' % \
 | 
						|
                                                            entity.identifier
 | 
						|
            raise AttributeError(msg)
 | 
						|
 | 
						|
        elif action == infrastructure.OFFLINE:
 | 
						|
            # OFFLINE, disconnected? disconnection supported in API otherwise
 | 
						|
            # not. The following is not an approach to use:
 | 
						|
            # self.volume_api.terminate_connection(context, volume, connector)
 | 
						|
 | 
						|
            # By default storage cannot be brought OFFLINE
 | 
						|
            msg = 'Offline storage action requested for resource: %s' % \
 | 
						|
                                                            entity.identifier
 | 
						|
            raise AttributeError(msg)
 | 
						|
 | 
						|
        elif action == infrastructure.BACKUP:
 | 
						|
            # BACKUP: create a complete copy of the volume.
 | 
						|
            msg = 'Backup action for storage resource with id: %s' % \
 | 
						|
                                                            entity.identifier
 | 
						|
            raise AttributeError(msg)
 | 
						|
 | 
						|
        elif action == infrastructure.SNAPSHOT:
 | 
						|
            # CDMI?!
 | 
						|
            # SNAPSHOT: create a time-stamped copy of the volume? Supported in
 | 
						|
            # OS volume API
 | 
						|
            volume_id = int(entity.attributes['occi.core.id'])
 | 
						|
            # occi.core.title, occi.core.summary
 | 
						|
            name = 'snapshot name'
 | 
						|
            description = 'snapshot description'
 | 
						|
            storage.snapshot_storage_instance(volume_id, name, description,
 | 
						|
                                          extras['nova_ctx'])
 | 
						|
 | 
						|
        elif action == infrastructure.RESIZE:
 | 
						|
            # TODO(dizz): not supported by API. A blueprint candidate?
 | 
						|
            # RESIZE: increase, decrease size of volume. Not supported directly
 | 
						|
            #         by the API
 | 
						|
 | 
						|
            msg = 'Resize storage actio requested resource with id: %s' % \
 | 
						|
                                                            entity.identifier
 | 
						|
            raise AttributeError(msg)
 | 
						|
 | 
						|
 | 
						|
def get_inst_to_attach(link):
 | 
						|
    """
 | 
						|
    Gets the compute instance that is to have the storage attached.
 | 
						|
    """
 | 
						|
    if link.target.kind == infrastructure.COMPUTE:
 | 
						|
        uid = link.target.attributes['occi.core.id']
 | 
						|
    elif link.source.kind == infrastructure.COMPUTE:
 | 
						|
        uid = link.source.attributes['occi.core.id']
 | 
						|
    else:
 | 
						|
        raise AttributeError('Id of the VM not found!')
 | 
						|
    return uid
 | 
						|
 | 
						|
 | 
						|
def get_vol_to_attach(link):
 | 
						|
    """
 | 
						|
    Gets the storage instance that is to have the compute attached.
 | 
						|
    """
 | 
						|
    if link.target.kind == infrastructure.STORAGE:
 | 
						|
        uid = link.target.attributes['occi.core.id']
 | 
						|
    elif link.source.kind == infrastructure.STORAGE:
 | 
						|
        uid = link.source.attributes['occi.core.id']
 | 
						|
    else:
 | 
						|
        raise AttributeError('Id of the Volume not found!')
 | 
						|
    return uid
 | 
						|
 | 
						|
 | 
						|
class StorageLinkBackend(backend.KindBackend):
 | 
						|
    """
 | 
						|
    A backend for the storage links.
 | 
						|
    """
 | 
						|
 | 
						|
    # TODO: need to implement retrieve so states get updated!!!!
 | 
						|
 | 
						|
    def create(self, link, extras):
 | 
						|
        """
 | 
						|
        Creates a link from a compute instance to a storage volume.
 | 
						|
        The user must specify what the device id is to be.
 | 
						|
        """
 | 
						|
        context = extras['nova_ctx']
 | 
						|
        instance_id = get_inst_to_attach(link)
 | 
						|
        volume_id = get_vol_to_attach(link)
 | 
						|
        mount_point = link.attributes['occi.storagelink.deviceid']
 | 
						|
 | 
						|
        vm.attach_volume(instance_id, volume_id, mount_point, context)
 | 
						|
 | 
						|
        link.attributes['occi.core.id'] = str(uuid.uuid4())
 | 
						|
        link.attributes['occi.storagelink.deviceid'] = \
 | 
						|
                                link.attributes['occi.storagelink.deviceid']
 | 
						|
        link.attributes['occi.storagelink.mountpoint'] = ''
 | 
						|
        link.attributes['occi.storagelink.state'] = 'active'
 | 
						|
 | 
						|
    def delete(self, link, extras):
 | 
						|
        """
 | 
						|
        Unlinks the the compute from the storage resource.
 | 
						|
        """
 | 
						|
        volume_id = get_vol_to_attach(link)
 | 
						|
        vm.detach_volume(volume_id, extras['nova_ctx'])
 | 
						|
 |