Retire stackforge/occi-os
This commit is contained in:
parent
a1dea0226c
commit
c1277826db
26
.gitignore
vendored
26
.gitignore
vendored
@ -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/
|
4
Authors
4
Authors
@ -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
176
LICENSE
@ -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.
|
||||
|
72
README.md
72
README.md
@ -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
7
README.rst
Normal 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".
|
||||
|
112
doc/occi.md
112
doc/occi.md
@ -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"'
|
||||
```
|
@ -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()
|
@ -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!
|
||||
"""
|
@ -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)
|
@ -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'])
|
@ -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
|
@ -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'])
|
@ -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.
|
||||
"""
|
@ -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)
|
@ -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
|
@ -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...
|
||||
"""
|
@ -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!')
|
@ -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!')
|
@ -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)
|
@ -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
|
@ -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
|
@ -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
|
53
run_tests.sh
53
run_tests.sh
@ -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',
|
||||
#}
|
43
runme.py
43
runme.py
@ -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()
|
58
setup.py
58
setup.py
@ -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
|
||||
''',
|
||||
)
|
@ -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)
|
@ -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()
|
@ -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)
|
@ -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()
|
Loading…
x
Reference in New Issue
Block a user