Merge branch 'stable/folsom' of github.com:tmetsch/occi-os into stable/essex
This commit is contained in:
commit
f27b8af46e
|
@ -12,7 +12,7 @@ Usage
|
|||
|
||||
0. Install dependencies: `pip install pyssf`
|
||||
1. Install this egg: `python setup.py install` (or `pip install
|
||||
openstackocci`)
|
||||
occi-os-folsom`)
|
||||
2. Configure OpenStack - Add application to `api-paste.ini` of nova and
|
||||
enable the API
|
||||
|
||||
|
@ -39,7 +39,7 @@ picked yourself):
|
|||
# pipeline = sizelimit authtoken keystonecontext ratelimit occiapp
|
||||
|
||||
[app:occiapp]
|
||||
use = egg:openstackocci#occi_app
|
||||
use = egg:occi-os-folsom#occi_app
|
||||
|
||||
Make sure the API (name from above) is enabled in `nova.conf`:
|
||||
|
||||
|
@ -60,4 +60,4 @@ There is further documentation on [setting up your development environment
|
|||
in the wiki](https://github.com/tmetsch/occi-os/wiki/DevEnv).
|
||||
|
||||
# Deployment using Puppet
|
||||
This library can be integrated using puppet as a configuration management tool. See [this blog post for more details](http://www.cloudcomp.ch/2012/09/automating-occi-installations/).
|
||||
This library can be integrated using puppet as a configuration management tool. See [this blog post for more details](http://www.cloudcomp.ch/2012/09/automating-occi-installations/).
|
||||
|
|
|
@ -37,7 +37,7 @@ which point to this function call (<module name>:function).
|
|||
# W0613:unused args
|
||||
# pylint: disable=W0613
|
||||
|
||||
from occiosapi import wsgi
|
||||
from occi_os_api import wsgi
|
||||
|
||||
|
||||
#noinspection PyUnusedLocal
|
||||
|
|
|
@ -24,9 +24,10 @@ The compute resource backend for OpenStack.
|
|||
|
||||
import logging
|
||||
|
||||
from occiosapi.extensions import os_mixins, os_addon
|
||||
|
||||
from occiosapi.nova_glue import vm, storage
|
||||
from occi_os_api.extensions import os_mixins
|
||||
from occi_os_api.extensions import os_addon
|
||||
from occi_os_api.nova_glue import vm
|
||||
from occi_os_api.nova_glue import storage
|
||||
|
||||
from occi.backend import KindBackend, ActionBackend
|
||||
from occi.extensions import infrastructure
|
||||
|
@ -74,6 +75,7 @@ class ComputeBackend(KindBackend, ActionBackend):
|
|||
infrastructure.SUSPEND,
|
||||
infrastructure.RESTART]
|
||||
|
||||
# Tell the world that is is an VM in OpenStack...
|
||||
entity.mixins.append(os_addon.OS_VM)
|
||||
|
||||
def retrieve(self, entity, extras):
|
||||
|
@ -110,6 +112,10 @@ class ComputeBackend(KindBackend, ActionBackend):
|
|||
LOG.debug('Updating an Virtual machine: ' + repr(uid))
|
||||
|
||||
# for now we will only handle one mixin change per request
|
||||
if len(new.mixins) != 1:
|
||||
raise AttributeError('Only updates with one mixin in request are'
|
||||
' currently supported')
|
||||
|
||||
mixin = new.mixins[0]
|
||||
if isinstance(mixin, os_mixins.ResourceTemplate):
|
||||
flavor_name = mixin.term
|
||||
|
|
|
@ -26,7 +26,7 @@ Network resource backend.
|
|||
|
||||
|
||||
from occi import backend
|
||||
from occiosapi.nova_glue import net
|
||||
from occi_os_api.nova_glue import net
|
||||
|
||||
|
||||
class NetworkBackend(backend.KindBackend, backend.ActionBackend):
|
||||
|
@ -77,12 +77,17 @@ class NetworkInterfaceBackend(backend.KindBackend):
|
|||
"""
|
||||
As nova does not support creation of L2 networks we don't.
|
||||
"""
|
||||
# TODO: add all network info!
|
||||
if link.target.identifier == '/network/public':
|
||||
# public means floating IP in OS!
|
||||
address = net.add_floating_ip_to_vm(link.source.attributes['occi.core.id'],
|
||||
extras['nova_ctx'])
|
||||
address = net.add_floating_ip(link.source.attributes[
|
||||
'occi.core.id'],
|
||||
extras['nova_ctx'])
|
||||
link.attributes['occi.networkinterface.interface'] = 'eth0'
|
||||
link.attributes['occi.networkinterface.mac'] = 'aa:bb:cc:dd:ee:ff'
|
||||
link.attributes['occi.networkinterface.state'] = 'active'
|
||||
link.attributes['occi.networkinterface.address'] = address
|
||||
link.attributes['occi.networkinterface.gateway'] = '0.0.0.0'
|
||||
link.attributes['occi.networkinterface.allocation'] = 'static'
|
||||
else:
|
||||
raise AttributeError('Currently not supported.')
|
||||
|
||||
|
@ -101,4 +106,3 @@ class NetworkInterfaceBackend(backend.KindBackend):
|
|||
net.remove_floating_ip(link.source.attributes['occi.core.id'],
|
||||
link.attributes['occi.networkinterface.address'],
|
||||
extras['nova_ctx'])
|
||||
|
||||
|
|
|
@ -25,10 +25,10 @@ import random
|
|||
from occi import backend
|
||||
from occi import exceptions
|
||||
|
||||
from occiosapi.extensions import os_addon
|
||||
from occi_os_api.extensions import os_addon
|
||||
from occi_os_api.nova_glue import vm
|
||||
from occi_os_api.nova_glue import security
|
||||
|
||||
from occiosapi.nova_glue import vm
|
||||
from occiosapi.nova_glue import security
|
||||
|
||||
class OsComputeBackend(backend.MixinBackend, backend.ActionBackend):
|
||||
"""
|
||||
|
@ -40,10 +40,7 @@ class OsComputeBackend(backend.MixinBackend, backend.ActionBackend):
|
|||
Add OpenStack related actions.
|
||||
"""
|
||||
if 'occi.compute.state' in entity.attributes and entity.attributes[
|
||||
'occi.compute' \
|
||||
'.state'] ==' \
|
||||
' ' \
|
||||
''active':
|
||||
'occi.compute.state'] == 'active':
|
||||
entity.actions.append(os_addon.OS_CREATE_IMAGE)
|
||||
entity.actions.append(os_addon.OS_CHG_PWD)
|
||||
|
||||
|
@ -137,7 +134,7 @@ class SecurityRuleBackend(backend.KindBackend):
|
|||
"""
|
||||
try:
|
||||
context = extras['nova_ctx']
|
||||
rule = security.get_rule(entity.attributes['occi.core.id'],
|
||||
rule = security.retrieve_rule(entity.attributes['occi.core.id'],
|
||||
context)
|
||||
|
||||
security.remove_rule(rule, context)
|
||||
|
@ -149,8 +146,6 @@ def make_sec_rule(entity, sec_grp_id):
|
|||
"""
|
||||
Create and validate the security rule.
|
||||
"""
|
||||
# TODO: add some checks for missing attributes!
|
||||
|
||||
name = random.randrange(0, 99999999)
|
||||
sg_rule = {'id': name,
|
||||
'parent_group_id': sec_grp_id}
|
||||
|
|
|
@ -21,12 +21,19 @@ Backends for the storage resource.
|
|||
"""
|
||||
|
||||
#pylint: disable=R0201,W0232,W0613
|
||||
from datetime import date
|
||||
|
||||
import logging
|
||||
import uuid
|
||||
|
||||
from occi import backend
|
||||
from occi import exceptions
|
||||
from occi.extensions import infrastructure
|
||||
from occiosapi.nova_glue import storage, vm
|
||||
|
||||
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):
|
||||
|
@ -56,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]
|
||||
|
@ -80,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):
|
||||
"""
|
||||
|
@ -124,80 +121,19 @@ 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'
|
||||
volume_id = entity.attributes['occi.core.id']
|
||||
name = volume_id + date.today().isoformat()
|
||||
if 'occi.core.summary' in entity.attributes:
|
||||
description = entity.attributes['occi.core.summary']
|
||||
else:
|
||||
description = 'N/A'
|
||||
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):
|
||||
|
@ -205,16 +141,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)
|
||||
|
@ -225,19 +159,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'])
|
||||
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
# coding=utf-8
|
||||
# vim: tabstop=4 shiftwidth=4 softtabstop=4
|
||||
|
||||
#
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
# coding=utf-8
|
||||
# vim: tabstop=4 shiftwidth=4 softtabstop=4
|
||||
|
||||
#
|
||||
|
@ -25,17 +26,18 @@ from nova import network
|
|||
from nova import exception
|
||||
from nova import compute
|
||||
from nova.compute import utils
|
||||
from occiosapi.nova_glue import vm
|
||||
|
||||
from occi_os_api.nova_glue import vm
|
||||
|
||||
# Connect to nova :-)
|
||||
|
||||
NETWORK_API = network.API()
|
||||
COMPUTE_API = compute.API()
|
||||
|
||||
LOG = logging.getLogger('nova.occiosapi.wsgi.occi.nova_glue.net')
|
||||
LOG = logging.getLogger(__name__)
|
||||
|
||||
|
||||
def get_adapter_info(uid, context):
|
||||
def get_network_details(uid, context):
|
||||
"""
|
||||
Extracts the VMs network adapter information.
|
||||
|
||||
|
@ -44,7 +46,7 @@ def get_adapter_info(uid, context):
|
|||
"""
|
||||
vm_instance = vm.get_vm(uid, context)
|
||||
|
||||
result = {'public':[], 'admin':[]}
|
||||
result = {'public': [], 'admin': []}
|
||||
try:
|
||||
net_info = NETWORK_API.get_instance_nw_info(context, vm_instance)[0]
|
||||
except IndexError:
|
||||
|
@ -56,13 +58,13 @@ def get_adapter_info(uid, context):
|
|||
|
||||
tmp = net_info['network']['subnets'][0]['ips'][0]
|
||||
for item in tmp['floating_ips']:
|
||||
result['public'].append({'interface':'eth0',
|
||||
'mac':'aa:bb:cc:dd:ee:ff',
|
||||
result['public'].append({'interface': 'eth0',
|
||||
'mac': 'aa:bb:cc:dd:ee:ff',
|
||||
'state': 'active',
|
||||
'address': item['address'],
|
||||
'gateway': '0.0.0.0',
|
||||
'allocation': 'static'})
|
||||
result['admin'].append({'interface':'eth0',
|
||||
result['admin'].append({'interface': 'eth0',
|
||||
'mac': mac,
|
||||
'state': 'active',
|
||||
'address': tmp['address'],
|
||||
|
@ -72,7 +74,7 @@ def get_adapter_info(uid, context):
|
|||
return result
|
||||
|
||||
|
||||
def add_floating_ip_to_vm(uid, context):
|
||||
def add_floating_ip(uid, context):
|
||||
"""
|
||||
Adds an ip to an VM instance.
|
||||
|
||||
|
@ -102,9 +104,6 @@ def add_floating_ip_to_vm(uid, context):
|
|||
except exception.NoFloatingIpInterface:
|
||||
msg = 'l3driver call to add floating ip failed'
|
||||
raise AttributeError(msg)
|
||||
except Exception as error:
|
||||
msg = 'Error. Unable to associate floating ip: ' + str(error)
|
||||
raise AttributeError(msg)
|
||||
return float_address
|
||||
|
||||
|
||||
|
@ -118,7 +117,9 @@ def remove_floating_ip(uid, address, context):
|
|||
"""
|
||||
vm_instance = vm.get_vm(uid, context)
|
||||
|
||||
# TODO: check exception handling!
|
||||
|
||||
NETWORK_API.disassociate_floating_ip(context, vm_instance, address)
|
||||
NETWORK_API.release_floating_ip(context, address)
|
||||
try:
|
||||
NETWORK_API.disassociate_floating_ip(context, vm_instance, address)
|
||||
NETWORK_API.release_floating_ip(context, address)
|
||||
except exception.FloatingIpNotAssociated:
|
||||
raise AttributeError('Unable to disassociate an unassociated '
|
||||
'floating up!')
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
# coding=utf-8
|
||||
# vim: tabstop=4 shiftwidth=4 softtabstop=4
|
||||
|
||||
#
|
||||
|
@ -19,17 +20,18 @@
|
|||
Security related 'glue'
|
||||
"""
|
||||
|
||||
# L8R: Check exception handling of this routines!
|
||||
|
||||
from nova import compute
|
||||
from nova import db
|
||||
from nova.flags import FLAGS
|
||||
from nova.openstack.common import importutils
|
||||
|
||||
# connect to nova
|
||||
from occi import exceptions
|
||||
|
||||
# connect to nova
|
||||
COMPUTE_API = compute.API()
|
||||
|
||||
# SEC_HANDLER = utils.import_object(FLAGS.security_group_handler)
|
||||
SEC_HANDLER = importutils.import_object(FLAGS.security_group_handler)
|
||||
|
||||
|
||||
|
@ -101,8 +103,10 @@ def create_rule(rule, context):
|
|||
rule -- The rule.
|
||||
context -- The os context.
|
||||
"""
|
||||
# TODO: check exception handling!
|
||||
db.security_group_rule_create(context, rule)
|
||||
try:
|
||||
db.security_group_rule_create(context, rule)
|
||||
except Exception as err:
|
||||
raise AttributeError('Unable to create rule: ' + str(err))
|
||||
|
||||
|
||||
def remove_rule(rule, context):
|
||||
|
@ -112,21 +116,17 @@ def remove_rule(rule, context):
|
|||
rule -- The rule
|
||||
context -- The os context.
|
||||
"""
|
||||
# TODO: check exception handling!
|
||||
group_id = rule['parent_group_id']
|
||||
# TODO(dizz): method seems to be gone!
|
||||
# self.compute_api.ensure_default_security_group(extras['nova_ctx'])
|
||||
security_group = db.security_group_get(context, group_id)
|
||||
|
||||
db.security_group_rule_destroy(context, rule['id'])
|
||||
SEC_HANDLER.trigger_security_group_rule_destroy_refresh(context,
|
||||
[rule['id']])
|
||||
# TODO: method is one!
|
||||
#COMPUTE_API.trigger_security_group_rules_refresh(context,
|
||||
# security_group['id'])
|
||||
try:
|
||||
db.security_group_rule_destroy(context, rule['id'])
|
||||
SEC_HANDLER.trigger_security_group_rule_destroy_refresh(context,
|
||||
[rule['id']])
|
||||
except Exception as err:
|
||||
raise AttributeError('Unable to remove rule: ' + str(err))
|
||||
|
||||
|
||||
def get_rule(uid, context):
|
||||
def retrieve_rule(uid, context):
|
||||
"""
|
||||
Retrieve a rule.
|
||||
|
||||
|
@ -134,8 +134,7 @@ def get_rule(uid, context):
|
|||
context -- The os context.
|
||||
"""
|
||||
try:
|
||||
rule = db.security_group_rule_get(context,
|
||||
return db.security_group_rule_get(context,
|
||||
int(uid))
|
||||
except Exception:
|
||||
raise exceptions.HTTPError(404, 'Rule not found!')
|
||||
return rule
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
# coding=utf-8
|
||||
# vim: tabstop=4 shiftwidth=4 softtabstop=4
|
||||
|
||||
#
|
||||
|
@ -25,10 +26,12 @@ from nova import exception
|
|||
from nova import volume
|
||||
from nova.image import glance
|
||||
|
||||
from cinder import exception as cinder_ex
|
||||
|
||||
from occi import exceptions
|
||||
|
||||
# Connection to the nova APIs
|
||||
from occiosapi.nova_glue import vm
|
||||
from occi_os_api.nova_glue import vm
|
||||
|
||||
VOLUME_API = volume.API()
|
||||
|
||||
|
@ -44,8 +47,7 @@ def create_storage(size, context, name=None, description=None):
|
|||
name -- defaults to a random number if needed.
|
||||
description -- defaults to the name
|
||||
"""
|
||||
# TODO: exception handling!
|
||||
# TODO(dizz): A blueprint?
|
||||
# L8R: A blueprint?
|
||||
# OpenStack deals with size in terms of integer.
|
||||
# Need to convert float to integer for now and only if the float
|
||||
# can be losslessly converted to integer
|
||||
|
@ -53,7 +55,6 @@ def create_storage(size, context, name=None, description=None):
|
|||
if not float(size).is_integer:
|
||||
raise AttributeError('Volume sizes cannot be specified as fractional'
|
||||
' floats.')
|
||||
#size = str(int(float(size)))
|
||||
size = int(float(size))
|
||||
|
||||
disp_name = ''
|
||||
|
@ -66,11 +67,15 @@ def create_storage(size, context, name=None, description=None):
|
|||
else:
|
||||
disp_descr = disp_name
|
||||
|
||||
new_volume = VOLUME_API.create(context,
|
||||
size,
|
||||
disp_name,
|
||||
disp_descr)
|
||||
return new_volume
|
||||
try:
|
||||
return VOLUME_API.create(context,
|
||||
size,
|
||||
disp_name,
|
||||
disp_descr)
|
||||
except cinder_ex.VolumeSizeExceedsAvailableQuota:
|
||||
raise AttributeError('The volume size quota has been reached!')
|
||||
except cinder_ex.VolumeLimitExceeded:
|
||||
raise AttributeError('The # of volumes quota has been reached!')
|
||||
|
||||
|
||||
def delete_storage_instance(uid, context):
|
||||
|
@ -80,9 +85,11 @@ def delete_storage_instance(uid, context):
|
|||
uid -- Id of the volume.
|
||||
context -- The os context.
|
||||
"""
|
||||
# TODO: exception handling!
|
||||
instance = get_storage(uid, context)
|
||||
VOLUME_API.delete(context, instance)
|
||||
try:
|
||||
instance = get_storage(uid, context)
|
||||
VOLUME_API.delete(context, instance)
|
||||
except cinder_ex.InvalidVolume:
|
||||
raise AttributeError('Volume is in wrong state or still attached!')
|
||||
|
||||
|
||||
def snapshot_storage_instance(uid, name, description, context):
|
||||
|
@ -92,9 +99,12 @@ def snapshot_storage_instance(uid, name, description, context):
|
|||
uid -- Id of the volume.
|
||||
context -- The os context.
|
||||
"""
|
||||
# TODO: exception handling!
|
||||
instance = get_storage(uid, context)
|
||||
VOLUME_API.create_snapshot(context, instance, name, description)
|
||||
try:
|
||||
instance = get_storage(uid, context)
|
||||
VOLUME_API.create_snapshot(context, instance, name, description)
|
||||
except cinder_ex.InvalidVolume:
|
||||
raise AttributeError('Volume is in wrong state!')
|
||||
|
||||
|
||||
def get_image(uid, context):
|
||||
"""
|
||||
|
@ -102,6 +112,7 @@ def get_image(uid, context):
|
|||
"""
|
||||
return IMAGE_API.show(context, uid)
|
||||
|
||||
|
||||
def get_image_architecture(uid, context):
|
||||
"""
|
||||
Extract architecture from either:
|
||||
|
@ -144,7 +155,8 @@ def get_storage(uid, context):
|
|||
raise exceptions.HTTPError(404, 'Volume not found!')
|
||||
return instance
|
||||
|
||||
def get_storages(context):
|
||||
|
||||
def get_storage_volumes(context):
|
||||
"""
|
||||
Retrieve all storage entities from user.
|
||||
"""
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
# coding=utf-8
|
||||
# vim: tabstop=4 shiftwidth=4 softtabstop=4
|
||||
|
||||
#
|
||||
|
@ -34,8 +35,8 @@ from nova.flags import FLAGS
|
|||
from occi import exceptions
|
||||
from occi.extensions import infrastructure
|
||||
|
||||
from occiosapi.extensions import os_mixins
|
||||
from occiosapi.extensions import os_addon
|
||||
from occi_os_api.extensions import os_mixins
|
||||
from occi_os_api.extensions import os_addon
|
||||
|
||||
import logging
|
||||
|
||||
|
@ -172,7 +173,7 @@ def resize_vm(uid, flavor_name, context):
|
|||
**kwargs)
|
||||
ready = False
|
||||
i = 0
|
||||
while i < 15:
|
||||
while not ready or i < 15:
|
||||
i += 1
|
||||
state = get_vm(uid, context)['vm_state']
|
||||
if state == 'resized':
|
||||
|
@ -265,8 +266,8 @@ def stop_vm(uid, context):
|
|||
instance = get_vm(uid, context)
|
||||
|
||||
try:
|
||||
# TODO(dizz): There are issues with the stop and start methods of
|
||||
# OS. For now we'll use suspend.
|
||||
# There are issues with the stop and start methods of OS. For now
|
||||
# we'll use suspend.
|
||||
# self.compute_api.stop(context, instance)
|
||||
COMPUTE_API.suspend(context, instance)
|
||||
except Exception as error:
|
||||
|
@ -299,8 +300,6 @@ def restart_vm(uid, method, context):
|
|||
COMPUTE_API.reboot(context, instance, reboot_type)
|
||||
except exception.InstanceInvalidState:
|
||||
raise exceptions.HTTPError(406, 'VM is in an invalid state.')
|
||||
except Exception as error:
|
||||
raise exceptions.HTTPError(500, 'Error in reboot %s' % error)
|
||||
|
||||
|
||||
def attach_volume(instance_id, volume_id, mount_point, context):
|
||||
|
@ -312,23 +311,19 @@ def attach_volume(instance_id, volume_id, mount_point, context):
|
|||
mount_point -- Where to mount.
|
||||
context -- The os security context.
|
||||
"""
|
||||
# TODO: check exception handling!
|
||||
instance = get_vm(instance_id, context)
|
||||
try:
|
||||
vol_instance = VOLUME_API.get(context, volume_id)
|
||||
except exception.NotFound:
|
||||
raise exceptions.HTTPError(404, 'Volume not found!')
|
||||
volume_id = vol_instance['id']
|
||||
|
||||
try:
|
||||
volume_id = vol_instance['id']
|
||||
COMPUTE_API.attach_volume(
|
||||
context,
|
||||
instance,
|
||||
volume_id,
|
||||
mount_point)
|
||||
except Exception as error:
|
||||
LOG.error(str(error))
|
||||
raise error
|
||||
except exception.NotFound:
|
||||
raise exceptions.HTTPError(404, 'Volume not found!')
|
||||
except exception.InvalidDevicePath:
|
||||
raise AttributeError('Invalid device path!')
|
||||
|
||||
|
||||
def detach_volume(volume_id, context):
|
||||
|
@ -338,18 +333,12 @@ def detach_volume(volume_id, context):
|
|||
volume_id -- Id of the volume.
|
||||
context -- the os context.
|
||||
"""
|
||||
#try:
|
||||
# instance = VOLUME_API.get(context, volume_id)
|
||||
#except exception.NotFound:
|
||||
# raise exceptions.HTTPError(404, 'Volume not found!')
|
||||
#volume_id = instance['id']
|
||||
|
||||
try:
|
||||
#TODO(dizz): see issue #15
|
||||
COMPUTE_API.detach_volume(context, volume_id)
|
||||
except Exception as error:
|
||||
LOG.error(str(error) + '; with id: ' + volume_id)
|
||||
raise error
|
||||
except exception.InvalidVolume:
|
||||
raise AttributeError('Invalid volume!')
|
||||
except exception.VolumeUnattached:
|
||||
raise AttributeError('Volume is not attached!')
|
||||
|
||||
|
||||
def set_password_for_vm(uid, password, context):
|
||||
|
@ -370,7 +359,7 @@ def set_password_for_vm(uid, password, context):
|
|||
|
||||
def get_vnc(uid, context):
|
||||
"""
|
||||
Retrieve VNC console.
|
||||
Retrieve VNC console or None is unavailable.
|
||||
|
||||
uid -- id of the instance
|
||||
context -- the os context
|
||||
|
@ -397,6 +386,7 @@ def get_vm(uid, context):
|
|||
raise exceptions.HTTPError(404, 'VM not found!')
|
||||
return instance
|
||||
|
||||
|
||||
def get_vms(context):
|
||||
"""
|
||||
Retrieve all VMs in a given context.
|
||||
|
@ -405,6 +395,7 @@ def get_vms(context):
|
|||
tmp = COMPUTE_API.get_all(context, search_opts=opts)
|
||||
return tmp
|
||||
|
||||
|
||||
def get_occi_state(uid, context):
|
||||
"""
|
||||
See nova/compute/vm_states.py nova/compute/task_states.py
|
||||
|
@ -439,7 +430,6 @@ def get_occi_state(uid, context):
|
|||
state = 'inactive'
|
||||
|
||||
# Some task states require a state
|
||||
# TODO: check for others!
|
||||
if instance['vm_state'] in [task_states.IMAGE_SNAPSHOT]:
|
||||
state = 'inactive'
|
||||
actions = []
|
||||
|
|
|
@ -20,17 +20,23 @@
|
|||
OCCI registry
|
||||
"""
|
||||
|
||||
#R0201:method could be func.E1002:old style obj
|
||||
#pylint: disable=R0201,E1002
|
||||
#R0201:method could be func.E1002:old style obj,R0914-R0912:# of branches
|
||||
#E1121:# positional args.
|
||||
#pylint: disable=R0201,E1002,R0914,R0912,E1121
|
||||
|
||||
import uuid
|
||||
|
||||
from occi import registry as occi_registry
|
||||
from occi import core_model
|
||||
from occi.extensions import infrastructure
|
||||
from occiosapi.backends import openstack
|
||||
from occiosapi.extensions import os_addon
|
||||
|
||||
from occiosapi.nova_glue import vm, storage, net
|
||||
from occi_os_api.backends import openstack
|
||||
from occi_os_api.extensions import os_addon
|
||||
|
||||
from occi_os_api.nova_glue import vm
|
||||
from occi_os_api.nova_glue import storage
|
||||
from occi_os_api.nova_glue import net
|
||||
|
||||
|
||||
class OCCIRegistry(occi_registry.NonePersistentRegistry):
|
||||
"""
|
||||
|
@ -43,6 +49,11 @@ class OCCIRegistry(occi_registry.NonePersistentRegistry):
|
|||
def __init__(self):
|
||||
super(OCCIRegistry, self).__init__()
|
||||
self.cache = {}
|
||||
self.adm_net = core_model.Resource('/network/admin',
|
||||
infrastructure.NETWORK, [infrastructure.IPNETWORK])
|
||||
self.pub_net = core_model.Resource('/network/public',
|
||||
infrastructure.NETWORK, [infrastructure.IPNETWORK])
|
||||
|
||||
self._setup_network()
|
||||
|
||||
def get_extras(self, extras):
|
||||
|
@ -116,14 +127,14 @@ class OCCIRegistry(occi_registry.NonePersistentRegistry):
|
|||
|
||||
vms = vm.get_vms(context)
|
||||
vm_res_ids = [item['uuid'] for item in vms]
|
||||
stors = storage.get_storages(context)
|
||||
stors = storage.get_storage_volumes(context)
|
||||
stor_res_ids = [item['id'] for item in stors]
|
||||
|
||||
if (key, context.user_id) in self.cache:
|
||||
# I have seen it - need to update or delete if gone in OS!
|
||||
# I have already seen it
|
||||
cached_item = self.cache[(key, context.user_id)]
|
||||
if not iden in vm_res_ids and cached_item.kind ==\
|
||||
if not iden in vm_res_ids and cached_item.kind == \
|
||||
infrastructure.COMPUTE:
|
||||
# it was delete in OS -> remove links, cache + KeyError!
|
||||
# can delete it because it was my item!
|
||||
|
@ -131,7 +142,7 @@ class OCCIRegistry(occi_registry.NonePersistentRegistry):
|
|||
self.cache.pop((link.identifier, repr(extras)))
|
||||
self.cache.pop((key, repr(extras)))
|
||||
raise KeyError
|
||||
if not iden in stor_res_ids and cached_item.kind ==\
|
||||
if not iden in stor_res_ids and cached_item.kind == \
|
||||
infrastructure.STORAGE:
|
||||
# it was delete in OS -> remove from cache + KeyError!
|
||||
# can delete it because it was my item!
|
||||
|
@ -154,9 +165,9 @@ class OCCIRegistry(occi_registry.NonePersistentRegistry):
|
|||
# construct it.
|
||||
if iden in vm_res_ids:
|
||||
# create new & add to cache!
|
||||
result = self._construct_occi_compute(iden, vms, extras)[0]
|
||||
result = self._construct_occi_compute(iden, extras)[0]
|
||||
elif iden in stor_res_ids:
|
||||
result = self._construct_occi_storage(iden, stors, extras)[0]
|
||||
result = self._construct_occi_storage(iden, extras)[0]
|
||||
else:
|
||||
# doesn't exist!
|
||||
raise KeyError
|
||||
|
@ -194,7 +205,7 @@ class OCCIRegistry(occi_registry.NonePersistentRegistry):
|
|||
vms = vm.get_vms(context)
|
||||
vm_res_ids = [item['uuid'] for item in vms]
|
||||
|
||||
stors = storage.get_storages(context)
|
||||
stors = storage.get_storage_volumes(context)
|
||||
stor_res_ids = [item['id'] for item in stors]
|
||||
|
||||
for item in self.cache.values():
|
||||
|
@ -206,23 +217,27 @@ class OCCIRegistry(occi_registry.NonePersistentRegistry):
|
|||
if item.extras is None:
|
||||
# add to result set
|
||||
result.append(item)
|
||||
elif item_id in vm_res_ids and item.kind == infrastructure.COMPUTE:
|
||||
elif item_id in vm_res_ids and item.kind == \
|
||||
infrastructure.COMPUTE:
|
||||
# check & update (take links, mixins from cache)
|
||||
# add compute and it's links to result
|
||||
self._update_occi_compute(item, extras)
|
||||
result.append(item)
|
||||
result.extend(item.links)
|
||||
elif item_id in stor_res_ids and item.kind == infrastructure.STORAGE:
|
||||
elif item_id in stor_res_ids and item.kind == \
|
||||
infrastructure.STORAGE:
|
||||
# check & update (take links, mixins from cache)
|
||||
# add compute and it's links to result
|
||||
self._update_occi_storage(item, extras)
|
||||
result.append(item)
|
||||
elif item_id not in vm_res_ids and item.kind == infrastructure.COMPUTE:
|
||||
elif item_id not in vm_res_ids and item.kind == \
|
||||
infrastructure.COMPUTE:
|
||||
# remove item and it's links from cache!
|
||||
for link in item.links:
|
||||
self.cache.pop((link.identifier, item.extras['user_id']))
|
||||
self.cache.pop((item.identifier, item.extras['user_id']))
|
||||
elif item_id not in stor_res_ids and item.kind == infrastructure.STORAGE:
|
||||
elif item_id not in stor_res_ids and item.kind == \
|
||||
infrastructure.STORAGE:
|
||||
# remove item
|
||||
self.cache.pop((item.identifier, item.extras['user_id']))
|
||||
for item in vms:
|
||||
|
@ -232,8 +247,7 @@ class OCCIRegistry(occi_registry.NonePersistentRegistry):
|
|||
else:
|
||||
# construct (with links and mixins and add to cache!
|
||||
# add compute and it's linke to result
|
||||
ent_list = self._construct_occi_compute(item['uuid'], vms,
|
||||
extras)
|
||||
ent_list = self._construct_occi_compute(item['uuid'], extras)
|
||||
result.extend(ent_list)
|
||||
for item in stors:
|
||||
if (infrastructure.STORAGE.location + item['id'],
|
||||
|
@ -242,8 +256,7 @@ class OCCIRegistry(occi_registry.NonePersistentRegistry):
|
|||
else:
|
||||
# construct (with links and mixins and add to cache!
|
||||
# add compute and it's linke to result
|
||||
ent_list = self._construct_occi_storage(item['id'], stors,
|
||||
extras)
|
||||
ent_list = self._construct_occi_storage(item['id'], extras)
|
||||
result.extend(ent_list)
|
||||
|
||||
return result
|
||||
|
@ -251,11 +264,14 @@ class OCCIRegistry(occi_registry.NonePersistentRegistry):
|
|||
# Not part of parent
|
||||
|
||||
def _update_occi_compute(self, entity, extras):
|
||||
"""
|
||||
Update an occi compute resource instance.
|
||||
"""
|
||||
# TODO: implement update of mixins and links (remove old mixins and
|
||||
# links)!
|
||||
return entity
|
||||
|
||||
def _construct_occi_compute(self, identifier, vms, extras):
|
||||
def _construct_occi_compute(self, identifier, extras):
|
||||
"""
|
||||
Construct a OCCI compute instance.
|
||||
|
||||
|
@ -285,7 +301,7 @@ class OCCIRegistry(occi_registry.NonePersistentRegistry):
|
|||
entity.mixins.append(os_tmp)
|
||||
|
||||
# 3. network links & get links from cache!
|
||||
net_links = net.get_adapter_info(identifier, context)
|
||||
net_links = net.get_network_details(identifier, context)
|
||||
for item in net_links['public']:
|
||||
link = self._construct_network_link(item, entity, self.pub_net,
|
||||
extras)
|
||||
|
@ -303,10 +319,12 @@ class OCCIRegistry(occi_registry.NonePersistentRegistry):
|
|||
return result
|
||||
|
||||
def _update_occi_storage(self, entity, extras):
|
||||
# TODO: is there sth to do here??
|
||||
"""
|
||||
Update a storage resource instance.
|
||||
"""
|
||||
return entity
|
||||
|
||||
def _construct_occi_storage(self, identifier, stors, extras):
|
||||
def _construct_occi_storage(self, identifier, extras):
|
||||
"""
|
||||
Construct a OCCI storage instance.
|
||||
|
||||
|
@ -347,8 +365,6 @@ class OCCIRegistry(occi_registry.NonePersistentRegistry):
|
|||
Add a public and an admin network interface.
|
||||
"""
|
||||
# TODO: read from openstack!
|
||||
self.pub_net = core_model.Resource('/network/public',
|
||||
infrastructure.NETWORK, [infrastructure.IPNETWORK])
|
||||
self.pub_net.attributes = {'occi.network.vlan': 'external',
|
||||
'occi.network.label': 'default',
|
||||
'occi.network.state': 'active',
|
||||
|
@ -359,12 +375,11 @@ class OCCIRegistry(occi_registry.NonePersistentRegistry):
|
|||
'.0.1',
|
||||
'occi.networkinterface.allocation':
|
||||
'static'}
|
||||
self.adm_net = core_model.Resource('/network/admin',
|
||||
infrastructure.NETWORK, [infrastructure.IPNETWORK])
|
||||
self.adm_net.attributes = {'occi.network.vlan': 'admin',
|
||||
'occi.network.label': 'default',
|
||||
'occi.network.state': 'active',
|
||||
'occi.networkinterface.address': '10.0.0.0/24',
|
||||
'occi.networkinterface.address': '10.0.0'
|
||||
'.0/24',
|
||||
'occi.networkinterface.gateway': '10.0.0'
|
||||
'.1',
|
||||
'occi.networkinterface.allocation':
|
||||
|
|
|
@ -27,19 +27,18 @@ import logging
|
|||
|
||||
from nova import flags
|
||||
from nova import wsgi
|
||||
from nova import context
|
||||
from nova import db
|
||||
from nova.image import glance
|
||||
from nova.compute import instance_types
|
||||
from nova.network import api
|
||||
from nova.openstack.common import cfg
|
||||
|
||||
from occiosapi import registry
|
||||
from occiosapi.backends import compute, openstack
|
||||
from occiosapi.backends import network
|
||||
from occiosapi.backends import storage
|
||||
from occiosapi.extensions import os_mixins
|
||||
from occiosapi.extensions import os_addon
|
||||
from occi_os_api import registry
|
||||
from occi_os_api.backends import compute
|
||||
from occi_os_api.backends import openstack
|
||||
from occi_os_api.backends import network
|
||||
from occi_os_api.backends import storage
|
||||
from occi_os_api.extensions import os_mixins
|
||||
from occi_os_api.extensions import os_addon
|
||||
|
||||
from occi import backend
|
||||
from occi import core_model
|
||||
|
@ -207,7 +206,7 @@ class OCCIApplication(occi_wsgi.Application, wsgi.Application):
|
|||
try:
|
||||
self.registry.get_backend(resource_template, extras)
|
||||
except AttributeError:
|
||||
msg = 'Registering an OpenStack flavour/instance type: %s' %\
|
||||
msg = 'Registering an OpenStack flavour/instance type: %s' % \
|
||||
str(resource_template)
|
||||
LOG.debug(msg)
|
||||
self.register_backend(resource_template, MIXIN_BACKEND)
|
||||
|
|
11
run_tests.sh
11
run_tests.sh
|
@ -5,7 +5,6 @@ mkdir -p build/html
|
|||
|
||||
echo '\n PyLint report \n****************************************\n'
|
||||
|
||||
#pylint -d I0011 -i y -f html occi_os_api tests >> build/html/lint.html
|
||||
pylint -d W0511,I0011,E1101,E0611,F0401 -i y --report no **/*.py
|
||||
|
||||
echo '\n Unittest coverage \n****************************************\n'
|
||||
|
@ -14,23 +13,25 @@ nc -z localhost 8787
|
|||
if [ "$?" -ne 0 ]; then
|
||||
echo "Unable to connect to OCCI endpoint localhost 8787 - will not run
|
||||
system test."
|
||||
nosetests --with-coverage --cover-erase --cover-package=occi_os_api --exclude=system
|
||||
else
|
||||
echo "Please make sure that the following line is available in nova.conf:"
|
||||
echo "allow_resize_to_same_host=True libvirt_inject_password=True enabled_apis=ec2,occiapi,osapi_compute,osapi_volume,metadata )"
|
||||
|
||||
source ../devstack/openrc
|
||||
nova-manage flavor create --name=itsy --cpu=1 --memory=128 --flavor=98 --root_gb=1 --ephemeral_gb=1
|
||||
nova-manage flavor create --name=bitsy --cpu=1 --memory=256 --flavor=99 --root_gb=1 --ephemeral_gb=1
|
||||
nosetests --with-coverage --cover-html --cover-html-dir=build/html/ --cover-erase --cover-package=occi_os_api
|
||||
nova-manage flavor create --name=itsy --cpu=1 --memory=32 --flavor=98 --root_gb=1 --ephemeral_gb=1
|
||||
nova-manage flavor create --name=bitsy --cpu=1 --memory=64 --flavor=99 --root_gb=1 --ephemeral_gb=1
|
||||
nosetests --with-coverage --cover-erase --cover-package=occi_os_api
|
||||
fi
|
||||
|
||||
echo '\n Code style \n****************************************\n'
|
||||
|
||||
pep8 --repeat --statistics --count occi_os_api
|
||||
pep8 --repeat --statistics --count occi_os_api tests
|
||||
|
||||
echo '\n Issues report \n****************************************\n'
|
||||
|
||||
pyflakes occi_os_api
|
||||
vulture occi_os_api
|
||||
|
||||
echo '\n Pychecker report \n****************************************\n'
|
||||
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
#!/usr/bin/env python
|
||||
# coding=utf-8
|
||||
# vim: tabstop=4 shiftwidth=4 softtabstop=4
|
||||
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
|
@ -28,8 +29,8 @@ import unittest
|
|||
import random
|
||||
|
||||
|
||||
HEADS = {'Content-Type':'text/occi',
|
||||
'Accept':'text/occi'
|
||||
HEADS = {'Content-Type': 'text/occi',
|
||||
'Accept': 'text/occi'
|
||||
}
|
||||
|
||||
KEYSTONE_HOST = '127.0.0.1:5000'
|
||||
|
@ -73,9 +74,9 @@ def get_os_token(username, password):
|
|||
"""
|
||||
Get a security token from Keystone.
|
||||
"""
|
||||
body = '{"auth": {"tenantName": "'+username+'", ' \
|
||||
'"passwordCredentials":{"username": "'+username+'", ' \
|
||||
'"password": "'+password+'"}}}'
|
||||
body = '{"auth": {"tenantName": "' + username + '", ' \
|
||||
'"passwordCredentials":{"username": "' + username + '", ' \
|
||||
'"password": "' + password + '"}}}'
|
||||
|
||||
heads = {'Content-Type': 'application/json'}
|
||||
conn = httplib.HTTPConnection(KEYSTONE_HOST)
|
||||
|
@ -153,7 +154,7 @@ def destroy_node(token, location):
|
|||
return heads
|
||||
|
||||
|
||||
def trigger_action(token, url, action_cat, action_param =None):
|
||||
def trigger_action(token, url, action_cat, action_param=None):
|
||||
"""
|
||||
Trigger an OCCI action.
|
||||
"""
|
||||
|
@ -171,7 +172,6 @@ class SystemTest(unittest.TestCase):
|
|||
Do a simple set of test.
|
||||
"""
|
||||
|
||||
|
||||
def setUp(self):
|
||||
"""
|
||||
Setup the test.
|
||||
|
@ -180,7 +180,6 @@ class SystemTest(unittest.TestCase):
|
|||
self.token = get_os_token('admin', 'os4all')
|
||||
LOG.info('security token is: ' + self.token)
|
||||
|
||||
|
||||
def test_compute_node(self):
|
||||
"""
|
||||
Test ops on a compute node!
|
||||
|
@ -241,7 +240,6 @@ class SystemTest(unittest.TestCase):
|
|||
# delete
|
||||
destroy_node(self.token, vm_location)
|
||||
|
||||
|
||||
def test_security_grouping(self):
|
||||
"""
|
||||
Test some security and accessibility stuff!
|
||||
|
@ -297,9 +295,9 @@ class SystemTest(unittest.TestCase):
|
|||
'.org/occi/infrastructure#"', 'ipnetworkinterface; '
|
||||
'scheme="http://schemas.ogf'
|
||||
'.org/occi/infrastructure/networkinterface#"']
|
||||
attrs = ['occi.core.source=http://"' + OCCI_HOST + vm_location + '"',
|
||||
attrs = ['occi.core.source=http://"' + OCCI_HOST + vm_location + '"',
|
||||
'occi.core.target=http://"' + OCCI_HOST +
|
||||
'/network/public"',]
|
||||
'/network/public"']
|
||||
float_ip_location = create_node(self.token, cats, attrs)
|
||||
|
||||
time.sleep(15)
|
||||
|
@ -326,33 +324,41 @@ class SystemTest(unittest.TestCase):
|
|||
heads['Category'] = name + '; scheme="http://www.mystuff.org/sec#"'
|
||||
do_request('DELETE', '/-/', heads)
|
||||
|
||||
|
||||
def test_storage_stuff(self):
|
||||
"""
|
||||
Test attaching and detaching storage volumes + snapshotting etc.
|
||||
"""
|
||||
|
||||
# create new VM
|
||||
cats = ['m1.tiny; scheme="http://schemas.openstack'
|
||||
'.org/template/resource#"',
|
||||
'cirros-0.3.0-x86_64-uec; scheme="http://schemas.openstack'
|
||||
'.org/template/os#"',
|
||||
'compute; scheme="http://schemas.ogf.org/occi/infrastructure#"']
|
||||
'.org/template/resource#"',
|
||||
'cirros-0.3.0-x86_64-uec; scheme="http://schemas.openstack'
|
||||
'.org/template/os#"',
|
||||
'compute; scheme="http://schemas.ogf.org/occi/infrastructure#"']
|
||||
vm_location = create_node(self.token, cats)
|
||||
|
||||
# create volume
|
||||
cats = ['storage; scheme="http://schemas.ogf.org/occi/infrastructure#"']
|
||||
cats = ['storage; scheme="http://schemas.ogf'
|
||||
'.org/occi/infrastructure#"']
|
||||
attrs = ['occi.storage.size = 1.0']
|
||||
vol_location = create_node(self.token, cats, attrs)
|
||||
|
||||
time.sleep(25)
|
||||
|
||||
# get individual node.
|
||||
LOG.debug(get_node(self.token, vol_location)['x-occi-attribute'])
|
||||
|
||||
time.sleep(15)
|
||||
# snapshot volume
|
||||
# snapshot will work - but than deletion of volume is impossible :-/
|
||||
#trigger_action(self.token, vol_location +
|
||||
# '?action=snapshot',
|
||||
# 'snapshot; scheme="http://schemas.ogf'
|
||||
# '.org/occi/infrastructure/storage/action#"')
|
||||
|
||||
# link volume and compute
|
||||
cats = ['storagelink; scheme="http://schemas.ogf'
|
||||
'.org/occi/infrastructure#"']
|
||||
attrs = ['occi.core.source=http://"' + OCCI_HOST + vm_location + '"',
|
||||
attrs = ['occi.core.source=http://"' + OCCI_HOST + vm_location + '"',
|
||||
'occi.core.target=http://"' + OCCI_HOST + vol_location + '"',
|
||||
'occi.storagelink.deviceid="/dev/vdc"']
|
||||
link_location = create_node(self.token, cats, attrs)
|
||||
|
@ -387,17 +393,16 @@ class SystemTest(unittest.TestCase):
|
|||
|
||||
destroy_node(self.token, vm_location)
|
||||
|
||||
|
||||
def test_scaling(self):
|
||||
"""
|
||||
Test the scaling operations
|
||||
"""
|
||||
# create new VM
|
||||
cats = ['itsy; scheme="http://schemas.openstack'
|
||||
'.org/template/resource#"',
|
||||
'cirros-0.3.0-x86_64-uec; scheme="http://schemas.openstack'
|
||||
'.org/template/os#"',
|
||||
'compute; scheme="http://schemas.ogf.org/occi/infrastructure#"']
|
||||
'.org/template/resource#"',
|
||||
'cirros-0.3.0-x86_64-uec; scheme="http://schemas.openstack'
|
||||
'.org/template/os#"',
|
||||
'compute; scheme="http://schemas.ogf.org/occi/infrastructure#"']
|
||||
vm_location = create_node(self.token, cats)
|
||||
|
||||
# wait
|
||||
|
@ -426,4 +431,3 @@ class SystemTest(unittest.TestCase):
|
|||
time.sleep(5)
|
||||
|
||||
destroy_node(self.token, vm_location)
|
||||
|
||||
|
|
|
@ -0,0 +1,330 @@
|
|||
# coding=utf-8
|
||||
# vim: tabstop=4 shiftwidth=4 softtabstop=4
|
||||
|
||||
# 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.
|
||||
|
||||
"""
|
||||
Unittest for the Compute Backend.
|
||||
"""
|
||||
|
||||
#pylint: disable=W0102,C0103,R0904
|
||||
|
||||
import unittest
|
||||
|
||||
# depenency from nova :-)
|
||||
import mox
|
||||
from nova.compute import vm_states
|
||||
|
||||
from occi import core_model
|
||||
from occi.extensions import infrastructure
|
||||
|
||||
from occi_os_api import nova_glue
|
||||
from occi_os_api.backends import compute
|
||||
from occi_os_api.extensions import os_mixins
|
||||
|
||||
|
||||
class TestComputeBackend(unittest.TestCase):
|
||||
"""
|
||||
Tests the compute backend.
|
||||
"""
|
||||
|
||||
os_template = os_mixins.OsTemplate('http://example.com', 'unix')
|
||||
os_template2 = os_mixins.OsTemplate('http://example.com', 'windows')
|
||||
|
||||
res_template = os_mixins.ResourceTemplate('http://example.com', 'itsy')
|
||||
res_template2 = os_mixins.ResourceTemplate('http://example.com', 'bitsy')
|
||||
|
||||
def setUp(self):
|
||||
"""
|
||||
Setup tests.
|
||||
"""
|
||||
self.backend = compute.ComputeBackend()
|
||||
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 for proper error handling.
|
||||
"""
|
||||
# msg OS template
|
||||
res = core_model.Resource('/foo/bar', infrastructure.COMPUTE, [])
|
||||
|
||||
self.assertRaises(AttributeError, self.backend.create, res,
|
||||
self.sec_obj)
|
||||
|
||||
# provide immutable attr
|
||||
res = core_model.Resource('/foo/bar', infrastructure.COMPUTE,
|
||||
[self.os_template])
|
||||
res.attributes = {'occi.compute.cores': 2}
|
||||
|
||||
self.assertRaises(AttributeError, self.backend.create, res,
|
||||
self.sec_obj)
|
||||
|
||||
def test_update_for_failure(self):
|
||||
"""
|
||||
Test if correct errors are thrown.
|
||||
"""
|
||||
# msg mixin
|
||||
res1 = core_model.Resource('/foo/bar', infrastructure.COMPUTE, [])
|
||||
res1.attributes = {'occi.core.id': 'bar'}
|
||||
res2 = core_model.Resource('/foo/bar', infrastructure.COMPUTE, [])
|
||||
|
||||
self.assertRaises(AttributeError, self.backend.update, res1, res2,
|
||||
self.sec_obj)
|
||||
|
||||
res2 = core_model.Resource('/foo/bar', infrastructure.COMPUTE,
|
||||
[core_model.Category('http://foo.com', 'bar', '', '', '')])
|
||||
|
||||
self.assertRaises(AttributeError, self.backend.update, res1, res2,
|
||||
self.sec_obj)
|
||||
|
||||
def test_action_for_failure(self):
|
||||
"""
|
||||
Test if correct errors are thrown.
|
||||
"""
|
||||
# wrong action
|
||||
res1 = core_model.Resource('/foo/bar', infrastructure.COMPUTE, [])
|
||||
res1.attributes = {'occi.core.id': 'bar'}
|
||||
self.mox.StubOutWithMock(nova_glue.vm, 'get_vm')
|
||||
nova_glue.vm.get_vm(mox.IsA(object), mox.IsA(object)).AndReturn(
|
||||
{
|
||||
'vm_state': vm_states.STOPPED
|
||||
})
|
||||
self.mox.ReplayAll()
|
||||
self.assertRaises(AttributeError, self.backend.action, res1,
|
||||
infrastructure.STOP, {}, self.sec_obj)
|
||||
self.mox.VerifyAll()
|
||||
|
||||
# missing method!
|
||||
self.mox.UnsetStubs()
|
||||
self.mox.StubOutWithMock(nova_glue.vm, 'get_vm')
|
||||
nova_glue.vm.get_vm(mox.IsA(object), mox.IsA(object)).AndReturn(
|
||||
{
|
||||
'vm_state': vm_states.ACTIVE
|
||||
})
|
||||
self.mox.ReplayAll()
|
||||
self.assertRaises(AttributeError, self.backend.action, res1,
|
||||
infrastructure.RESTART, {}, self.sec_obj)
|
||||
self.mox.VerifyAll()
|
||||
|
||||
# Test for Sanity
|
||||
|
||||
def test_create_for_sanity(self):
|
||||
"""
|
||||
Simulate a create call!
|
||||
"""
|
||||
res = core_model.Resource('/foo/bar', infrastructure.COMPUTE,
|
||||
[self.os_template])
|
||||
|
||||
self.mox.StubOutWithMock(nova_glue.vm, 'create_vm')
|
||||
nova_glue.vm.create_vm(mox.IsA(object), mox.IsA(object)).AndReturn(
|
||||
{
|
||||
'uuid': 'foo',
|
||||
'hostname': 'Server foo',
|
||||
'vcpus': 1,
|
||||
'memory_mb': 256
|
||||
})
|
||||
self.mox.StubOutWithMock(nova_glue.storage, 'get_image_architecture')
|
||||
nova_glue.storage.get_image_architecture(mox.IsA(object),
|
||||
mox.IsA(object)).AndReturn(
|
||||
'foo')
|
||||
|
||||
self.mox.ReplayAll()
|
||||
|
||||
self.backend.create(res, self.sec_obj)
|
||||
|
||||
# check if all attrs are there!
|
||||
self.assertIn('occi.compute.hostname', res.attributes)
|
||||
self.assertIn('occi.compute.architecture', res.attributes)
|
||||
self.assertIn('occi.compute.cores', res.attributes)
|
||||
self.assertIn('occi.compute.speed', res.attributes)
|
||||
self.assertIn('occi.compute.memory', res.attributes)
|
||||
self.assertIn('occi.compute.state', res.attributes)
|
||||
|
||||
self.assertEqual('inactive', res.attributes['occi.compute.state'])
|
||||
|
||||
self.assertListEqual([infrastructure.STOP, infrastructure.SUSPEND,
|
||||
infrastructure.RESTART], res.actions)
|
||||
|
||||
self.mox.VerifyAll()
|
||||
|
||||
def test_retrieve_for_sanity(self):
|
||||
"""
|
||||
Simulate a retrieve call!
|
||||
"""
|
||||
res = core_model.Resource('/foo/bar', infrastructure.COMPUTE,
|
||||
[self.os_template])
|
||||
res.attributes = {'occi.core.id': 'bar'}
|
||||
|
||||
self.mox.StubOutWithMock(nova_glue.vm, 'get_occi_state')
|
||||
nova_glue.vm.get_occi_state(mox.IsA(object),
|
||||
mox.IsA(object)).AndReturn(('active', [infrastructure.STOP,
|
||||
infrastructure.SUSPEND,
|
||||
infrastructure.RESTART]))
|
||||
self.mox.StubOutWithMock(nova_glue.vm, 'get_vm')
|
||||
nova_glue.vm.get_vm(mox.IsA(object), mox.IsA(object)).AndReturn(
|
||||
{
|
||||
'hostname': 'bar',
|
||||
'vcpus': 1,
|
||||
'memory_mb': 256
|
||||
})
|
||||
self.mox.StubOutWithMock(nova_glue.storage, 'get_image_architecture')
|
||||
nova_glue.storage.get_image_architecture(mox.IsA(object),
|
||||
mox.IsA(object)).AndReturn(
|
||||
'foo')
|
||||
self.mox.ReplayAll()
|
||||
|
||||
self.backend.retrieve(res, self.sec_obj)
|
||||
|
||||
# check if all attrs are there!
|
||||
self.assertIn('occi.compute.hostname', res.attributes)
|
||||
self.assertIn('occi.compute.architecture', res.attributes)
|
||||
self.assertIn('occi.compute.cores', res.attributes)
|
||||
self.assertIn('occi.compute.speed', res.attributes)
|
||||
self.assertIn('occi.compute.memory', res.attributes)
|
||||
self.assertIn('occi.compute.state', res.attributes)
|
||||
|
||||
self.assertIn('occi.core.id', res.attributes)
|
||||
|
||||
self.assertEqual('active', res.attributes['occi.compute.state'])
|
||||
|
||||
self.assertListEqual([infrastructure.STOP, infrastructure.SUSPEND,
|
||||
infrastructure.RESTART], res.actions)
|
||||
|
||||
self.mox.VerifyAll()
|
||||
|
||||
def test_update_for_sanity(self):
|
||||
"""
|
||||
Simulate a update call!
|
||||
"""
|
||||
res1 = core_model.Resource('/foo/bar', infrastructure.COMPUTE,
|
||||
[self.os_template])
|
||||
res1.attributes = {'occi.core.id': 'bar'}
|
||||
|
||||
# case 1 - rebuild VM with different OS
|
||||
res2 = core_model.Resource('/foo/bar', infrastructure.COMPUTE,
|
||||
[self.os_template2])
|
||||
|
||||
self.mox.StubOutWithMock(nova_glue.vm, 'rebuild_vm')
|
||||
nova_glue.vm.rebuild_vm(mox.IsA(object), mox.IsA(object),
|
||||
mox.IsA(object))
|
||||
self.mox.ReplayAll()
|
||||
self.backend.update(res1, res2, self.sec_obj)
|
||||
|
||||
self.assertIn(self.os_template2, res1.mixins)
|
||||
|
||||
self.mox.VerifyAll()
|
||||
|
||||
# case 2 - resize the VM
|
||||
res2 = core_model.Resource('/foo/bar', infrastructure.COMPUTE,
|
||||
[self.res_template2])
|
||||
|
||||
self.mox.StubOutWithMock(nova_glue.vm, 'resize_vm')
|
||||
nova_glue.vm.resize_vm(mox.IsA(object), mox.IsA(object),
|
||||
mox.IsA(object))
|
||||
self.mox.ReplayAll()
|
||||
self.backend.update(res1, res2, self.sec_obj)
|
||||
|
||||
self.assertIn(self.res_template2, res1.mixins)
|
||||
|
||||
self.mox.VerifyAll()
|
||||
|
||||
def test_replace_for_sanity(self):
|
||||
"""
|
||||
Simulate a replace call - does nothing atm.
|
||||
"""
|
||||
self.backend.replace(None, None, self.sec_obj)
|
||||
|
||||
def test_delete_for_sanity(self):
|
||||
"""
|
||||
Simulate a delete call.
|
||||
"""
|
||||
res = core_model.Resource('/foo/bar', infrastructure.COMPUTE,
|
||||
[self.os_template])
|
||||
res.attributes = {'occi.core.id': 'bar'}
|
||||
|
||||
self.mox.StubOutWithMock(nova_glue.vm, 'delete_vm')
|
||||
nova_glue.vm.delete_vm(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
|
||||
"""
|
||||
res1 = core_model.Resource('/foo/bar', infrastructure.COMPUTE, [])
|
||||
res1.attributes = {'occi.core.id': 'bar'}
|
||||
|
||||
# start
|
||||
self.mox.StubOutWithMock(nova_glue.vm, 'start_vm')
|
||||
nova_glue.vm.start_vm(mox.IsA(object), mox.IsA(object))
|
||||
self.mox.StubOutWithMock(nova_glue.vm, 'get_vm')
|
||||
nova_glue.vm.get_vm(mox.IsA(object), mox.IsA(object)).AndReturn(
|
||||
{
|
||||
'vm_state': vm_states.STOPPED
|
||||
})
|
||||
self.mox.ReplayAll()
|
||||
self.backend.action(res1, infrastructure.START, {}, self.sec_obj)
|
||||
self.mox.VerifyAll()
|
||||
|
||||
# stop
|
||||
self.mox.UnsetStubs()
|
||||
self.mox.StubOutWithMock(nova_glue.vm, 'stop_vm')
|
||||
nova_glue.vm.stop_vm(mox.IsA(object), mox.IsA(object))
|
||||
self.mox.StubOutWithMock(nova_glue.vm, 'get_vm')
|
||||
nova_glue.vm.get_vm(mox.IsA(object), mox.IsA(object)).AndReturn(
|
||||
{
|
||||
'vm_state': vm_states.ACTIVE
|
||||
})
|
||||
self.mox.ReplayAll()
|
||||
self.backend.action(res1, infrastructure.STOP, {}, self.sec_obj)
|
||||
self.mox.VerifyAll()
|
||||
|
||||
# reboot
|
||||
self.mox.UnsetStubs()
|
||||
self.mox.StubOutWithMock(nova_glue.vm, 'restart_vm')
|
||||
nova_glue.vm.restart_vm(mox.IsA(object), mox.IsA(str),
|
||||
mox.IsA(object))
|
||||
self.mox.StubOutWithMock(nova_glue.vm, 'get_vm')
|
||||
nova_glue.vm.get_vm(mox.IsA(object), mox.IsA(object)).AndReturn(
|
||||
{
|
||||
'vm_state': vm_states.ACTIVE
|
||||
})
|
||||
self.mox.ReplayAll()
|
||||
self.backend.action(res1, infrastructure.RESTART,
|
||||
{'method': 'graceful'},
|
||||
self.sec_obj)
|
||||
self.mox.VerifyAll()
|
||||
|
||||
# suspend
|
||||
self.mox.UnsetStubs()
|
||||
self.mox.StubOutWithMock(nova_glue.vm, 'suspend_vm')
|
||||
nova_glue.vm.suspend_vm(mox.IsA(object), mox.IsA(object))
|
||||
self.mox.StubOutWithMock(nova_glue.vm, 'get_vm')
|
||||
nova_glue.vm.get_vm(mox.IsA(object), mox.IsA(object)).AndReturn(
|
||||
{
|
||||
'vm_state': vm_states.ACTIVE
|
||||
})
|
||||
self.mox.ReplayAll()
|
||||
self.backend.action(res1, infrastructure.SUSPEND, {}, self.sec_obj)
|
||||
self.mox.VerifyAll()
|
|
@ -0,0 +1,176 @@
|
|||
# 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
|
||||
|
||||
from occi_os_api import nova_glue
|
||||
from occi_os_api.backends import network
|
||||
|
||||
|
||||
class TestNetworkInterfaceBackend(unittest.TestCase):
|
||||
"""
|
||||
Tests the network interface backend!
|
||||
"""
|
||||
|
||||
def setUp(self):
|
||||
"""
|
||||
Setup the tests.
|
||||
"""
|
||||
self.backend = network.NetworkInterfaceBackend()
|
||||
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 create for failure!
|
||||
"""
|
||||
source = mox.MockObject(core_model.Resource)
|
||||
source.attributes = {'occi.core.id': 'bar'}
|
||||
target = mox.MockObject(core_model.Resource)
|
||||
target.identifier = '/network/admin'
|
||||
|
||||
link = core_model.Link('foo', None, [], source, target)
|
||||
|
||||
self.mox.ReplayAll()
|
||||
|
||||
self.assertRaises(AttributeError, self.backend.create, link,
|
||||
self.sec_obj)
|
||||
|
||||
self.mox.VerifyAll()
|
||||
|
||||
def test_update_for_failure(self):
|
||||
"""
|
||||
No updates allowed!
|
||||
"""
|
||||
self.assertRaises(AttributeError, self.backend.update, None, None,
|
||||
None)
|
||||
|
||||
# Test for sanity!
|
||||
|
||||
def test_create_for_sanity(self):
|
||||
"""
|
||||
Test create for sanity!
|
||||
"""
|
||||
source = mox.MockObject(core_model.Resource)
|
||||
source.attributes = {'occi.core.id': 'bar'}
|
||||
target = mox.MockObject(core_model.Resource)
|
||||
target.identifier = '/network/public'
|
||||
|
||||
link = core_model.Link('foo', None, [], source, target)
|
||||
|
||||
self.mox.StubOutWithMock(nova_glue.net, 'add_floating_ip')
|
||||
nova_glue.net.add_floating_ip(mox.IsA(object),
|
||||
mox.IsA(object)).AndReturn('10.0.0.1')
|
||||
|
||||
self.mox.ReplayAll()
|
||||
|
||||
self.backend.create(link, self.sec_obj)
|
||||
|
||||
# verify all attrs and mixins!
|
||||
self.assertIn('occi.networkinterface.interface', link.attributes)
|
||||
self.assertIn('occi.networkinterface.mac', link.attributes)
|
||||
self.assertIn('occi.networkinterface.state', link.attributes)
|
||||
self.assertIn('occi.networkinterface.address', link.attributes)
|
||||
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):
|
||||
"""
|
||||
Test create for sanity!
|
||||
"""
|
||||
source = mox.MockObject(core_model.Resource)
|
||||
source.attributes = {'occi.core.id': 'bar'}
|
||||
target = mox.MockObject(core_model.Resource)
|
||||
target.identifier = '/network/public'
|
||||
|
||||
link = core_model.Link('foo', None, [], source, target)
|
||||
link.attributes = {'occi.networkinterface.address': '10.0.0.1'}
|
||||
|
||||
self.mox.StubOutWithMock(nova_glue.net, 'remove_floating_ip')
|
||||
nova_glue.net.remove_floating_ip(mox.IsA(object), mox.IsA(object),
|
||||
mox.IsA(object))
|
||||
|
||||
self.mox.ReplayAll()
|
||||
|
||||
self.backend.delete(link, self.sec_obj)
|
||||
|
||||
self.mox.VerifyAll()
|
||||
|
||||
|
||||
class TestNetworkBackend(unittest.TestCase):
|
||||
"""
|
||||
Some tests for network resources.
|
||||
"""
|
||||
|
||||
def setUp(self):
|
||||
"""
|
||||
Initialize test.
|
||||
"""
|
||||
self.backend = network.NetworkBackend()
|
||||
|
||||
def test_create_for_failure(self):
|
||||
"""
|
||||
Expecting an error!
|
||||
"""
|
||||
self.assertRaises(AttributeError, self.backend.create, None, None)
|
||||
|
||||
def test_action_for_failure(self):
|
||||
"""
|
||||
Expecting an error!
|
||||
"""
|
||||
self.assertRaises(AttributeError, self.backend.action, None,
|
||||
None, None, None)
|
||||
|
||||
|
||||
class TestIpNetworkBackend(unittest.TestCase):
|
||||
"""
|
||||
Some tests for network resources.
|
||||
"""
|
||||
|
||||
def setUp(self):
|
||||
"""
|
||||
Initialize test.
|
||||
"""
|
||||
self.backend = network.IpNetworkBackend()
|
||||
|
||||
def test_create_for_failure(self):
|
||||
"""
|
||||
Expecting an error!
|
||||
"""
|
||||
self.assertRaises(AttributeError, self.backend.create, None, None)
|
|
@ -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,R0801
|
||||
|
||||
|
||||
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