Retire stackforge/occi-os

This commit is contained in:
Monty Taylor 2015-10-17 16:03:52 -04:00
parent a1dea0226c
commit c1277826db
29 changed files with 7 additions and 4335 deletions

26
.gitignore vendored
View File

@ -1,26 +0,0 @@
*.py[co]
# Packages
*.egg
*.egg-info
*.old
dist
build
eggs
parts
bin
var
sdist
develop-eggs
.installed.cfg
# Installer logs
pip-log.txt
# Unit test / coverage reports
.coverage
.tox
#Translations
*.mo
#Mr Developer
.mr.developer.cfg
.idea/*
/.idea/

View File

@ -1,4 +0,0 @@
Thijs Metsch <thijsx.metsch@intel.com>
Andy Edmonds <andrew.edmonds@zhaw.ch>
Álvaro López García <aloga@ifca.unican.es>
Björn Hagemeier <b.hagemeier@fz-juelich.de>

176
LICENSE
View File

@ -1,176 +0,0 @@
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions.
"License" shall mean the terms and conditions for use, reproduction,
and distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by
the copyright owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all
other entities that control, are controlled by, or are under common
control with that entity. For the purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the
direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity
exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation
source, and configuration files.
"Object" form shall mean any form resulting from mechanical
transformation or translation of a Source form, including but
not limited to compiled object code, generated documentation,
and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or
Object form, made available under the License, as indicated by a
copyright notice that is included in or attached to the work
(an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object
form, that is based on (or derived from) the Work and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship. For the purposes
of this License, Derivative Works shall not include works that remain
separable from, or merely link (or bind by name) to the interfaces of,
the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including
the original version of the Work and any modifications or additions
to that Work or Derivative Works thereof, that is intentionally
submitted to Licensor for inclusion in the Work by the copyright owner
or by an individual or Legal Entity authorized to submit on behalf of
the copyright owner. For the purposes of this definition, "submitted"
means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems,
and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of discussing and improving the Work, but
excluding communication that is conspicuously marked or otherwise
designated in writing by the copyright owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity
on behalf of whom a Contribution has been received by Licensor and
subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the
Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
(except as stated in this section) patent license to make, have made,
use, offer to sell, sell, import, and otherwise transfer the Work,
where such license applies only to those patent claims licensable
by such Contributor that are necessarily infringed by their
Contribution(s) alone or by combination of their Contribution(s)
with the Work to which such Contribution(s) was submitted. If You
institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work
or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate
as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the
Work or Derivative Works thereof in any medium, with or without
modifications, and in Source or Object form, provided that You
meet the following conditions:
(a) You must give any other recipients of the Work or
Derivative Works a copy of this License; and
(b) You must cause any modified files to carry prominent notices
stating that You changed the files; and
(c) You must retain, in the Source form of any Derivative Works
that You distribute, all copyright, patent, trademark, and
attribution notices from the Source form of the Work,
excluding those notices that do not pertain to any part of
the Derivative Works; and
(d) If the Work includes a "NOTICE" text file as part of its
distribution, then any Derivative Works that You distribute must
include a readable copy of the attribution notices contained
within such NOTICE file, excluding those notices that do not
pertain to any part of the Derivative Works, in at least one
of the following places: within a NOTICE text file distributed
as part of the Derivative Works; within the Source form or
documentation, if provided along with the Derivative Works; or,
within a display generated by the Derivative Works, if and
wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and
do not modify the License. You may add Your own attribution
notices within Derivative Works that You distribute, alongside
or as an addendum to the NOTICE text from the Work, provided
that such additional attribution notices cannot be construed
as modifying the License.
You may add Your own copyright statement to Your modifications and
may provide additional or different license terms and conditions
for use, reproduction, or distribution of Your modifications, or
for any such Derivative Works as a whole, provided Your use,
reproduction, and distribution of the Work otherwise complies with
the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise,
any Contribution intentionally submitted for inclusion in the Work
by You to the Licensor shall be under the terms and conditions of
this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify
the terms of any separate license agreement you may have executed
with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade
names, trademarks, service marks, or product names of the Licensor,
except as required for reasonable and customary use in describing the
origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or
agreed to in writing, Licensor provides the Work (and each
Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied, including, without limitation, any warranties or conditions
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any
risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory,
whether in tort (including negligence), contract, or otherwise,
unless required by applicable law (such as deliberate and grossly
negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special,
incidental, or consequential damages of any character arising as a
result of this License or out of the use or inability to use the
Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses), even if such Contributor
has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing
the Work or Derivative Works thereof, You may choose to offer,
and charge a fee for, acceptance of support, warranty, indemnity,
or other liability obligations and/or rights consistent with this
License. However, in accepting such obligations, You may act only
on Your own behalf and on Your sole responsibility, not on behalf
of any other Contributor, and only if You agree to indemnify,
defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.

View File

@ -1,72 +0,0 @@
OCCI for OpenStack
==================
This is a clone and continuation of https://github.com/dizz/nova - it
provides a python egg which can be easily deployed in [OpenStack](http://www
.openstack.org) and will thereby add the 3rd party [OCCI](http://www.occi-wg
.org) interface to OpenStack. For usage examples, [see the OpenStack wiki]
(http://wiki.openstack.org/occi).
Usage
-----
0. Install dependencies: `pip install pyssf`
1. Install this egg: `python setup.py install` (or `pip install
openstackocci-grizzly`)
2. Configure OpenStack - Add application to `api-paste.ini` of nova and
enable the API
***Note***: do not install the [occi](http://pypi.python.org/pypi/occi/0.6)
package via `pip`. This is a seperate project and not related to OpenStack &
OCCI.
### Configuration
Make sure an application is configured in `api-paste.ini` (name can be
picked yourself):
########
# OCCI #
########
[composite:occiapi]
use = egg:Paste#urlmap
/: occiapppipe
[pipeline:occiapppipe]
pipeline = authtoken keystonecontext occiapp
# with request body size limiting and rate limiting
# pipeline = sizelimit authtoken keystonecontext ratelimit occiapp
[app:occiapp]
use = egg:openstackocci-grizzly#occi_app
Make sure the API (name from above) is enabled in `nova.conf`:
[...]
enabled_apis=ec2,occiapi,osapi_compute,osapi_volume,metadata
[...]
#### Hacking the port number
(Optional) You can set the port option via the `nova.conf` configuration
file - default is 8787:
[...]
occiapi_listen_port=9999
[...]
There is further documentation on [setting up your development environment
in the wiki](https://github.com/tmetsch/occi-os/wiki/DevEnv).
#Versioning
The general naming scheme for the Python eggs is:
* openstackocci - for the latest and greatest
* openstackocci-\<openstack release name\> - for OpenStack release specific stable releases
# 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/).

7
README.rst Normal file
View File

@ -0,0 +1,7 @@
This project is no longer maintained.
The contents of this repository are still available in the Git source code
management system. To see the contents of this repository before it reached
its end of life, please check out the previous commit with
"git checkout HEAD^1".

View File

@ -1,112 +0,0 @@
# Note:
This documentation may not be current in places. There is also further documentation at the [openstack wiki](https://wiki.openstack.org/wiki/Occi)
# OCCI and OpenStack: What can I do?
This guide will explain what you can do with the current OCCI implementation for OpenStack
## First up, prerequisites:
### Get a running instance of OpenStack
Lots of ways to do this:
* Install via apt-get
* Install with puppet
* Install with chef
* Install with crowbar
* Install with devstack
The easiest is devstack.
### Get the OCCI code
#### OCCI Library
On the machine(s) that you run the OCCI API, likely the same as the machine(s) as you run the OS-API, run the following:
>```pip install pyssf```
#### OCCI API Implementation
On the machine(s) that you want to run the OCCI API, likely the same as the machine(s) as you run the OS-API, run the following:
>```cd $YOUR_NOVA_INSTALL_LOCATION```
>```git add remote occi-upstream git://git@github.com/dizz/nova```
>```git fetch occi-upstream```
>```git merge occi-upstream/master```
### Configure devstack to run the volume service. Edit localrc and insert:
>```ENABLED_SERVICES=g-api,g-reg,key,n-api,n-crt,n-obj,n-cpu,n-net,n-sch,n-novnc,n-xvnc,n-cauth,horizon,mysql,rabbit,n-vol,openstackx```
### Run the OS-OCCI API
>```./bin/nova-api-occi --verbose --glance_api_servers=10.211.55.27:9292 --rabbit_host=10.211.55.27 --rabbit_password=admin --sql_connection=mysql://root:admin@10.211.55.27/nova```
### Get Authentication Credentials from Keystone
>```curl -d '{"auth":{"passwordCredentials":{"username": "admin", "password": "admin"}}}' -H "Content-type: application/json" http://10.211.55.27:35357/v2.0/tokens
```
>```export $KID=<<Token from Keystone>>```
## OCCI-ness
_Note:_ some confusion will happen if a content-type is not specified.
### See What Can be Provisioned
>```curl -v -H 'X-Auth-Token: '$KID -H -X GET 0.0.0.0:8787/-/
```
### Create a VM
>```curl -v -X POST localhost:8787/compute/ -H 'Category: compute; scheme="http://schemas.ogf.org/occi/infrastructure#"; class="kind"' -H 'Content-Type: text/occi' -H 'X-Auth-Token: '$KID -H 'X-Auth-Project-ID: 1' -H 'Category: m1.tiny; scheme="http://schemas.openstack.org/template/resource#"; class="mixin"' -H 'Category: cirros-0.3.0-x86_64-blank; scheme="http://schemas.openstack.org/template/os#"; class="mixin"'
```
### Get a Listing of VMs
>```curl -v -X GET localhost:8787/compute/ -H 'Content-Type: text/occi' -H 'X-Auth-Token: '$KID -H 'X-Auth-Project-ID: 1'
```
### Get an Individual VM's details
>```curl -v -X GET localhost:8787/compute/d54b4344-16be-486a-9871-2c566ef2263d -H 'Content-Type: text/occi' -H 'X-Auth-Token: '$KID -H 'X-Auth-Project-ID: 1'
```
### Execute a Stop Action Upon a VM
>```curl -v -X POST localhost:8787/compute/d54b4344-16be-486a-9871-2c566ef2263d?action=stop -H 'Content-Type: text/occi' -H 'X-Auth-Token: '$KID -H 'X-Auth-Project-ID: 1' -H 'Category: stop; scheme="http://schemas.ogf.org/occi/infrastructure/compute/action#"; class="action"'
```
### Execute a Start Action Upon a VM
_Note: this will probably result in an error state. Currently looking into the issue._
>```curl -v -X POST localhost:8787/compute/888fc64a-4500-4543-bed4-8ddf3938dcb5?action=start -H 'Content-Type: text/occi' -H 'X-Auth-Token: '$KID -H 'X-Auth-Project-ID: 1' -H 'Category: start; scheme="http://schemas.ogf.org/occi/infrastructure/compute/action#"; class="action"'
```
### Delete a VM
>```curl -v -X DELETE localhost:8787/compute/d54b4344-16be-486a-9871-2c566ef2263d -H 'Content-Type: text/occi' -H 'X-Auth-Token: '$KID -H 'X-Auth-Project-ID: 1'
```
### Create some a block storage volume
>```curl -v -X POST localhost:8787/storage/ -H 'Category: storage; scheme="http://schemas.ogf.org/occi/infrastructure#"; class="kind"' -H 'Content-Type: text/occi' -H 'X-Auth-Token: '$KID -H 'X-Auth-Project-ID: 1' -H 'occi.storage.size = 1.0'
```
### Link and associate that volume to the new instance
>```curl -v -X POST localhost:8787/storage/link/ -H 'X-Auth-Token: '$KID -H 'X-Auth-Project-ID: 1' -H 'Category: storagelink; scheme="http://schemas.ogf.org/occi/infrastructure#"; class="kind"' -H 'X-OCCI-Attribute: occi.core.source="http://localhost:8787/compute/e7a34bc4-02e7-43e3-a543-8aec630b5364"' -H 'X-OCCI-Attribute: occi.core.target="http://localhost:8787/storage/1"' -H 'X-OCCI-Attribute: occi.storagelink.mountpoint="/dev/vdb"' -H 'Content-Type: text/occi'```
### Unlink and disassociate that volume with the new instance
>```curl -v -X DELETE localhost:8787/storage/link/6cb97f63-7d8a-4474-87cb-4d1c9c581de1 -H 'X-Auth-Token: '$KID -H 'X-Auth-Project-ID: 1' -H 'Content-Type: text/occi'```
## Upcoming
### Update a VM: Scale up!
Let's bump the current instance from tiny (512) to a custom flavour (768R, 1C).
_Notes:_
* This is a partial update with respect to OCCI.
* This only works with Xen currently
* otherwise it fails silently
>```curl -v -X POST localhost:8787/compute/2ee26373-db62-4bbf-9325-ff68a81097e3 -H 'Content-Type: text/occi' -H 'X-Auth-Token: '$KID -H 'X-Auth-Project-ID: 1' -H 'Category: m1.medium; scheme="http://schemas.openstack.org/template/resource#"; class="mixin"'
```
### Update a VM: Change the OS!
Let's use SmartOS as the new OS
_Notes:_
* this is in effect a partial update.
>```curl -v -X POST localhost:8787/compute/d54b4344-16be-486a-9871-2c566ef2263d -H 'Content-Type: text/occi' -H 'X-Auth-Token: '$KID -H 'X-Auth-Project-ID: 1' -H 'Category: SmartOS; scheme="http://schemas.openstack.org/template/os#"; class="mixin"'
```

View File

@ -1,48 +0,0 @@
# 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.
"""
This it the entry point for paste.
Paste config file needs to point to egg:<package name>:<entrypoint name>:
use = egg:occi-os#sample_app
sample_app entry point is defined in setup.py:
entry_points='''
[paste.app_factory]
sample_app = occiosapi:main
''',
which point to this function call (<module name>:function).
"""
# W0613:unused args
# pylint: disable=W0613
from occi_os_api import wsgi
#noinspection PyUnusedLocal
def main(global_config, **settings):
"""
This is the entry point for paste into the OCCI OS world.
"""
return wsgi.OCCIApplication()

View File

@ -1,21 +0,0 @@
# 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.
"""
Package for all the backends!
"""

View File

@ -1,184 +0,0 @@
# 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.
"""
The compute resource backend for OpenStack.
"""
#pylint: disable=W0232,R0201
import logging
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
LOG = logging.getLogger(__name__)
class ComputeBackend(KindBackend, ActionBackend):
"""
The compute backend.
"""
def create(self, entity, extras):
"""
Create a VM.
"""
LOG.debug('Creating an Virtual machine')
# ignore some attributes - done via templating
if 'occi.compute.cores' in entity.attributes or \
'occi.compute.speed' in entity.attributes or \
'occi.compute.memory' in entity.attributes or \
'occi.compute.architecture' in entity.attributes:
raise AttributeError('There are unsupported attributes in the '
'request.')
# create the VM
context = extras['nova_ctx']
instance = vm.create_vm(entity, context)
uid = instance['uuid']
entity.identifier = '/compute/' + uid
# set some attributes
entity.attributes['occi.compute.hostname'] = instance['hostname']
entity.attributes['occi.compute.architecture'] = \
storage.get_image_architecture(uid, extras['nova_ctx'])
entity.attributes['occi.compute.cores'] = str(instance['vcpus'])
entity.attributes['occi.compute.speed'] = str(0.0) # N/A in instance
value = str(float(instance['memory_mb']) / 1024)
entity.attributes['occi.compute.memory'] = value
entity.attributes['occi.compute.state'] = 'inactive'
# set valid actions
entity.actions = [infrastructure.STOP,
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):
"""
Retrieve a VM.
"""
uid = entity.attributes['occi.core.id']
context = extras['nova_ctx']
instance = vm.get_vm(uid, context)
LOG.debug('Retrieving an Virtual machine: ' + repr(uid))
# set state and applicable actions!
state, actions = vm.get_occi_state(uid, context)
entity.attributes['occi.compute.state'] = state
entity.actions = actions
# set up to date attributes
entity.attributes['occi.compute.hostname'] = instance['hostname']
entity.attributes['occi.compute.architecture'] =\
storage.get_image_architecture(uid, extras['nova_ctx'])
entity.attributes['occi.compute.cores'] = str(instance['vcpus'])
entity.attributes['occi.compute.speed'] = str(0.0) # N/A in instance
value = str(float(instance['memory_mb']) / 1024)
entity.attributes['occi.compute.memory'] = value
def update(self, old, new, extras):
"""
Update an VM.
"""
context = extras['nova_ctx']
uid = old.attributes['occi.core.id']
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_id = mixin.res_id
vm.resize_vm(uid, flavor_id, context)
old.attributes['occi.compute.state'] = 'inactive'
# now update the mixin info
old.mixins.append(mixin)
elif isinstance(mixin, os_mixins.OsTemplate):
image_href = mixin.os_id
vm.rebuild_vm(uid, image_href, context)
old.attributes['occi.compute.state'] = 'inactive'
# now update the mixin info
old.mixins.append(mixin)
else:
msg = 'Unrecognized mixin. %s' % str(mixin)
LOG.error(msg)
raise AttributeError(msg)
def replace(self, old, new, extras):
"""
XXX:not doing anything - full updates are hard :-)
"""
pass
def delete(self, entity, extras):
"""
Remove a VM.
"""
msg = 'Removing representation of virtual machine with id: %s' %\
entity.identifier
LOG.info(msg)
context = extras['nova_ctx']
uid = entity.attributes['occi.core.id']
vm.delete_vm(uid, context)
def action(self, entity, action, attributes, extras):
"""
Perform an action.
"""
# As there is no callback mechanism to update the state
# of computes known by occi, a call to get the latest representation
# must be made.
context = extras['nova_ctx']
uid = entity.attributes['occi.core.id']
# set state and applicable actions - so even if the user hasn't done
# a GET het can still the most applicable action now...
state, actions = vm.get_occi_state(uid, context)
entity.attributes['occi.compute.state'] = state
entity.actions = actions
if action not in entity.actions:
raise AttributeError("This action is currently not applicable.")
elif action == infrastructure.START:
vm.start_vm(uid, context)
elif action == infrastructure.STOP:
vm.stop_vm(uid, context)
elif action == infrastructure.RESTART:
if not 'method' in attributes:
raise AttributeError('Please provide a method!')
method = attributes['method']
vm.restart_vm(uid, method, context)
elif action == infrastructure.SUSPEND:
vm.suspend_vm(uid, context)

View File

@ -1,120 +0,0 @@
# 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.
"""
Network resource backend.
"""
#W0613:unused arguments,R0201:mth could be func,R0903:too few pub mthd.
#W0232:no init
#pylint: disable=W0613,R0201,R0903,W0232
from occi import backend
from occi_os_api.extensions import os_addon
from occi_os_api.nova_glue import net
class NetworkBackend(backend.KindBackend, backend.ActionBackend):
"""
Backend to handle network resources.
"""
def create(self, entity, extras):
"""
Currently unsupported.
"""
raise AttributeError('Currently not supported.')
def action(self, entity, action, attributes, extras):
"""
Currently unsupported.
"""
raise AttributeError('Currently not supported.')
class IpNetworkBackend(backend.MixinBackend):
"""
A mixin backend for the IPnetworking.
"""
def create(self, entity, extras):
"""
Currently unsupported.
"""
raise AttributeError('Currently not supported.')
class IpNetworkInterfaceBackend(backend.MixinBackend):
"""
A mixin backend for the IpNetworkingInterface (covered by
NetworkInterfaceBackend).
"""
pass
class NetworkInterfaceBackend(backend.KindBackend):
"""
A backend for network links.
"""
def create(self, link, extras):
"""
As nova does not support creation of L2 networks we don't.
"""
if link.target.identifier == '/network/public':
# public means floating IP in OS!
# if the os_net_link mixin is avail. a pool must be provided:
if not 'org.openstack.network.floating.pool' in link.attributes\
and os_addon.OS_NET_LINK in link.mixins:
raise AttributeError('Please specify the pool name when using'
' this mixin!')
elif os_addon.OS_NET_LINK in link.mixins:
pool = link.attributes['org.openstack.network.floating.pool']
else:
pool = None
address = net.add_floating_ip(link.source.attributes['occi.'
'core.id'],
pool,
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.')
def update(self, old, new, extras):
"""
Allows for the update of network links.
"""
raise AttributeError('Currently not supported.')
def delete(self, link, extras):
"""
Remove a floating ip!
"""
if link.target.identifier == '/network/public':
# public means floating IP in OS!
net.remove_floating_ip(link.source.attributes['occi.core.id'],
link.attributes['occi.networkinterface.'
'address'],
extras['nova_ctx'])

View File

@ -1,244 +0,0 @@
# 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.
"""
The compute resource backend for OpenStack.
"""
#pylint: disable=W0232,R0201
import random
from occi import backend
from occi import exceptions
from occi_os_api.extensions import os_addon
from occi_os_api.nova_glue import vm
from occi_os_api.nova_glue import security
class OsComputeBackend(backend.MixinBackend, backend.ActionBackend):
"""
The OpenStackCompute backend.
"""
def retrieve(self, entity, extras):
"""
Add OpenStack related actions.
"""
uid = entity.attributes['occi.core.id']
context = extras['nova_ctx']
# set additional actions
if 'occi.compute.state' in entity.attributes and entity.attributes[
'occi.compute.state'] == 'active':
entity.actions.append(os_addon.OS_CREATE_IMAGE)
entity.actions.append(os_addon.OS_CHG_PWD)
# add VNC link if available
console = vm.get_vnc(uid, context)
if console:
entity.attributes['org.openstack.compute.console.vnc'] =\
console['url']
else:
entity.attributes['org.openstack.compute.console.vnc'] = 'N/A'
# also expose the exact openstack state
entity.attributes['org.openstack.compute.state'] = \
vm.get_vm(uid, context)['vm_state']
def action(self, entity, action, attributes, extras):
"""
This is called by pyssf when an action request is issued.
"""
context = extras['nova_ctx']
uid = entity.attributes['occi.core.id']
if action == os_addon.OS_CHG_PWD:
if 'org.openstack.credentials.admin_pwd' not in attributes:
msg = 'org.openstack.credentials.admin_pwd was not supplied'\
' in the request.'
raise AttributeError(msg)
new_password = attributes['org.openstack.credentials.admin_pwd']
vm.set_password_for_vm(uid, new_password, context)
elif action == os_addon.OS_CREATE_IMAGE:
if 'org.openstack.snapshot.image_name' not in attributes:
raise AttributeError('Missing image name')
image_name = attributes['org.openstack.snapshot.image_name']
vm.snapshot_vm(uid, image_name, context)
else:
raise AttributeError('Not an applicable action.')
class OsNetLinkBackend(backend.MixinBackend, backend.ActionBackend):
"""
The OpenStack network link backend.
"""
pass
class SecurityGroupBackend(backend.UserDefinedMixinBackend):
"""
Security Group backend.
"""
def init_sec_group(self, category, extras):
"""
Creates the security group as specified in the request.
"""
#do not recreate default openstack security groups
if category.scheme == \
'http://schemas.openstack.org/infrastructure/security/group#':
return
context = extras['nova_ctx']
group_name = category.term.strip()
group_description = (category.title.strip()
if category.title else group_name)
security.create_group(group_name, group_description, context)
def destroy(self, category, extras):
"""
Deletes the specified security group.
"""
context = extras['nova_ctx']
security_group = security.retrieve_group(category.term,
extras['nova_ctx'])
security.remove_group(security_group.id, context)
class SecurityRuleBackend(backend.KindBackend):
"""
Security rule backend.
"""
def create(self, entity, extras):
"""
Creates a security rule.
The group to add the rule to must exist.
In OCCI-speak this means the mixin must be supplied with the request
"""
sec_mixin = get_sec_mixin(entity)
context = extras['nova_ctx']
security_group = security.retrieve_group(sec_mixin.term,
context)
sg_rule = make_sec_rule(entity, security_group['id'])
if security_group_rule_exists(security_group, sg_rule):
#This rule already exists in group
msg = 'This rule already exists in group. %s' %\
str(security_group)
raise AttributeError(msg)
security.create_rule(sg_rule, context)
def delete(self, entity, extras):
"""
Deletes the security rule.
"""
try:
context = extras['nova_ctx']
rule = security.retrieve_rule(entity.attributes['occi.core.id'],
context)
security.remove_rule(rule, context)
except Exception as error:
raise exceptions.HTTPError(500, str(error))
def make_sec_rule(entity, sec_grp_id):
"""
Create and validate the security rule.
"""
name = random.randrange(0, 99999999)
sg_rule = {'id': name,
'parent_group_id': sec_grp_id}
entity.attributes['occi.core.id'] = str(sg_rule['id'])
prot = \
entity.attributes['occi.network.security.protocol'].lower().strip()
if prot in ('tcp', 'udp', 'icmp'):
sg_rule['protocol'] = prot
else:
raise AttributeError('Invalid protocol defined:' + prot)
from_p = entity.attributes['occi.network.security.to'].strip()
from_p = int(from_p)
if (type(from_p) is int) and 0 < from_p <= 65535:
sg_rule['from_port'] = from_p
else:
raise AttributeError('No valid from port defined.')
to_p = entity.attributes['occi.network.security.to'].strip()
to_p = int(to_p)
if (type(to_p) is int) and 0 < to_p <= 65535:
sg_rule['to_port'] = to_p
else:
raise AttributeError('No valid to port defined.')
if from_p > to_p:
raise AttributeError('From port is bigger than to port defined.')
cidr = entity.attributes['occi.network.security.range'].strip()
if len(cidr) <= 0:
cidr = '0.0.0.0/0'
if True:
sg_rule['cidr'] = cidr
else:
raise AttributeError('No valid CIDR defined.')
sg_rule['group'] = {}
return sg_rule
def get_sec_mixin(entity):
"""
Get the security mixin of the supplied entity.
"""
sec_mixin_present = 0
sec_mixin = None
for mixin in entity.mixins:
if os_addon.SEC_GROUP in mixin.related:
sec_mixin = mixin
sec_mixin_present += 1
if not sec_mixin_present:
# no mixin of the type security group was found
msg = 'No security group mixin was found'
raise AttributeError(msg)
if sec_mixin_present > 1:
msg = 'More than one security group mixin was found'
raise AttributeError(msg)
return sec_mixin
def security_group_rule_exists(security_group, values):
"""
Indicates whether the specified rule values are already
defined in the given security group.
"""
# Taken directly from security_groups.py as that method is not
# directly import-able.
for rule in security_group['rules']:
is_duplicate = True
keys = ('group_id', 'cidr', 'from_port', 'to_port', 'protocol')
for key in keys:
if rule.get(key) != values.get(key):
is_duplicate = False
break
if is_duplicate:
return True
return False

View File

@ -1,168 +0,0 @@
# 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
from datetime import date
import logging
import uuid
from occi import backend
from occi import exceptions
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):
"""
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)
entity.identifier = infrastructure.STORAGE.location + vol_id
if new_volume['status'] == 'available':
entity.attributes['occi.storage.state'] = 'active'
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'
entity.actions = [infrastructure.ONLINE]
def update(self, old, new, extras):
"""
Updates simple attributes of a storage resource:
occi.core.title, occi.core.summary
"""
# update attributes.
if len(new.attributes) > 0:
# support only title and summary changes now.
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):
"""
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 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:
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'])
class StorageLinkBackend(backend.KindBackend):
"""
A backend for the storage links.
"""
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 = 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)
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 = link.target.attributes['occi.core.id']
vm.detach_volume(volume_id, extras['nova_ctx'])

View File

@ -1,21 +0,0 @@
# 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.
"""
A package containing extensions for OCCI.
"""

View File

@ -1,82 +0,0 @@
# 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.
"""
Set of extensions to get OCCI work with OpenStack.
"""
#pylint: disable=W0232,R0912,R0201,R0903
from occi import core_model
# Network security rule extension to specify firewall rules
_SEC_RULE_ATTRIBUTES = {
'occi.network.security.protocol': '',
'occi.network.security.to': '',
'occi.network.security.from': '',
'occi.network.security.range': '',
}
SEC_RULE = core_model.Kind(
'http://schemas.openstack.org/occi/infrastructure/network/security#',
'rule',
[core_model.Resource.kind],
None,
'Network security rule kind',
_SEC_RULE_ATTRIBUTES,
'/network/security/rule/')
# Network security rule group
SEC_GROUP = core_model.Mixin(
'http://schemas.ogf.org/occi/infrastructure/security#',
'group', attributes=None)
# OS change adminstrative password action
_OS_CHG_PWD_ATTRIBUTES = {'org.openstack.credentials.admin_pwd': '', }
OS_CHG_PWD = core_model.Action(
'http://schemas.openstack.org/instance/action#',
'chg_pwd', 'Changes Admin password.',
_OS_CHG_PWD_ATTRIBUTES)
# OS create image from VM action
_OS_CREATE_IMAGE_ATTRIBUTES = {'org.openstack.snapshot.image_name': '', }
OS_CREATE_IMAGE = core_model.Action(
'http://schemas.openstack.org/instance/action#',
'create_image', 'Creates a new image for the given server.',
_OS_CREATE_IMAGE_ATTRIBUTES)
# A Mixin for OpenStack VMs
_OS_VM_ATTRIBUTES = {'org.openstack.compute.console.vnc': 'immutable',
'org.openstack.compute.state': 'immutable'}
OS_VM = core_model.Mixin(
'http://schemas.openstack.org/compute/instance#',
'os_vms', actions=[OS_CHG_PWD, OS_CREATE_IMAGE],
attributes=_OS_VM_ATTRIBUTES)
# OS Key pair extension
_OS_KEY_PAIR_ATTRIBUTES = {'org.openstack.credentials.publickey.name': '',
'org.openstack.credentials.publickey.data': '', }
OS_KEY_PAIR_EXT = core_model.Mixin(
'http://schemas.openstack.org/instance/credentials#',
'public_key', attributes=_OS_KEY_PAIR_ATTRIBUTES)
# A Mixin for OpenStack Network links
_OS_NET_LINK_ATTRIBUTES = {'org.openstack.network.floating.pool': 'required'}
OS_NET_LINK = core_model.Mixin(
'http://schemas.openstack.org/network/instance#',
'os_net_link', actions=[],
attributes=_OS_NET_LINK_ATTRIBUTES)

View File

@ -1,58 +0,0 @@
# 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.
"""
Set of templates.
"""
#pylint: disable=R0913,E1002,R0903,W0232
from occi import core_model
class OsTemplate(core_model.Mixin):
"""
Represents the OS Template mechanism as per OCCI specification.
An OS template is equivalent to an image in OpenStack
"""
def __init__(self, scheme, term, os_id=None, related=None, actions=None,
title='', attributes=None, location=None):
super(OsTemplate, self).__init__(scheme, term, related, actions,
title, attributes, location)
self.os_id = os_id
class ResourceTemplate(core_model.Mixin):
"""
Here to make identification of template type easier in backends.
"""
def __init__(self, scheme, term, flavor_id=None, related=None,
actions=None, title='',
attributes=None, location=None):
super(ResourceTemplate, self).__init__(scheme, term, related,
actions, title, attributes,
location)
self.res_id = flavor_id
class UserSecurityGroupMixin(core_model.Mixin):
"""
Empty Mixin.
"""
pass

View File

@ -1,21 +0,0 @@
# 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.
"""
Package which connects everything to the nova layer...
"""

View File

@ -1,128 +0,0 @@
# 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.
"""
Network related 'glue' :-)
"""
import logging
from nova import network
from nova import exception
from nova import compute
from nova.compute import utils
from occi_os_api.nova_glue import vm
# Connect to nova :-)
NETWORK_API = network.API()
COMPUTE_API = compute.API()
LOG = logging.getLogger(__name__)
def get_network_details(uid, context):
"""
Extracts the VMs network adapter information.
uid -- Id of the VM.
context -- The os context.
"""
vm_instance = vm.get_vm(uid, context)
result = {'public': [], 'admin': []}
try:
net_info = NETWORK_API.get_instance_nw_info(context, vm_instance)[0]
except IndexError:
LOG.warn('Unable to retrieve network information - this is because '
'of OpenStack!!')
return result
gw = net_info['network']['subnets'][0]['gateway']['address']
mac = net_info['address']
if len(net_info['network']['subnets'][0]['ips']) == 0:
tmp = {'floating_ips': [], 'address': '0.0.0.0'}
else:
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',
'state': 'active',
'address': item['address'],
'gateway': '0.0.0.0',
'allocation': 'static'})
result['admin'].append({'interface': 'eth0',
'mac': mac,
'state': 'active',
'address': tmp['address'],
'gateway': gw,
'allocation': 'static'})
return result
def add_floating_ip(uid, pool_name, context):
"""
Adds an ip to an VM instance.
uid -- id of the VM.
pool_name -- name of the pool
context -- The os context.
"""
vm_instance = vm.get_vm(uid, context)
cached_nwinfo = utils.get_nw_info_for_instance(vm_instance)
if not cached_nwinfo:
raise AttributeError('No nw_info cache associated with instance')
fixed_ips = cached_nwinfo.fixed_ips()
if not fixed_ips:
raise AttributeError('No fixed ips associated to instance')
float_address = NETWORK_API.allocate_floating_ip(context, pool_name)
try:
address = fixed_ips[0]['address']
NETWORK_API.associate_floating_ip(context, vm_instance,
float_address, address)
except exception.FloatingIpAssociated:
msg = 'floating ip is already associated'
raise AttributeError(msg)
except exception.NoFloatingIpInterface:
msg = 'l3driver call to add floating ip failed'
raise AttributeError(msg)
return float_address
def remove_floating_ip(uid, address, context):
"""
Remove a given address from an VM instance.
uid -- Id of the VM.
address -- The ip address.
context -- The os context.
"""
vm_instance = vm.get_vm(uid, context)
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!')

View File

@ -1,134 +0,0 @@
# 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.
"""
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
from occi import exceptions
# connect to nova
COMPUTE_API = compute.API()
SEC_HANDLER = importutils.import_object(FLAGS.security_group_handler)
def create_group(name, description, context):
"""
Create a OS security group.
name -- Name of the group.
description -- Description.
context -- The os context.
"""
if db.security_group_exists(context, context.project_id, name):
raise AttributeError('Security group already exists: ' + name)
group = {'user_id': context.user_id,
'project_id': context.project_id,
'name': name,
'description': description}
db.security_group_create(context, group)
SEC_HANDLER.trigger_security_group_create_refresh(context, group)
def remove_group(group_id, context):
"""
Remove a security group.
group_id -- the group.
context -- The os context.
"""
try:
#if db.security_group_in_use(context, group_id):
# raise AttributeError('Security group is still in use')
db.security_group_destroy(context, group_id)
SEC_HANDLER.trigger_security_group_destroy_refresh(
context, group_id)
except Exception as error:
raise AttributeError(error)
def retrieve_group(mixin_term, context):
"""
Retrieve the security group associated with the security mixin.
mixin_term -- The term of the mixin representing the group.
context -- The os context.
"""
try:
sec_group = db.security_group_get_by_name(context, context.project_id,
mixin_term)
except Exception as err:
msg = err.message
raise AttributeError(msg)
return sec_group
def create_rule(rule, context):
"""
Create a security rule.
rule -- The rule.
context -- The os context.
"""
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):
"""
Remove a security rule.
rule -- The rule
context -- The os context.
"""
group_id = rule['parent_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 retrieve_rule(uid, context):
"""
Retrieve a rule.
uid -- Id of the rule (entity.attributes['occi.core.id'])
context -- The os context.
"""
try:
return db.security_group_rule_get(context,
int(uid))
except Exception:
raise exceptions.HTTPError(404, 'Rule not found!')

View File

@ -1,166 +0,0 @@
# 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.
"""
Storage related glue :-)
"""
import random
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 occi_os_api.nova_glue import vm
VOLUME_API = volume.API()
IMAGE_API = glance.get_default_image_service()
def create_storage(size, context, name=None, description=None):
"""
Create a storage instance.
size -- Size of the storage. ('occi.storage.size')
context -- The os context.
name -- defaults to a random number if needed.
description -- defaults to the name
"""
# 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
# e.g. See nova/quota.py:allowed_volumes(...)
if not float(size).is_integer:
raise AttributeError('Volume sizes cannot be specified as fractional'
' floats.')
size = int(float(size))
disp_name = ''
if name is not None:
disp_name = name
else:
disp_name = str(random.randrange(0, 99999999)) + '-storage.occi-wg.org'
if description is not None:
disp_descr = description
else:
disp_descr = disp_name
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):
"""
Delete a storage instance.
uid -- Id of the volume.
context -- The os context.
"""
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):
"""
Snapshots an storage instance.
uid -- Id of the volume.
context -- The os context.
"""
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):
"""
Return details on an image.
"""
try:
return IMAGE_API.show(context, uid)
except exception.ImageNotFound as err:
raise AttributeError(str(err))
def get_image_architecture(uid, context):
"""
Extract architecture from either:
- image name, title or metadata. The architecture is sometimes
encoded in the image's name
- db::glance::image_properties could be used reliably so long as the
information is supplied when registering an image with glance.
- else return a default of x86
uid -- id of the instance!
context -- The os context.
"""
instance = vm.get_vm(uid, context)
arch = ''
uid = instance['image_ref']
img = IMAGE_API.show(context, uid)
img_properties = img['properties']
if 'arch' in img_properties:
arch = img['properties']['arch']
elif 'architecture' in img_properties:
arch = img['properties']['architecture']
if arch == '':
# if all attempts fail set it to a default value
arch = 'x86'
return arch
def get_storage(uid, context):
"""
Retrieve an Volume instance from nova.
uid -- id of the instance
context -- the os context
"""
try:
instance = VOLUME_API.get(context, uid)
except exception.NotFound:
raise exceptions.HTTPError(404, 'Volume not found!')
return instance
def get_storage_volumes(context):
"""
Retrieve all storage entities from user.
"""
return VOLUME_API.get_all(context)

View File

@ -1,448 +0,0 @@
# 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.
"""
VM related 'glue' :-)
"""
#pylint: disable=R0914,W0142,R0912,R0915
from nova import compute, volume
from nova import exception
from nova import utils
from nova.compute import vm_states
from nova.compute import task_states
from nova.compute import instance_types
from nova.exception import InstancePasswordSetFailed
from nova.flags import FLAGS
from occi import exceptions
from occi.extensions import infrastructure
from occi_os_api.extensions import os_mixins
from occi_os_api.extensions import os_addon
from occi_os_api.nova_glue import security
import logging
# Connection to the nova APIs
COMPUTE_API = compute.API()
VOLUME_API = volume.API()
LOG = logging.getLogger(__name__)
# NOTE(aloga): we need to import the option
FLAGS.import_opt('vnc_enabled', 'nova.vnc')
def create_vm(entity, context):
"""
Create a VM for an given OCCI entity.
entity -- the OCCI resource entity.
context -- the os context.
"""
if 'occi.compute.hostname' in entity.attributes:
name = entity.attributes['occi.compute.hostname']
else:
name = None
key_name = key_data = None
password = utils.generate_password(FLAGS.password_length)
access_ip_v4 = None
access_ip_v6 = None
user_data = None
metadata = {}
injected_files = []
min_count = max_count = 1
requested_networks = None
sg_names = []
availability_zone = None
config_drive = None
block_device_mapping = None
kernel_id = ramdisk_id = None
auto_disk_config = None
scheduler_hints = None
resource_template = None
os_template = None
for mixin in entity.mixins:
if isinstance(mixin, os_mixins.ResourceTemplate):
resource_template = mixin
elif isinstance(mixin, os_mixins.OsTemplate):
os_template = mixin
elif mixin == os_addon.OS_KEY_PAIR_EXT:
attr = 'org.openstack.credentials.publickey.name'
key_name = entity.attributes[attr]
attr = 'org.openstack.credentials.publickey.data'
key_data = entity.attributes[attr]
# Look for security group. If the group is non-existant, the
# call to create will fail.
if os_addon.SEC_GROUP in mixin.related:
secgroup = security.retrieve_group(mixin.term, context)
sg_names.append(secgroup["name"])
if not os_template:
raise AttributeError('Please provide a valid OS Template.')
if resource_template:
inst_type = compute.instance_types.\
get_instance_type_by_flavor_id(resource_template.res_id)
else:
inst_type = compute.instance_types.get_default_instance_type()
msg = ('No resource template was found in the request. '
'Using the default: %s') % inst_type['name']
LOG.warn(msg)
# make the call
try:
(instances, _reservation_id) = COMPUTE_API.create(
context=context,
instance_type=inst_type,
image_href=os_template.os_id,
kernel_id=kernel_id,
ramdisk_id=ramdisk_id,
min_count=min_count,
max_count=max_count,
display_name=name,
display_description=name,
key_name=key_name,
key_data=key_data,
security_group=sg_names,
availability_zone=availability_zone,
user_data=user_data,
metadata=metadata,
injected_files=injected_files,
admin_password=password,
block_device_mapping=block_device_mapping,
access_ip_v4=access_ip_v4,
access_ip_v6=access_ip_v6,
requested_networks=requested_networks,
config_drive=config_drive,
auto_disk_config=auto_disk_config,
scheduler_hints=scheduler_hints)
except Exception as error:
raise AttributeError(str(error))
# return first instance
return instances[0]
def rebuild_vm(uid, image_href, context):
"""
Rebuilds the specified VM with the supplied OsTemplate mixin.
uid -- id of the instance
image_href -- image reference.
context -- the os context
"""
instance = get_vm(uid, context)
admin_password = utils.generate_password(FLAGS.password_length)
kwargs = {}
try:
COMPUTE_API.rebuild(context, instance, image_href, admin_password,
**kwargs)
except exception.InstanceInvalidState:
raise AttributeError('VM is in an invalid state.')
except exception.ImageNotFound:
raise AttributeError('Cannot find image for rebuild')
def resize_vm(uid, flavor_id, context):
"""
Resizes a VM up or down
Update: libvirt now supports resize see:
http://wiki.openstack.org/HypervisorSupportMatrix
uid -- id of the instance
flavor_id -- image reference.
context -- the os context
"""
instance = get_vm(uid, context)
kwargs = {}
try:
flavor = instance_types.get_instance_type_by_flavor_id(flavor_id)
COMPUTE_API.resize(context, instance, flavor_id=flavor['flavorid'],
**kwargs)
ready = False
i = 0
while not ready or i < 15:
i += 1
state = get_vm(uid, context)['vm_state']
if state == 'resized':
ready = True
import time
time.sleep(1)
instance = get_vm(uid, context)
COMPUTE_API.confirm_resize(context, instance)
except exception.FlavorNotFound:
raise AttributeError('Unable to locate requested flavor.')
except exception.InstanceInvalidState as error:
raise AttributeError('VM is in an invalid state: ' + str(error))
def delete_vm(uid, context):
"""
Destroy a VM.
uid -- id of the instance
context -- the os context
"""
instance = get_vm(uid, context)
if FLAGS.reclaim_instance_interval:
COMPUTE_API.soft_delete(context, instance)
else:
COMPUTE_API.delete(context, instance)
def suspend_vm(uid, context):
"""
Suspends a VM. Use the start action to unsuspend a VM.
uid -- id of the instance
context -- the os context
"""
instance = get_vm(uid, context)
try:
COMPUTE_API.pause(context, instance)
except Exception as error:
raise exceptions.HTTPError(500, str(error))
def snapshot_vm(uid, image_name, context):
"""
Snapshots a VM. Use the start action to unsuspend a VM.
uid -- id of the instance
image_name -- name of the new image
context -- the os context
"""
instance = get_vm(uid, context)
try:
COMPUTE_API.snapshot(context,
instance,
image_name)
except exception.InstanceInvalidState:
raise AttributeError('VM is not in an valid state.')
def start_vm(uid, context):
"""
Starts a vm that is in the stopped state. Note, currently we do not
use the nova start and stop, rather the resume/suspend methods. The
start action also unpauses a paused VM.
uid -- id of the instance
state -- the state the VM is in (str)
context -- the os context
"""
instance = get_vm(uid, context)
try:
COMPUTE_API.resume(context, instance)
except Exception as error:
raise exceptions.HTTPError(500, 'Error while starting VM: ' +
str(error))
def stop_vm(uid, context):
"""
Stops a VM. Rather than use stop, suspend is used.
OCCI -> graceful, acpioff, poweroff
OS -> unclear
uid -- id of the instance
context -- the os context
"""
instance = get_vm(uid, context)
try:
# 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:
raise exceptions.HTTPError(500, 'Error while stopping VM: ' +
str(error))
def restart_vm(uid, method, context):
"""
Restarts a VM.
OS types == SOFT, HARD
OCCI -> graceful, warm and cold
mapping:
- SOFT -> graceful, warm
- HARD -> cold
uid -- id of the instance
method -- how the machine should be restarted.
context -- the os context
"""
instance = get_vm(uid, context)
if method in ('graceful', 'warm'):
reboot_type = 'SOFT'
elif method is 'cold':
reboot_type = 'HARD'
else:
raise AttributeError('Unknown method.')
try:
COMPUTE_API.reboot(context, instance, reboot_type)
except exception.InstanceInvalidState:
raise exceptions.HTTPError(406, 'VM is in an invalid state.')
def attach_volume(instance_id, volume_id, mount_point, context):
"""
Attaches a storage volume.
instance_id -- Id of the VM.
volume_id -- Id of the storage volume.
mount_point -- Where to mount.
context -- The os security context.
"""
instance = get_vm(instance_id, context)
try:
vol_instance = VOLUME_API.get(context, volume_id)
volume_id = vol_instance['id']
COMPUTE_API.attach_volume(
context,
instance,
volume_id,
mount_point)
except exception.NotFound:
raise exceptions.HTTPError(404, 'Volume not found!')
except exception.InvalidDevicePath:
raise AttributeError('Invalid device path!')
def detach_volume(volume_id, context):
"""
Detach a storage volume.
volume_id -- Id of the volume.
context -- the os context.
"""
try:
COMPUTE_API.detach_volume(context, volume_id)
except exception.InvalidVolume:
raise AttributeError('Invalid volume!')
except exception.VolumeUnattached:
raise AttributeError('Volume is not attached!')
def set_password_for_vm(uid, password, context):
"""
Set new password for an VM.
uid -- Id of the instance.
password -- The new password.
context -- The os context.
"""
instance = get_vm(uid, context)
try:
COMPUTE_API.set_admin_password(context, instance, password)
except InstancePasswordSetFailed as error:
LOG.warn('Unable to set password - driver might not support it! ' +
str(error))
def get_vnc(uid, context):
"""
Retrieve VNC console or None is unavailable.
uid -- id of the instance
context -- the os context
"""
console = None
if FLAGS.vnc_enabled:
instance = get_vm(uid, context)
try:
console = COMPUTE_API.get_vnc_console(context, instance, 'novnc')
except exception.InstanceNotFound:
LOG.warn('Console info is not available atm!')
return console
def get_vm(uid, context):
"""
Retrieve an VM instance from nova.
uid -- id of the instance
context -- the os context
"""
try:
instance = COMPUTE_API.get(context, uid)
except exception.NotFound:
raise exceptions.HTTPError(404, 'VM not found!')
return instance
def get_vms(context):
"""
Retrieve all VMs in a given context.
"""
opts = {'deleted': False}
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
Mapping assumptions:
- active == VM can service requests from network. These requests
can be from users or VMs
- inactive == the oppose! :-)
- suspended == machine in a frozen state e.g. via suspend or pause
uid -- Id of the VM.
context -- the os context.
"""
instance = get_vm(uid, context)
state = 'inactive'
actions = []
if instance['vm_state'] in [vm_states.ACTIVE]:
state = 'active'
actions.append(infrastructure.STOP)
actions.append(infrastructure.SUSPEND)
actions.append(infrastructure.RESTART)
elif instance['vm_state'] in [vm_states.BUILDING]:
state = 'inactive'
elif instance['vm_state'] in [vm_states.PAUSED, vm_states.SUSPENDED,
vm_states.STOPPED]:
state = 'inactive'
actions.append(infrastructure.START)
elif instance['vm_state'] in [vm_states.RESCUED,
vm_states.ERROR,
vm_states.DELETED]:
state = 'inactive'
# Some task states require a state
if instance['vm_state'] in [task_states.IMAGE_SNAPSHOT]:
state = 'inactive'
actions = []
return state, actions

View File

@ -1,422 +0,0 @@
# 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.
"""
OCCI registry
"""
#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 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
from nova.flags import FLAGS
class OCCIRegistry(occi_registry.NonePersistentRegistry):
"""
Registry for OpenStack.
Idea is the following: Create the OCCI entities (Resource and their
links) here and let the backends handle actions, attributes etc.
"""
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 set_hostname(self, hostname):
if FLAGS.occi_custom_location_hostname:
hostname = FLAGS.occi_custom_location_hostname
super(OCCIRegistry, self).set_hostname(hostname)
def get_extras(self, extras):
"""
Get data which is encapsulated in the extras.
"""
sec_extras = None
if extras is not None:
sec_extras = {'user_id': extras['nova_ctx'].user_id,
'project_id': extras['nova_ctx'].project_id}
return sec_extras
# The following two are here to deal with the security group mixins
def delete_mixin(self, mixin, extras):
"""
Allows for the deletion of user defined mixins.
If the mixin is a security group mixin then that mixin's
backend is called.
"""
if (hasattr(mixin, 'related') and
os_addon.SEC_GROUP in mixin.related):
backend = self.get_backend(mixin, extras)
backend.destroy(mixin, extras)
super(OCCIRegistry, self).delete_mixin(mixin, extras)
def set_backend(self, category, backend, extras):
"""
Assigns user id and tenant id to user defined mixins
"""
if (hasattr(category, 'related') and
os_addon.SEC_GROUP in category.related):
backend = openstack.SecurityGroupBackend()
backend.init_sec_group(category, extras)
super(OCCIRegistry, self).set_backend(category, backend, extras)
# The following two deal with the creation and deletion os links.
def add_resource(self, key, resource, extras):
"""
Just here to prevent the super class from filling up an unused dict.
"""
if (key, extras['nova_ctx'].user_id) not in self.cache and \
core_model.Link.kind in resource.kind.related:
# don't need to cache twice, only adding links :-)
self.cache[(key, extras['nova_ctx'].user_id)] = resource
elif (key, extras['nova_ctx'].user_id) not in self.cache and \
resource.kind == os_addon.SEC_RULE:
# don't need to cache twice, only adding links :-)
self.cache[(key, extras['nova_ctx'].user_id)] = resource
def delete_resource(self, key, extras):
"""
Just here to prevent the super class from messing up.
"""
if (key, extras['nova_ctx'].user_id) in self.cache:
self.cache.pop((key, extras['nova_ctx'].user_id))
# the following routines actually retrieve the info form OpenStack. Note
# that a cache is used. The cache is stable - so delete resources
# eventually also get deleted form the cache.
def get_resource(self, key, extras):
"""
Retrieve a single resource.
"""
context = extras['nova_ctx']
iden = key[key.rfind('/') + 1:]
vms = vm.get_vms(context)
vm_res_ids = [item['uuid'] for item in vms]
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 == \
infrastructure.COMPUTE:
# it was delete in OS -> remove links, cache + KeyError!
# can delete it because it was my item!
for link in cached_item.links:
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 == \
infrastructure.STORAGE:
# it was delete in OS -> remove from cache + KeyError!
# can delete it because it was my item!
self.cache.pop((key, repr(extras)))
raise KeyError
elif iden in vm_res_ids:
# it also exists in OS -> update it (take links, mixins
# from cached one)
result = self._update_occi_compute(cached_item, extras)
elif iden in stor_res_ids:
# it also exists in OS -> update it!
result = self._update_occi_storage(cached_item, extras)
else:
# return cached item (links)
return cached_item
elif (key, None) in self.cache:
# return shared entities from cache!
return self.cache[(key, None)]
else:
# construct it.
if iden in vm_res_ids:
# create new & add to cache!
result = self._construct_occi_compute(iden, extras)[0]
elif iden in stor_res_ids:
result = self._construct_occi_storage(iden, extras)[0]
else:
# doesn't exist!
raise KeyError
if result.identifier != key:
raise AttributeError('Key/identifier mismatch! Requested: ' +
key + ' Got: ' + result.identifier)
return result
def get_resource_keys(self, extras):
"""
Retrieve the keys of all resources.
"""
keys = []
for item in self.cache.values():
if item.extras is not None and item.extras != extras:
# filter out items not belonging to this user!
continue
else:
# add identifier
keys.append(item.identifier)
return keys
def get_resources(self, extras):
"""
Retrieve a set of resources.
"""
# TODO: add security rules!
context = extras['nova_ctx']
result = []
vms = vm.get_vms(context)
vm_res_ids = [item['uuid'] for item in vms]
stors = storage.get_storage_volumes(context)
stor_res_ids = [item['id'] for item in stors]
for item in self.cache.values():
if item.extras is not None and item.extras['user_id'] != \
context.user_id:
# filter out items not belonging to this user!
continue
item_id = item.identifier[item.identifier.rfind('/') + 1:]
if item.extras is None:
# add to result set
result.append(item)
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:
# 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:
# 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:
# remove item
self.cache.pop((item.identifier, item.extras['user_id']))
for item in vms:
if (infrastructure.COMPUTE.location + item['uuid'],
context.user_id) in self.cache:
continue
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'], extras)
result.extend(ent_list)
for item in stors:
if (infrastructure.STORAGE.location + item['id'],
context.user_id) in self.cache:
continue
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'], extras)
result.extend(ent_list)
return result
# 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, extras):
"""
Construct a OCCI compute instance.
First item in result list is entity self!
Adds it to the cache too!
"""
result = []
context = extras['nova_ctx']
instance = vm.get_vm(identifier, context)
# 1. get identifier
iden = infrastructure.COMPUTE.location + identifier
entity = core_model.Resource(iden, infrastructure.COMPUTE,
[os_addon.OS_VM])
result.append(entity)
# 2. os and res templates
flavor_id = instance['instance_type'].flavorid
res_tmp = self.get_category('/' + flavor_id + '/', extras)
if res_tmp:
entity.mixins.append(res_tmp)
os_id = instance['image_ref']
image_id = storage.get_image(os_id, context)['id']
image_tmp = self.get_category('/' + image_id + '/', extras)
if image_tmp:
entity.mixins.append(image_tmp)
# 3. network links & get links from cache!
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)
result.append(link)
for item in net_links['admin']:
link = self._construct_network_link(item, entity, self.adm_net,
extras)
result.append(link)
# core.id and cache it!
entity.attributes['occi.core.id'] = identifier
entity.extras = self.get_extras(extras)
self.cache[(entity.identifier, context.user_id)] = entity
return result
def _update_occi_storage(self, entity, extras):
"""
Update a storage resource instance.
"""
return entity
def _construct_occi_storage(self, identifier, extras):
"""
Construct a OCCI storage instance.
First item in result list is entity self!
Adds it to the cache too!
"""
result = []
context = extras['nova_ctx']
stor = storage.get_storage(identifier, context)
# id, display_name, size, status
iden = infrastructure.STORAGE.location + identifier
entity = core_model.Resource(iden, infrastructure.STORAGE, [])
result.append(entity)
# create links on VM resources
if stor['status'] == 'in-use':
source = self.get_resource(infrastructure.COMPUTE.location +
str(stor['instance_uuid']), extras)
link = core_model.Link(infrastructure.STORAGELINK.location +
str(uuid.uuid4()),
infrastructure.STORAGELINK, [], source,
entity)
link.extras = self.get_extras(extras)
source.links.append(link)
result.append(link)
self.cache[(link.identifier, context.user_id)] = link
# core.id and cache it!
entity.attributes['occi.core.id'] = identifier
entity.extras = self.get_extras(extras)
self.cache[(entity.identifier, context.user_id)] = entity
return result
def _setup_network(self):
"""
Add a public and an admin network interface.
"""
# TODO: read from openstack!
self.pub_net.attributes = {'occi.network.vlan': 'external',
'occi.network.label': 'default',
'occi.network.state': 'active',
'occi.networkinterface.address': '192'
'.168'
'.0.0/24',
'occi.networkinterface.gateway': '192.168'
'.0.1',
'occi.networkinterface.allocation':
'static'}
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.gateway': '10.0.0'
'.1',
'occi.networkinterface.allocation':
'static'}
self.cache[(self.adm_net.identifier, None)] = self.adm_net
self.cache[(self.pub_net.identifier, None)] = self.pub_net
def _construct_network_link(self, net_desc, source, target, extras):
"""
Construct a network link and add to cache!
"""
link = core_model.Link(infrastructure.NETWORKINTERFACE.location +
str(uuid.uuid4()),
infrastructure.NETWORKINTERFACE,
[infrastructure.IPNETWORKINTERFACE], source,
target)
link.attributes = {
'occi.networkinterface.interface': net_desc['interface'],
'occi.networkinterface.mac': net_desc['mac'],
'occi.networkinterface.state': net_desc['state'],
'occi.networkinterface.address': net_desc['address'],
'occi.networkinterface.gateway': net_desc['gateway'],
'occi.networkinterface.allocation': net_desc['allocation']
}
link.extras = self.get_extras(extras)
source.links.append(link)
self.cache[(link.identifier, extras['nova_ctx'].user_id)] = link
return link

View File

@ -1,261 +0,0 @@
# 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.
"""
OCCI WSGI app :-)
"""
# W0613:unused args,R0903:too few pub methods
# pylint: disable=W0613,R0903
import logging
from nova import flags
from nova import wsgi
from nova import db
from nova.image import glance
from nova.compute import instance_types
from nova.openstack.common import cfg
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
from occi import wsgi as occi_wsgi
from occi.extensions import infrastructure
from urllib import quote
LOG = logging.getLogger(__name__)
#Setup options
OCCI_OPTS = [
cfg.IntOpt("occiapi_listen_port",
default=8787,
help="Port OCCI interface will listen on."),
cfg.StrOpt("occi_custom_location_hostname",
default=None,
help="Override OCCI location hostname with custom value")
]
FLAGS = flags.FLAGS
FLAGS.register_opts(OCCI_OPTS)
MIXIN_BACKEND = backend.MixinBackend()
class OCCIApplication(occi_wsgi.Application, wsgi.Application):
"""
Adapter which 'translates' represents a nova WSGI application into and OCCI
WSGI application.
"""
def __init__(self):
"""
Initialize the WSGI OCCI application.
"""
super(OCCIApplication, self).__init__(registry=registry.OCCIRegistry())
self._register_backends()
def _register_backends(self):
"""
Registers the OCCI infrastructure resources to ensure compliance
with GFD184
"""
compute_backend = compute.ComputeBackend()
network_backend = network.NetworkBackend()
networkinterface_backend = network.NetworkInterfaceBackend()
ipnetwork_backend = network.IpNetworkBackend()
ipnetworking_backend = network.IpNetworkInterfaceBackend()
storage_backend = storage.StorageBackend()
storage_link_backend = storage.StorageLinkBackend()
# register kinds with backends
self.register_backend(infrastructure.COMPUTE, compute_backend)
self.register_backend(infrastructure.START, compute_backend)
self.register_backend(infrastructure.STOP, compute_backend)
self.register_backend(infrastructure.RESTART, compute_backend)
self.register_backend(infrastructure.SUSPEND, compute_backend)
self.register_backend(infrastructure.OS_TEMPLATE, MIXIN_BACKEND)
self.register_backend(infrastructure.RESOURCE_TEMPLATE, MIXIN_BACKEND)
self.register_backend(infrastructure.NETWORK, network_backend)
self.register_backend(infrastructure.UP, network_backend)
self.register_backend(infrastructure.DOWN, network_backend)
self.register_backend(infrastructure.NETWORKINTERFACE,
networkinterface_backend)
self.register_backend(infrastructure.IPNETWORK, ipnetwork_backend)
self.register_backend(infrastructure.IPNETWORKINTERFACE,
ipnetworking_backend)
self.register_backend(infrastructure.STORAGE, storage_backend)
self.register_backend(infrastructure.ONLINE, storage_backend)
self.register_backend(infrastructure.OFFLINE, storage_backend)
self.register_backend(infrastructure.BACKUP, storage_backend)
self.register_backend(infrastructure.SNAPSHOT, storage_backend)
self.register_backend(infrastructure.RESIZE, storage_backend)
self.register_backend(infrastructure.STORAGELINK, storage_link_backend)
# add extensions for occi.
self.register_backend(os_addon.SEC_GROUP,
openstack.SecurityGroupBackend())
self.register_backend(os_addon.SEC_RULE,
openstack.SecurityRuleBackend())
self.register_backend(os_addon.OS_VM,
openstack.OsComputeBackend())
self.register_backend(os_addon.OS_CREATE_IMAGE,
openstack.OsComputeBackend())
self.register_backend(os_addon.OS_KEY_PAIR_EXT,
openstack.OsComputeBackend())
self.register_backend(os_addon.OS_CHG_PWD,
openstack.OsComputeBackend())
self.register_backend(os_addon.OS_NET_LINK,
openstack.OsNetLinkBackend())
def __call__(self, environ, response):
"""
This will be called as defined by WSGI.
Deals with incoming requests and outgoing responses
Takes the incoming request, sends it on to the OCCI WSGI application,
which finds the appropriate backend for it and then executes the
request. The backend then is responsible for the return content.
environ -- The environ.
response -- The response.
"""
extras = {'nova_ctx': environ['nova.context']}
# register/refresh openstack images
self._refresh_os_mixins(extras)
# register/refresh openstack instance types (flavours)
self._refresh_resource_mixins(extras)
# register/refresh the openstack security groups as Mixins
self._refresh_security_mixins(extras)
return self._call_occi(environ, response, nova_ctx=extras['nova_ctx'],
registry=self.registry)
def _refresh_os_mixins(self, extras):
"""
Register images as OsTemplate mixins from
information retrieved from glance (shared and user-specific).
"""
template_schema = 'http://schemas.openstack.org/template/os#'
image_service = glance.get_default_image_service()
images = image_service.detail(extras['nova_ctx'])
for img in images:
# If the image is a kernel or ram one
# and we're not to filter them out then register it.
if (((img['container_format'] or img['disk_format']) in ('ari',
'aki'))):
msg = 'Not registering kernel/RAM image.'
LOG.debug(msg)
continue
ctg_term = occify_terms(img['name'])
os_template = os_mixins.OsTemplate(term=ctg_term,
scheme=template_schema,
os_id=img['id'],
related=[infrastructure.
OS_TEMPLATE],
attributes=None,
title='This is an OS ' +
img['name'] + ' VM image',
location='/' + ctg_term + '/')
try:
self.registry.get_backend(os_template, extras)
except AttributeError:
msg = 'Registering an OS image type as: %s' % str(os_template)
LOG.debug(msg)
self.register_backend(os_template, MIXIN_BACKEND)
def _refresh_resource_mixins(self, extras):
"""
Register the flavors as ResourceTemplates to which the user has access.
"""
template_schema = 'http://schemas.openstack.org/template/resource#'
os_flavours = instance_types.get_all_types()
for itype in os_flavours.values():
ctg_term = occify_terms(itype['name'])
resource_template = os_mixins.ResourceTemplate(
term=quote(ctg_term),
flavor_id=itype['flavorid'],
scheme=template_schema,
related=[infrastructure.RESOURCE_TEMPLATE],
title='This is an openstack ' + itype['name'] + ' flavor.',
location='/' + quote(ctg_term) + '/')
try:
self.registry.get_backend(resource_template, extras)
except AttributeError:
msg = 'Registering an OpenStack flavour/instance type: %s' % \
str(resource_template)
LOG.debug(msg)
self.register_backend(resource_template, MIXIN_BACKEND)
def _refresh_security_mixins(self, extras):
"""
Registers security groups as security mixins
"""
# ensures that preexisting openstack security groups are
# added and only once.
# collect these and add them to an exclusion list so they're
# not created again when listing non-user-defined sec. groups
excld_grps = []
for cat in self.registry.get_categories(extras):
if (isinstance(cat, core_model.Mixin) and
os_addon.SEC_GROUP in cat.related):
excld_grps.append(cat.term)
groups = db.security_group_get_by_project(extras['nova_ctx'],
extras['nova_'
'ctx'].project_id)
sec_grp = 'http://schemas.openstack.org/infrastructure/security/group#'
for group in groups:
if group['name'] not in excld_grps:
sec_mix = os_mixins.UserSecurityGroupMixin(
term=str(group["id"]),
scheme=sec_grp,
related=[os_addon.SEC_GROUP],
attributes=None,
title=group['name'],
location='/security/' + quote(str(group['name'])) + '/')
try:
self.registry.get_backend(sec_mix, extras)
except AttributeError:
self.register_backend(sec_mix, MIXIN_BACKEND)
def occify_terms(term_name):
'''
Occifies a term_name so that it is compliant with GFD 185.
'''
term = term_name.strip().replace(' ', '_').replace('.', '-').lower()
return term

View File

@ -1,53 +0,0 @@
#!/bin/sh
rm -rf build/html
mkdir -p build/html
echo '\n PyLint report \n****************************************\n'
pylint -d W0511,I0011,E1101,E0611,F0401 -i y --report no **/*.py
echo '\n Unittest coverage \n****************************************\n'
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=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 tests
echo '\n Issues report \n****************************************\n'
pyflakes occi_os_api
vulture occi_os_api
echo '\n Pychecker report \n****************************************\n'
pychecker -# 99 occi_os_api/*.py occi_os_api/backends/*.py
occi_os_api/nova_glue/*.py occi_os_api/extensions/*.py
# TODO: create project!
#epydoc epydoc.prj
# Fix:
#tmetsch@ubuntu:~/devstack$ cat /etc/tgt/targets.conf
#include /etc/tgt/conf.d/cinder.conf
#
# in devstack/files/horizon_settings:
#HORIZON_CONFIG = {
# #'dashboards': ('nova', 'syspanel', 'settings',),
# 'dashboards': ('project', 'admin', 'settings',),
# 'default_dashboard': 'project',
#}

View File

@ -1,43 +0,0 @@
#!/usr/bin/env python
# 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.
"""Starter script for Nova OCCI API."""
import eventlet
eventlet.monkey_patch(os=False)
import os
import sys
TOPDIR = os.path.normpath(os.path.join(os.path.abspath(
sys.argv[0]), os.pardir, os.pardir))
if os.path.exists(os.path.join(TOPDIR, "nova", "__init__.py")):
sys.path.insert(0, TOPDIR)
from nova import flags
from nova import service
from nova import utils
from nova.openstack.common import log as logging
if __name__ == '__main__':
flags.parse_args(sys.argv)
logging.setup("nova")
utils.monkey_patch()
SERVER = service.WSGIService('occiapi')
service.serve(SERVER)
service.wait()

View File

@ -1,58 +0,0 @@
# 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.
"""
Setupstools script which defines an entry point which can be used for OCCI
app later.
"""
from setuptools import setup
setup(
name='openstackocci-grizzly',
version='1.0',
description='OCCI interface for Openstack (stable/grizzly).',
long_description='''
This is a clone of https://github.com/dizz/nova - it provides a
python egg which can be deployed in OpenStack and will thereby add the
3rd party OCCI interface to OpenStack.
''',
classifiers=[
'Programming Language :: Python',
'Development Status :: 5 - Production/Stable',
'Topic :: Internet :: WWW/HTTP :: WSGI :: Application',
],
keywords='',
author='Intel Performance Learning Solutions Ltd.',
author_email='thijsx.metsch@intel.com',
url='http://intel.com',
license='Apache License, Version 2.0',
include_package_data=True,
packages=['occi_os_api','occi_os_api.backends','occi_os_api.extensions',
'occi_os_api.nova_glue'],
zip_safe=False,
install_requires=[
'setuptools',
],
entry_points='''
[paste.app_factory]
occi_app = occi_os_api:main
''',
)

View File

@ -1,438 +0,0 @@
#!/usr/bin/env python
# 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.
"""
Will test the OS occiosapi against a local running instance.
"""
#pylint: disable=W0102,C0103,R0904
import json
import sys
import time
import httplib
import logging
import unittest
import random
HEADS = {'Content-Type': 'text/occi',
'Accept': 'text/occi'}
KEYSTONE_HOST = '127.0.0.1:5000'
OCCI_HOST = '127.0.0.1:8787'
# Init a simple logger...
logging.basicConfig(level=logging.DEBUG)
CONSOLE = logging.StreamHandler()
CONSOLE.setLevel(logging.DEBUG)
LOG = logging.getLogger()
LOG.addHandler(CONSOLE)
def do_request(verb, url, headers):
"""
Do an HTTP request defined by a HTTP verb, an URN and a dict of headers.
"""
conn = httplib.HTTPConnection(OCCI_HOST)
conn.request(verb, url, None, headers)
response = conn.getresponse()
if response.status not in [200, 201]:
LOG.error(response.reason)
LOG.warn(response.read())
sys.exit(1)
heads = response.getheaders()
result = {}
for item in heads:
if item[0] in ['category', 'link', 'x-occi-attribute',
'x-occi-location', 'location']:
tmp = []
for val in item[1].split(','):
tmp.append(val.strip())
result[item[0]] = tmp
conn.close()
return result
def get_os_token(username, password):
"""
Get a security token from Keystone.
"""
body = '{"auth": {"tenantName": "' + username + '", ' \
'"passwordCredentials":{"username": "' + username + '", ' \
'"password": "' + password + '"}}}'
heads = {'Content-Type': 'application/json'}
conn = httplib.HTTPConnection(KEYSTONE_HOST)
conn.request("POST", "/v2.0/tokens", body, heads)
response = conn.getresponse()
data = response.read()
tokens = json.loads(data)
token = tokens['access']['token']['id']
return token
def get_qi_listing(token):
"""
Retrieve categories from QI.
"""
heads = HEADS.copy()
heads['X-Auth-Token'] = token
result = do_request('GET', '/-/', heads)
LOG.debug(result['category'])
def create_node(token, category_list, attributes=[]):
"""
Create a VM.
"""
heads = HEADS.copy()
heads['X-Auth-Token'] = token
for cat in category_list:
if 'Category' in heads:
heads['Category'] += ', ' + cat
else:
heads['Category'] = cat
for attr in attributes:
if 'X-OCCI-Attribute' in heads:
heads['X-OCCI-Attribute'] += ', ' + attr
else:
heads['X-OCCI-Attribute'] = attr
heads = do_request('POST', '/compute/', heads)
loc = heads['location'][0]
loc = loc[len('http://' + OCCI_HOST):]
LOG.debug('Location is: ' + loc)
return loc
def list_nodes(token, url):
"""
List a bunch of resource.
"""
heads = HEADS.copy()
heads['X-Auth-Token'] = token
heads = do_request('GET', url, heads)
return heads['x-occi-location']
def get_node(token, location):
"""
Retrieve a single resource.
"""
heads = HEADS.copy()
heads['X-Auth-Token'] = token
heads = do_request('GET', location, heads)
return heads
def destroy_node(token, location):
"""
Destroy a single node.
"""
heads = HEADS.copy()
heads['X-Auth-Token'] = token
heads = do_request('DELETE', location, heads)
return heads
def trigger_action(token, url, action_cat, action_param=None):
"""
Trigger an OCCI action.
"""
heads = HEADS.copy()
heads['X-Auth-Token'] = token
heads['Category'] = action_cat
if action_param is not None:
heads['X-OCCI-Attribute'] = action_param
do_request('POST', url, heads)
class SystemTest(unittest.TestCase):
"""
Do a simple set of test.
"""
def setUp(self):
"""
Setup the test.
"""
# Get a security token:
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!
"""
# QI listing
get_qi_listing(self.token)
# create 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#"']
vm_location = create_node(self.token, cats)
# list computes
if 'http://' + OCCI_HOST + vm_location not \
in list_nodes(self.token, '/compute/'):
LOG.error('VM should be listed!')
# wait
cont = False
while not cont:
if 'occi.compute.state="active"' in \
get_node(self.token, vm_location)['x-occi-attribute']:
cont = True
else:
time.sleep(5)
# trigger stop
trigger_action(self.token, vm_location + '?action=stop',
'stop; scheme="http://schemas.ogf.org/occi/'
'infrastructure/compute/action#"')
# wait
cont = False
while not cont:
if 'occi.compute.state="inactive"' in \
get_node(self.token, vm_location)['x-occi-attribute']:
cont = True
else:
time.sleep(5)
# trigger start
trigger_action(self.token, vm_location + '?action=start',
'start; scheme="http://schemas.ogf.org/occi/'
'infrastructure/compute/action#"')
# wait
cont = False
while not cont:
if 'occi.compute.state="active"' in \
get_node(self.token, vm_location)['x-occi-attribute']:
cont = True
else:
time.sleep(5)
# delete
destroy_node(self.token, vm_location)
def test_security_grouping(self):
"""
Test some security and accessibility stuff!
"""
# create sec group
heads = HEADS.copy()
heads['X-Auth-Token'] = self.token
name = 'my_grp' + str(random.randint(1, 999999))
heads['Category'] = name + '; scheme="http://www.mystuff.org/sec#"; ' \
'rel="http://schemas.ogf.org/occi/' \
'infrastructure/security#group"; ' \
'location="/mygroups/"'
do_request('POST', '/-/', heads)
# create sec rule
cats = [name + '; scheme="http://www.mystuff.org/sec#";',
'rule; scheme="http://schemas.openstack'
'.org/occi/infrastructure/network/security#";']
attrs = ['occi.network.security.protocol="tcp"',
'occi.network.security.to="22"',
'occi.network.security.from="22"',
'occi.network.security.range="0.0.0.0/0"']
sec_rule_loc = create_node(self.token, cats, attrs)
# list
LOG.error(list_nodes(self.token, '/mygroups/'))
LOG.debug(do_request('GET', sec_rule_loc, heads))
# FIXME: add VM to sec group - see #22
#heads['X-OCCI-Location'] = vm_location
#print do_request('POST', '/mygroups/', heads)
# 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#"',
name + '; scheme="http://www.mystuff.org/sec#"',
'compute; scheme="http://schemas.ogf'
'.org/occi/infrastructure#"']
vm_location = create_node(self.token, cats)
# wait
cont = False
while not cont:
if 'occi.compute.state="active"' in \
get_node(self.token, vm_location)['x-occi-attribute']:
cont = True
else:
time.sleep(5)
# allocate floating IP
cats = ['networkinterface; scheme="http://schemas.ogf'
'.org/occi/infrastructure#"', 'ipnetworkinterface; '
'scheme="http://schemas.ogf'
'.org/occi/infrastructure/networkinterface#"']
attrs = ['occi.core.source=http://"' + OCCI_HOST + vm_location + '"',
'occi.core.target=http://"' + OCCI_HOST +
'/network/public"']
float_ip_location = create_node(self.token, cats, attrs)
time.sleep(15)
# Deallocate Floating IP to VM
destroy_node(self.token, float_ip_location)
# change pw
LOG.debug(trigger_action(self.token, vm_location + '?action=chg_pwd',
'chg_pwd; scheme="http://schemas.'
'openstack.org/instance/action#"',
'org.openstack.credentials.admin_pwd'
'="new_pass"'))
# clean VM
destroy_node(self.token, vm_location)
# delete rule
destroy_node(self.token, sec_rule_loc)
time.sleep(5)
# FIXME: delete sec group - see #18
heads = HEADS.copy()
heads['X-Auth-Token'] = self.token
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#"']
vm_location = create_node(self.token, cats)
# create volume
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'])
# 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 + '"',
'occi.core.target=http://"' + OCCI_HOST + vol_location + '"',
'occi.storagelink.deviceid="/dev/vdc"']
link_location = create_node(self.token, cats, attrs)
# retrieve link
LOG.debug(get_node(self.token, link_location)['x-occi-attribute'])
time.sleep(30)
# deassociate storage vol - see #15
destroy_node(self.token, link_location)
time.sleep(15)
destroy_node(self.token, vol_location)
# wait
cont = False
while not cont:
if 'occi.compute.state="active"' in \
get_node(self.token, vm_location)['x-occi-attribute']:
cont = True
else:
time.sleep(5)
# Create a Image from an Active VM
LOG.debug(trigger_action(self.token, vm_location + '?action='
'create_image',
'create_image; scheme="http://schemas.'
'openstack.org/instance/action#"',
'org.openstack.snapshot.image_name='
'"awesome_ware"'))
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#"']
vm_location = create_node(self.token, cats)
# wait
cont = False
while not cont:
if 'occi.compute.state="active"' in \
get_node(self.token, vm_location)['x-occi-attribute']:
cont = True
else:
time.sleep(5)
# scale up VM - see #17
heads = HEADS.copy()
heads['X-Auth-Token'] = self.token
heads['Category'] = 'bitsy; scheme="http://schemas.openstack' \
'.org/template/resource#"'
do_request('POST', vm_location, heads)
# wait
cont = False
while not cont:
if 'occi.compute.state="active"' in \
get_node(self.token, vm_location)['x-occi-attribute']:
cont = True
else:
time.sleep(5)
destroy_node(self.token, vm_location)

View File

@ -1,331 +0,0 @@
# 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()

View File

@ -1,200 +0,0 @@
# 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
from occi_os_api.extensions import os_addon
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()
# should have pool name in attribute...
target.identifier = '/network/public'
link = core_model.Link('foo', None, [os_addon.OS_NET_LINK], 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, [os_addon.OS_NET_LINK], source,
target)
link.attributes = {'org.openstack.network.floating.pool': 'nova'}
self.mox.StubOutWithMock(nova_glue.net, 'add_floating_ip')
nova_glue.net.add_floating_ip(mox.IsA(str), mox.IsA(str),
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)
# test without pool name...
self.mox.UnsetStubs()
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(str), mox.IsA(None),
mox.IsA(object)).AndReturn('10.0.0.2')
self.mox.ReplayAll()
self.backend.create(link, self.sec_obj)
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)

View File

@ -1,296 +0,0 @@
# 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()