storage unittest
This commit is contained in:
parent
a2e1e8b398
commit
9d692982e3
|
@ -21,6 +21,9 @@ Backends for the storage resource.
|
|||
"""
|
||||
|
||||
#pylint: disable=R0201,W0232,W0613
|
||||
from datetime import date
|
||||
|
||||
import logging
|
||||
import uuid
|
||||
|
||||
from occi import backend
|
||||
|
@ -30,6 +33,8 @@ from occi.extensions import infrastructure
|
|||
from occi_os_api.nova_glue import storage
|
||||
from occi_os_api.nova_glue import vm
|
||||
|
||||
LOG = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class StorageBackend(backend.KindBackend, backend.ActionBackend):
|
||||
"""
|
||||
|
@ -58,7 +63,7 @@ class StorageBackend(backend.KindBackend, backend.ActionBackend):
|
|||
entity.identifier = infrastructure.STORAGE.location + vol_id
|
||||
|
||||
if new_volume['status'] == 'available':
|
||||
entity.attributes['occi.storage.state'] = 'online'
|
||||
entity.attributes['occi.storage.state'] = 'active'
|
||||
|
||||
entity.actions = [infrastructure.OFFLINE, infrastructure.BACKUP,
|
||||
infrastructure.SNAPSHOT, infrastructure.RESIZE]
|
||||
|
@ -82,34 +87,24 @@ class StorageBackend(backend.KindBackend, backend.ActionBackend):
|
|||
infrastructure.SNAPSHOT, infrastructure.RESIZE]
|
||||
else:
|
||||
entity.attributes['occi.storage.state'] = 'offline'
|
||||
entity.actions = [infrastructure.ONLINE]
|
||||
|
||||
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
|
||||
if 'occi.core.title' in new.attributes and \
|
||||
len(new.attributes['occi.core.title']) > 0:
|
||||
old.attributes['occi.core.title'] = \
|
||||
new.attributes['occi.core.title']
|
||||
if 'occi.core.title' in new.attributes and\
|
||||
len(new.attributes['occi.core.summary']) > 0:
|
||||
old.attributes['occi.core.summary'] = \
|
||||
new.attributes['occi.core.summary']
|
||||
|
||||
def delete(self, entity, extras):
|
||||
"""
|
||||
|
@ -126,80 +121,16 @@ class StorageBackend(backend.KindBackend, backend.ActionBackend):
|
|||
"""
|
||||
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 in [infrastructure.ONLINE, infrastructure.OFFLINE,
|
||||
infrastructure.BACKUP, infrastructure.RESIZE]:
|
||||
LOG.warn('The operations online, offline, backup and resize are '
|
||||
'currently not supported!')
|
||||
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'
|
||||
name = str(volume_id) + date.today().isoformat()
|
||||
description = entity.attributes['occi.core.summary']
|
||||
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
|
||||
extras['nova_ctx'])
|
||||
|
||||
|
||||
class StorageLinkBackend(backend.KindBackend):
|
||||
|
@ -207,16 +138,14 @@ 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)
|
||||
instance_id = link.source.attributes['occi.core.id']
|
||||
volume_id = link.target.attributes['occi.core.id']
|
||||
mount_point = link.attributes['occi.storagelink.deviceid']
|
||||
|
||||
vm.attach_volume(instance_id, volume_id, mount_point, context)
|
||||
|
@ -227,18 +156,9 @@ class StorageLinkBackend(backend.KindBackend):
|
|||
link.attributes['occi.storagelink.mountpoint'] = ''
|
||||
link.attributes['occi.storagelink.state'] = 'active'
|
||||
|
||||
def retrieve(self, entity, extras):
|
||||
"""
|
||||
Get most up to date attribute informations.
|
||||
"""
|
||||
# occi.storagelink.deviceid
|
||||
# occi.storagelink.mountpoint
|
||||
# occi.storagelink.state
|
||||
pass
|
||||
|
||||
def delete(self, link, extras):
|
||||
"""
|
||||
Unlinks the the compute from the storage resource.
|
||||
"""
|
||||
volume_id = get_vol_to_attach(link)
|
||||
volume_id = link.target.attributes['occi.core.id']
|
||||
vm.detach_volume(volume_id, extras['nova_ctx'])
|
||||
|
|
|
@ -322,7 +322,6 @@ class OCCIRegistry(occi_registry.NonePersistentRegistry):
|
|||
"""
|
||||
Update a storage resource instance.
|
||||
"""
|
||||
# TODO: is there sth to do here??
|
||||
return entity
|
||||
|
||||
def _construct_occi_storage(self, identifier, extras):
|
||||
|
|
|
@ -25,6 +25,7 @@ Test network resource backend.
|
|||
import mox
|
||||
import unittest
|
||||
from occi import core_model
|
||||
from occi.extensions import infrastructure
|
||||
|
||||
from occi_os_api import nova_glue
|
||||
from occi_os_api.backends import network
|
||||
|
@ -56,7 +57,7 @@ class TestNetworkInterfaceBackend(unittest.TestCase):
|
|||
Test create for failure!
|
||||
"""
|
||||
source = mox.MockObject(core_model.Resource)
|
||||
source.attributes={'occi.core.id': 'bar'}
|
||||
source.attributes = {'occi.core.id': 'bar'}
|
||||
target = mox.MockObject(core_model.Resource)
|
||||
target.identifier = '/network/admin'
|
||||
|
||||
|
@ -83,7 +84,7 @@ class TestNetworkInterfaceBackend(unittest.TestCase):
|
|||
Test create for sanity!
|
||||
"""
|
||||
source = mox.MockObject(core_model.Resource)
|
||||
source.attributes={'occi.core.id': 'bar'}
|
||||
source.attributes = {'occi.core.id': 'bar'}
|
||||
target = mox.MockObject(core_model.Resource)
|
||||
target.identifier = '/network/public'
|
||||
|
||||
|
@ -105,6 +106,9 @@ class TestNetworkInterfaceBackend(unittest.TestCase):
|
|||
self.assertIn('occi.networkinterface.gateway', link.attributes)
|
||||
self.assertIn('occi.networkinterface.allocation', link.attributes)
|
||||
|
||||
# self.assertIn(infrastructure.IPNETWORKINTERFACE, link.mixins)
|
||||
# self.assertIn(infrastructure.NETWORKINTERFACE, link.mixins)
|
||||
|
||||
self.mox.VerifyAll()
|
||||
|
||||
def test_delete_for_sanity(self):
|
||||
|
@ -112,7 +116,7 @@ class TestNetworkInterfaceBackend(unittest.TestCase):
|
|||
Test create for sanity!
|
||||
"""
|
||||
source = mox.MockObject(core_model.Resource)
|
||||
source.attributes={'occi.core.id': 'bar'}
|
||||
source.attributes = {'occi.core.id': 'bar'}
|
||||
target = mox.MockObject(core_model.Resource)
|
||||
target.identifier = '/network/public'
|
||||
|
||||
|
|
|
@ -0,0 +1,287 @@
|
|||
# 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.
|
||||
|
||||
"""
|
||||
Test network resource backend.
|
||||
"""
|
||||
|
||||
#pylint: disable=W0102,C0103,R0904
|
||||
|
||||
|
||||
import mox
|
||||
import unittest
|
||||
|
||||
from occi import core_model, exceptions
|
||||
from occi.extensions import infrastructure
|
||||
|
||||
from occi_os_api import nova_glue
|
||||
from occi_os_api.backends import storage
|
||||
|
||||
|
||||
class TestStorageBackend(unittest.TestCase):
|
||||
"""
|
||||
Tests the storage backend!
|
||||
"""
|
||||
|
||||
def setUp(self):
|
||||
"""
|
||||
Setup the tests.
|
||||
"""
|
||||
self.backend = storage.StorageBackend()
|
||||
self.sec_obj = {'nova_ctx': None}
|
||||
self.mox = mox.Mox()
|
||||
|
||||
def tearDown(self):
|
||||
"""
|
||||
Cleanup mocks.
|
||||
"""
|
||||
self.mox.UnsetStubs()
|
||||
|
||||
# Test for failure
|
||||
|
||||
def test_create_for_failure(self):
|
||||
"""
|
||||
Test attachement.
|
||||
"""
|
||||
# msg size attribute
|
||||
res = mox.MockObject(core_model.Resource)
|
||||
res.attributes = {}
|
||||
self.assertRaises(AttributeError, self.backend.create, res,
|
||||
self.sec_obj)
|
||||
|
||||
# error in volume creation
|
||||
res.attributes = {'occi.storage.size': '1'}
|
||||
|
||||
self.mox.StubOutWithMock(nova_glue.storage, 'create_storage')
|
||||
nova_glue.storage.create_storage(mox.IsA(object),
|
||||
mox.IsA(object)).AndReturn({'id': '1'})
|
||||
self.mox.StubOutWithMock(nova_glue.storage, 'get_storage')
|
||||
nova_glue.storage.get_storage(mox.IsA(object),
|
||||
mox.IsA(object)).AndReturn({'status': 'error'})
|
||||
|
||||
self.mox.ReplayAll()
|
||||
|
||||
self.assertRaises(exceptions.HTTPError, self.backend.create, res,
|
||||
self.sec_obj)
|
||||
|
||||
self.mox.VerifyAll()
|
||||
|
||||
def test_action_for_failure(self):
|
||||
"""
|
||||
Test actions
|
||||
"""
|
||||
res = mox.MockObject(core_model.Resource)
|
||||
res.actions = []
|
||||
|
||||
# snapshot
|
||||
self.assertRaises(AttributeError, self.backend.action, res,
|
||||
infrastructure.SNAPSHOT, {}, self.sec_obj)
|
||||
|
||||
# Test for sanity
|
||||
|
||||
def test_create_for_sanity(self):
|
||||
"""
|
||||
Test creation.
|
||||
"""
|
||||
res = mox.MockObject(core_model.Resource)
|
||||
res.attributes = {'occi.storage.size': '1'}
|
||||
|
||||
self.mox.StubOutWithMock(nova_glue.storage, 'create_storage')
|
||||
nova_glue.storage.create_storage(mox.IsA(object),
|
||||
mox.IsA(object)).AndReturn({'id': '1'})
|
||||
self.mox.StubOutWithMock(nova_glue.storage, 'get_storage')
|
||||
nova_glue.storage.get_storage(mox.IsA(object),
|
||||
mox.IsA(object)).AndReturn({'status': 'available'})
|
||||
|
||||
self.mox.ReplayAll()
|
||||
|
||||
self.backend.create(res, self.sec_obj)
|
||||
|
||||
# verify all attrs.
|
||||
self.assertEqual(res.attributes['occi.storage.state'], 'active')
|
||||
self.assertListEqual([infrastructure.OFFLINE, infrastructure.BACKUP,
|
||||
infrastructure.SNAPSHOT, infrastructure.RESIZE],
|
||||
res.actions)
|
||||
|
||||
self.mox.VerifyAll()
|
||||
|
||||
def test_retrieve_for_sanity(self):
|
||||
"""
|
||||
Test retrieval.
|
||||
"""
|
||||
res = mox.MockObject(core_model.Resource)
|
||||
res.attributes = {'occi.core.id': '1'}
|
||||
|
||||
self.mox.StubOutWithMock(nova_glue.storage, 'get_storage')
|
||||
nova_glue.storage.get_storage(mox.IsA(object),
|
||||
mox.IsA(object)).AndReturn({'status': 'available', 'size': '1'})
|
||||
|
||||
self.mox.ReplayAll()
|
||||
|
||||
self.backend.retrieve(res, self.sec_obj)
|
||||
|
||||
# verify all attrs.
|
||||
self.assertEqual(res.attributes['occi.storage.state'], 'online')
|
||||
self.assertListEqual([infrastructure.OFFLINE, infrastructure.BACKUP,
|
||||
infrastructure.SNAPSHOT, infrastructure.RESIZE],
|
||||
res.actions)
|
||||
|
||||
self.mox.VerifyAll()
|
||||
|
||||
self.mox.UnsetStubs()
|
||||
self.mox.StubOutWithMock(nova_glue.storage, 'get_storage')
|
||||
nova_glue.storage.get_storage(mox.IsA(object),
|
||||
mox.IsA(object)).AndReturn({'status': 'bla', 'size': '1'})
|
||||
|
||||
self.mox.ReplayAll()
|
||||
|
||||
self.backend.retrieve(res, self.sec_obj)
|
||||
|
||||
# verify all attrs.
|
||||
self.assertEqual(res.attributes['occi.storage.state'], 'offline')
|
||||
self.assertTrue(len(res.actions) == 1)
|
||||
self.mox.VerifyAll()
|
||||
|
||||
def test_update_for_sanity(self):
|
||||
"""
|
||||
Test updating.
|
||||
"""
|
||||
res1 = mox.MockObject(core_model.Resource)
|
||||
res1.attributes = {}
|
||||
res2 = mox.MockObject(core_model.Resource)
|
||||
res2.attributes = {'occi.core.title': 'foo', 'occi.core.summary':
|
||||
'bar'}
|
||||
|
||||
self.mox.ReplayAll()
|
||||
|
||||
self.backend.update(res1, res2, self.sec_obj)
|
||||
|
||||
# verify all attrs.
|
||||
self.assertEqual(res1.attributes['occi.core.title'], 'foo')
|
||||
self.assertEqual(res1.attributes['occi.core.summary'], 'bar')
|
||||
|
||||
self.mox.VerifyAll()
|
||||
|
||||
def test_remove_for_sanity(self):
|
||||
"""
|
||||
Test removal.
|
||||
"""
|
||||
res = mox.MockObject(core_model.Resource)
|
||||
res.attributes = {'occi.core.id': '1'}
|
||||
|
||||
self.mox.StubOutWithMock(nova_glue.storage, 'delete_storage_instance')
|
||||
nova_glue.storage.delete_storage_instance(mox.IsA(object),
|
||||
mox.IsA(object))
|
||||
|
||||
self.mox.ReplayAll()
|
||||
|
||||
self.backend.delete(res, self.sec_obj)
|
||||
|
||||
self.mox.VerifyAll()
|
||||
|
||||
def test_action_for_sanity(self):
|
||||
"""
|
||||
Test actions
|
||||
"""
|
||||
res = mox.MockObject(core_model.Resource)
|
||||
res.attributes = {'occi.core.id': '1',
|
||||
'occi.core.summary': 'foo'}
|
||||
res.actions = [infrastructure.SNAPSHOT, infrastructure.BACKUP]
|
||||
|
||||
# snapshot
|
||||
self.mox.StubOutWithMock(nova_glue.storage,
|
||||
'snapshot_storage_instance')
|
||||
nova_glue.storage.snapshot_storage_instance(mox.IsA(object),
|
||||
mox.IsA(object), mox.IsA(object), mox.IsA(object))
|
||||
self.mox.ReplayAll()
|
||||
self.backend.action(res, infrastructure.SNAPSHOT, {}, self.sec_obj)
|
||||
self.mox.VerifyAll()
|
||||
|
||||
# some other action
|
||||
self.mox.ReplayAll()
|
||||
self.backend.action(res, infrastructure.BACKUP, {}, self.sec_obj)
|
||||
self.mox.VerifyAll()
|
||||
|
||||
|
||||
class TestStorageLinkBackend(unittest.TestCase):
|
||||
"""
|
||||
Tests storage linking.
|
||||
"""
|
||||
|
||||
def setUp(self):
|
||||
"""
|
||||
Setup the tests.
|
||||
"""
|
||||
self.backend = storage.StorageLinkBackend()
|
||||
self.sec_obj = {'nova_ctx': None}
|
||||
self.mox = mox.Mox()
|
||||
|
||||
def tearDown(self):
|
||||
"""
|
||||
Cleanup mocks.
|
||||
"""
|
||||
self.mox.UnsetStubs()
|
||||
|
||||
# Test for sanity
|
||||
|
||||
def test_create_for_sanity(self):
|
||||
"""
|
||||
Test attachement.
|
||||
"""
|
||||
source = mox.MockObject(core_model.Resource)
|
||||
source.attributes = {'occi.core.id': 'foo'}
|
||||
target = mox.MockObject(core_model.Resource)
|
||||
target.attributes = {'occi.core.id': 'bar'}
|
||||
|
||||
link = core_model.Link('foo', None, [], source, target)
|
||||
link.attributes = {'occi.storagelink.deviceid': '/dev/sda'}
|
||||
|
||||
self.mox.StubOutWithMock(nova_glue.vm, 'attach_volume')
|
||||
nova_glue.vm.attach_volume(mox.IsA(object), mox.IsA(object),
|
||||
mox.IsA(object), mox.IsA(object)).AndReturn({})
|
||||
|
||||
self.mox.ReplayAll()
|
||||
|
||||
self.backend.create(link, self.sec_obj)
|
||||
|
||||
# verify all attrs.
|
||||
self.assertEqual(link.attributes['occi.storagelink.deviceid'],
|
||||
'/dev/sda')
|
||||
self.assertIn('occi.storagelink.mountpoint', link.attributes)
|
||||
self.assertEqual(link.attributes['occi.storagelink.state'], 'active')
|
||||
|
||||
self.mox.VerifyAll()
|
||||
|
||||
def test_delete_for_sanity(self):
|
||||
"""
|
||||
Test deattachement.
|
||||
"""
|
||||
source = mox.MockObject(core_model.Resource)
|
||||
target = mox.MockObject(core_model.Resource)
|
||||
target.attributes = {'occi.core.id': 'bar'}
|
||||
|
||||
link = core_model.Link('foo', None, [], source, target)
|
||||
|
||||
self.mox.StubOutWithMock(nova_glue.vm, 'detach_volume')
|
||||
nova_glue.vm.detach_volume(mox.IsA(object), mox.IsA(object))
|
||||
|
||||
self.mox.ReplayAll()
|
||||
|
||||
self.backend.delete(link, self.sec_obj)
|
||||
|
||||
self.mox.VerifyAll()
|
Loading…
Reference in New Issue