Refactored test suite.

* Moves all tests to use a single set of central test data and
    Adds a nifty manager for it.
    Implements blueprint unified-test-data.

  * Cleans up API code and API tests so that we're not testing
    code that has no custom logic, or wrapping objects unnecessarily.
    Implements blueprint api-simplification.

  * Adds lots of docs on testing. It's a good step towards
    blueprint improve-dev-documentation.

  * Improves API exception handling for duplicate objects and containers.
    Fixes bug 930816.

Change-Id: I5526ccf5b38708885ea2d1f06b0d3483c58c4dad
This commit is contained in:
Gabriel Hurley 2012-02-11 18:44:39 -08:00
parent 2e914f9578
commit aca739445e
61 changed files with 2562 additions and 3119 deletions

View File

@ -40,10 +40,28 @@ How to use Horizon in your own projects.
intro
quickstart
topics/branding
Developer Docs
==============
For those wishing to develop Horizon itself, or go in-depth with building
your own :class:`~horizon.Dashboard` or :class:`~horizon.Panel` classes,
the following documentation is provided.
General information
-------------------
Brief guides to areas of interest and importance when developing Horizon.
.. toctree::
:maxdepth: 1
contributing
testing
Topic Guides
============
------------
Information on how to work with specific areas of Horizon can be found in
the following topic guides.
@ -52,25 +70,7 @@ the following topic guides.
:maxdepth: 1
topics/tables
Developer Reference
===================
For those wishing to develop Horizon itself, or go in-depth with building
your own :class:`~horizon.Dashboard` or :class:`~horizon.Panel` classes,
the following documentation is provided.
Topics
------
Brief guides to areas of interest and importance when developing Horizon.
.. toctree::
:maxdepth: 1
branding
contributing
testing
topics/testing
API Reference
-------------
@ -90,6 +90,7 @@ In-depth documentation for Horizon and it's APIs.
ref/context_processors
ref/decorators
ref/exceptions
ref/test
Source Code Reference
---------------------

17
docs/source/ref/test.rst Normal file
View File

@ -0,0 +1,17 @@
========================
Horizon TestCase Classes
========================
.. module:: horizon.test
Horizon provides several useful base classes for testing both views and
API functions.
.. autoclass:: TestCase
:members:
.. autoclass:: APITestCase
:members:
.. autoclass:: BaseAdminViewTests
:members:

View File

@ -1,22 +1,6 @@
..
Copyright 2012 OpenStack, LLC
All Rights Reserved.
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.
===============
Testing Horizon
===============
=======================
Horizon's tests and you
=======================
How to run the tests
====================
@ -41,22 +25,15 @@ To run the tests::
:doc:`ref/run_tests`
Full reference for the ``run_tests.sh`` script.
How to write good tests
=======================
Writing tests
=============
Horizon uses Django's unit test machinery (which extends Python's ``unittest2``
library) as the core of it's test suite. As such, all tests for the Python code
library) as the core of its test suite. As such, all tests for the Python code
should be written as unit tests. No doctests please.
A few pointers for writing good tests:
* Write tests as you go--If you save them to the end you'll write less of
them and they'll often miss large chunks of code.
* Keep it as simple as possible--Make sure each test tests one thing
and tests it thoroughly.
* Think about all the possible inputs your code could have--It's usually
the edge cases that end up revealing bugs.
* Use ``coverage.py`` to find out what code is *not* being tested.
In general new code without unit tests will not be accepted, and every bugfix
*must* include a regression test.
For a much more in-depth discussion of testing, see the :doc:`testing topic
guide </topics/testing>`.

View File

@ -1,6 +1,7 @@
==============================
Change the branding of Horizon
------------------------------
=======================
==============================
Changing the Page Title
=======================
@ -11,7 +12,6 @@ to ``local_settings.py`` with the value being the desired name.
The file ``local_settings.py`` can be found at the Horizon directory path of
``horizon/openstack-dashboard/local/local_settings.py``.
=======================
Changing the Page Logo
=======================

View File

@ -0,0 +1,276 @@
===================
Testing Topic Guide
===================
Having good tests in place is absolutely critical for ensuring a stable,
maintainable codebase. Hopefully that doesn't need any more explanation.
However, what defines a "good" test is not always obvious, and there are
a lot of common pitfalls that can easily shoot your test suite in the
foot.
If you already know everything about testing but are fed up with trying to
debug why a specific test failed, you can skip the intro and jump
stright to :ref:`debugging_unit_tests`.
An overview of testing
======================
There are three main types of tests, each with their associated pros and cons:
Unit tests
----------
These are isolated, stand-alone tests with no external dependencies. They are
written from the a perspective of "knowing the code", and test the assumptions
of the codebase and the developer.
Pros:
* Generally lightweight and fast.
* Can be run anywhere, anytime since they have no external dependencies.
Cons:
* Easy to be lax in writing them, or lazy in constructing them.
* Can't test interactions with live external services.
Functional tests
----------------
These are generally also isolated tests, though sometimes they may interact
with other services running locally. The key difference between functional
tests and unit tests, however, is that functional tests are written from the
perspective of the user (who knows nothing about the code) and only knows
what they put in and what they get back. Essentially this is a higher-level
testing of "does the result match the spec?".
Pros:
* Ensures that your code *always* meets the stated functional requirements.
* Verifies things from an "end user" perspective, which helps to ensure
a high-quality experience.
* Designing your code with a functional testing perspective in mind helps
keep a higher-level viewpoint in mind.
Cons:
* Requires an additional layer of thinking to define functional requirements
in terms of inputs and outputs.
* Often requires writing a separate set of tests and/or using a different
testing framework from your unit tests.
* Don't offer any insight into the quality or status of the underlying code,
only verifies that it works or it doesn't.
Integration Tests
-----------------
This layer of testing involves testing all of the components that your
codebase interacts with or relies on in conjunction. This is equivalent to
"live" testing, but in a repeatable manner.
Pros:
* Catches *many* bugs that unit and functional tests will not.
* Doesn't rely on assumptions about the inputs and outputs.
* Will warn you when changes in external components break your code.
Cons:
* Difficult and time-consuming to create a repeatable test environment.
* Did I mention that setting it up is a pain?
So what should I write?
-----------------------
A few simple guidelines:
#. Every bug fix should have a regression test. Period.
#. When writing a new feature, think about writing unit tests to verify
the behavior step-by-step as you write the feature. Every time you'd
go to run your code by hand and verify it manually, think "could I
write a test to do this instead?". That way when the feature is done
and you're ready to commit it you've already got a whole set of tests
that are more thorough than anything you'd write after the fact.
#. Write tests that hit every view in your application. Even if they
don't assert a single thing about the code, it tells you that your
users aren't getting fatal errors just by interacting with your code.
What makes a good unit test?
============================
Limiting our focus just to unit tests, there are a number of things you can
do to make your unit tests as useful, maintainable, and unburdensome as
possible.
Test data
---------
Use a single, consistent set of test data. Grow it over time, but do everything
you can not to fragment it. It quickly becomes unmaintainable and perniciously
out-of-sync with reality.
Make your test data as accurate to reality as possible. Supply *all* the
attributes of an object, provide objects in all the various states you may want
to test.
If you do the first suggestion above *first* it makes the second one far less
painful. Write once, use everywhere.
To make your life even easier, if your codebase doesn't have a built-in
ORM-like function to manage your test data you can consider buidling (or
borrowing) one yourself. Being able to do simple retrieval queries on your
test data is incredibly valuable.
Mocking
-------
Mocking is the practice of providing stand-ins for objects or pieces of code
you don't need to test. While convenient, they should be used with *extreme*
caution.
Why? Because overuse of mocks can rapidly land you in a situation where you're
not testing any real code. All you've done is verified that your mocking
framework returns what you tell it to. This problem can be very tricky to
recognize, since you may be mocking things in ``setUp`` methods, other modules,
etc.
A good rule of thumb is to mock as close to the source as possible. If you have
a function call that calls an external API in a view , mock out the external
API, not the whole function. If you mock the whole function you've suddenly
lost test coverage for an entire chunk of code *inside* your codebase. Cut the
ties cleanly right where your system ends and the external world begins.
Similarly, don't mock return values when you could construct a real return
value of the correct type with the correct attributes. You're just adding
another point of potential failure by exercising your mocking framework instead
of real code. Following the suggestions for testing above will make this a lot
less burdensome.
Assertions and verification
---------------------------
Think long and hard about what you really want to verify in your unit test. In
particular, think about what custom logic your code executes.
A common pitfall is to take a known test object, pass it through your code,
and then verify the properties of that object on the output. This is all well
and good, except if you're verifying properties that were untouched by your
code. What you want to check are the pieces that were *changed*, *added*, or
*removed*. Don't check the object's id attribute unless you have reason to
suspect it's not the object you started with. But if you added a new attribute
to it, be damn sure you verify that came out right.
It's also very common to avoid testing things you really care about because
it's more difficult. Verifying that the proper messages were displayed to the
user after an action, testing for form errors, making sure exception handling
is tested... these types of things aren't always easy, but they're extremely
necessary.
To that end, Horizon includes several custom assertions to make these tasks
easier. :meth:`~horizon.test.TestCase.assertNoFormErrors`,
:meth:`~horizon.test.TestCase.assertMessageCount`, and
:meth:`~horizon.test.TestCase.asertNoMessages` all exist for exactly these
purposes. Moreover, they provide useful output when things go wrong so you're
not left scratching your head wondering why your view test didn't redirect
as expected when you posted a form.
.. _debugging_unit_tests:
Debugging Unit Tests
====================
Tips and tricks
---------------
#. Use :meth:`~horizon.test.TestCase.assertNoFormErrors` immediately after
your ``client.post`` call for tests that handle form views. This will
immediately fail if your form POST failed due to a validation error and
tell you what the error was.
#. Use :meth:`~horizon.test.TestCase.assertMessageCount` and
:meth:`~horizon.test.TestCase.asertNoMessages` when a piece of code is
failing inexplicably. Since the core error handlers attach user-facing
error messages (and since the core logging is silenced during test runs)
these methods give you the dual benefit of verifying the output you expect
while clearly showing you the problematic error message if they fail.
#. Use Python's ``pdb`` module liberally. Many people don't realize it works
just as well in a test case as it does in a live view. Simply inserting
``import pdb; pdb.set_trace()`` anywhere in your codebase will drop the
interpreter into an interactive shell so you can explore your test
environment and see which of your assumptions about the code isn't,
in fact, flawlessly correct.
Common pitfalls
---------------
There are a number of typical (and non-obvious) ways to break the unit tests.
Some common things to look for:
#. Make sure you stub out the method exactly as it's called in the code
being tested. For example, if your real code calls
``api.keystone.tenant_get``, stubbing out ``api.tenant_get`` (available
for legacy reasons) will fail.
#. When defining the expected input to a stubbed call, make sure the
arguments are *identical*, this includes ``str`` vs. ``int`` differences.
#. Make sure your test data are completely in line with the expected inputs.
Again, ``str`` vs. ``int`` or missing properties on test objects will
kill your tests.
#. Make sure there's nothing amiss in your templates (particularly the
``{% url %}`` tag and its arguments). This often comes up when refactoring
views or renaming context variables. It can easily result in errors that
you might not stumble across while clicking around the development server.
#. Make sure you're not redirecting to views that no longer exist, e.g.
the ``index`` view for a panel that got combined (such as instances &
volumes).
#. Make sure your mock calls are in order before calling ``mox.ReplayAll``.
The order matters.
#. Make sure you repeat any stubbed out method calls that happen more than
once. They don't automatically repeat, you have to explicitly define them.
While this is a nuisance, it makes you acutely aware of how many API
calls are involved in a particular function.
Understanding the output from ``mox``
-------------------------------------
Horizon uses ``mox`` as it's mocking framework of choice, and while it
offers many nice features, its output when a test fails can be quite
mysterious.
Unexpected Method Call
~~~~~~~~~~~~~~~~~~~~~~
This occurs when you stubbed out a piece of code, and it was subsequently
called in a way that you didn't specify it would be. There are two reasons
this tends to come up:
#. You defined the expected call, but a subtle difference crept in. This
may be a string versus integer difference, a string versus unicode
difference, a slightly off date/time, or passing a name instead of an id.
#. The method is actually being called *multiple times*. Since mox uses
a call stack internally, it simply pops off the expected method calls to
verify them. That means once a call is used once, it's gone. An easy way
to see if this is the case is simply to copy and paste your method call a
second time to see if the error changes. If it does, that means your method
is being called more times than you think it is.
Expected Method Never Called
~~~~~~~~~~~~~~~~~~~~~~~~~~~~
This one is the opposite of the unexpected method call. This one means you
tol mox to expect a call and it didn't happen. This is almost always the
result of an error in the conditions of the test. Using the
:meth:`~horizon.test.TestCase.assertNoFormErrors` and
:meth:`~horizon.test.TestCase.assertMessageCount` will make it readily apparent
what the problem is in the majority of cases. If not, then use ``pdb`` and
start interrupting the code flow to see where things are getting off track.

View File

@ -69,7 +69,7 @@ class APIDictWrapper(object):
def __getattr__(self, attr):
try:
return self._apidict[attr]
except KeyError, e:
except KeyError:
msg = 'Unknown attribute "%(attr)s" on APIResource object ' \
'of type "%(cls)s"' % {'attr': attr, 'cls': self.__class__}
LOG.debug(msg)
@ -90,9 +90,10 @@ class APIDictWrapper(object):
def get_service_from_catalog(catalog, service_type):
for service in catalog:
if service['type'] == service_type:
return service
if catalog:
for service in catalog:
if service['type'] == service_type:
return service
return None

View File

@ -25,34 +25,42 @@ import urlparse
from glance import client as glance_client
from horizon.api.base import *
from horizon.api.base import APIDictWrapper, url_for
LOG = logging.getLogger(__name__)
class Image(APIDictWrapper):
"""Simple wrapper around glance image dictionary"""
"""
Wrapper around glance image dictionary to make it object-like and provide
access to image properties.
"""
_attrs = ['checksum', 'container_format', 'created_at', 'deleted',
'deleted_at', 'disk_format', 'id', 'is_public', 'location',
'name', 'properties', 'size', 'status', 'updated_at', 'owner']
def __getattr__(self, attrname):
if attrname == "properties":
return ImageProperties(super(Image, self).__getattr__(attrname))
if not hasattr(self, "_properties"):
properties_dict = super(Image, self).__getattr__(attrname)
self._properties = ImageProperties(properties_dict)
return self._properties
else:
return super(Image, self).__getattr__(attrname)
class ImageProperties(APIDictWrapper):
"""Simple wrapper around glance image properties dictionary"""
"""
Wrapper around glance image properties dictionary to make it object-like.
"""
_attrs = ['architecture', 'image_location', 'image_state', 'kernel_id',
'project_id', 'ramdisk_id', 'image_type']
def glance_api(request):
def glanceclient(request):
o = urlparse.urlparse(url_for(request, 'image'))
LOG.debug('glance_api connection created for host "%s:%d"' %
LOG.debug('glanceclient connection created for host "%s:%d"' %
(o.hostname, o.port))
return glance_client.Client(o.hostname,
o.port,
@ -60,11 +68,11 @@ def glance_api(request):
def image_create(request, image_meta, image_file):
return Image(glance_api(request).add_image(image_meta, image_file))
return Image(glanceclient(request).add_image(image_meta, image_file))
def image_delete(request, image_id):
return glance_api(request).delete_image(image_id)
return glanceclient(request).delete_image(image_id)
def image_get(request, image_id):
@ -72,7 +80,7 @@ def image_get(request, image_id):
Returns the actual image file from Glance for image with
supplied identifier
"""
return glance_api(request).get_image(image_id)[1]
return glanceclient(request).get_image(image_id)[1]
def image_get_meta(request, image_id):
@ -80,16 +88,16 @@ def image_get_meta(request, image_id):
Returns an Image object populated with metadata for image
with supplied identifier.
"""
return Image(glance_api(request).get_image_meta(image_id))
return Image(glanceclient(request).get_image_meta(image_id))
def image_list_detailed(request):
return [Image(i) for i in glance_api(request).get_images_detailed()]
return [Image(i) for i in glanceclient(request).get_images_detailed()]
def image_update(request, image_id, image_meta=None):
image_meta = image_meta and image_meta or {}
return Image(glance_api(request).update_image(image_id,
return Image(glanceclient(request).update_image(image_id,
image_meta=image_meta))
@ -97,5 +105,5 @@ def snapshot_list_detailed(request):
filters = {}
filters['property-image_type'] = 'snapshot'
filters['is_public'] = 'none'
return [Image(i) for i in glance_api(request)
return [Image(i) for i in glanceclient(request)
.get_images_detailed(filters=filters)]

View File

@ -27,7 +27,6 @@ from keystoneclient.v2_0 import client as keystone_client
from keystoneclient.v2_0 import tokens
from horizon import exceptions
from horizon.api import APIResourceWrapper
LOG = logging.getLogger(__name__)
@ -39,21 +38,6 @@ def _get_endpoint_url(request):
getattr(settings, 'OPENSTACK_KEYSTONE_URL'))
class Token(APIResourceWrapper):
"""Simple wrapper around keystoneclient.tokens.Tenant"""
_attrs = ['id', 'user', 'serviceCatalog', 'tenant']
class User(APIResourceWrapper):
"""Simple wrapper around keystoneclient.users.User"""
_attrs = ['email', 'enabled', 'id', 'tenantId', 'name']
class Services(APIResourceWrapper):
_attrs = ['disabled', 'host', 'id', 'last_update', 'stats', 'type', 'up',
'zone']
def keystoneclient(request, username=None, password=None, tenant_id=None,
token_id=None, endpoint=None, endpoint_type=None):
"""Returns a client connected to the Keystone backend.
@ -159,7 +143,7 @@ def token_create(request, tenant, username, password):
token = c.tokens.authenticate(username=username,
password=password,
tenant_id=tenant)
return Token(token)
return token
def token_create_scoped(request, tenant, token):
@ -184,17 +168,19 @@ def token_create_scoped(request, tenant, token):
c.management_url = c.service_catalog.url_for(service_type='identity',
endpoint_type='publicURL')
scoped_token = tokens.Token(tokens.TokenManager, raw_token)
return Token(scoped_token)
return scoped_token
def user_list(request, tenant_id=None):
return [User(u) for u in
keystoneclient(request).users.list(tenant_id=tenant_id)]
return keystoneclient(request).users.list(tenant_id=tenant_id)
def user_create(request, user_id, email, password, tenant_id, enabled):
return User(keystoneclient(request).users.create(
user_id, password, email, tenant_id, enabled))
return keystoneclient(request).users.create(user_id,
password,
email,
tenant_id,
enabled)
def user_delete(request, user_id):
@ -202,25 +188,23 @@ def user_delete(request, user_id):
def user_get(request, user_id):
return User(keystoneclient(request).users.get(user_id))
return keystoneclient(request).users.get(user_id)
def user_update_email(request, user_id, email):
return User(keystoneclient(request).users.update_email(user_id, email))
return keystoneclient(request).users.update_email(user_id, email)
def user_update_enabled(request, user_id, enabled):
return User(keystoneclient(request).users.update_enabled(user_id, enabled))
return keystoneclient(request).users.update_enabled(user_id, enabled)
def user_update_password(request, user_id, password):
return User(keystoneclient(request).users \
.update_password(user_id, password))
return keystoneclient(request).users.update_password(user_id, password)
def user_update_tenant(request, user_id, tenant_id):
return User(keystoneclient(request).users \
.update_tenant(user_id, tenant_id))
return keystoneclient(request).users.update_tenant(user_id, tenant_id)
def role_list(request):

View File

@ -27,7 +27,7 @@ from novaclient.v1_1 import client as nova_client
from novaclient.v1_1 import security_group_rules as nova_rules
from novaclient.v1_1.servers import REBOOT_HARD
from horizon.api.base import *
from horizon.api.base import APIResourceWrapper, APIDictWrapper, url_for
LOG = logging.getLogger(__name__)
@ -38,43 +38,16 @@ INSTANCE_ACTIVE_STATE = 'ACTIVE'
VOLUME_STATE_AVAILABLE = "available"
class Flavor(APIResourceWrapper):
"""Simple wrapper around novaclient.flavors.Flavor"""
_attrs = ['disk', 'id', 'links', 'name', 'ram', 'vcpus']
class FloatingIp(APIResourceWrapper):
"""Simple wrapper for floating ip pools"""
_attrs = ['ip', 'fixed_ip', 'instance_id', 'id', 'pool']
class FloatingIpPool(APIResourceWrapper):
"""Simple wrapper for floating ips"""
_attrs = ['name']
class KeyPair(APIResourceWrapper):
"""Simple wrapper around novaclient.keypairs.Keypair"""
_attrs = ['fingerprint', 'name', 'private_key']
class VirtualInterface(APIResourceWrapper):
_attrs = ['id', 'mac_address']
class Volume(APIResourceWrapper):
"""Nova Volume representation"""
_attrs = ['id', 'status', 'displayName', 'size', 'volumeType', 'createdAt',
'attachments', 'displayDescription']
class VNCConsole(APIDictWrapper):
"""Simple wrapper for floating ips"""
"""
Wrapper for the "console" dictionary returned by the
novaclient.servers.get_vnc_console method.
"""
_attrs = ['url', 'type']
class Quota(object):
""" Basic wrapper for individual limits in a quota."""
""" Wrapper for individual limits in a quota. """
def __init__(self, name, limit):
self.name = name
self.limit = limit
@ -84,7 +57,10 @@ class Quota(object):
class QuotaSet(object):
""" Basic wrapper for quota sets."""
"""
Wrapper for novaclient.quotas.QuotaSet objects which wraps the individual
quotas inside Quota objects.
"""
def __init__(self, apiresource):
self.items = []
for k in apiresource._info.keys():
@ -167,7 +143,10 @@ class Usage(APIResourceWrapper):
class SecurityGroup(APIResourceWrapper):
"""Simple wrapper around novaclient.security_groups.SecurityGroup"""
"""
Wrapper around novaclient.security_groups.SecurityGroup which wraps its
rules in SecurityGroupRule objects and allows access to them.
"""
_attrs = ['id', 'name', 'description', 'tenant_id']
@property
@ -185,7 +164,7 @@ class SecurityGroup(APIResourceWrapper):
class SecurityGroupRule(APIResourceWrapper):
""" Simple wrapper for individual rules in a SecurityGroup. """
""" Wrapper for individual rules in a SecurityGroup. """
_attrs = ['id', 'ip_protocol', 'from_port', 'to_port', 'ip_range']
def __unicode__(self):
@ -207,14 +186,14 @@ def novaclient(request):
return c
def server_vnc_console(request, instance_id, type='novnc'):
def server_vnc_console(request, instance_id, console_type='novnc'):
return VNCConsole(novaclient(request).servers.get_vnc_console(instance_id,
type)['console'])
console_type)['console'])
def flavor_create(request, name, memory, vcpu, disk, flavor_id):
return Flavor(novaclient(request).flavors.create(
name, int(memory), int(vcpu), int(disk), flavor_id))
return novaclient(request).flavors.create(name, int(memory), int(vcpu),
int(disk), flavor_id)
def flavor_delete(request, flavor_id):
@ -222,26 +201,25 @@ def flavor_delete(request, flavor_id):
def flavor_get(request, flavor_id):
return Flavor(novaclient(request).flavors.get(flavor_id))
return novaclient(request).flavors.get(flavor_id)
def flavor_list(request):
return [Flavor(f) for f in novaclient(request).flavors.list()]
return novaclient(request).flavors.list()
def tenant_floating_ip_list(request):
"""
Fetches a list of all floating ips.
"""
return [FloatingIp(ip) for ip in novaclient(request).floating_ips.list()]
return novaclient(request).floating_ips.list()
def floating_ip_pools_list(request):
"""
Fetches a list of all floating ip pools.
"""
return [FloatingIpPool(pool)
for pool in novaclient(request).floating_ip_pools.list()]
return novaclient(request).floating_ip_pools.list()
def tenant_floating_ip_get(request, floating_ip_id):
@ -271,11 +249,11 @@ def snapshot_create(request, instance_id, name):
def keypair_create(request, name):
return KeyPair(novaclient(request).keypairs.create(name))
return novaclient(request).keypairs.create(name)
def keypair_import(request, name, public_key):
return KeyPair(novaclient(request).keypairs.create(name, public_key))
return novaclient(request).keypairs.create(name, public_key)
def keypair_delete(request, keypair_id):
@ -283,7 +261,7 @@ def keypair_delete(request, keypair_id):
def keypair_list(request):
return [KeyPair(key) for key in novaclient(request).keypairs.list()]
return novaclient(request).keypairs.list()
def server_create(request, name, image, flavor, key_name, user_data,
@ -336,9 +314,7 @@ def server_resume(request, instance_id):
novaclient(request).servers.resume(instance_id)
def server_reboot(request,
instance_id,
hardness=REBOOT_HARD):
def server_reboot(request, instance_id, hardness=REBOOT_HARD):
server = server_get(request, instance_id)
server.reboot(hardness)
@ -347,24 +323,22 @@ def server_update(request, instance_id, name):
return novaclient(request).servers.update(instance_id, name=name)
def server_add_floating_ip(request, server, address):
def server_add_floating_ip(request, server, floating_ip):
"""
Associates floating IP to server's fixed IP.
"""
server = novaclient(request).servers.get(server)
fip = novaclient(request).floating_ips.get(address)
return novaclient(request).servers.add_floating_ip(server, fip)
fip = novaclient(request).floating_ips.get(floating_ip)
return novaclient(request).servers.add_floating_ip(server.id, fip.id)
def server_remove_floating_ip(request, server, address):
def server_remove_floating_ip(request, server, floating_ip):
"""
Removes relationship between floating and server's fixed ip.
"""
fip = novaclient(request).floating_ips.get(address)
fip = novaclient(request).floating_ips.get(floating_ip)
server = novaclient(request).servers.get(fip.instance_id)
return novaclient(request).servers.remove_floating_ip(server, fip)
return novaclient(request).servers.remove_floating_ip(server.id, fip.id)
def tenant_quota_get(request, tenant_id):
@ -427,11 +401,11 @@ def virtual_interfaces_list(request, instance_id):
def volume_list(request):
return [Volume(vol) for vol in novaclient(request).volumes.list()]
return novaclient(request).volumes.list()
def volume_get(request, volume_id):
return Volume(novaclient(request).volumes.get(volume_id))
return novaclient(request).volumes.get(volume_id)
def volume_instance_list(request, instance_id):
@ -439,8 +413,9 @@ def volume_instance_list(request, instance_id):
def volume_create(request, size, name, description):
return Volume(novaclient(request).volumes.create(
size, display_name=name, display_description=description))
return novaclient(request).volumes.create(size,
display_name=name,
display_description=description)
def volume_delete(request, volume_id):
@ -448,8 +423,9 @@ def volume_delete(request, volume_id):
def volume_attach(request, volume_id, instance_id, device):
novaclient(request).volumes.create_server_volume(
instance_id, volume_id, device)
novaclient(request).volumes.create_server_volume(instance_id,
volume_id,
device)
def volume_detach(request, instance_id, attachment_id):

View File

@ -22,25 +22,24 @@
from __future__ import absolute_import
import logging
import urlparse
from django.conf import settings
from quantum import client as quantum_client
from horizon.api.base import url_for
from horizon.api import nova
from horizon.api.base import *
LOG = logging.getLogger(__name__)
def quantum_api(request):
if hasattr(request, 'user'):
tenant = request.user.tenant_id
else:
tenant = settings.QUANTUM_TENANT
# FIXME(gabriel): Add object wrappers for Quantum client. The quantum client
# returns plain dicts (a la Glance) which we should wrap.
return quantum_client.Client(settings.QUANTUM_URL, settings.QUANTUM_PORT,
False, tenant, 'json')
def quantum_api(request):
tenant = request.user.tenant_id
url = urlparse.urlparse(url_for(request, 'network'))
return quantum_client.Client(url.hostname, url.port, False, tenant, 'json')
def quantum_list_networks(request):

View File

@ -23,29 +23,15 @@ import logging
import cloudfiles
from django.conf import settings
from horizon.api.base import *
from horizon import exceptions
from horizon.api.base import url_for
LOG = logging.getLogger(__name__)
class Container(APIResourceWrapper):
"""Simple wrapper around cloudfiles.container.Container"""
_attrs = ['name', 'size_used', 'object_count', ]
class SwiftObject(APIResourceWrapper):
_attrs = ['name', 'container', 'size', 'metadata', 'last_modified',
'metadata']
def sync_metadata(self):
self._apiresource.sync_metadata()
class SwiftAuthentication(object):
"""Auth container to pass CloudFiles storage URL and token from
session.
"""
""" Auth container in the format CloudFiles expects. """
def __init__(self, storage_url, auth_token):
self.storage_url = storage_url
self.auth_token = auth_token
@ -55,11 +41,10 @@ class SwiftAuthentication(object):
def swift_api(request):
LOG.debug('object store connection created using token "%s"'
' and url "%s"' %
(request.session['token'], url_for(request, 'object-store')))
auth = SwiftAuthentication(url_for(request, 'object-store'),
request.session['token'])
endpoint = url_for(request, 'object-store')
LOG.debug('Swift connection created using token "%s" and url "%s"'
% (request.session['token'], endpoint))
auth = SwiftAuthentication(endpoint, request.session['token'])
return cloudfiles.get_connection(auth=auth)
@ -83,9 +68,8 @@ def swift_object_exists(request, container_name, object_name):
def swift_get_containers(request, marker=None):
limit = getattr(settings, 'API_RESULT_LIMIT', 1000)
containers = [Container(c) for c in swift_api(request).get_all_containers(
limit=limit + 1,
marker=marker)]
containers = swift_api(request).get_all_containers(limit=limit + 1,
marker=marker)
if(len(containers) > limit):
return (containers[0:-1], True)
else:
@ -94,9 +78,8 @@ def swift_get_containers(request, marker=None):
def swift_create_container(request, name):
if swift_container_exists(request, name):
raise Exception('Container with name %s already exists.' % (name))
return Container(swift_api(request).create_container(name))
raise exceptions.AlreadyExists(name, 'container')
return swift_api(request).create_container(name)
def swift_delete_container(request, name):
@ -106,9 +89,9 @@ def swift_delete_container(request, name):
def swift_get_objects(request, container_name, prefix=None, marker=None):
limit = getattr(settings, 'API_RESULT_LIMIT', 1000)
container = swift_api(request).get_container(container_name)
objects = [SwiftObject(o) for o in
container.get_objects(prefix=prefix, marker=marker,
limit=limit + 1)]
objects = container.get_objects(prefix=prefix,
marker=marker,
limit=limit + 1)
if(len(objects) > limit):
return (objects[0:-1], True)
else:
@ -120,8 +103,7 @@ def swift_copy_object(request, orig_container_name, orig_object_name,
container = swift_api(request).get_container(orig_container_name)
if swift_object_exists(request, new_container_name, new_object_name):
raise Exception('Object with name %s already exists in container %s'
% (new_object_name, new_container_name))
raise exceptions.AlreadyExists(new_object_name, 'object')
orig_obj = container.get_object(orig_object_name)
return orig_obj.copy_to(new_container_name, new_object_name)

View File

@ -25,8 +25,6 @@ import logging
from django.conf import settings
from horizon import api
LOG = logging.getLogger(__name__)

View File

@ -48,16 +48,18 @@ class FloatingIpAssociate(forms.SelfHandlingForm):
label=_("Instance"))
def handle(self, request, data):
ip_id = int(data['floating_ip_id'])
try:
api.server_add_floating_ip(request,
data['instance_id'],
data['floating_ip_id'])
ip_id)
LOG.info('Associating Floating IP "%s" with Instance "%s"'
% (data['floating_ip'], data['instance_id']))
messages.info(request, _('Successfully associated Floating IP \
%(ip)s with Instance: %(inst)s'
% {"ip": data['floating_ip'],
"inst": data['instance_id']}))
messages.success(request,
_('Successfully associated Floating IP %(ip)s '
'with Instance: %(inst)s')
% {"ip": data['floating_ip'],
"inst": data['instance_id']})
except novaclient_exceptions.ClientException, e:
LOG.exception("ClientException in FloatingIpAssociate")
messages.error(request, _('Error associating Floating IP: %s') % e)

View File

@ -20,9 +20,9 @@ import logging
from django import shortcuts
from django.contrib import messages
from django.utils.translation import ugettext as _
from novaclient import exceptions as novaclient_exceptions
from horizon import api
from horizon import exceptions
from horizon import tables
@ -76,13 +76,12 @@ class DisassociateIP(tables.Action):
fip = table.get_object_by_id(int(obj_id))
api.server_remove_floating_ip(request, fip.instance_id, fip.id)
LOG.info('Disassociating Floating IP "%s".' % obj_id)
messages.info(request,
_('Successfully disassociated Floating IP: %s')
% obj_id)
except novaclient_exceptions.ClientException, e:
LOG.exception("ClientException in FloatingIpAssociate")
messages.error(request, _('Error disassociating Floating IP: %s')
% e.message)
messages.success(request,
_('Successfully disassociated Floating IP: %s')
% obj_id)
except:
exceptions.handle(request,
_('Unable to disassociate floating IP.'))
return shortcuts.redirect('horizon:nova:access_and_security:index')

View File

@ -29,165 +29,129 @@ from horizon import test
INDEX_URL = reverse('horizon:nova:access_and_security:index')
NAMESPACE = "horizon:nova:access_and_security:floating_ips"
class FloatingIpViewTests(test.BaseViewTests):
def setUp(self):
super(FloatingIpViewTests, self).setUp()
keypair = api.KeyPair(None)
keypair.name = 'keyName'
self.keypairs = (keypair,)
server = api.Server(None, self.request)
server.id = 1
server.name = 'serverName'
self.server = server
self.servers = (server, )
floating_ip = api.FloatingIp(None)
floating_ip.id = 1
floating_ip.fixed_ip = '10.0.0.4'
floating_ip.instance_id = 1
floating_ip.ip = '58.58.58.58'
self.floating_ip = floating_ip
self.floating_ips = [floating_ip, ]
security_group = api.SecurityGroup(None)
security_group.id = '1'
security_group.name = 'default'
self.security_groups = (security_group,)
class FloatingIpViewTests(test.TestCase):
def test_associate(self):
floating_ip = self.floating_ips.first()
self.mox.StubOutWithMock(api, 'server_list')
api.server_list = self.mox.CreateMockAnything()
api.server_list(IsA(http.HttpRequest)).AndReturn(self.servers)
self.mox.StubOutWithMock(api, 'tenant_floating_ip_get')
api.tenant_floating_ip_get = self.mox.CreateMockAnything()
api.tenant_floating_ip_get(IsA(http.HttpRequest), str(1)).\
AndReturn(self.floating_ip)
api.server_list(IsA(http.HttpRequest)).AndReturn(self.servers.list())
api.tenant_floating_ip_get(IsA(http.HttpRequest),
floating_ip.id).AndReturn(floating_ip)
self.mox.ReplayAll()
res = self.client.get(
reverse('horizon:nova:access_and_security:floating_ips:associate',
args=[1]))
url = reverse('%s:associate' % NAMESPACE, args=[floating_ip.id])
res = self.client.get(url)
self.assertTemplateUsed(res,
'nova/access_and_security/floating_ips/associate.html')
def test_associate_post(self):
floating_ip = self.floating_ips.first()
server = self.servers.first()
self.mox.StubOutWithMock(api, 'server_list')
api.server_list = self.mox.CreateMockAnything()
api.server_list(IsA(http.HttpRequest)).AndReturn(self.servers)
self.mox.StubOutWithMock(api, 'tenant_floating_ip_list')
api.tenant_floating_ip_list(IsA(http.HttpRequest)).\
AndReturn(self.floating_ips)
self.mox.StubOutWithMock(api, 'server_add_floating_ip')
api.server_add_floating_ip = self.mox.CreateMockAnything()
api.server_add_floating_ip(IsA(http.HttpRequest), IsA(unicode),
IsA(unicode)).\
AndReturn(None)
self.mox.StubOutWithMock(api, 'tenant_floating_ip_get')
api.tenant_floating_ip_get = self.mox.CreateMockAnything()
api.tenant_floating_ip_get(IsA(http.HttpRequest), str(1)).\
AndReturn(self.floating_ip)
api.server_list(IsA(http.HttpRequest)).AndReturn(self.servers.list())
api.tenant_floating_ip_list(IsA(http.HttpRequest)) \
.AndReturn(self.floating_ips.list())
api.server_add_floating_ip(IsA(http.HttpRequest),
server.id,
floating_ip.id)
api.tenant_floating_ip_get(IsA(http.HttpRequest),
floating_ip.id).AndReturn(floating_ip)
self.mox.ReplayAll()
res = self.client.post(
reverse('horizon:nova:access_and_security:floating_ips:associate',
args=[1]),
{'instance_id': 1,
'floating_ip_id': self.floating_ip.id,
'floating_ip': self.floating_ip.ip,
'method': 'FloatingIpAssociate'})
form_data = {'instance_id': server.id,
'floating_ip_id': floating_ip.id,
'floating_ip': floating_ip.ip,
'method': 'FloatingIpAssociate'}
url = reverse('%s:associate' % NAMESPACE, args=[floating_ip.id])
res = self.client.post(url, form_data)
self.assertRedirects(res, INDEX_URL)
def test_associate_post_with_exception(self):
floating_ip = self.floating_ips.first()
server = self.servers.first()
self.mox.StubOutWithMock(api, 'server_list')
api.server_list = self.mox.CreateMockAnything()
api.server_list(IsA(http.HttpRequest)).AndReturn(self.servers)
self.mox.StubOutWithMock(api, 'tenant_floating_ip_list')
api.tenant_floating_ip_list(IsA(http.HttpRequest)).\
AndReturn(self.floating_ips)
self.mox.StubOutWithMock(api, 'security_group_list')
api.security_group_list(IsA(http.HttpRequest)).\
AndReturn(self.security_groups)
self.mox.StubOutWithMock(api.nova, 'keypair_list')
api.nova.keypair_list(IsA(http.HttpRequest)).AndReturn(self.keypairs)
self.mox.StubOutWithMock(api, 'server_add_floating_ip')
api.server_add_floating_ip = self.mox.CreateMockAnything()
exception = novaclient_exceptions.ClientException('ClientException',
message='clientException')
api.server_add_floating_ip(IsA(http.HttpRequest), IsA(unicode),
IsA(unicode)).\
AndRaise(exception)
self.mox.StubOutWithMock(api, 'tenant_floating_ip_get')
api.tenant_floating_ip_get = self.mox.CreateMockAnything()
api.tenant_floating_ip_get(IsA(http.HttpRequest), IsA(unicode)).\
AndReturn(self.floating_ip)
api.server_list(IsA(http.HttpRequest)).AndReturn(self.servers.list())
api.tenant_floating_ip_list(IsA(http.HttpRequest)) \
.AndReturn(self.floating_ips.list())
api.security_group_list(IsA(http.HttpRequest)) \
.AndReturn(self.security_groups.list())
api.nova.keypair_list(IsA(http.HttpRequest)) \
.AndReturn(self.keypairs.list())
exc = novaclient_exceptions.ClientException('ClientException')
api.server_add_floating_ip(IsA(http.HttpRequest),
server.id,
floating_ip.id).AndRaise(exc)
api.tenant_floating_ip_get(IsA(http.HttpRequest),
floating_ip.id).AndReturn(floating_ip)
self.mox.ReplayAll()
res = self.client.post(reverse(
'horizon:nova:access_and_security:floating_ips:associate',
args=[1]),
url = reverse('%s:associate' % NAMESPACE, args=[floating_ip.id])
res = self.client.post(url,
{'instance_id': 1,
'floating_ip_id': self.floating_ip.id,
'floating_ip': self.floating_ip.ip,
'floating_ip_id': floating_ip.id,
'floating_ip': floating_ip.ip,
'method': 'FloatingIpAssociate'})
self.assertRaises(novaclient_exceptions.ClientException)
self.assertRedirects(res, INDEX_URL)
def test_disassociate_post(self):
floating_ip = self.floating_ips.first()
server = self.servers.first()
self.mox.StubOutWithMock(api.nova, 'keypair_list')
self.mox.StubOutWithMock(api, 'security_group_list')
self.mox.StubOutWithMock(api, 'tenant_floating_ip_list')
self.mox.StubOutWithMock(api, 'tenant_floating_ip_get')
self.mox.StubOutWithMock(api, 'server_remove_floating_ip')
api.nova.keypair_list(IsA(http.HttpRequest)).AndReturn(self.keypairs)
api.security_group_list(IsA(http.HttpRequest)).\
AndReturn(self.security_groups)
api.tenant_floating_ip_list(IsA(http.HttpRequest)).\
AndReturn(self.floating_ips)
api.server_remove_floating_ip(IsA(http.HttpRequest), IsA(int),
IsA(int)).\
AndReturn(None)
api.nova.keypair_list(IsA(http.HttpRequest)) \
.AndReturn(self.keypairs.list())
api.security_group_list(IsA(http.HttpRequest)) \
.AndReturn(self.security_groups.list())
api.tenant_floating_ip_list(IsA(http.HttpRequest)) \
.AndReturn(self.floating_ips.list())
api.server_remove_floating_ip(IsA(http.HttpRequest),
server.id,
floating_ip.id)
self.mox.ReplayAll()
action = "floating_ips__disassociate__%s" % self.floating_ip.id
action = "floating_ips__disassociate__%s" % floating_ip.id
res = self.client.post(INDEX_URL, {"action": action})
self.assertMessageCount(success=1)
self.assertRedirectsNoFollow(res, INDEX_URL)
def test_disassociate_post_with_exception(self):
floating_ip = self.floating_ips.first()
server = self.servers.first()
self.mox.StubOutWithMock(api.nova, 'keypair_list')
self.mox.StubOutWithMock(api, 'security_group_list')
self.mox.StubOutWithMock(api, 'tenant_floating_ip_list')
self.mox.StubOutWithMock(api, 'tenant_floating_ip_get')
self.mox.StubOutWithMock(api, 'server_remove_floating_ip')
api.nova.keypair_list(IsA(http.HttpRequest)) \
.AndReturn(self.keypairs.list())
api.security_group_list(IsA(http.HttpRequest)) \
.AndReturn(self.security_groups.list())
api.tenant_floating_ip_list(IsA(http.HttpRequest)) \
.AndReturn(self.floating_ips.list())
api.nova.keypair_list(IsA(http.HttpRequest)).AndReturn(self.keypairs)
api.security_group_list(IsA(http.HttpRequest)).\
AndReturn(self.security_groups)
api.tenant_floating_ip_list(IsA(http.HttpRequest)).\
AndReturn(self.floating_ips)
exception = novaclient_exceptions.ClientException('ClientException',
message='clientException')
exc = novaclient_exceptions.ClientException('ClientException')
api.server_remove_floating_ip(IsA(http.HttpRequest),
IsA(int),
IsA(int)).AndRaise(exception)
server.id,
floating_ip.id).AndRaise(exc)
self.mox.ReplayAll()
action = "floating_ips__disassociate__%s" % self.floating_ip.id
action = "floating_ips__disassociate__%s" % floating_ip.id
res = self.client.post(INDEX_URL, {"action": action})
self.assertRaises(novaclient_exceptions.ClientException)
self.assertRedirectsNoFollow(res, INDEX_URL)

View File

@ -24,11 +24,11 @@ Views for managing Nova floating IPs.
"""
import logging
from django import http
from django.contrib import messages
from django.core.urlresolvers import reverse
from django.utils.translation import ugettext as _
from horizon import api
from horizon import exceptions
from horizon import forms
from .forms import FloatingIpAssociate, FloatingIpAllocate
@ -42,19 +42,24 @@ class AssociateView(forms.ModalFormView):
context_object_name = 'floating_ip'
def get_object(self, *args, **kwargs):
ip_id = kwargs['ip_id']
ip_id = int(kwargs['ip_id'])
try:
return api.tenant_floating_ip_get(self.request, ip_id)
except Exception as e:
LOG.exception('Error fetching floating ip with id "%s".' % ip_id)
messages.error(self.request,
_('Unable to associate floating ip: %s') % e)
raise http.Http404("Floating IP %s not available." % ip_id)
except:
redirect = reverse('horizon:nova:access_and_security:index')
exceptions.handle(self.request,
_('Unable to associate floating IP.'),
redirect=redirect)
def get_initial(self):
instances = [(server.id, 'id: %s, name: %s' %
(server.id, server.name))
for server in api.server_list(self.request)]
try:
servers = api.server_list(self.request)
except:
redirect = reverse('horizon:nova:access_and_security:index')
exceptions.handle(self.request,
_('Unable to retrieve instance list.'),
redirect=redirect)
instances = [(server.id, server.name) for server in servers]
return {'floating_ip_id': self.object.id,
'floating_ip': self.object.ip,
'instances': instances}
@ -71,6 +76,6 @@ class AllocateView(forms.ModalFormView):
pool_list = [(pool.name, pool.name)
for pool in api.floating_ip_pools_list(self.request)]
else:
pool_list = [(None, _("There are no Floating IP Pools"))]
pool_list = [(None, _("No floating IP pools available."))]
return {'tenant_id': self.request.user.tenant_id,
'pool_list': pool_list}

View File

@ -30,97 +30,67 @@ from horizon import test
INDEX_VIEW_URL = reverse('horizon:nova:access_and_security:index')
class KeyPairViewTests(test.BaseViewTests):
def setUp(self):
super(KeyPairViewTests, self).setUp()
keypair = api.KeyPair(None)
keypair.name = 'keyName'
self.keypairs = (keypair,)
class KeyPairViewTests(test.TestCase):
def test_delete_keypair(self):
KEYPAIR_ID = self.keypairs[0].name
formData = {'action': 'keypairs__delete__%s' % KEYPAIR_ID}
keypair = self.keypairs.first()
self.mox.StubOutWithMock(api.nova, 'keypair_list')
self.mox.StubOutWithMock(api.nova, 'keypair_delete')
api.nova.keypair_list(IsA(http.HttpRequest)).AndReturn(self.keypairs)
api.nova.keypair_delete(IsA(http.HttpRequest), unicode(KEYPAIR_ID))
api.nova.keypair_list(IsA(http.HttpRequest)) \
.AndReturn(self.keypairs.list())
api.nova.keypair_delete(IsA(http.HttpRequest), keypair.name)
self.mox.ReplayAll()
formData = {'action': 'keypairs__delete__%s' % keypair.name}
res = self.client.post(INDEX_VIEW_URL, formData)
self.assertRedirectsNoFollow(res, INDEX_VIEW_URL)
def test_delete_keypair_exception(self):
KEYPAIR_ID = self.keypairs[0].name
formData = {'action': 'keypairs__delete__%s' % KEYPAIR_ID}
keypair = self.keypairs.first()
self.mox.StubOutWithMock(api.nova, 'keypair_list')
self.mox.StubOutWithMock(api.nova, 'keypair_delete')
api.nova.keypair_list(IsA(http.HttpRequest)).AndReturn(self.keypairs)
exception = novaclient_exceptions.ClientException('clientException',
message='clientException')
api.nova.keypair_delete(IsA(http.HttpRequest), unicode(KEYPAIR_ID)) \
.AndRaise(exception)
api.nova.keypair_list(IsA(http.HttpRequest)) \
.AndReturn(self.keypairs.list())
exc = novaclient_exceptions.ClientException('clientException')
api.nova.keypair_delete(IsA(http.HttpRequest), keypair.name) \
.AndRaise(exc)
self.mox.ReplayAll()
formData = {'action': 'keypairs__delete__%s' % keypair.name}
res = self.client.post(INDEX_VIEW_URL, formData)
self.assertRedirectsNoFollow(res, INDEX_VIEW_URL)
def test_create_keypair_get(self):
res = self.client.get(
reverse('horizon:nova:access_and_security:keypairs:create'))
self.assertTemplateUsed(res,
'nova/access_and_security/keypairs/create.html')
def test_create_keypair_post(self):
KEYPAIR_NAME = 'newKeypair'
PRIVATE_KEY = 'privateKey'
newKeyPair = self.mox.CreateMock(api.KeyPair)
newKeyPair.name = KEYPAIR_NAME
newKeyPair.private_key = PRIVATE_KEY
formData = {'method': 'CreateKeypair',
'name': KEYPAIR_NAME,
}
keypair = self.keypairs.first()
keypair.private_key = "secret"
self.mox.StubOutWithMock(api, 'keypair_create')
api.keypair_create(IsA(http.HttpRequest),
KEYPAIR_NAME).AndReturn(newKeyPair)
keypair.name).AndReturn(keypair)
self.mox.ReplayAll()
res = self.client.post(
reverse('horizon:nova:access_and_security:keypairs:create'),
formData)
formData = {'method': 'CreateKeypair',
'name': keypair.name}
url = reverse('horizon:nova:access_and_security:keypairs:create')
res = self.client.post(url, formData)
self.assertTrue(res.has_header('Content-Disposition'))
def test_create_keypair_exception(self):
KEYPAIR_NAME = 'newKeypair'
formData = {'method': 'CreateKeypair',
'name': KEYPAIR_NAME,
}
exception = novaclient_exceptions.ClientException('clientException',
message='clientException')
keypair = self.keypairs.first()
exc = novaclient_exceptions.ClientException('clientException')
self.mox.StubOutWithMock(api, 'keypair_create')
api.keypair_create(IsA(http.HttpRequest),
KEYPAIR_NAME).AndRaise(exception)
api.keypair_create(IsA(http.HttpRequest), keypair.name).AndRaise(exc)
self.mox.ReplayAll()
res = self.client.post(
reverse('horizon:nova:access_and_security:keypairs:create'),
formData)
formData = {'method': 'CreateKeypair',
'name': keypair.name}
url = reverse('horizon:nova:access_and_security:keypairs:create')
res = self.client.post(url, formData)
self.assertRedirectsNoFollow(res,
reverse('horizon:nova:access_and_security:keypairs:create'))
self.assertRedirectsNoFollow(res, url)

View File

@ -27,6 +27,7 @@ from django.utils.translation import ugettext as _
from novaclient import exceptions as novaclient_exceptions
from horizon import api
from horizon import exceptions
from horizon import forms
@ -40,20 +41,15 @@ class CreateGroup(forms.SelfHandlingForm):
def handle(self, request, data):
try:
LOG.info('Add security_group: "%s"' % data)
api.security_group_create(request,
data['name'],
data['description'])
messages.success(request,
_('Successfully created security_group: %s')
% data['name'])
return shortcuts.redirect(
'horizon:nova:access_and_security:index')
except novaclient_exceptions.ClientException, e:
LOG.exception("ClientException in CreateGroup")
messages.error(request, _('Error creating security group: %s') %
e.message)
except:
exceptions.handle(request, _('Unable to create security group.'))
return shortcuts.redirect('horizon:nova:access_and_security:index')
class AddRule(forms.SelfHandlingForm):
@ -66,7 +62,7 @@ class AddRule(forms.SelfHandlingForm):
# TODO (anthony) source group support
# group_id = forms.CharField()
security_group_id = forms.CharField(widget=forms.HiddenInput())
security_group_id = forms.IntegerField(widget=forms.HiddenInput())
tenant_id = forms.CharField(widget=forms.HiddenInput())
def handle(self, request, data):

View File

@ -22,257 +22,195 @@ from django import http
from django.conf import settings
from django.core.urlresolvers import reverse
from novaclient import exceptions as novaclient_exceptions
from novaclient.v1_1 import security_group_rules as nova_rules
from mox import IsA
from horizon import api
from horizon import test
from .tables import SecurityGroupsTable, RulesTable
SECGROUP_ID = '2'
INDEX_URL = reverse('horizon:nova:access_and_security:index')
SG_CREATE_URL = \
reverse('horizon:nova:access_and_security:security_groups:create')
SG_EDIT_RULE_URL = \
reverse('horizon:nova:access_and_security:security_groups:edit_rules',
args=[SECGROUP_ID])
def strip_absolute_base(uri):
return uri.split(settings.TESTSERVER, 1)[-1]
class SecurityGroupsViewTests(test.BaseViewTests):
class SecurityGroupsViewTests(test.TestCase):
def setUp(self):
super(SecurityGroupsViewTests, self).setUp()
sg1 = api.SecurityGroup(None)
sg1.id = 1
sg1.name = 'default'
sg2 = api.SecurityGroup(None)
sg2.id = 2
sg2.name = 'group_2'
rule = {'id': 1,
'ip_protocol': u"tcp",
'from_port': "80",
'to_port': "80",
'parent_group_id': "2",
'ip_range': {'cidr': "0.0.0.0/32"}}
manager = nova_rules.SecurityGroupRuleManager
rule_obj = nova_rules.SecurityGroupRule(manager, rule)
self.rules = [rule_obj]
sg1.rules = self.rules
sg2.rules = self.rules
self.security_groups = (sg1, sg2)
sec_group = self.security_groups.first()
self.edit_url = reverse('horizon:nova:access_and_security:'
'security_groups:edit_rules',
args=[sec_group.id])
def test_create_security_groups_get(self):
res = self.client.get(SG_CREATE_URL)
self.assertTemplateUsed(res,
'nova/access_and_security/security_groups/create.html')
def test_create_security_groups_post(self):
SECGROUP_NAME = 'fakegroup'
SECGROUP_DESC = 'fakegroup_desc'
new_group = self.mox.CreateMock(api.SecurityGroup)
new_group.name = SECGROUP_NAME
formData = {'method': 'CreateGroup',
'tenant_id': self.TEST_TENANT,
'name': SECGROUP_NAME,
'description': SECGROUP_DESC,
}
sec_group = self.security_groups.first()
self.mox.StubOutWithMock(api, 'security_group_create')
api.security_group_create(IsA(http.HttpRequest),
SECGROUP_NAME, SECGROUP_DESC).AndReturn(new_group)
sec_group.name,
sec_group.description).AndReturn(sec_group)
self.mox.ReplayAll()
formData = {'method': 'CreateGroup',
'tenant_id': self.tenant.id,
'name': sec_group.name,
'description': sec_group.description}
res = self.client.post(SG_CREATE_URL, formData)
self.assertRedirectsNoFollow(res, INDEX_URL)
def test_create_security_groups_post_exception(self):
SECGROUP_NAME = 'fakegroup'
SECGROUP_DESC = 'fakegroup_desc'
exception = novaclient_exceptions.ClientException('ClientException',
message='ClientException')
sec_group = self.security_groups.first()
self.mox.StubOutWithMock(api, 'security_group_create')
exc = novaclient_exceptions.ClientException('ClientException')
api.security_group_create(IsA(http.HttpRequest),
sec_group.name,
sec_group.description).AndRaise(exc)
self.mox.ReplayAll()
formData = {'method': 'CreateGroup',
'tenant_id': self.TEST_TENANT,
'name': SECGROUP_NAME,
'description': SECGROUP_DESC,
}
self.mox.StubOutWithMock(api, 'security_group_create')
api.security_group_create(IsA(http.HttpRequest),
SECGROUP_NAME, SECGROUP_DESC).AndRaise(exception)
self.mox.ReplayAll()
'tenant_id': self.tenant.id,
'name': sec_group.name,
'description': sec_group.description}
res = self.client.post(SG_CREATE_URL, formData)
self.assertTemplateUsed(res,
'nova/access_and_security/security_groups/create.html')
self.assertMessageCount(error=1)
self.assertRedirectsNoFollow(res, INDEX_URL)
def test_edit_rules_get(self):
sec_group = self.security_groups.first()
self.mox.StubOutWithMock(api, 'security_group_get')
api.security_group_get(IsA(http.HttpRequest), SECGROUP_ID).AndReturn(
self.security_groups[1])
api.security_group_get(IsA(http.HttpRequest),
sec_group.id).AndReturn(sec_group)
self.mox.ReplayAll()
res = self.client.get(SG_EDIT_RULE_URL)
res = self.client.get(self.edit_url)
self.assertTemplateUsed(res,
'nova/access_and_security/security_groups/edit_rules.html')
self.assertItemsEqual(res.context['security_group'].name,
self.security_groups[1].name)
sec_group.name)
def test_edit_rules_get_exception(self):
exception = novaclient_exceptions.ClientException('ClientException',
message='ClientException')
sec_group = self.security_groups.first()
self.mox.StubOutWithMock(api, 'security_group_get')
api.security_group_get(IsA(http.HttpRequest), SECGROUP_ID) \
.AndRaise(exception)
exc = novaclient_exceptions.ClientException('ClientException')
api.security_group_get(IsA(http.HttpRequest),
sec_group.id).AndRaise(exc)
self.mox.ReplayAll()
res = self.client.get(SG_EDIT_RULE_URL)
res = self.client.get(self.edit_url)
self.assertRedirects(res, INDEX_URL)
def test_edit_rules_add_rule(self):
RULE_ID = '1'
FROM_PORT = '-1'
TO_PORT = '-1'
IP_PROTOCOL = 'icmp'
CIDR = '0.0.0.0/0'
new_rule = self.mox.CreateMock(api.SecurityGroup)
new_rule.from_port = FROM_PORT
new_rule.to_port = TO_PORT
new_rule.ip_protocol = IP_PROTOCOL
new_rule.cidr = CIDR
new_rule.security_group_id = SECGROUP_ID
new_rule.id = RULE_ID
formData = {'method': 'AddRule',
'tenant_id': self.TEST_TENANT,
'security_group_id': SECGROUP_ID,
'from_port': FROM_PORT,
'to_port': TO_PORT,
'ip_protocol': IP_PROTOCOL,
'cidr': CIDR}
sec_group = self.security_groups.first()
rule = self.security_group_rules.first()
self.mox.StubOutWithMock(api, 'security_group_rule_create')
api.security_group_rule_create(IsA(http.HttpRequest),
SECGROUP_ID, IP_PROTOCOL, FROM_PORT, TO_PORT, CIDR)\
.AndReturn(new_rule)
sec_group.id,
rule.ip_protocol,
rule.from_port,
rule.to_port,
rule.ip_range['cidr']).AndReturn(rule)
self.mox.ReplayAll()
res = self.client.post(SG_EDIT_RULE_URL, formData)
formData = {'method': 'AddRule',
'tenant_id': self.tenant.id,
'security_group_id': sec_group.id,
'from_port': rule.from_port,
'to_port': rule.to_port,
'ip_protocol': rule.ip_protocol,
'cidr': rule.ip_range['cidr']}
res = self.client.post(self.edit_url, formData)
self.assertRedirectsNoFollow(res, INDEX_URL)
def test_edit_rules_add_rule_exception(self):
exception = novaclient_exceptions.ClientException('ClientException',
message='ClientException')
FROM_PORT = '-1'
TO_PORT = '-1'
IP_PROTOCOL = 'icmp'
CIDR = '0.0.0.0/0'
formData = {'method': 'AddRule',
'tenant_id': self.TEST_TENANT,
'security_group_id': SECGROUP_ID,
'from_port': FROM_PORT,
'to_port': TO_PORT,
'ip_protocol': IP_PROTOCOL,
'cidr': CIDR}
sec_group = self.security_groups.first()
rule = self.security_group_rules.first()
exc = novaclient_exceptions.ClientException('ClientException')
self.mox.StubOutWithMock(api, 'security_group_rule_create')
api.security_group_rule_create(IsA(http.HttpRequest),
SECGROUP_ID, IP_PROTOCOL, FROM_PORT,
TO_PORT, CIDR).AndRaise(exception)
sec_group.id,
rule.ip_protocol,
rule.from_port,
rule.to_port,
rule.ip_range['cidr']).AndRaise(exc)
self.mox.ReplayAll()
res = self.client.post(SG_EDIT_RULE_URL, formData)
formData = {'method': 'AddRule',
'tenant_id': self.tenant.id,
'security_group_id': sec_group.id,
'from_port': rule.from_port,
'to_port': rule.to_port,
'ip_protocol': rule.ip_protocol,
'cidr': rule.ip_range['cidr']}
res = self.client.post(self.edit_url, formData)
self.assertRedirectsNoFollow(res, INDEX_URL)
def test_edit_rules_delete_rule(self):
RULE_ID = 1
sec_group = self.security_groups.first()
rule = self.security_group_rules.first()
self.mox.StubOutWithMock(api, 'security_group_rule_delete')
api.security_group_rule_delete(IsA(http.HttpRequest), RULE_ID)
api.security_group_rule_delete(IsA(http.HttpRequest), rule.id)
self.mox.ReplayAll()
form_data = {"action": "rules__delete__%s" % RULE_ID}
req = self.factory.post(SG_EDIT_RULE_URL, form_data)
table = RulesTable(req, self.rules)
form_data = {"action": "rules__delete__%s" % rule.id}
req = self.factory.post(self.edit_url, form_data)
table = RulesTable(req, sec_group.rules)
handled = table.maybe_handle()
self.assertEqual(strip_absolute_base(handled['location']),
INDEX_URL)
self.assertEqual(strip_absolute_base(handled['location']), INDEX_URL)
def test_edit_rules_delete_rule_exception(self):
RULE_ID = 1
rule = self.security_group_rules.first()
self.mox.StubOutWithMock(api, 'security_group_rule_delete')
exception = novaclient_exceptions.ClientException('ClientException',
message='ClientException')
api.security_group_rule_delete(IsA(http.HttpRequest), RULE_ID) \
.AndRaise(exception)
exc = novaclient_exceptions.ClientException('ClientException')
api.security_group_rule_delete(IsA(http.HttpRequest),
rule.id).AndRaise(exc)
self.mox.ReplayAll()
form_data = {"action": "rules__delete__%s" % RULE_ID}
req = self.factory.post(SG_EDIT_RULE_URL, form_data)
table = RulesTable(req, self.rules)
form_data = {"action": "rules__delete__%s" % rule.id}
req = self.factory.post(self.edit_url, form_data)
table = RulesTable(req, self.security_group_rules.list())
handled = table.maybe_handle()
self.assertEqual(strip_absolute_base(handled['location']),
INDEX_URL)
def test_delete_group(self):
self.mox.StubOutWithMock(api, 'security_group_delete')
api.security_group_delete(IsA(http.HttpRequest), 2)
sec_group = self.security_groups.get(name="other_group")
self.mox.StubOutWithMock(api, 'security_group_delete')
api.security_group_delete(IsA(http.HttpRequest), sec_group.id)
self.mox.ReplayAll()
form_data = {"action": "security_groups__delete__%s" % '2'}
form_data = {"action": "security_groups__delete__%s" % sec_group.id}
req = self.factory.post(INDEX_URL, form_data)
table = SecurityGroupsTable(req, self.security_groups)
table = SecurityGroupsTable(req, self.security_groups.list())
handled = table.maybe_handle()
self.assertEqual(strip_absolute_base(handled['location']),
INDEX_URL)
def test_delete_group_exception(self):
sec_group = self.security_groups.get(name="other_group")
self.mox.StubOutWithMock(api, 'security_group_delete')
exception = novaclient_exceptions.ClientException('ClientException',
message='ClientException')
api.security_group_delete(IsA(http.HttpRequest), 2).\
AndRaise(exception)
exc = novaclient_exceptions.ClientException('ClientException')
api.security_group_delete(IsA(http.HttpRequest),
sec_group.id).AndRaise(exc)
self.mox.ReplayAll()
form_data = {"action": "security_groups__delete__%s" % '2'}
form_data = {"action": "security_groups__delete__%s" % sec_group.id}
req = self.factory.post(INDEX_URL, form_data)
table = SecurityGroupsTable(req, self.security_groups)
table = SecurityGroupsTable(req, self.security_groups.list())
handled = table.maybe_handle()
self.assertEqual(strip_absolute_base(handled['location']),

View File

@ -43,10 +43,10 @@ class EditRulesView(tables.DataTableView):
template_name = 'nova/access_and_security/security_groups/edit_rules.html'
def get_data(self):
security_group_id = self.kwargs['security_group_id']
security_group_id = int(self.kwargs['security_group_id'])
try:
self.object = api.security_group_get(self.request,
security_group_id)
security_group_id)
rules = [api.nova.SecurityGroupRule(rule) for
rule in self.object.rules]
except novaclient_exceptions.ClientException, e:

View File

@ -26,43 +26,19 @@ from horizon import api
from horizon import test
class AccessAndSecurityTests(test.BaseViewTests):
def setUp(self):
super(AccessAndSecurityTests, self).setUp()
keypair = api.KeyPair(None)
keypair.name = 'keyName'
self.keypairs = (keypair,)
server = api.Server(None, self.request)
server.id = 1
server.name = 'serverName'
self.server = server
self.servers = (server, )
floating_ip = api.FloatingIp(None)
floating_ip.id = 1
floating_ip.fixed_ip = '10.0.0.4'
floating_ip.instance_id = 1
floating_ip.ip = '58.58.58.58'
self.floating_ip = floating_ip
self.floating_ips = (floating_ip,)
security_group = api.SecurityGroup(None)
security_group.id = '1'
security_group.name = 'default'
self.security_groups = (security_group,)
class AccessAndSecurityTests(test.TestCase):
def test_index(self):
keypairs = self.keypairs.list()
sec_groups = self.security_groups.list()
floating_ips = self.floating_ips.list()
self.mox.StubOutWithMock(api, 'tenant_floating_ip_list')
self.mox.StubOutWithMock(api, 'security_group_list')
self.mox.StubOutWithMock(api.nova, 'keypair_list')
api.nova.keypair_list(IsA(http.HttpRequest)).AndReturn(self.keypairs)
api.tenant_floating_ip_list(IsA(http.HttpRequest)).\
AndReturn(self.floating_ips)
api.security_group_list(IsA(http.HttpRequest)).\
AndReturn(self.security_groups)
api.nova.keypair_list(IsA(http.HttpRequest)).AndReturn(keypairs)
api.tenant_floating_ip_list(IsA(http.HttpRequest)) \
.AndReturn(floating_ips)
api.security_group_list(IsA(http.HttpRequest)).AndReturn(sec_groups)
self.mox.ReplayAll()
@ -70,9 +46,8 @@ class AccessAndSecurityTests(test.BaseViewTests):
reverse('horizon:nova:access_and_security:index'))
self.assertTemplateUsed(res, 'nova/access_and_security/index.html')
self.assertItemsEqual(res.context['keypairs_table'].data,
self.keypairs)
self.assertItemsEqual(res.context['keypairs_table'].data, keypairs)
self.assertItemsEqual(res.context['security_groups_table'].data,
self.security_groups)
sec_groups)
self.assertItemsEqual(res.context['floating_ips_table'].data,
self.floating_ips)
floating_ips)

View File

@ -28,257 +28,202 @@ from mox import IsA
from horizon import api
from horizon import test
from .tables import ContainersTable, ObjectsTable
from . import forms
CONTAINER_INDEX_URL = reverse('horizon:nova:containers:index')
class ContainerViewTests(test.BaseViewTests):
def setUp(self):
super(ContainerViewTests, self).setUp()
self.container = api.Container(None)
self.container.name = 'containerName'
self.container.size_used = 128
self.containers = (self.container,)
class ContainerViewTests(test.TestCase):
def test_index(self):
containers = self.containers.list()
self.mox.StubOutWithMock(api, 'swift_get_containers')
api.swift_get_containers(
IsA(http.HttpRequest), marker=None).AndReturn(
([self.container], False))
api.swift_get_containers(IsA(http.HttpRequest), marker=None) \
.AndReturn((containers, False))
self.mox.ReplayAll()
res = self.client.get(CONTAINER_INDEX_URL)
self.assertTemplateUsed(res, 'nova/containers/index.html')
self.assertIn('table', res.context)
containers = res.context['table'].data
self.assertEqual(len(containers), 1)
self.assertEqual(containers[0].name, 'containerName')
resp_containers = res.context['table'].data
self.assertEqual(len(resp_containers), len(containers))
def test_delete_container(self):
container = self.containers.get(name="container_two")
self.mox.StubOutWithMock(api, 'swift_delete_container')
api.swift_delete_container(IsA(http.HttpRequest),
'containerName')
api.swift_delete_container(IsA(http.HttpRequest), container.name)
self.mox.ReplayAll()
action_string = "containers__delete__%s" % self.container.name
action_string = "containers__delete__%s" % container.name
form_data = {"action": action_string}
req = self.factory.post(CONTAINER_INDEX_URL, form_data)
table = ContainersTable(req, self.containers)
table = ContainersTable(req, self.containers.list())
handled = table.maybe_handle()
self.assertEqual(handled['location'], CONTAINER_INDEX_URL)
def test_delete_container_nonempty(self):
container = self.containers.first()
self.mox.StubOutWithMock(api, 'swift_delete_container')
exception = ContainerNotEmpty('containerNotEmpty')
api.swift_delete_container(
IsA(http.HttpRequest),
'containerName').AndRaise(exception)
exc = ContainerNotEmpty('containerNotEmpty')
api.swift_delete_container(IsA(http.HttpRequest),
container.name).AndRaise(exc)
self.mox.ReplayAll()
action_string = "containers__delete__%s" % self.container.name
action_string = "containers__delete__%s" % container.name
form_data = {"action": action_string}
req = self.factory.post(CONTAINER_INDEX_URL, form_data)
table = ContainersTable(req, self.containers)
table = ContainersTable(req, self.containers.list())
handled = table.maybe_handle()
self.assertEqual(handled['location'], CONTAINER_INDEX_URL)
def test_create_container_get(self):
res = self.client.get(reverse('horizon:nova:containers:create'))
self.assertTemplateUsed(res, 'nova/containers/create.html')
def test_create_container_post(self):
formData = {'name': 'containerName',
'method': 'CreateContainer'}
self.mox.StubOutWithMock(api, 'swift_create_container')
api.swift_create_container(
IsA(http.HttpRequest), u'containerName')
api.swift_create_container(IsA(http.HttpRequest),
self.containers.first().name)
self.mox.ReplayAll()
formData = {'name': self.containers.first().name,
'method': forms.CreateContainer.__name__}
res = self.client.post(reverse('horizon:nova:containers:create'),
formData)
self.assertRedirectsNoFollow(res, CONTAINER_INDEX_URL)
class ObjectViewTests(test.BaseViewTests):
CONTAINER_NAME = 'containerName'
def setUp(self):
class FakeCloudFile(object):
def __init__(self):
self.metadata = {}
def sync_metadata(self):
pass
super(ObjectViewTests, self).setUp()
swift_object = api.swift.SwiftObject(FakeCloudFile())
swift_object.name = u"test_object"
swift_object.size = '128'
swift_object.container = api.swift.Container(None)
swift_object.container.name = self.CONTAINER_NAME
self.swift_objects = [swift_object]
class ObjectViewTests(test.TestCase):
def test_index(self):
self.mox.StubOutWithMock(api, 'swift_get_objects')
api.swift_get_objects(
IsA(http.HttpRequest),
self.CONTAINER_NAME,
marker=None).AndReturn((self.swift_objects, False))
ret = (self.objects.list(), False)
api.swift_get_objects(IsA(http.HttpRequest),
self.containers.first().name,
marker=None).AndReturn(ret)
self.mox.ReplayAll()
res = self.client.get(reverse('horizon:nova:containers:object_index',
args=[self.CONTAINER_NAME]))
args=[self.containers.first().name]))
self.assertTemplateUsed(res, 'nova/objects/index.html')
self.assertItemsEqual(res.context['table'].data, self.swift_objects)
expected = [obj.name for obj in self.objects.list()]
self.assertQuerysetEqual(res.context['table'].data,
expected,
lambda obj: obj.name)
def test_upload_index(self):
res = self.client.get(reverse('horizon:nova:containers:object_upload',
args=[self.CONTAINER_NAME]))
args=[self.containers.first().name]))
self.assertTemplateUsed(res, 'nova/objects/upload.html')
def test_upload(self):
container = self.containers.first()
obj = self.objects.first()
OBJECT_DATA = 'objectData'
OBJECT_FILE = tempfile.TemporaryFile()
OBJECT_FILE.write(OBJECT_DATA)
OBJECT_FILE.flush()
OBJECT_FILE.seek(0)
OBJECT_NAME = 'objectName'
formData = {'method': 'UploadObject',
'container_name': self.CONTAINER_NAME,
'name': OBJECT_NAME,
'object_file': OBJECT_FILE}
temp_file = tempfile.TemporaryFile()
temp_file.write(OBJECT_DATA)
temp_file.flush()
temp_file.seek(0)
self.mox.StubOutWithMock(api, 'swift_upload_object')
api.swift_upload_object(IsA(http.HttpRequest),
unicode(self.CONTAINER_NAME),
unicode(OBJECT_NAME),
OBJECT_DATA).AndReturn(self.swift_objects[0])
container.name,
obj.name,
OBJECT_DATA).AndReturn(obj)
self.mox.StubOutWithMock(obj, 'sync_metadata')
obj.sync_metadata()
self.mox.ReplayAll()
res = self.client.get(reverse('horizon:nova:containers:object_upload',
args=[self.CONTAINER_NAME]))
upload_url = reverse('horizon:nova:containers:object_upload',
args=[container.name])
res = self.client.get(upload_url)
self.assertContains(res, 'enctype="multipart/form-data"')
res = self.client.post(reverse('horizon:nova:containers:object_upload',
args=[self.CONTAINER_NAME]),
formData)
formData = {'method': forms.UploadObject.__name__,
'container_name': container.name,
'name': obj.name,
'object_file': temp_file}
res = self.client.post(upload_url, formData)
self.assertRedirectsNoFollow(res,
reverse('horizon:nova:containers:object_index',
args=[self.CONTAINER_NAME]))
index_url = reverse('horizon:nova:containers:object_index',
args=[container.name])
self.assertRedirectsNoFollow(res, index_url)
def test_delete(self):
container = self.containers.first()
obj = self.objects.first()
index_url = reverse('horizon:nova:containers:object_index',
args=[container.name])
self.mox.StubOutWithMock(api, 'swift_delete_object')
api.swift_delete_object(
IsA(http.HttpRequest),
self.CONTAINER_NAME, self.swift_objects[0].name)
api.swift_delete_object(IsA(http.HttpRequest),
container.name,
obj.name)
self.mox.ReplayAll()
OBJECT_INDEX_URL = reverse('horizon:nova:containers:object_index',
args=[self.CONTAINER_NAME])
action_string = "objects__delete__%s" % self.swift_objects[0].name
action_string = "objects__delete__%s" % obj.name
form_data = {"action": action_string}
req = self.factory.post(OBJECT_INDEX_URL, form_data)
kwargs = {"container_name": self.CONTAINER_NAME}
table = ObjectsTable(req, self.swift_objects, **kwargs)
req = self.factory.post(index_url, form_data)
kwargs = {"container_name": container.name}
table = ObjectsTable(req, self.objects.list(), **kwargs)
handled = table.maybe_handle()
self.assertEqual(handled['location'], OBJECT_INDEX_URL)
self.assertEqual(handled['location'], index_url)
def test_download(self):
container = self.containers.first()
obj = self.objects.first()
OBJECT_DATA = 'objectData'
OBJECT_NAME = 'objectName'
self.mox.StubOutWithMock(api, 'swift_get_object_data')
self.mox.StubOutWithMock(api.swift, 'swift_get_object')
api.swift.swift_get_object(IsA(http.HttpRequest),
unicode(self.CONTAINER_NAME),
unicode(OBJECT_NAME)) \
.AndReturn(self.swift_objects[0])
container.name,
obj.name).AndReturn(obj)
api.swift_get_object_data(IsA(http.HttpRequest),
unicode(self.CONTAINER_NAME),
unicode(OBJECT_NAME)).AndReturn(OBJECT_DATA)
container.name,
obj.name).AndReturn(OBJECT_DATA)
self.mox.ReplayAll()
res = self.client.get(reverse(
'horizon:nova:containers:object_download',
args=[self.CONTAINER_NAME, OBJECT_NAME]))
download_url = reverse('horizon:nova:containers:object_download',
args=[container.name, obj.name])
res = self.client.get(download_url)
self.assertEqual(res.content, OBJECT_DATA)
self.assertTrue(res.has_header('Content-Disposition'))
def test_copy_index(self):
OBJECT_NAME = 'objectName'
container = self.mox.CreateMock(api.Container)
container.name = self.CONTAINER_NAME
self.mox.StubOutWithMock(api, 'swift_get_containers')
api.swift_get_containers(
IsA(http.HttpRequest)).AndReturn(([container], False))
ret = (self.containers.list(), False)
api.swift_get_containers(IsA(http.HttpRequest)).AndReturn(ret)
self.mox.ReplayAll()
res = self.client.get(reverse('horizon:nova:containers:object_copy',
args=[self.CONTAINER_NAME,
OBJECT_NAME]))
args=[self.containers.first().name,
self.objects.first().name]))
self.assertTemplateUsed(res, 'nova/objects/copy.html')
def test_copy(self):
NEW_CONTAINER_NAME = self.CONTAINER_NAME
NEW_OBJECT_NAME = 'newObjectName'
ORIG_CONTAINER_NAME = 'origContainerName'
ORIG_OBJECT_NAME = 'origObjectName'
formData = {'method': 'CopyObject',
'new_container_name': NEW_CONTAINER_NAME,
'new_object_name': NEW_OBJECT_NAME,
'orig_container_name': ORIG_CONTAINER_NAME,
'orig_object_name': ORIG_OBJECT_NAME}
container = self.mox.CreateMock(api.Container)
container.name = self.CONTAINER_NAME
container_1 = self.containers.get(name="container_one")
container_2 = self.containers.get(name="container_two")
obj = self.objects.first()
self.mox.StubOutWithMock(api, 'swift_get_containers')
api.swift_get_containers(
IsA(http.HttpRequest)).AndReturn(([container], False))
self.mox.StubOutWithMock(api, 'swift_copy_object')
ret = (self.containers.list(), False)
api.swift_get_containers(IsA(http.HttpRequest)).AndReturn(ret)
api.swift_copy_object(IsA(http.HttpRequest),
ORIG_CONTAINER_NAME,
ORIG_OBJECT_NAME,
NEW_CONTAINER_NAME,
NEW_OBJECT_NAME)
container_1.name,
obj.name,
container_2.name,
obj.name)
self.mox.ReplayAll()
res = self.client.post(reverse('horizon:nova:containers:object_copy',
args=[ORIG_CONTAINER_NAME,
ORIG_OBJECT_NAME]),
formData)
self.assertRedirectsNoFollow(res,
reverse('horizon:nova:containers:object_index',
args=[NEW_CONTAINER_NAME]))
formData = {'method': forms.CopyObject.__name__,
'new_container_name': container_2.name,
'new_object_name': obj.name,
'orig_container_name': container_1.name,
'orig_object_name': obj.name}
copy_url = reverse('horizon:nova:containers:object_copy',
args=[container_1.name, obj.name])
res = self.client.post(copy_url, formData)
index_url = reverse('horizon:nova:containers:object_index',
args=[container_2.name])
self.assertRedirectsNoFollow(res, index_url)

View File

@ -19,10 +19,8 @@
# under the License.
from django import http
from django.contrib import messages
from django.core.urlresolvers import reverse
from keystoneclient import exceptions as keystone_exceptions
from novaclient.v1_1 import client as nova_client, volume_snapshots
from mox import IgnoreArg, IsA
from horizon import api
@ -32,116 +30,48 @@ from horizon import test
IMAGES_INDEX_URL = reverse('horizon:nova:images_and_snapshots:index')
class FakeQuota:
ram = 100
class ImageViewTests(test.BaseViewTests):
def setUp(self):
super(ImageViewTests, self).setUp()
image_dict = {'name': 'visibleImage',
'container_format': 'novaImage'}
self.visibleImage = api.Image(image_dict)
self.visibleImage.id = 1
image_dict = {'name': 'invisibleImage',
'container_format': 'aki'}
self.invisibleImage = api.Image(image_dict)
self.invisibleImage.id = 2
self.images = (self.visibleImage, self.invisibleImage)
flavor = api.Flavor(None)
flavor.id = 1
flavor.name = 'm1.massive'
flavor.vcpus = 1000
flavor.disk = 1024
flavor.ram = 10000
self.flavors = (flavor,)
keypair = api.KeyPair(None)
keypair.name = 'keyName'
self.keypairs = (keypair,)
security_group = api.SecurityGroup(None)
security_group.name = 'default'
self.security_groups = (security_group,)
volume = api.Volume(None)
volume.id = 1
volume.name = 'vol'
volume.status = 'available'
volume.size = 40
volume.displayName = ''
self.volumes = (volume,)
self.volume_snapshot = volume_snapshots.Snapshot(
volume_snapshots.SnapshotManager,
{'id': 2,
'displayName': 'test snapshot',
'displayDescription': 'test snapshot description',
'size': 40,
'status': 'available',
'volumeId': 1})
self.volume_snapshots = [self.volume_snapshot]
class ImageViewTests(test.TestCase):
def test_launch_get(self):
IMAGE_ID = 1
image = self.images.first()
tenant = self.tenants.first()
quota = self.quotas.first()
self.mox.StubOutWithMock(api, 'image_get_meta')
self.mox.StubOutWithMock(api, 'tenant_quota_get')
self.mox.StubOutWithMock(api, 'flavor_list')
self.mox.StubOutWithMock(api, 'keypair_list')
self.mox.StubOutWithMock(api, 'security_group_list')
api.image_get_meta(IsA(http.HttpRequest), str(IMAGE_ID)) \
.AndReturn(self.visibleImage)
api.tenant_quota_get(IsA(http.HttpRequest),
self.TEST_TENANT).AndReturn(FakeQuota)
api.flavor_list(IsA(http.HttpRequest)).AndReturn(self.flavors)
api.keypair_list(IsA(http.HttpRequest)).AndReturn(self.keypairs)
api.security_group_list(IsA(http.HttpRequest)).AndReturn(
self.security_groups)
api.image_get_meta(IsA(http.HttpRequest), image.id).AndReturn(image)
api.tenant_quota_get(IsA(http.HttpRequest), tenant.id).AndReturn(quota)
api.flavor_list(IsA(http.HttpRequest)).AndReturn(self.flavors.list())
api.keypair_list(IsA(http.HttpRequest)).AndReturn(self.keypairs.list())
api.security_group_list(IsA(http.HttpRequest)) \
.AndReturn(self.security_groups.list())
self.mox.ReplayAll()
res = self.client.get(
reverse('horizon:nova:images_and_snapshots:images:launch',
args=[IMAGE_ID]))
url = reverse('horizon:nova:images_and_snapshots:images:launch',
args=[image.id])
res = self.client.get(url)
form = res.context['form']
self.assertTemplateUsed(res,
'nova/images_and_snapshots/images/launch.html')
self.assertEqual(res.context['image'].name, self.visibleImage.name)
self.assertIn('m1.massive', form.fields['flavor'].choices[0][1])
self.assertEqual(form.fields['keypair'].choices[0][0],
self.keypairs[0].name)
self.assertEqual(res.context['image'].name, image.name)
self.assertIn(self.flavors.first().name,
form.fields['flavor'].choices[0][1])
self.assertEqual(self.keypairs.first().name,
form.fields['keypair'].choices[0][0])
def test_launch_post(self):
FLAVOR_ID = unicode(self.flavors[0].id)
IMAGE_ID = u'1'
keypair = unicode(self.keypairs[0].name)
SERVER_NAME = u'serverName'
USER_DATA = u'userData'
volume = u'%s:vol' % self.volumes[0].id
flavor = self.flavors.first()
image = self.images.first()
keypair = self.keypairs.first()
server = self.servers.first()
volume = self.volumes.first()
sec_group = self.security_groups.first()
USER_DATA = 'user data'
device_name = u'vda'
BLOCK_DEVICE_MAPPING = {device_name: u"1:vol::0"}
form_data = {'method': 'LaunchForm',
'flavor': FLAVOR_ID,
'image_id': IMAGE_ID,
'keypair': keypair,
'name': SERVER_NAME,
'user_data': USER_DATA,
'count': 1,
'tenant_id': self.TEST_TENANT,
'security_groups': 'default',
'volume': volume,
'device_name': device_name}
volume_choice = "%s:vol" % volume.id
block_device_mapping = {device_name: u"%s::0" % volume_choice}
self.mox.StubOutWithMock(api, 'image_get_meta')
self.mox.StubOutWithMock(api, 'flavor_list')
@ -150,98 +80,97 @@ class ImageViewTests(test.BaseViewTests):
self.mox.StubOutWithMock(api, 'server_create')
self.mox.StubOutWithMock(api, 'volume_list')
api.flavor_list(IsA(http.HttpRequest)).AndReturn(self.flavors)
api.keypair_list(IsA(http.HttpRequest)).AndReturn(self.keypairs)
api.security_group_list(IsA(http.HttpRequest)).AndReturn(
self.security_groups)
api.image_get_meta(IsA(http.HttpRequest), IMAGE_ID).AndReturn(
self.visibleImage)
api.volume_list(IsA(http.HttpRequest)).AndReturn(self.volumes)
api.server_create(IsA(http.HttpRequest), SERVER_NAME,
str(IMAGE_ID), str(FLAVOR_ID),
keypair, USER_DATA, [self.security_groups[0].name],
BLOCK_DEVICE_MAPPING,
api.flavor_list(IsA(http.HttpRequest)).AndReturn(self.flavors.list())
api.keypair_list(IsA(http.HttpRequest)).AndReturn(self.keypairs.list())
api.security_group_list(IsA(http.HttpRequest)) \
.AndReturn(self.security_groups.list())
api.image_get_meta(IsA(http.HttpRequest), image.id).AndReturn(image)
api.volume_list(IsA(http.HttpRequest)).AndReturn(self.volumes.list())
api.server_create(IsA(http.HttpRequest),
server.name,
image.id,
flavor.id,
keypair.name,
USER_DATA,
[sec_group.name],
block_device_mapping,
instance_count=IsA(int))
self.mox.ReplayAll()
res = self.client.post(
reverse('horizon:nova:images_and_snapshots:images:launch',
args=[IMAGE_ID]),
form_data)
form_data = {'method': 'LaunchForm',
'flavor': flavor.id,
'image_id': image.id,
'keypair': keypair.name,
'name': server.name,
'user_data': USER_DATA,
'tenant_id': self.tenants.first().id,
'security_groups': sec_group.name,
'volume': volume_choice,
'device_name': device_name,
'count': 1}
url = reverse('horizon:nova:images_and_snapshots:images:launch',
args=[image.id])
res = self.client.post(url, form_data)
self.assertNoFormErrors(res)
self.assertRedirectsNoFollow(res,
reverse('horizon:nova:instances_and_volumes:index'))
def test_launch_flavorlist_error(self):
IMAGE_ID = '1'
image = self.images.first()
self.mox.StubOutWithMock(api, 'image_get_meta')
self.mox.StubOutWithMock(api, 'tenant_quota_get')
self.mox.StubOutWithMock(api, 'flavor_list')
self.mox.StubOutWithMock(api, 'keypair_list')
self.mox.StubOutWithMock(api, 'security_group_list')
api.image_get_meta(IsA(http.HttpRequest),
IMAGE_ID).AndReturn(self.visibleImage)
image.id).AndReturn(image)
api.tenant_quota_get(IsA(http.HttpRequest),
self.TEST_TENANT).AndReturn(FakeQuota)
exception = keystone_exceptions.ClientException('Failed.')
api.flavor_list(IsA(http.HttpRequest)).AndRaise(exception)
api.keypair_list(IsA(http.HttpRequest)).AndReturn(self.keypairs)
api.security_group_list(IsA(http.HttpRequest)).AndReturn(
self.security_groups)
self.tenant.id).AndReturn(self.quotas.first())
exc = keystone_exceptions.ClientException('Failed.')
api.flavor_list(IsA(http.HttpRequest)).AndRaise(exc)
api.keypair_list(IsA(http.HttpRequest)).AndReturn(self.keypairs.list())
api.security_group_list(IsA(http.HttpRequest)) \
.AndReturn(self.security_groups.list())
self.mox.ReplayAll()
res = self.client.get(
reverse('horizon:nova:images_and_snapshots:images:launch',
args=[IMAGE_ID]))
url = reverse('horizon:nova:images_and_snapshots:images:launch',
args=[image.id])
res = self.client.get(url)
self.assertTemplateUsed(res,
'nova/images_and_snapshots/images/launch.html')
def test_launch_keypairlist_error(self):
IMAGE_ID = '2'
image = self.images.first()
self.mox.StubOutWithMock(api, 'image_get_meta')
api.image_get_meta(IsA(http.HttpRequest),
IMAGE_ID).AndReturn(self.visibleImage)
self.mox.StubOutWithMock(api, 'tenant_quota_get')
api.tenant_quota_get(IsA(http.HttpRequest),
self.TEST_TENANT).AndReturn(FakeQuota)
self.mox.StubOutWithMock(api, 'flavor_list')
api.flavor_list(IsA(http.HttpRequest)).AndReturn(self.flavors)
exception = keystone_exceptions.ClientException('Failed.')
self.mox.StubOutWithMock(api, 'keypair_list')
api.keypair_list(IsA(http.HttpRequest)).AndRaise(exception)
self.mox.StubOutWithMock(api, 'security_group_list')
api.security_group_list(IsA(http.HttpRequest)).AndReturn(
self.security_groups)
api.image_get_meta(IsA(http.HttpRequest), image.id).AndReturn(image)
api.tenant_quota_get(IsA(http.HttpRequest),
self.tenant.id).AndReturn(self.quotas.first())
api.flavor_list(IsA(http.HttpRequest)).AndReturn(self.flavors.list())
exception = keystone_exceptions.ClientException('Failed.')
api.keypair_list(IsA(http.HttpRequest)).AndRaise(exception)
api.security_group_list(IsA(http.HttpRequest)) \
.AndReturn(self.security_groups.list())
self.mox.ReplayAll()
res = self.client.get(
reverse('horizon:nova:images_and_snapshots:images:launch',
args=[IMAGE_ID]))
url = reverse('horizon:nova:images_and_snapshots:images:launch',
args=[image.id])
res = self.client.get(url)
self.assertTemplateUsed(res,
'nova/images_and_snapshots/images/launch.html')
form = res.context['form']
form_keyfield = form.fields['keypair']
self.assertEqual(len(form_keyfield.choices), 0)
self.assertEqual(len(res.context['form'].fields['keypair'].choices), 0)
def test_launch_form_keystone_exception(self):
FLAVOR_ID = self.flavors[0].id
IMAGE_ID = '1'
keypair = self.keypairs[0].name
SERVER_NAME = 'serverName'
flavor = self.flavors.first()
image = self.images.first()
keypair = self.keypairs.first()
server = self.servers.first()
sec_group = self.security_groups.first()
USER_DATA = 'userData'
self.mox.StubOutWithMock(api, 'image_get_meta')
@ -251,37 +180,34 @@ class ImageViewTests(test.BaseViewTests):
self.mox.StubOutWithMock(api, 'server_create')
self.mox.StubOutWithMock(api, 'volume_list')
form_data = {'method': 'LaunchForm',
'flavor': FLAVOR_ID,
'image_id': IMAGE_ID,
'keypair': keypair,
'name': SERVER_NAME,
'tenant_id': self.TEST_TENANT,
'user_data': USER_DATA,
'count': int(1),
'security_groups': 'default'}
api.flavor_list(IgnoreArg()).AndReturn(self.flavors)
api.keypair_list(IgnoreArg()).AndReturn(self.keypairs)
api.security_group_list(IsA(http.HttpRequest)).AndReturn(
self.security_groups)
api.image_get_meta(IgnoreArg(), IMAGE_ID).AndReturn(self.visibleImage)
api.volume_list(IgnoreArg()).AndReturn(self.volumes)
exception = keystone_exceptions.ClientException('Failed')
api.flavor_list(IgnoreArg()).AndReturn(self.flavors.list())
api.keypair_list(IgnoreArg()).AndReturn(self.keypairs.list())
api.security_group_list(IsA(http.HttpRequest)) \
.AndReturn(self.security_groups.list())
api.image_get_meta(IgnoreArg(), image.id).AndReturn(image)
api.volume_list(IgnoreArg()).AndReturn(self.volumes.list())
exc = keystone_exceptions.ClientException('Failed')
api.server_create(IsA(http.HttpRequest),
SERVER_NAME,
IMAGE_ID,
str(FLAVOR_ID),
keypair,
server.name,
image.id,
flavor.id,
keypair.name,
USER_DATA,
[group.name for group in self.security_groups],
[sec_group.name],
None,
instance_count=IsA(int)).AndRaise(exception)
instance_count=IsA(int)).AndRaise(exc)
self.mox.ReplayAll()
url = reverse('horizon:nova:images_and_snapshots:images:launch',
args=[IMAGE_ID])
res = self.client.post(url, form_data)
form_data = {'method': 'LaunchForm',
'flavor': flavor.id,
'image_id': image.id,
'keypair': keypair.name,
'name': server.name,
'tenant_id': self.tenant.id,
'user_data': USER_DATA,
'count': 1,
'security_groups': sec_group.name}
url = reverse('horizon:nova:images_and_snapshots:images:launch',
args=[image.id])
res = self.client.post(url, form_data)
self.assertRedirectsNoFollow(res, IMAGES_INDEX_URL)

View File

@ -30,144 +30,84 @@ from horizon import test
INDEX_URL = reverse('horizon:nova:images_and_snapshots:index')
class SnapshotsViewTests(test.BaseViewTests):
def setUp(self):
super(SnapshotsViewTests, self).setUp()
image_dict = {'name': 'snapshot',
'container_format': 'novaImage',
'id': 3}
self.images = [image_dict]
server = api.Server(None, self.request)
server.id = 1
server.status = 'ACTIVE'
server.name = 'sgoody'
self.good_server = server
server = api.Server(None, self.request)
server.id = 2
server.status = 'BUILD'
server.name = 'baddy'
self.bad_server = server
flavor = api.Flavor(None)
flavor.id = 1
flavor.name = 'm1.massive'
flavor.vcpus = 1000
flavor.disk = 1024
flavor.ram = 10000
self.flavors = (flavor,)
keypair = api.KeyPair(None)
keypair.name = 'keyName'
self.keypairs = (keypair,)
security_group = api.SecurityGroup(None)
security_group.name = 'default'
self.security_groups = (security_group,)
class SnapshotsViewTests(test.TestCase):
def test_create_snapshot_get(self):
server = self.servers.first()
self.mox.StubOutWithMock(api, 'server_get')
api.server_get(IsA(http.HttpRequest),
str(self.good_server.id)).AndReturn(self.good_server)
api.server_get(IsA(http.HttpRequest), server.id).AndReturn(server)
self.mox.ReplayAll()
res = self.client.get(
reverse('horizon:nova:images_and_snapshots:snapshots:create',
args=[self.good_server.id]))
url = reverse('horizon:nova:images_and_snapshots:snapshots:create',
args=[server.id])
res = self.client.get(url)
self.assertTemplateUsed(res,
'nova/images_and_snapshots/snapshots/create.html')
def test_create_snapshot_get_with_invalid_status(self):
server = self.servers.get(status='BUILD')
self.mox.StubOutWithMock(api, 'server_get')
api.server_get(IsA(http.HttpRequest),
str(self.bad_server.id)).AndReturn(self.bad_server)
api.server_get(IsA(http.HttpRequest), server.id).AndReturn(server)
self.mox.ReplayAll()
res = self.client.get(
reverse('horizon:nova:images_and_snapshots:snapshots:create',
args=[self.bad_server.id]))
url = reverse("horizon:nova:instances_and_volumes:index")
self.assertRedirectsNoFollow(res, url)
url = reverse('horizon:nova:images_and_snapshots:snapshots:create',
args=[server.id])
res = self.client.get(url)
redirect = reverse("horizon:nova:instances_and_volumes:index")
self.assertRedirectsNoFollow(res, redirect)
def test_create_get_server_exception(self):
server = self.servers.first()
self.mox.StubOutWithMock(api, 'server_get')
exception = novaclient_exceptions.ClientException('apiException')
api.server_get(IsA(http.HttpRequest),
str(self.good_server.id)).AndRaise(exception)
exc = novaclient_exceptions.ClientException('apiException')
api.server_get(IsA(http.HttpRequest), server.id).AndRaise(exc)
self.mox.ReplayAll()
res = self.client.get(
reverse('horizon:nova:images_and_snapshots:snapshots:create',
args=[self.good_server.id]))
url = reverse("horizon:nova:instances_and_volumes:index")
self.assertRedirectsNoFollow(res, url)
url = reverse('horizon:nova:images_and_snapshots:snapshots:create',
args=[server.id])
res = self.client.get(url)
redirect = reverse("horizon:nova:instances_and_volumes:index")
self.assertRedirectsNoFollow(res, redirect)
def test_create_snapshot_post(self):
SNAPSHOT_NAME = 'snappy'
new_snapshot = self.mox.CreateMock(api.Image)
new_snapshot.name = SNAPSHOT_NAME
formData = {'method': 'CreateSnapshot',
'tenant_id': self.TEST_TENANT,
'instance_id': self.good_server.id,
'name': SNAPSHOT_NAME}
server = self.servers.first()
snapshot = self.snapshots.first()
self.mox.StubOutWithMock(api, 'server_get')
self.mox.StubOutWithMock(api, 'snapshot_create')
api.server_get(IsA(http.HttpRequest),
str(self.good_server.id)).AndReturn(self.good_server)
api.snapshot_create(IsA(http.HttpRequest),
str(self.good_server.id), SNAPSHOT_NAME).\
AndReturn(new_snapshot)
api.server_get(IsA(http.HttpRequest),
str(self.good_server.id)).AndReturn(self.good_server)
api.server_get(IsA(http.HttpRequest), server.id).AndReturn(server)
api.snapshot_create(IsA(http.HttpRequest), server.id, snapshot.name) \
.AndReturn(snapshot)
api.server_get(IsA(http.HttpRequest), server.id).AndReturn(server)
self.mox.ReplayAll()
res = self.client.post(
reverse('horizon:nova:images_and_snapshots:snapshots:create',
args=[self.good_server.id]),
formData)
formData = {'method': 'CreateSnapshot',
'tenant_id': self.tenant.id,
'instance_id': server.id,
'name': snapshot.name}
url = reverse('horizon:nova:images_and_snapshots:snapshots:create',
args=[server.id])
res = self.client.post(url, formData)
self.assertRedirectsNoFollow(res, INDEX_URL)
def test_create_snapshot_post_exception(self):
SNAPSHOT_NAME = 'snappy'
new_snapshot = self.mox.CreateMock(api.Image)
new_snapshot.name = SNAPSHOT_NAME
formData = {'method': 'CreateSnapshot',
'tenant_id': self.TEST_TENANT,
'instance_id': self.good_server.id,
'name': SNAPSHOT_NAME}
server = self.servers.first()
snapshot = self.snapshots.first()
self.mox.StubOutWithMock(api, 'server_get')
self.mox.StubOutWithMock(api, 'snapshot_create')
api.server_get(IsA(http.HttpRequest),
str(self.good_server.id)).AndReturn(self.good_server)
exception = novaclient_exceptions.ClientException('apiException',
message='apiException')
api.snapshot_create(IsA(http.HttpRequest),
str(self.good_server.id), SNAPSHOT_NAME).\
AndRaise(exception)
api.server_get(IsA(http.HttpRequest), server.id).AndReturn(server)
exc = novaclient_exceptions.ClientException('apiException')
api.snapshot_create(IsA(http.HttpRequest), server.id, snapshot.name) \
.AndRaise(exc)
self.mox.ReplayAll()
res = self.client.post(
reverse('horizon:nova:images_and_snapshots:snapshots:create',
args=[self.good_server.id]),
formData)
url = reverse("horizon:nova:instances_and_volumes:index")
self.assertRedirectsNoFollow(res, url)
formData = {'method': 'CreateSnapshot',
'tenant_id': self.tenant.id,
'instance_id': server.id,
'name': snapshot.name}
url = reverse('horizon:nova:images_and_snapshots:snapshots:create',
args=[server.id])
res = self.client.post(url, formData)
redirect = reverse("horizon:nova:instances_and_volumes:index")
self.assertRedirectsNoFollow(res, redirect)

View File

@ -31,97 +31,43 @@ from horizon import test
INDEX_URL = reverse('horizon:nova:images_and_snapshots:index')
class ImagesAndSnapshotsTests(test.BaseViewTests):
def setUp(self):
super(ImagesAndSnapshotsTests, self).setUp()
snapshot_properties = api.glance.ImageProperties(None)
snapshot_properties.image_type = u'snapshot'
snapshot_dict = {'name': u'snapshot',
'container_format': u'ami',
'id': 3}
snapshot = api.glance.Image(snapshot_dict)
snapshot.properties = snapshot_properties
self.snapshots = [snapshot]
image_properties = api.glance.ImageProperties(None)
image_properties.image_type = u'image'
image_dict = {'name': u'visibleImage',
'container_format': u'novaImage'}
self.visibleImage = api.glance.Image(image_dict)
self.visibleImage.id = '1'
self.visibleImage.properties = image_properties
image_dict = {'name': 'invisibleImage',
'container_format': 'aki'}
self.invisibleImage = api.Image(image_dict)
self.invisibleImage.id = '2'
flavor = api.Flavor(None)
flavor.id = 1
flavor.name = 'm1.massive'
flavor.vcpus = 1000
flavor.disk = 1024
flavor.ram = 10000
self.flavors = (flavor,)
self.images = (self.visibleImage, self.invisibleImage)
keypair = api.KeyPair(None)
keypair.name = 'keyName'
self.keypairs = (keypair,)
security_group = api.SecurityGroup(None)
security_group.name = 'default'
self.security_groups = (security_group,)
class ImagesAndSnapshotsTests(test.TestCase):
def test_index(self):
images = self.images.list()
snapshots = self.snapshots.list()
self.mox.StubOutWithMock(api, 'image_list_detailed')
api.image_list_detailed(IsA(http.HttpRequest)).AndReturn(self.images)
self.mox.StubOutWithMock(api, 'snapshot_list_detailed')
api.snapshot_list_detailed(IsA(http.HttpRequest)).AndReturn(
self.snapshots)
api.image_list_detailed(IsA(http.HttpRequest)).AndReturn(images)
api.snapshot_list_detailed(IsA(http.HttpRequest)).AndReturn(snapshots)
self.mox.ReplayAll()
res = self.client.get(INDEX_URL)
self.assertTemplateUsed(res,
'nova/images_and_snapshots/index.html')
self.assertTemplateUsed(res, 'nova/images_and_snapshots/index.html')
self.assertIn('images_table', res.context)
images = res.context['images_table'].data
self.assertEqual(len(images), 1)
self.assertEqual(images[0].name, 'visibleImage')
filter_func = lambda im: im.container_format not in ['aki', 'ari']
filtered_images = filter(filter_func, images)
self.assertItemsEqual(images, filtered_images)
def test_index_no_images(self):
self.mox.StubOutWithMock(api, 'snapshot_list_detailed')
self.mox.StubOutWithMock(api, 'image_list_detailed')
api.image_list_detailed(IsA(http.HttpRequest)).AndReturn([])
api.snapshot_list_detailed(IsA(http.HttpRequest)).\
AndReturn(self.snapshots)
api.snapshot_list_detailed(IsA(http.HttpRequest)) \
.AndReturn(self.snapshots.list())
self.mox.ReplayAll()
res = self.client.get(INDEX_URL)
self.assertTemplateUsed(res, 'nova/images_and_snapshots/index.html')
def test_index_client_conn_error(self):
self.mox.StubOutWithMock(api, 'image_list_detailed')
self.mox.StubOutWithMock(api, 'snapshot_list_detailed')
exception = glance_exception.ClientConnectionError('clientConnError')
api.image_list_detailed(IsA(http.HttpRequest)).AndRaise(exception)
api.snapshot_list_detailed(IsA(http.HttpRequest)).\
AndReturn(self.snapshots)
exc = glance_exception.ClientConnectionError('clientConnError')
api.image_list_detailed(IsA(http.HttpRequest)).AndRaise(exc)
api.snapshot_list_detailed(IsA(http.HttpRequest)) \
.AndReturn(self.snapshots.list())
self.mox.ReplayAll()
res = self.client.get(INDEX_URL)
self.assertTemplateUsed(res, 'nova/images_and_snapshots/index.html')

View File

@ -20,7 +20,6 @@
from django import http
from django.core.urlresolvers import reverse
from novaclient.v1_1 import volume_snapshots
from mox import IsA
from horizon import api
@ -30,49 +29,34 @@ from horizon import test
INDEX_URL = reverse('horizon:nova:images_and_snapshots:index')
class SnapshotsViewTests(test.BaseViewTests):
class VolumeSnapshotsViewTests(test.TestCase):
def test_create_snapshot_get(self):
VOLUME_ID = u'1'
volume = self.volumes.first()
res = self.client.get(reverse('horizon:nova:instances_and_volumes:'
'volumes:create_snapshot',
args=[VOLUME_ID]))
args=[volume.id]))
self.assertTemplateUsed(res, 'nova/instances_and_volumes/'
'volumes/create_snapshot.html')
def test_create_snapshot_post(self):
VOLUME_ID = u'1'
SNAPSHOT_NAME = u'vol snap'
SNAPSHOT_DESCRIPTION = u'vol snap desc'
volume_snapshot = volume_snapshots.Snapshot(
volume_snapshots.SnapshotManager,
{'id': 1,
'displayName': 'test snapshot',
'displayDescription': 'test snapshot description',
'size': 40,
'status': 'available',
'volumeId': 1})
formData = {'method': 'CreateSnapshotForm',
'tenant_id': self.TEST_TENANT,
'volume_id': VOLUME_ID,
'name': SNAPSHOT_NAME,
'description': SNAPSHOT_DESCRIPTION}
volume = self.volumes.first()
snapshot = self.volume_snapshots.first()
self.mox.StubOutWithMock(api, 'volume_snapshot_create')
api.volume_snapshot_create(
IsA(http.HttpRequest), str(VOLUME_ID), SNAPSHOT_NAME,
SNAPSHOT_DESCRIPTION).AndReturn(volume_snapshot)
api.volume_snapshot_create(IsA(http.HttpRequest),
volume.id,
snapshot.displayName,
snapshot.displayDescription) \
.AndReturn(snapshot)
self.mox.ReplayAll()
res = self.client.post(
reverse('horizon:nova:instances_and_volumes:volumes:'
'create_snapshot',
args=[VOLUME_ID]),
formData)
formData = {'method': 'CreateSnapshotForm',
'tenant_id': self.tenant.id,
'volume_id': volume.id,
'name': snapshot.displayName,
'description': snapshot.displayDescription}
url = reverse('horizon:nova:instances_and_volumes:volumes:'
'create_snapshot', args=[volume.id])
res = self.client.post(url, formData)
self.assertRedirectsNoFollow(res, INDEX_URL)

View File

@ -30,114 +30,87 @@ from horizon import test
INDEX_URL = reverse('horizon:nova:instances_and_volumes:index')
class InstanceViewTests(test.BaseViewTests):
class InstanceViewTests(test.TestCase):
def setUp(self):
super(InstanceViewTests, self).setUp()
self.now = self.override_times()
server = api.Server(None, self.request)
server.id = "1"
server.name = 'serverName'
server.status = "ACTIVE"
server.flavor = {'id': '1'}
flavor = api.nova.Flavor(None)
flavor.id = '1'
volume = api.Volume(self.request)
volume.id = "1"
self.servers = (server,)
self.volumes = (volume,)
self.flavors = (flavor,)
def tearDown(self):
super(InstanceViewTests, self).tearDown()
self.reset_times()
def test_terminate_instance(self):
server = self.servers.first()
self.mox.StubOutWithMock(api, 'server_list')
self.mox.StubOutWithMock(api, 'flavor_list')
self.mox.StubOutWithMock(api, 'server_delete')
api.server_list(IsA(http.HttpRequest)).AndReturn(self.servers)
api.flavor_list(IgnoreArg()).AndReturn(self.flavors)
api.server_delete(IsA(http.HttpRequest),
self.servers[0].id)
api.server_list(IsA(http.HttpRequest)).AndReturn(self.servers.list())
api.flavor_list(IgnoreArg()).AndReturn(self.flavors.list())
api.server_delete(IsA(http.HttpRequest), server.id)
self.mox.ReplayAll()
formData = {'action': 'instances__terminate__%s' % self.servers[0].id}
formData = {'action': 'instances__terminate__%s' % server.id}
res = self.client.post(INDEX_URL, formData)
self.assertRedirectsNoFollow(res, INDEX_URL)
def test_terminate_instance_exception(self):
server = self.servers.first()
self.mox.StubOutWithMock(api, 'server_list')
self.mox.StubOutWithMock(api, 'flavor_list')
self.mox.StubOutWithMock(api, 'server_delete')
api.server_list(IsA(http.HttpRequest)).AndReturn(self.servers)
api.flavor_list(IgnoreArg()).AndReturn(self.flavors)
exception = nova_exceptions.ClientException(500)
api.server_delete(IsA(http.HttpRequest),
self.servers[0].id).AndRaise(exception)
api.server_list(IsA(http.HttpRequest)).AndReturn(self.servers.list())
api.flavor_list(IgnoreArg()).AndReturn(self.flavors.list())
exc = nova_exceptions.ClientException(500)
api.server_delete(IsA(http.HttpRequest), server.id).AndRaise(exc)
self.mox.ReplayAll()
formData = {'action': 'instances__terminate__%s' % self.servers[0].id}
formData = {'action': 'instances__terminate__%s' % server.id}
res = self.client.post(INDEX_URL, formData)
self.assertRedirectsNoFollow(res, INDEX_URL)
def test_reboot_instance(self):
server = self.servers.first()
self.mox.StubOutWithMock(api, 'server_reboot')
self.mox.StubOutWithMock(api, 'server_list')
api.server_list(IsA(http.HttpRequest)).AndReturn(self.servers)
api.server_reboot(IsA(http.HttpRequest), unicode(self.servers[0].id))
api.server_list(IsA(http.HttpRequest)).AndReturn(self.servers.list())
api.server_reboot(IsA(http.HttpRequest), server.id)
self.mox.ReplayAll()
formData = {'action': 'instances__reboot__%s' % self.servers[0].id}
formData = {'action': 'instances__reboot__%s' % server.id}
res = self.client.post(INDEX_URL, formData)
self.assertRedirectsNoFollow(res, INDEX_URL)
def test_reboot_instance_exception(self):
server = self.servers.first()
self.mox.StubOutWithMock(api, 'server_reboot')
self.mox.StubOutWithMock(api, 'server_list')
api.server_list(IsA(http.HttpRequest)).AndReturn(self.servers)
exception = nova_exceptions.ClientException(500)
api.server_reboot(IsA(http.HttpRequest),
unicode(self.servers[0].id)).AndRaise(exception)
api.server_list(IsA(http.HttpRequest)).AndReturn(self.servers.list())
exc = nova_exceptions.ClientException(500)
api.server_reboot(IsA(http.HttpRequest), server.id).AndRaise(exc)
self.mox.ReplayAll()
formData = {'action': 'instances__reboot__%s' % self.servers[0].id}
formData = {'action': 'instances__reboot__%s' % server.id}
res = self.client.post(INDEX_URL, formData)
self.assertRedirectsNoFollow(res, INDEX_URL)
def test_instance_console(self):
server = self.servers.first()
CONSOLE_OUTPUT = 'output'
INSTANCE_ID = self.servers[0].id
self.mox.StubOutWithMock(api, 'server_console_output')
api.server_console_output(IsA(http.HttpRequest),
unicode(INSTANCE_ID),
server.id,
tail_length=None).AndReturn(CONSOLE_OUTPUT)
self.mox.ReplayAll()
res = self.client.get(
reverse('horizon:nova:instances_and_volumes:instances:console',
args=[INSTANCE_ID]))
url = reverse('horizon:nova:instances_and_volumes:instances:console',
args=[server.id])
res = self.client.get(url)
self.assertIsInstance(res, http.HttpResponse)
self.assertContains(res, CONSOLE_OUTPUT)
def test_instance_vnc(self):
INSTANCE_ID = self.servers[0].id
server = self.servers.first()
CONSOLE_OUTPUT = '/vncserver'
console_mock = self.mox.CreateMock(api.VNCConsole)
@ -145,113 +118,88 @@ class InstanceViewTests(test.BaseViewTests):
self.mox.StubOutWithMock(api, 'server_vnc_console')
self.mox.StubOutWithMock(api, 'server_get')
api.server_get(IsA(http.HttpRequest),
str(self.servers[0].id)).AndReturn(self.servers[0])
api.server_vnc_console(IgnoreArg(),
unicode(INSTANCE_ID)).AndReturn(console_mock)
api.server_get(IsA(http.HttpRequest), server.id).AndReturn(server)
api.server_vnc_console(IgnoreArg(), server.id).AndReturn(console_mock)
self.mox.ReplayAll()
res = self.client.get(
reverse('horizon:nova:instances_and_volumes:instances:vnc',
args=[INSTANCE_ID]))
self.assertRedirectsNoFollow(res,
CONSOLE_OUTPUT + '&title=serverName(1)')
url = reverse('horizon:nova:instances_and_volumes:instances:vnc',
args=[server.id])
res = self.client.get(url)
redirect = CONSOLE_OUTPUT + '&title=%s(1)' % server.name
self.assertRedirectsNoFollow(res, redirect)
def test_instance_vnc_exception(self):
INSTANCE_ID = self.servers[0].id
exception = nova_exceptions.ClientException(500)
server = self.servers.first()
self.mox.StubOutWithMock(api, 'server_vnc_console')
api.server_vnc_console(IsA(http.HttpRequest),
unicode(INSTANCE_ID)).AndRaise(exception)
exc = nova_exceptions.ClientException(500)
api.server_vnc_console(IsA(http.HttpRequest), server.id).AndRaise(exc)
self.mox.ReplayAll()
res = self.client.get(
reverse('horizon:nova:instances_and_volumes:instances:vnc',
args=[INSTANCE_ID]))
url = reverse('horizon:nova:instances_and_volumes:instances:vnc',
args=[server.id])
res = self.client.get(url)
self.assertRedirectsNoFollow(res, INDEX_URL)
def test_instance_update_get(self):
INSTANCE_ID = self.servers[0].id
server = self.servers.first()
self.mox.StubOutWithMock(api, 'server_get')
api.server_get(IsA(http.HttpRequest),
unicode(INSTANCE_ID)).AndReturn(self.servers[0])
api.server_get(IsA(http.HttpRequest), server.id).AndReturn(server)
self.mox.ReplayAll()
res = self.client.get(
reverse('horizon:nova:instances_and_volumes:instances:update',
args=[INSTANCE_ID]))
url = reverse('horizon:nova:instances_and_volumes:instances:update',
args=[server.id])
res = self.client.get(url)
self.assertTemplateUsed(res,
'nova/instances_and_volumes/instances/update.html')
def test_instance_update_get_server_get_exception(self):
INSTANCE_ID = self.servers[0].id
exception = nova_exceptions.ClientException(500)
server = self.servers.first()
self.mox.StubOutWithMock(api, 'server_get')
api.server_get(IsA(http.HttpRequest),
unicode(INSTANCE_ID)).AndRaise(exception)
exc = nova_exceptions.ClientException(500)
api.server_get(IsA(http.HttpRequest), server.id).AndRaise(exc)
self.mox.ReplayAll()
res = self.client.get(
reverse('horizon:nova:instances_and_volumes:instances:update',
args=[INSTANCE_ID]))
url = reverse('horizon:nova:instances_and_volumes:instances:update',
args=[server.id])
res = self.client.get(url)
self.assertRedirectsNoFollow(res, INDEX_URL)
def test_instance_update_post(self):
INSTANCE_ID = self.servers[0].id
NAME = 'myname'
formData = {'method': 'UpdateInstance',
'instance': self.servers[0].id,
'name': NAME,
'tenant_id': self.TEST_TENANT}
server = self.servers.first()
self.mox.StubOutWithMock(api, 'server_get')
api.server_get(IsA(http.HttpRequest),
unicode(INSTANCE_ID)).AndReturn(self.servers[0])
self.mox.StubOutWithMock(api, 'server_update')
api.server_update(IsA(http.HttpRequest),
str(INSTANCE_ID), NAME)
api.server_get(IsA(http.HttpRequest), server.id).AndReturn(server)
api.server_update(IsA(http.HttpRequest), server.id, server.name)
self.mox.ReplayAll()
res = self.client.post(
reverse('horizon:nova:instances_and_volumes:instances:update',
args=[INSTANCE_ID]), formData)
formData = {'method': 'UpdateInstance',
'instance': server.id,
'name': server.name,
'tenant_id': self.tenant.id}
url = reverse('horizon:nova:instances_and_volumes:instances:update',
args=[server.id])
res = self.client.post(url, formData)
self.assertRedirectsNoFollow(res, INDEX_URL)
def test_instance_update_post_api_exception(self):
SERVER = self.servers[0]
server = self.servers.first()
self.mox.StubOutWithMock(api, 'server_get')
self.mox.StubOutWithMock(api, 'server_update')
api.server_get(IsA(http.HttpRequest), unicode(SERVER.id)) \
.AndReturn(self.servers[0])
exception = nova_exceptions.ClientException(500)
api.server_update(IsA(http.HttpRequest), str(SERVER.id), SERVER.name) \
.AndRaise(exception)
api.server_get(IsA(http.HttpRequest), server.id).AndReturn(server)
exc = nova_exceptions.ClientException(500)
api.server_update(IsA(http.HttpRequest), server.id, server.name) \
.AndRaise(exc)
self.mox.ReplayAll()
formData = {'method': 'UpdateInstance',
'instance': SERVER.id,
'name': SERVER.name,
'tenant_id': self.TEST_TENANT}
'instance': server.id,
'name': server.name,
'tenant_id': self.tenant.id}
url = reverse('horizon:nova:instances_and_volumes:instances:update',
args=[SERVER.id])
args=[server.id])
res = self.client.post(url, formData)
self.assertRedirectsNoFollow(res, INDEX_URL)

View File

@ -27,27 +27,12 @@ from horizon import api
from horizon import test
class InstancesAndVolumesViewTest(test.BaseViewTests):
def setUp(self):
super(InstancesAndVolumesViewTest, self).setUp()
server = api.Server(None, self.request)
server.id = 1
server.name = 'serverName'
server.status = "ACTIVE"
volume = api.Volume(self.request)
volume.id = 1
volume.size = 10
volume.attachments = [{}]
self.servers = (server,)
self.volumes = (volume,)
class InstancesAndVolumesViewTest(test.TestCase):
def test_index(self):
self.mox.StubOutWithMock(api, 'server_list')
self.mox.StubOutWithMock(api, 'volume_list')
api.server_list(IsA(http.HttpRequest)).AndReturn(self.servers)
api.volume_list(IsA(http.HttpRequest)).AndReturn(self.volumes)
api.server_list(IsA(http.HttpRequest)).AndReturn(self.servers.list())
api.volume_list(IsA(http.HttpRequest)).AndReturn(self.volumes.list())
self.mox.ReplayAll()
@ -57,14 +42,14 @@ class InstancesAndVolumesViewTest(test.BaseViewTests):
self.assertTemplateUsed(res,
'nova/instances_and_volumes/index.html')
instances = res.context['instances_table'].data
self.assertItemsEqual(instances, self.servers)
self.assertItemsEqual(instances, self.servers.list())
def test_index_server_list_exception(self):
self.mox.StubOutWithMock(api, 'server_list')
self.mox.StubOutWithMock(api, 'volume_list')
exception = novaclient_exceptions.ClientException('apiException')
api.server_list(IsA(http.HttpRequest)).AndRaise(exception)
api.volume_list(IsA(http.HttpRequest)).AndReturn(self.volumes)
api.volume_list(IsA(http.HttpRequest)).AndReturn(self.volumes.list())
self.mox.ReplayAll()

View File

@ -19,17 +19,18 @@
# under the License.
from django import http
from django.contrib import messages
from django.core.urlresolvers import reverse
from mox import IgnoreArg, IsA
from mox import IsA
from horizon import api
from horizon import test
class NetworkViewTests(test.BaseViewTests):
class NetworkViewTests(test.TestCase):
def setUp(self):
super(NetworkViewTests, self).setUp()
# TODO(gabriel): Move this to horizon.tests.test_data.quantum_data
# after the wrapper classes are added for Quantum.
self.network = {}
self.network['networks'] = []
self.network['networks'].append({'id': 'n1'})
@ -186,9 +187,6 @@ class NetworkViewTests(test.BaseViewTests):
'network': 'n1',
'method': 'CreatePort'}
self.mox.StubOutWithMock(messages, 'success')
messages.success(IgnoreArg(), IsA(basestring))
self.mox.ReplayAll()
res = self.client.post(reverse('horizon:nova:networks:port_create',
@ -226,9 +224,6 @@ class NetworkViewTests(test.BaseViewTests):
formData = {'action': 'network_details__delete__p1'}
self.mox.StubOutWithMock(messages, 'success')
messages.success(IgnoreArg(), IsA(basestring))
self.mox.ReplayAll()
detail_url = reverse('horizon:nova:networks:detail', args=["n1"])
@ -292,9 +287,6 @@ class NetworkViewTests(test.BaseViewTests):
formData = {'action': "network_details__detach_port__p1"}
self.mox.StubOutWithMock(messages, 'success')
messages.success(IgnoreArg(), IsA(basestring))
self.mox.ReplayAll()
detail_url = reverse('horizon:nova:networks:detail', args=["n1"])

View File

@ -24,7 +24,6 @@ from django import http
from django.core.urlresolvers import reverse
from mox import IsA
from novaclient import exceptions as nova_exceptions
from novaclient.v1_1 import usage as nova_usage
from horizon import api
from horizon import test
@ -32,137 +31,83 @@ from horizon import usage
INDEX_URL = reverse('horizon:nova:overview:index')
USAGE_DATA = {
'total_memory_mb_usage': 64246.89777777778,
'total_vcpus_usage': 125.48222222222223,
'total_hours': 125.48222222222223,
'total_local_gb_usage': 0.0,
'tenant_id': u'99e7c0197c3643289d89e9854469a4ae',
'stop': u'2012-01-3123: 30: 46',
'start': u'2012-01-0100: 00: 00',
'server_usages': [
{
u'memory_mb': 512,
u'uptime': 442321,
u'started_at': u'2012-01-2620: 38: 21',
u'ended_at': None,
u'name': u'testing',
u'tenant_id': u'99e7c0197c3643289d89e9854469a4ae',
u'state': u'active',
u'hours': 122.87361111111112,
u'vcpus': 1,
u'flavor': u'm1.tiny',
u'local_gb': 0
},
{
u'memory_mb': 512,
u'uptime': 9367,
u'started_at': u'2012-01-3120: 54: 15',
u'ended_at': None,
u'name': u'instance2',
u'tenant_id': u'99e7c0197c3643289d89e9854469a4ae',
u'state': u'active',
u'hours': 2.608611111111111,
u'vcpus': 1,
u'flavor': u'm1.tiny',
u'local_gb': 0
}
]
}
class UsageViewTests(test.BaseViewTests):
def setUp(self):
super(UsageViewTests, self).setUp()
usage_resource = nova_usage.Usage(nova_usage.UsageManager, USAGE_DATA)
self.usage = api.nova.Usage(usage_resource)
self.usages = (self.usage,)
class UsageViewTests(test.TestCase):
def tearDown(self):
super(UsageViewTests, self).tearDown()
self.reset_times()
self.reset_times() # override_times is called in the tests
def test_usage(self):
now = self.override_times()
usage_obj = api.nova.Usage(self.usages.first())
self.mox.StubOutWithMock(api, 'usage_get')
api.usage_get(IsA(http.HttpRequest), self.TEST_TENANT,
api.usage_get(IsA(http.HttpRequest), self.tenant.id,
datetime.datetime(now.year, now.month, 1,
now.hour, now.minute, now.second),
datetime.datetime(now.year, now.month, now.day, now.hour,
now.minute, now.second)) \
.AndReturn(self.usage)
.AndReturn(usage_obj)
self.mox.ReplayAll()
res = self.client.get(reverse('horizon:nova:overview:index'))
self.assertTemplateUsed(res, 'nova/overview/usage.html')
self.assertTrue(isinstance(res.context['usage'], usage.TenantUsage))
self.assertContains(res, 'form-horizontal')
def test_usage_csv(self):
now = self.override_times()
usage_obj = api.nova.Usage(self.usages.first())
self.mox.StubOutWithMock(api, 'usage_get')
timestamp = datetime.datetime(now.year, now.month, 1,
now.hour, now.minute,
now.second)
api.usage_get(IsA(http.HttpRequest),
self.TEST_TENANT,
self.tenant.id,
timestamp,
datetime.datetime(now.year, now.month, now.day, now.hour,
now.minute, now.second)) \
.AndReturn(self.usage)
.AndReturn(usage_obj)
self.mox.ReplayAll()
res = self.client.get(reverse('horizon:nova:overview:index') +
"?format=csv")
self.assertTemplateUsed(res, 'nova/overview/usage.csv')
self.assertTrue(isinstance(res.context['usage'], usage.TenantUsage))
def test_usage_exception(self):
now = self.override_times()
self.mox.StubOutWithMock(api, 'usage_get')
timestamp = datetime.datetime(now.year, now.month, 1, now.hour,
now.minute, now.second)
exception = nova_exceptions.ClientException(500)
api.usage_get(IsA(http.HttpRequest),
self.TEST_TENANT,
self.tenant.id,
timestamp,
datetime.datetime(now.year, now.month, now.day, now.hour,
now.minute, now.second)) \
.AndRaise(exception)
self.mox.ReplayAll()
res = self.client.get(reverse('horizon:nova:overview:index'))
self.assertTemplateUsed(res, 'nova/overview/usage.html')
self.assertEqual(res.context['usage'].usage_list, [])
def test_usage_default_tenant(self):
now = self.override_times()
usage_obj = api.nova.Usage(self.usages.first())
self.mox.StubOutWithMock(api, 'usage_get')
timestamp = datetime.datetime(now.year, now.month, 1,
now.hour, now.minute,
now.second)
api.usage_get(IsA(http.HttpRequest),
self.TEST_TENANT,
self.tenant.id,
timestamp,
datetime.datetime(now.year, now.month, now.day, now.hour,
now.minute, now.second)) \
.AndReturn(self.usage)
.AndReturn(usage_obj)
self.mox.ReplayAll()
res = self.client.get(reverse('horizon:nova:overview:index'))
self.assertTemplateUsed(res, 'nova/overview/usage.html')
self.assertTrue(isinstance(res.context['usage'], usage.TenantUsage))

View File

@ -24,37 +24,20 @@ from horizon import test
class InstanceViewTest(test.BaseAdminViewTests):
def setUp(self):
super(InstanceViewTest, self).setUp()
self.server = api.Server(None, self.request)
self.server.id = 1
self.server.name = 'serverName'
self.server.status = "ACTIVE"
self.server.flavor = {'id': '1'}
self.flavor = api.nova.Flavor(None)
self.flavor.id = '1'
self.flavor.ram = 512
self.flavor.vcpus = 512
self.flavor.disk = 1
self.servers = (self.server,)
self.flavors = (self.flavor,)
def test_index(self):
servers = self.servers.list()
flavors = self.flavors.list()
self.mox.StubOutWithMock(api.nova, 'server_list')
self.mox.StubOutWithMock(api.nova, 'flavor_list')
api.nova.server_list(IsA(http.HttpRequest),
all_tenants=True).AndReturn(self.servers)
api.nova.flavor_list(IsA(http.HttpRequest)).AndReturn(self.flavors)
all_tenants=True).AndReturn(servers)
api.nova.flavor_list(IsA(http.HttpRequest)).AndReturn(flavors)
self.mox.ReplayAll()
res = self.client.get(reverse('horizon:syspanel:instances:index'))
self.assertTemplateUsed(res, 'syspanel/instances/index.html')
instances = res.context['table'].data
self.assertItemsEqual(instances, self.servers)
self.assertItemsEqual(instances, servers)
def test_index_server_list_exception(self):
self.mox.StubOutWithMock(api.nova, 'server_list')
@ -66,6 +49,5 @@ class InstanceViewTest(test.BaseAdminViewTests):
self.mox.ReplayAll()
res = self.client.get(reverse('horizon:syspanel:instances:index'))
self.assertTemplateUsed(res, 'syspanel/instances/index.html')
self.assertEqual(len(res.context['instances_table'].data), 0)

View File

@ -25,71 +25,39 @@ from horizon import test
INDEX_URL = reverse('horizon:syspanel:projects:index')
class FakeResource(object):
def __init__(self, **kwargs):
for key, value in kwargs.items():
setattr(self, key, value)
class TenantsViewTests(test.BaseAdminViewTests):
def setUp(self):
super(TenantsViewTests, self).setUp()
self.tenant = FakeResource(id=self.TEST_TENANT,
name=self.TEST_TENANT_NAME,
enabled=True)
self.quota_data = dict(metadata_items='1',
injected_file_content_bytes='1',
volumes='1',
gigabytes='1',
ram=1,
floating_ips='1',
instances='1',
injected_files='1',
cores='1')
self.quota = FakeResource(id=self.TEST_TENANT, **self.quota_data)
self.tenants = [self.tenant]
def test_index(self):
self.mox.StubOutWithMock(api, 'tenant_list')
api.tenant_list(IsA(http.HttpRequest)).AndReturn(self.tenants)
api.tenant_list(IsA(http.HttpRequest)).AndReturn(self.tenants.list())
self.mox.ReplayAll()
res = self.client.get(INDEX_URL)
self.assertTemplateUsed(res, 'syspanel/projects/index.html')
self.assertItemsEqual(res.context['table'].data, self.tenants)
self.assertItemsEqual(res.context['table'].data, self.tenants.list())
def test_modify_quota(self):
tenant = self.tenants.first()
quota = self.quotas.first()
quota_data = {"metadata_items": '1',
"injected_files": '1',
"injected_file_content_bytes": '1',
"cores": '1',
"instances": '1',
"volumes": '1',
"gigabytes": '1',
"ram": 1,
"floating_ips": '1'}
self.mox.StubOutWithMock(api.keystone, 'tenant_get')
self.mox.StubOutWithMock(api.nova, 'tenant_quota_get')
self.mox.StubOutWithMock(api.nova, 'tenant_quota_update')
api.keystone.tenant_get(IgnoreArg(), self.TEST_TENANT) \
.AndReturn(self.tenant)
api.nova.tenant_quota_get(IgnoreArg(), self.TEST_TENANT) \
.AndReturn(self.quota)
api.nova.tenant_quota_update(IgnoreArg(),
self.TEST_TENANT,
**self.quota_data)
api.keystone.tenant_get(IgnoreArg(), tenant.id).AndReturn(tenant)
api.nova.tenant_quota_get(IgnoreArg(), tenant.id).AndReturn(quota)
api.nova.tenant_quota_update(IgnoreArg(), tenant.id, **quota_data)
self.mox.ReplayAll()
url = reverse('horizon:syspanel:projects:quotas',
args=(self.TEST_TENANT,))
data = {"method": "UpdateQuotas",
"tenant_id": self.TEST_TENANT,
"metadata_items": '1',
"injected_files": '1',
"injected_file_content_bytes": '1',
"cores": '1',
"instances": '1',
"volumes": '1',
"gigabytes": '1',
"ram": 1,
"floating_ips": '1'}
res = self.client.post(url, data)
args=[self.tenant.id])
quota_data.update({"method": "UpdateQuotas",
"tenant_id": self.tenant.id})
res = self.client.post(url, quota_data)
self.assertRedirectsNoFollow(res, INDEX_URL)

View File

@ -19,8 +19,8 @@
# under the License.
from django.core.urlresolvers import reverse
from mox import IgnoreArg
from keystoneclient import exceptions as keystone_exceptions
from mox import IgnoreArg
from horizon import api
from horizon import test
@ -30,68 +30,50 @@ USERS_INDEX_URL = reverse('horizon:syspanel:users:index')
class UsersViewTests(test.BaseAdminViewTests):
def setUp(self):
super(UsersViewTests, self).setUp()
self.user = api.User(None)
self.user.enabled = True
self.user.id = self.TEST_USER_ID
self.user.name = self.TEST_USER
self.user.roles = self.TEST_ROLES
self.user.tenantId = self.TEST_TENANT
self.users = [self.user]
def test_index(self):
self.mox.StubOutWithMock(api, 'user_list')
api.user_list(IgnoreArg()).AndReturn(self.users)
api.user_list(IgnoreArg()).AndReturn(self.users.list())
self.mox.ReplayAll()
res = self.client.get(USERS_INDEX_URL)
self.assertTemplateUsed(res, 'syspanel/users/index.html')
self.assertItemsEqual(res.context['table'].data, self.users)
self.assertItemsEqual(res.context['table'].data, self.users.list())
def test_enable_user(self):
formData = {'action': 'users__enable__%s' % self.user.id}
user = self.users.get(id="2")
self.mox.StubOutWithMock(api.keystone, 'user_update_enabled')
api.keystone.user_update_enabled(IgnoreArg(), self.user.id, True) \
.AndReturn(self.mox.CreateMock(api.User))
api.keystone.user_update_enabled(IgnoreArg(),
user.id,
True).AndReturn(user)
self.mox.ReplayAll()
formData = {'action': 'users__enable__%s' % user.id}
res = self.client.post(USERS_INDEX_URL, formData)
self.assertRedirects(res, USERS_INDEX_URL)
def test_disable_user(self):
OTHER_USER_ID = '5'
formData = {'action': 'users__disable__%s' % OTHER_USER_ID}
user = self.users.get(id="2")
self.mox.StubOutWithMock(api.keystone, 'user_update_enabled')
api.keystone.user_update_enabled(IgnoreArg(), OTHER_USER_ID, False) \
.AndReturn(self.mox.CreateMock(api.User))
api.keystone.user_update_enabled(IgnoreArg(),
user.id,
False).AndReturn(user)
self.mox.ReplayAll()
formData = {'action': 'users__disable__%s' % user.id}
res = self.client.post(USERS_INDEX_URL, formData)
self.assertRedirects(res, USERS_INDEX_URL)
def test_enable_disable_user_exception(self):
OTHER_USER_ID = '5'
formData = {'action': 'users__enable__%s' % OTHER_USER_ID}
user = self.users.get(id="2")
self.mox.StubOutWithMock(api.keystone, 'user_update_enabled')
api_exception = keystone_exceptions.ClientException('apiException',
message='apiException')
api.keystone.user_update_enabled(IgnoreArg(), OTHER_USER_ID, True) \
.AndRaise(api_exception)
api.keystone.user_update_enabled(IgnoreArg(),
user.id,
True).AndRaise(api_exception)
self.mox.ReplayAll()
formData = {'action': 'users__enable__%s' % user.id}
res = self.client.post(USERS_INDEX_URL, formData)
self.assertRedirects(res, USERS_INDEX_URL)
@ -99,10 +81,10 @@ class UsersViewTests(test.BaseAdminViewTests):
def test_shoot_yourself_in_the_foot(self):
self.mox.StubOutWithMock(api, 'user_list')
# Four times... one for each post and one for each followed redirect
api.user_list(IgnoreArg()).AndReturn(self.users)
api.user_list(IgnoreArg()).AndReturn(self.users)
api.user_list(IgnoreArg()).AndReturn(self.users)
api.user_list(IgnoreArg()).AndReturn(self.users)
api.user_list(IgnoreArg()).AndReturn(self.users.list())
api.user_list(IgnoreArg()).AndReturn(self.users.list())
api.user_list(IgnoreArg()).AndReturn(self.users.list())
api.user_list(IgnoreArg()).AndReturn(self.users.list())
self.mox.ReplayAll()
@ -115,4 +97,5 @@ class UsersViewTests(test.BaseAdminViewTests):
formData = {'action': 'users__delete__%s' % self.request.user.id}
res = self.client.post(USERS_INDEX_URL, formData, follow=True)
self.assertEqual(list(res.context['messages'])[0].message,
u'You do not have permission to delete user: test')
u'You do not have permission to delete user: %s'
% self.request.user.username)

View File

@ -32,6 +32,81 @@ from novaclient import exceptions as novaclient
LOG = logging.getLogger(__name__)
class HorizonException(Exception):
""" Base exception class for distinguishing our own exception classes. """
pass
class Http302(HorizonException):
"""
Error class which can be raised from within a handler to cause an
early bailout and redirect at the middleware level.
"""
status_code = 302
def __init__(self, location, message=None):
self.location = location
self.message = message
class NotAuthorized(HorizonException):
"""
Raised whenever a user attempts to access a resource which they do not
have role-based access to (such as when failing the
:func:`~horizon.decorators.require_roles` decorator).
The included :class:`~horizon.middleware.HorizonMiddleware` catches
``NotAuthorized`` and handles it gracefully by displaying an error
message and redirecting the user to a login page.
"""
status_code = 401
class NotFound(HorizonException):
""" Generic error to replace all "Not Found"-type API errors. """
status_code = 404
class RecoverableError(HorizonException):
""" Generic error to replace any "Recoverable"-type API errors. """
status_code = 100 # HTTP status code "Continue"
class ServiceCatalogException(HorizonException):
"""
Raised when a requested service is not available in the ``ServiceCatalog``
returned by Keystone.
"""
def __init__(self, service_name):
message = 'Invalid service catalog service: %s' % service_name
super(ServiceCatalogException, self).__init__(message)
class AlreadyExists(HorizonException):
"""
Exception to be raised when trying to create an API resource which
already exists.
"""
def __init__(self, name, resource_type):
self.attrs = {"name": name, "resource": resource_type}
self.msg = 'A %(resource)s with the name "%(name)s" already exists.'
def __repr__(self):
return self.msg % self.attrs
def __unicode__(self):
return _(self.msg) % self.attrs
class HandledException(HorizonException):
"""
Used internally to track exceptions that have gone through
:func:`horizon.exceptions.handle` more than once.
"""
def __init__(self, wrapped):
self.wrapped = wrapped
UNAUTHORIZED = (keystoneclient.Unauthorized,
keystoneclient.Forbidden,
novaclient.Unauthorized,
@ -51,61 +126,8 @@ NOT_FOUND = (keystoneclient.NotFound,
RECOVERABLE = (keystoneclient.ClientException,
novaclient.ClientException,
glanceclient.GlanceException,
swiftclient.Error)
class Http302(Exception):
"""
Error class which can be raised from within a handler to cause an
early bailout and redirect at the middleware level.
"""
status_code = 302
def __init__(self, location, message=None):
self.location = location
self.message = message
class NotAuthorized(Exception):
"""
Raised whenever a user attempts to access a resource which they do not
have role-based access to (such as when failing the
:func:`~horizon.decorators.require_roles` decorator).
The included :class:`~horizon.middleware.HorizonMiddleware` catches
``NotAuthorized`` and handles it gracefully by displaying an error
message and redirecting the user to a login page.
"""
status_code = 401
class NotFound(Exception):
""" Generic error to replace all "Not Found"-type API errors. """
status_code = 404
class RecoverableError(Exception):
""" Generic error to replace any "Recoverable"-type API errors. """
status_code = 100 # HTTP status code "Continue"
class ServiceCatalogException(Exception):
"""
Raised when a requested service is not available in the ``ServiceCatalog``
returned by Keystone.
"""
def __init__(self, service_name):
message = 'Invalid service catalog service: %s' % service_name
super(ServiceCatalogException, self).__init__(message)
class HandledException(Exception):
"""
Used internally to track exceptions that have gone through
:func:`horizon.exceptions.handle` more than once.
"""
def __init__(self, wrapped):
self.wrapped = wrapped
swiftclient.Error,
AlreadyExists)
def handle(request, message=None, redirect=None, ignore=False, escalate=False):
@ -149,8 +171,11 @@ def handle(request, message=None, redirect=None, ignore=False, escalate=False):
exc_type, exc_value, exc_traceback = exc_value.wrapped
wrap = True
# We trust messages from our own exceptions
if issubclass(exc_type, HorizonException):
message = exc_value
# If the message has a placeholder for the exception, fill it in
if message and "%(exc)s" in message:
elif message and "%(exc)s" in message:
message = message % {"exc": exc_value}
if issubclass(exc_type, UNAUTHORIZED):

View File

@ -20,18 +20,24 @@
import datetime
import cloudfiles as swift_client
from django import http
from django import test as django_test
from django.conf import settings
from django.contrib.messages.storage import default_storage
from django.core.handlers import wsgi
from django.test.client import RequestFactory
from glance import client as glance_client
from keystoneclient.v2_0 import client as keystone_client
from novaclient.v1_1 import client as nova_client
import httplib2
import mox
from horizon import api
from horizon import context_processors
from horizon import middleware
from horizon import users
from horizon.tests.test_data.utils import load_test_data
from .time import time
from .time import today
@ -51,85 +57,49 @@ class RequestFactoryWithMessages(RequestFactory):
class TestCase(django_test.TestCase):
TEST_STAFF_USER = 'staffUser'
TEST_TENANT = '1'
TEST_TENANT_NAME = 'aTenant'
TEST_TOKEN = 'aToken'
TEST_USER = 'test'
TEST_USER_ID = '1'
TEST_ROLES = [{'name': 'admin', 'id': '1'}]
TEST_CONTEXT = {'authorized_tenants': [{'enabled': True,
'name': 'aTenant',
'id': '1',
'description': "None"}]}
TEST_SERVICE_CATALOG = [
{"endpoints": [{
"adminURL": "http://cdn.admin-nets.local:8774/v1.0",
"region": "RegionOne",
"internalURL": "http://127.0.0.1:8774/v1.0",
"publicURL": "http://cdn.admin-nets.local:8774/v1.0/"}],
"type": "nova_compat",
"name": "nova_compat"},
{"endpoints": [{
"adminURL": "http://nova/novapi/admin",
"region": "RegionOne",
"internalURL": "http://nova/novapi/internal",
"publicURL": "http://nova/novapi/public"}],
"type": "compute",
"name": "nova"},
{"endpoints": [{
"adminURL": "http://glance/glanceapi/admin",
"region": "RegionOne",
"internalURL": "http://glance/glanceapi/internal",
"publicURL": "http://glance/glanceapi/public"}],
"type": "image",
"name": "glance"},
{"endpoints": [{
"adminURL": "http://cdn.admin-nets.local:35357/v2.0",
"region": "RegionOne",
"internalURL": "http://127.0.0.1:5000/v2.0",
"publicURL": "http://cdn.admin-nets.local:5000/v2.0"}],
"type": "identity",
"name": "identity"},
{"endpoints": [{
"adminURL": "http://example.com:9696/quantum",
"region": "RegionOne",
"internalURL": "http://example.com:9696/quantum",
"publicURL": "http://example.com:9696/quantum"}],
"type": "network",
"name": "quantum"},
{"endpoints": [{
"adminURL": "http://swift/swiftapi/admin",
"region": "RegionOne",
"internalURL": "http://swift/swiftapi/internal",
"publicURL": "http://swift/swiftapi/public"}],
"type": "object-store",
"name": "swift"}]
"""
Specialized base test case class for Horizon which gives access to
numerous additional features:
* A full suite of test data through various attached objects and
managers (e.g. ``self.servers``, ``self.user``, etc.). See the
docs for :class:`~horizon.tests.test_data.utils.TestData` for more
information.
* The ``mox`` mocking framework via ``self.mox``.
* A set of request context data via ``self.context``.
* A ``RequestFactory`` class which supports Django's ``contrib.messages``
framework via ``self.factory``.
* A ready-to-go request object via ``self.request``.
* The ability to override specific time data controls for easier testing.
* Several handy additional assertion methods.
"""
def setUp(self):
load_test_data(self)
self.mox = mox.Mox()
self.factory = RequestFactoryWithMessages()
self.context = {'authorized_tenants': self.tenants.list()}
def fake_conn_request(*args, **kwargs):
raise Exception("An external URI request tried to escape through "
"an httplib2 client. Args: %s, kwargs: %s"
% (args, kwargs))
self._real_conn_request = httplib2.Http._conn_request
httplib2.Http._conn_request = fake_conn_request
self._real_horizon_context_processor = context_processors.horizon
context_processors.horizon = lambda request: self.TEST_CONTEXT
context_processors.horizon = lambda request: self.context
self._real_get_user_from_request = users.get_user_from_request
tenants = self.TEST_CONTEXT['authorized_tenants']
self.setActiveUser(token=self.TEST_TOKEN,
username=self.TEST_USER,
tenant_id=self.TEST_TENANT,
service_catalog=self.TEST_SERVICE_CATALOG,
tenants = self.context['authorized_tenants']
self.setActiveUser(token=self.token.id,
username=self.user.name,
tenant_id=self.tenant.id,
service_catalog=self.service_catalog,
authorized_tenants=tenants)
self.request = http.HttpRequest()
self.request.session = self.client._session()
self.request.session['token'] = self.token.id
middleware.HorizonMiddleware().process_request(self.request)
def tearDown(self):
@ -151,41 +121,150 @@ class TestCase(django_test.TestCase):
authorized_tenants=authorized_tenants)
def override_times(self):
""" Overrides the "current" time with immutable values. """
now = datetime.datetime.utcnow()
time.override_time = \
datetime.time(now.hour, now.minute, now.second)
today.override_time = datetime.date(now.year, now.month, now.day)
utcnow.override_time = now
return now
def reset_times(self):
""" Undoes the changes made by ``override_times``. """
time.override_time = None
today.override_time = None
utcnow.override_time = None
class BaseViewTests(TestCase):
"""
Base class for view based unit tests.
"""
def assertRedirectsNoFollow(self, response, expected_url):
"""
Asserts that the given response issued a 302 redirect without
processing the view which is redirected to.
"""
if response.status_code / 100 != 3:
assert("The response did not return a redirect.")
self.assertEqual(response._headers.get('location', None),
('Location', settings.TESTSERVER + expected_url))
self.assertEqual(response.status_code, 302)
def assertNoMessages(self):
"""
Asserts that no messages have been attached by the ``contrib.messages``
framework.
"""
self.assertMessageCount(success=0, warn=0, info=0, error=0)
class BaseAdminViewTests(BaseViewTests):
def assertMessageCount(self, **kwargs):
"""
Asserts that the specified number of messages have been attached
for various message types. Usage would look like
``self.assertMessageCount(success=1)``.
"""
temp_req = self.client.request(**{'wsgi.input': None})
temp_req.COOKIES = self.client.cookies
storage = default_storage(temp_req)
# To gain early access to the messages we have to decode the
# cookie on the test client.
if 'messages' in self.client.cookies:
messages = storage._decode(self.client.cookies['messages'].value)
elif any(kwargs.values()):
error_msg = "Messages were expected, but none were set."
assert 0 == sum(kwargs.values()), error_msg
for msg_type, count in kwargs.items():
msgs = [m.message for m in messages if msg_type in m.tags]
assert len(msgs) == count, \
"%s messages not as expected: %s" % (msg_type.title(),
", ".join(msgs))
def assertNoFormErrors(self, response, context_name="form"):
"""
Asserts that the response either does not contain a form in it's
context, or that if it does, that form has no errors.
"""
context = getattr(response, "context", {})
if not context or context_name not in context:
return True
errors = response.context[context_name]._errors
assert len(errors) == 0, \
"Unexpected errors were found on the form: %s" % errors
class BaseAdminViewTests(TestCase):
"""
A ``TestCase`` subclass which sets an active user with the "admin" role
for testing admin-only views and functionality.
"""
def setActiveUser(self, id=None, token=None, username=None, tenant_id=None,
service_catalog=None, tenant_name=None, roles=None,
authorized_tenants=None):
users.get_user_from_request = lambda x: \
users.User(id=self.TEST_USER_ID,
token=self.TEST_TOKEN,
user=self.TEST_USER,
tenant_id=self.TEST_TENANT,
service_catalog=self.TEST_SERVICE_CATALOG,
roles=self.TEST_ROLES,
users.User(id=self.user.id,
token=self.token.id,
user=self.user.name,
tenant_id=self.tenant.id,
service_catalog=self.service_catalog,
roles=[self.roles.admin._info],
authorized_tenants=None)
class APITestCase(TestCase):
"""
The ``APITestCase`` class is for use with tests which deal with the
underlying clients rather than stubbing out the horizon.api.* methods.
"""
def setUp(self):
super(APITestCase, self).setUp()
def fake_keystoneclient(request, username=None, password=None,
tenant_id=None, token_id=None, endpoint=None):
"""
Wrapper function which returns the stub keystoneclient. Only
necessary because the function takes too many arguments to
conveniently be a lambda.
"""
return self.stub_keystoneclient()
# Store the original clients
self._original_glanceclient = api.glance.glanceclient
self._original_keystoneclient = api.keystone.keystoneclient
self._original_novaclient = api.nova.novaclient
# Replace the clients with our stubs.
api.glance.glanceclient = lambda request: self.stub_glanceclient()
api.keystone.keystoneclient = fake_keystoneclient
api.nova.novaclient = lambda request: self.stub_novaclient()
def tearDown(self):
super(APITestCase, self).tearDown()
api.glance.glanceclient = self._original_glanceclient
api.nova.novaclient = self._original_novaclient
api.keystone.keystoneclient = self._original_keystoneclient
def stub_novaclient(self):
if not hasattr(self, "novaclient"):
self.mox.StubOutWithMock(nova_client, 'Client')
self.novaclient = self.mox.CreateMock(nova_client.Client)
return self.novaclient
def stub_keystoneclient(self):
if not hasattr(self, "keystoneclient"):
self.mox.StubOutWithMock(keystone_client, 'Client')
self.keystoneclient = self.mox.CreateMock(keystone_client.Client)
return self.keystoneclient
def stub_glanceclient(self):
if not hasattr(self, "glanceclient"):
self.mox.StubOutWithMock(glance_client, 'Client')
self.glanceclient = self.mox.CreateMock(glance_client.Client)
self.glanceclient.token = self.tokens.first().id
return self.glanceclient
def stub_swiftclient(self, expected_calls=1):
if not hasattr(self, "swiftclient"):
self.mox.StubOutWithMock(swift_client, 'Connection')
self.swiftclient = self.mox.CreateMock(swift_client.Connection)
while expected_calls:
swift_client.Connection(auth=mox.IgnoreArg())\
.AndReturn(self.swiftclient)
expected_calls -= 1
return self.swiftclient

View File

@ -1,28 +0,0 @@
# vim: tabstop=4 shiftwidth=4 softtabstop=4
# Copyright 2012 United States Government as represented by the
# Administrator of the National Aeronautics and Space Administration.
# All Rights Reserved.
#
# Copyright 2012 Nebula, Inc.
#
# 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.
from horizon.tests.api_tests.base import (APIResourceWrapperTests,
APIDictWrapperTests, ApiHelperTests)
from horizon.tests.api_tests.glance import GlanceApiTests, ImageWrapperTests
from horizon.tests.api_tests.keystone import (TokenApiTests, RoleAPITests,
UserAPITests)
from horizon.tests.api_tests.nova import (ServerWrapperTests,
ComputeApiTests, VolumeTests)
from horizon.tests.api_tests.swift import SwiftApiTests

View File

@ -20,12 +20,38 @@
from __future__ import absolute_import
from django import http
from django.conf import settings
from mox import IsA
from horizon import exceptions
from horizon.tests.api_tests.utils import *
from horizon import test
from horizon.api import base as api_base
class APIResource(api_base.APIResourceWrapper):
""" Simple APIResource for testing """
_attrs = ['foo', 'bar', 'baz']
@staticmethod
def get_instance(innerObject=None):
if innerObject is None:
class InnerAPIResource(object):
pass
innerObject = InnerAPIResource()
innerObject.foo = 'foo'
innerObject.bar = 'bar'
return APIResource(innerObject)
class APIDict(api_base.APIDictWrapper):
""" Simple APIDict for testing """
_attrs = ['foo', 'bar', 'baz']
@staticmethod
def get_instance(innerDict=None):
if innerDict is None:
innerDict = {'foo': 'foo',
'bar': 'bar'}
return APIDict(innerDict)
# Wrapper classes that only define _attrs don't need extra testing.
@ -85,28 +111,25 @@ class ApiHelperTests(test.TestCase):
""" Tests for functions that don't use one of the api objects """
def test_url_for(self):
GLANCE_URL = 'http://glance/glanceapi/'
NOVA_URL = 'http://nova/novapi/'
url = api_base.url_for(self.request, 'image')
self.assertEqual(url, 'http://internal.glance.example.com:9292/v1')
url = api.url_for(self.request, 'image')
self.assertEqual(url, GLANCE_URL + 'internal')
url = api_base.url_for(self.request, 'image', admin=False)
self.assertEqual(url, 'http://internal.glance.example.com:9292/v1')
url = api.url_for(self.request, 'image', admin=False)
self.assertEqual(url, GLANCE_URL + 'internal')
url = api_base.url_for(self.request, 'image', admin=True)
self.assertEqual(url, 'http://admin.glance.example.com:9292/v1')
url = api.url_for(self.request, 'image', admin=True)
self.assertEqual(url, GLANCE_URL + 'admin')
url = api_base.url_for(self.request, 'compute')
self.assertEqual(url, 'http://internal.nova.example.com:8774/v1.0')
url = api.url_for(self.request, 'compute')
self.assertEqual(url, NOVA_URL + 'internal')
url = api_base.url_for(self.request, 'compute', admin=False)
self.assertEqual(url, 'http://internal.nova.example.com:8774/v1.0')
url = api.url_for(self.request, 'compute', admin=False)
self.assertEqual(url, NOVA_URL + 'internal')
url = api.url_for(self.request, 'compute', admin=True)
self.assertEqual(url, NOVA_URL + 'admin')
url = api_base.url_for(self.request, 'compute', admin=True)
self.assertEqual(url, 'http://admin.nova.example.com:8774/v1.0')
self.assertNotIn('notAnApi', self.request.user.service_catalog,
'Select a new nonexistent service catalog key')
with self.assertRaises(exceptions.ServiceCatalogException):
url = api.url_for(self.request, 'notAnApi')
url = api_base.url_for(self.request, 'notAnApi')

View File

@ -1,164 +0,0 @@
# vim: tabstop=4 shiftwidth=4 softtabstop=4
# Copyright 2012 United States Government as represented by the
# Administrator of the National Aeronautics and Space Administration.
# All Rights Reserved.
#
# Copyright 2012 Nebula, Inc.
#
# 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.
from __future__ import absolute_import
from django import http
from glance import client as glance_client
from mox import IsA
from horizon.tests.api_tests.utils import *
class GlanceApiTests(APITestCase):
def stub_glance_api(self, count=1):
self.mox.StubOutWithMock(api.glance, 'glance_api')
glance_api = self.mox.CreateMock(glance_client.Client)
glance_api.token = TEST_TOKEN
for i in range(count):
api.glance.glance_api(IsA(http.HttpRequest)).AndReturn(glance_api)
return glance_api
def test_get_glance_api(self):
self.mox.StubOutClassWithMocks(glance_client, 'Client')
client_instance = glance_client.Client(TEST_HOSTNAME, TEST_PORT,
auth_tok=TEST_TOKEN)
# Normally ``auth_tok`` is set in ``Client.__init__``, but mox doesn't
# duplicate that behavior so we set it manually.
client_instance.auth_tok = TEST_TOKEN
self.mox.StubOutWithMock(api.glance, 'url_for')
api.glance.url_for(IsA(http.HttpRequest), 'image').AndReturn(TEST_URL)
self.mox.ReplayAll()
ret_val = api.glance.glance_api(self.request)
self.assertIsNotNone(ret_val)
self.assertEqual(ret_val.auth_tok, TEST_TOKEN)
def test_image_create(self):
IMAGE_FILE = 'someData'
IMAGE_META = {'metadata': 'foo'}
glance_api = self.stub_glance_api()
glance_api.add_image(IMAGE_META, IMAGE_FILE).AndReturn(TEST_RETURN)
self.mox.ReplayAll()
ret_val = api.image_create(self.request, IMAGE_META, IMAGE_FILE)
self.assertIsInstance(ret_val, api.Image)
self.assertEqual(ret_val._apidict, TEST_RETURN)
def test_image_delete(self):
IMAGE_ID = '1'
glance_api = self.stub_glance_api()
glance_api.delete_image(IMAGE_ID).AndReturn(TEST_RETURN)
self.mox.ReplayAll()
ret_val = api.image_delete(self.request, IMAGE_ID)
self.assertEqual(ret_val, TEST_RETURN)
def test_image_get_meta(self):
IMAGE_ID = '1'
glance_api = self.stub_glance_api()
glance_api.get_image_meta(IMAGE_ID).AndReturn([TEST_RETURN])
self.mox.ReplayAll()
ret_val = api.image_get_meta(self.request, IMAGE_ID)
self.assertIsInstance(ret_val, api.Image)
self.assertEqual(ret_val._apidict, [TEST_RETURN])
def test_image_list_detailed(self):
images = (TEST_RETURN, TEST_RETURN + '2')
glance_api = self.stub_glance_api()
glance_api.get_images_detailed().AndReturn(images)
self.mox.ReplayAll()
ret_val = api.image_list_detailed(self.request)
self.assertEqual(len(ret_val), len(images))
for image in ret_val:
self.assertIsInstance(image, api.Image)
self.assertIn(image._apidict, images)
def test_image_update(self):
IMAGE_ID = '1'
IMAGE_META = {'metadata': 'foobar'}
glance_api = self.stub_glance_api(count=2)
glance_api.update_image(IMAGE_ID, image_meta={}).AndReturn(TEST_RETURN)
glance_api.update_image(IMAGE_ID,
image_meta=IMAGE_META).AndReturn(TEST_RETURN)
self.mox.ReplayAll()
ret_val = api.image_update(self.request, IMAGE_ID)
self.assertIsInstance(ret_val, api.Image)
self.assertEqual(ret_val._apidict, TEST_RETURN)
ret_val = api.image_update(self.request,
IMAGE_ID,
image_meta=IMAGE_META)
self.assertIsInstance(ret_val, api.Image)
self.assertEqual(ret_val._apidict, TEST_RETURN)
# Wrapper classes that have other attributes or methods need testing
class ImageWrapperTests(test.TestCase):
dict_with_properties = {
'properties':
{'image_state': 'running'},
'size': 100,
}
dict_without_properties = {
'size': 100,
}
def test_get_properties(self):
image = api.Image(self.dict_with_properties)
image_props = image.properties
self.assertIsInstance(image_props, api.ImageProperties)
self.assertEqual(image_props.image_state, 'running')
def test_get_other(self):
image = api.Image(self.dict_with_properties)
self.assertEqual(image.size, 100)
def test_get_properties_missing(self):
image = api.Image(self.dict_without_properties)
with self.assertRaises(AttributeError):
image.properties
def test_get_other_missing(self):
image = api.Image(self.dict_without_properties)
with self.assertRaises(AttributeError):
self.assertNotIn('missing', image._attrs,
msg="Test assumption broken. Find new missing attribute")
image.missing

View File

@ -0,0 +1,86 @@
# vim: tabstop=4 shiftwidth=4 softtabstop=4
# Copyright 2012 United States Government as represented by the
# Administrator of the National Aeronautics and Space Administration.
# All Rights Reserved.
#
# Copyright 2012 Nebula, Inc.
#
# 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.
from horizon import api
from horizon import test
class GlanceApiTests(test.APITestCase):
def test_get_glanceclient(self):
""" Verify the client connection method does what we expect. """
# Replace the original client which is stubbed out in setUp()
api.glance.glanceclient = self._original_glanceclient
client = api.glance.glanceclient(self.request)
self.assertEqual(client.auth_tok, self.tokens.first().id)
def test_image_get_meta(self):
""" Verify "get" returns our custom Image class. """
image = self.images.get(id='1')
glanceclient = self.stub_glanceclient()
glanceclient.get_image_meta(image.id).AndReturn(image)
self.mox.ReplayAll()
ret_val = api.image_get_meta(self.request, image.id)
self.assertIsInstance(ret_val, api.glance.Image)
self.assertEqual(ret_val._apidict, image)
def test_image_list_detailed(self):
""" Verify "list" returns our custom Image class. """
images = self.images.list()
glanceclient = self.stub_glanceclient()
glanceclient.get_images_detailed().AndReturn(images)
self.mox.ReplayAll()
ret_val = api.image_list_detailed(self.request)
for image in ret_val:
self.assertIsInstance(image, api.glance.Image)
self.assertIn(image._apidict, images)
class ImageWrapperTests(test.TestCase):
""" Tests for wrapper classes since they have extra logic attached. """
WITHOUT_PROPERTIES = {'size': 100}
WITH_PROPERTIES = {'properties': {'image_state': 'running'},
'size': 100}
def test_get_properties(self):
image = api.Image(self.WITH_PROPERTIES)
image_props = image.properties
self.assertIsInstance(image_props, api.ImageProperties)
self.assertEqual(image_props.image_state, 'running')
def test_get_other(self):
image = api.Image(self.WITH_PROPERTIES)
self.assertEqual(image.size, 100)
def test_get_properties_missing(self):
image = api.Image(self.WITHOUT_PROPERTIES)
with self.assertRaises(AttributeError):
image.properties
def test_get_other_missing(self):
image = api.Image(self.WITHOUT_PROPERTIES)
with self.assertRaises(AttributeError):
self.assertNotIn('missing', image._attrs,
msg="Test assumption broken. Find new missing attribute")
image.missing

View File

@ -1,224 +0,0 @@
# vim: tabstop=4 shiftwidth=4 softtabstop=4
# Copyright 2012 United States Government as represented by the
# Administrator of the National Aeronautics and Space Administration.
# All Rights Reserved.
#
# Copyright 2012 Nebula, Inc.
#
# 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.
from __future__ import absolute_import
from django.conf import settings
from keystoneclient.v2_0.roles import Role, RoleManager
from horizon import api
from horizon.tests.api_tests.utils import (APITestCase,
TEST_RETURN, TEST_URL, TEST_USERNAME, TEST_TENANT_ID, TEST_TOKEN_ID,
TEST_TENANT_NAME, TEST_PASSWORD, TEST_EMAIL)
class Token(object):
""" More or less fakes what the api is looking for """
def __init__(self, id, username, tenant_id,
tenant_name, serviceCatalog=None):
self.id = id
self.user = {'name': username}
self.tenant = {'id': tenant_id, 'name': tenant_name}
self.serviceCatalog = serviceCatalog
def __eq__(self, other):
return self.id == other.id and \
self.user['name'] == other.user['name'] and \
self.tenant_id == other.tenant_id and \
self.serviceCatalog == other.serviceCatalog
def __ne__(self, other):
return not self == other
class TokenApiTests(APITestCase):
def setUp(self):
super(TokenApiTests, self).setUp()
self._prev_OPENSTACK_KEYSTONE_URL = getattr(settings,
'OPENSTACK_KEYSTONE_URL',
None)
settings.OPENSTACK_KEYSTONE_URL = TEST_URL
def tearDown(self):
super(TokenApiTests, self).tearDown()
settings.OPENSTACK_KEYSTONE_URL = self._prev_OPENSTACK_KEYSTONE_URL
def test_token_create(self):
test_token = Token(TEST_TOKEN_ID, TEST_USERNAME,
TEST_TENANT_ID, TEST_TENANT_NAME)
keystoneclient = self.stub_keystoneclient()
keystoneclient.tokens = self.mox.CreateMockAnything()
keystoneclient.tokens.authenticate(username=TEST_USERNAME,
password=TEST_PASSWORD,
tenant_id=TEST_TENANT_ID)\
.AndReturn(test_token)
self.mox.ReplayAll()
ret_val = api.token_create(self.request, TEST_TENANT_ID,
TEST_USERNAME, TEST_PASSWORD)
self.assertEqual(test_token.tenant['id'], ret_val.tenant['id'])
class RoleAPITests(APITestCase):
def setUp(self):
super(RoleAPITests, self).setUp()
self.role = Role(RoleManager,
{'id': '2',
'name': settings.OPENSTACK_KEYSTONE_DEFAULT_ROLE})
self.roles = (self.role,)
def test_remove_tenant_user(self):
"""
Tests api.keystone.remove_tenant_user.
Verifies that remove_tenant_user is called with the right arguments
after iterating the user's roles.
There are no assertions in this test because the checking is handled
by mox in the VerifyAll() call in tearDown().
"""
keystoneclient = self.stub_keystoneclient()
keystoneclient.roles = self.mox.CreateMockAnything()
keystoneclient.roles.roles_for_user(TEST_USERNAME, TEST_TENANT_ID) \
.AndReturn(self.roles)
keystoneclient.roles.remove_user_role(TEST_USERNAME,
self.role.id,
TEST_TENANT_ID) \
.AndReturn(self.role)
self.mox.ReplayAll()
api.keystone.remove_tenant_user(self.request,
TEST_TENANT_ID,
TEST_USERNAME)
def test_get_default_role(self):
keystoneclient = self.stub_keystoneclient()
keystoneclient.roles = self.mox.CreateMockAnything()
keystoneclient.roles.list().AndReturn(self.roles)
self.mox.ReplayAll()
role = api.keystone.get_default_role(self.request)
self.assertEqual(role, self.role)
# Verify that a second call doesn't hit the API again,
# (it would show up in mox as an unexpected method call)
role = api.keystone.get_default_role(self.request)
class UserAPITests(APITestCase):
def test_user_create(self):
keystoneclient = self.stub_keystoneclient()
keystoneclient.users = self.mox.CreateMockAnything()
keystoneclient.users.create(TEST_USERNAME, TEST_PASSWORD, TEST_EMAIL,
TEST_TENANT_ID, True).AndReturn(TEST_RETURN)
self.mox.ReplayAll()
ret_val = api.user_create(self.request, TEST_USERNAME, TEST_EMAIL,
TEST_PASSWORD, TEST_TENANT_ID, True)
self.assertIsInstance(ret_val, api.User)
self.assertEqual(ret_val._apiresource, TEST_RETURN)
def test_user_delete(self):
keystoneclient = self.stub_keystoneclient()
keystoneclient.users = self.mox.CreateMockAnything()
keystoneclient.users.delete(TEST_USERNAME).AndReturn(TEST_RETURN)
self.mox.ReplayAll()
ret_val = api.user_delete(self.request, TEST_USERNAME)
self.assertIsNone(ret_val)
def test_user_get(self):
keystoneclient = self.stub_keystoneclient()
keystoneclient.users = self.mox.CreateMockAnything()
keystoneclient.users.get(TEST_USERNAME).AndReturn(TEST_RETURN)
self.mox.ReplayAll()
ret_val = api.user_get(self.request, TEST_USERNAME)
self.assertIsInstance(ret_val, api.User)
self.assertEqual(ret_val._apiresource, TEST_RETURN)
def test_user_list(self):
users = (TEST_USERNAME, TEST_USERNAME + '2')
keystoneclient = self.stub_keystoneclient()
keystoneclient.users = self.mox.CreateMockAnything()
keystoneclient.users.list(tenant_id=None).AndReturn(users)
self.mox.ReplayAll()
ret_val = api.user_list(self.request)
self.assertEqual(len(ret_val), len(users))
for user in ret_val:
self.assertIsInstance(user, api.User)
self.assertIn(user._apiresource, users)
def test_user_update_email(self):
keystoneclient = self.stub_keystoneclient()
keystoneclient.users = self.mox.CreateMockAnything()
keystoneclient.users.update_email(TEST_USERNAME,
TEST_EMAIL).AndReturn(TEST_RETURN)
self.mox.ReplayAll()
ret_val = api.user_update_email(self.request, TEST_USERNAME,
TEST_EMAIL)
self.assertIsInstance(ret_val, api.User)
self.assertEqual(ret_val._apiresource, TEST_RETURN)
def test_user_update_password(self):
keystoneclient = self.stub_keystoneclient()
keystoneclient.users = self.mox.CreateMockAnything()
keystoneclient.users.update_password(TEST_USERNAME,
TEST_PASSWORD).AndReturn(TEST_RETURN)
self.mox.ReplayAll()
ret_val = api.user_update_password(self.request, TEST_USERNAME,
TEST_PASSWORD)
self.assertIsInstance(ret_val, api.User)
self.assertEqual(ret_val._apiresource, TEST_RETURN)
def test_user_update_tenant(self):
keystoneclient = self.stub_keystoneclient()
keystoneclient.users = self.mox.CreateMockAnything()
keystoneclient.users.update_tenant(TEST_USERNAME,
TEST_TENANT_ID).AndReturn(TEST_RETURN)
self.mox.ReplayAll()
ret_val = api.user_update_tenant(self.request, TEST_USERNAME,
TEST_TENANT_ID)
self.assertIsInstance(ret_val, api.User)
self.assertEqual(ret_val._apiresource, TEST_RETURN)

View File

@ -0,0 +1,84 @@
# vim: tabstop=4 shiftwidth=4 softtabstop=4
# Copyright 2012 United States Government as represented by the
# Administrator of the National Aeronautics and Space Administration.
# All Rights Reserved.
#
# Copyright 2012 Nebula, Inc.
#
# 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.
from __future__ import absolute_import
from horizon import api
from horizon import test
class TokenApiTests(test.APITestCase):
def test_token_create(self):
token = self.tokens.scoped_token
keystoneclient = self.stub_keystoneclient()
keystoneclient.tokens = self.mox.CreateMockAnything()
keystoneclient.tokens.authenticate(username=self.user.name,
password=self.user.password,
tenant_id=token.tenant['id'])\
.AndReturn(token)
self.mox.ReplayAll()
ret_val = api.token_create(self.request, token.tenant['id'],
self.user.name, self.user.password)
self.assertEqual(token.tenant['id'], ret_val.tenant['id'])
class RoleAPITests(test.APITestCase):
def setUp(self):
super(RoleAPITests, self).setUp()
self.role = self.roles.member
self.roles = self.roles.list()
def test_remove_tenant_user(self):
"""
Tests api.keystone.remove_tenant_user
Verifies that remove_tenant_user is called with the right arguments
after iterating the user's roles.
There are no assertions in this test because the checking is handled
by mox in the VerifyAll() call in tearDown().
"""
keystoneclient = self.stub_keystoneclient()
tenant = self.tenants.first()
keystoneclient.roles = self.mox.CreateMockAnything()
keystoneclient.roles.roles_for_user(self.user.id,
tenant.id).AndReturn(self.roles)
for role in self.roles:
keystoneclient.roles.remove_user_role(self.user.id,
role.id,
tenant.id)
self.mox.ReplayAll()
api.keystone.remove_tenant_user(self.request, tenant.id, self.user.id)
def test_get_default_role(self):
keystoneclient = self.stub_keystoneclient()
keystoneclient.roles = self.mox.CreateMockAnything()
keystoneclient.roles.list().AndReturn(self.roles)
self.mox.ReplayAll()
role = api.keystone.get_default_role(self.request)
self.assertEqual(role, self.role)
# Verify that a second call doesn't hit the API again,
# (it would show up in mox as an unexpected method call)
role = api.keystone.get_default_role(self.request)

View File

@ -1,617 +0,0 @@
# vim: tabstop=4 shiftwidth=4 softtabstop=4
# Copyright 2012 United States Government as represented by the
# Administrator of the National Aeronautics and Space Administration.
# All Rights Reserved.
#
# Copyright 2012 Nebula, Inc.
# Copyright (c) 2012 X.commerce, a business unit of eBay Inc.
#
# 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.
from __future__ import absolute_import
from django import http
from mox import IsA, IgnoreArg
from novaclient.v1_1 import servers
from horizon.tests.api_tests.utils import *
class Server(object):
""" More or less fakes what the api is looking for """
def __init__(self, id, image, attrs=None):
self.id = id
self.image = image
if attrs is not None:
self.attrs = attrs
def __eq__(self, other):
if self.id != other.id or \
self.image['id'] != other.image['id']:
return False
for k in self.attrs:
if other.attrs.__getattr__(k) != v:
return False
return True
def __ne__(self, other):
return not self == other
class ServerWrapperTests(test.TestCase):
HOST = 'hostname'
ID = '1'
IMAGE_NAME = 'imageName'
IMAGE_OBJ = {'id': '3', 'links': [{'href': '3', u'rel': u'bookmark'}]}
def setUp(self):
super(ServerWrapperTests, self).setUp()
# these are all objects "fetched" from the api
self.inner_attrs = {'host': self.HOST}
self.inner_server = Server(self.ID, self.IMAGE_OBJ, self.inner_attrs)
self.inner_server_no_attrs = Server(self.ID, self.IMAGE_OBJ)
#self.request = self.mox.CreateMock(http.HttpRequest)
def test_get_other(self):
server = api.Server(self.inner_server, self.request)
self.assertEqual(server.id, self.ID)
def test_get_attrs_missing(self):
server = api.Server(self.inner_server_no_attrs, self.request)
with self.assertRaises(AttributeError):
server.attrs
def test_get_other_missing(self):
server = api.Server(self.inner_server, self.request)
with self.assertRaises(AttributeError):
self.assertNotIn('missing', server._attrs,
msg="Test assumption broken. Find new missing attribute")
server.missing
def test_image_name(self):
image = api.Image({'name': self.IMAGE_NAME})
self.mox.StubOutWithMock(api.glance, 'image_get_meta')
api.glance.image_get_meta(IsA(http.HttpRequest),
self.IMAGE_OBJ['id']).AndReturn(image)
server = api.Server(self.inner_server, self.request)
self.mox.ReplayAll()
image_name = server.image_name
self.assertEqual(image_name, self.IMAGE_NAME)
class ComputeApiTests(APITestCase):
def setUp(self):
super(ComputeApiTests, self).setUp()
keypair = api.KeyPair(APIResource.get_instance())
keypair.id = 1
keypair.name = TEST_RETURN
self.keypair = keypair
self.keypairs = [keypair, ]
floating_ip = api.FloatingIp(APIResource.get_instance())
floating_ip.id = 1
floating_ip.fixed_ip = '10.0.0.4'
floating_ip.instance_id = 1
floating_ip.ip = '58.58.58.58'
self.floating_ip = floating_ip
self.floating_ips = [floating_ip, ]
server = api.Server(APIResource.get_instance(), self.request)
server.id = 1
self.server = server
self.servers = [server, ]
def test_flavor_create(self):
FLAVOR_DISK = 1000
FLAVOR_ID = 6
FLAVOR_MEMORY = 1024
FLAVOR_NAME = 'newFlavor'
FLAVOR_VCPU = 2
novaclient = self.stub_novaclient()
novaclient.flavors = self.mox.CreateMockAnything()
novaclient.flavors.create(FLAVOR_NAME, FLAVOR_MEMORY, FLAVOR_VCPU,
FLAVOR_DISK, FLAVOR_ID).AndReturn(TEST_RETURN)
self.mox.ReplayAll()
ret_val = api.flavor_create(self.request, FLAVOR_NAME,
str(FLAVOR_MEMORY), str(FLAVOR_VCPU),
str(FLAVOR_DISK), FLAVOR_ID)
self.assertIsInstance(ret_val, api.Flavor)
self.assertEqual(ret_val._apiresource, TEST_RETURN)
def test_flavor_delete(self):
FLAVOR_ID = 6
novaclient = self.stub_novaclient()
novaclient.flavors = self.mox.CreateMockAnything()
novaclient.flavors.delete(FLAVOR_ID).AndReturn(TEST_RETURN)
self.mox.ReplayAll()
ret_val = api.flavor_delete(self.request, FLAVOR_ID)
self.assertIsNone(ret_val)
def test_flavor_get(self):
FLAVOR_ID = 6
novaclient = self.stub_novaclient()
novaclient.flavors = self.mox.CreateMockAnything()
novaclient.flavors.get(FLAVOR_ID).AndReturn(TEST_RETURN)
self.mox.ReplayAll()
ret_val = api.flavor_get(self.request, FLAVOR_ID)
self.assertIsInstance(ret_val, api.Flavor)
self.assertEqual(ret_val._apiresource, TEST_RETURN)
def test_server_delete(self):
INSTANCE = 'anInstance'
novaclient = self.stub_novaclient()
novaclient.servers = self.mox.CreateMockAnything()
novaclient.servers.delete(INSTANCE).AndReturn(TEST_RETURN)
self.mox.ReplayAll()
ret_val = api.server_delete(self.request, INSTANCE)
self.assertIsNone(ret_val)
def test_server_pause(self):
INSTANCE = 'anInstance'
novaclient = self.stub_novaclient()
novaclient.servers = self.mox.CreateMockAnything()
novaclient.servers.pause(INSTANCE).AndReturn(TEST_RETURN)
self.mox.ReplayAll()
server = self.mox.CreateMock(servers.Server)
ret_val = api.server_pause(self.request, INSTANCE)
self.assertIsNone(ret_val)
def test_server_unpause(self):
INSTANCE = 'anInstance'
novaclient = self.stub_novaclient()
novaclient.servers = self.mox.CreateMockAnything()
novaclient.servers.unpause(INSTANCE).AndReturn(TEST_RETURN)
self.mox.ReplayAll()
ret_val = api.server_unpause(self.request, INSTANCE)
self.assertIsNone(ret_val)
def test_server_suspend(self):
INSTANCE = 'anInstance'
novaclient = self.stub_novaclient()
novaclient.servers = self.mox.CreateMockAnything()
novaclient.servers.suspend(INSTANCE).AndReturn(TEST_RETURN)
self.mox.ReplayAll()
ret_val = api.server_suspend(self.request, INSTANCE)
self.assertIsNone(ret_val)
def test_server_resume(self):
INSTANCE = 'anInstance'
novaclient = self.stub_novaclient()
novaclient.servers = self.mox.CreateMockAnything()
novaclient.servers.resume(INSTANCE).AndReturn(TEST_RETURN)
self.mox.ReplayAll()
ret_val = api.server_resume(self.request, INSTANCE)
self.assertIsNone(ret_val)
def test_server_reboot(self):
INSTANCE_ID = '2'
HARDNESS = servers.REBOOT_HARD
server = self.mox.CreateMock(servers.Server)
server.reboot(HARDNESS)
self.mox.StubOutWithMock(api.nova, 'server_get')
api.nova.server_get(IsA(http.HttpRequest),
INSTANCE_ID).AndReturn(server)
self.mox.ReplayAll()
ret_val = api.server_reboot(self.request, INSTANCE_ID)
self.assertIsNone(ret_val)
def test_server_create(self):
NAME = 'server'
IMAGE = 'anImage'
FLAVOR = 'cherry'
USER_DATA = {'nuts': 'berries'}
KEY = 'user'
SECGROUP = self.mox.CreateMock(api.SecurityGroup)
BLOCK_DEVICE_MAPPING = {'vda': '1:::0'}
novaclient = self.stub_novaclient()
novaclient.servers = self.mox.CreateMockAnything()
novaclient.servers.create(NAME, IMAGE, FLAVOR, userdata=USER_DATA,
security_groups=[SECGROUP], key_name=KEY,
block_device_mapping=BLOCK_DEVICE_MAPPING,
min_count=IsA(int)).AndReturn(TEST_RETURN)
self.mox.ReplayAll()
ret_val = api.server_create(self.request, NAME, IMAGE, FLAVOR,
KEY, USER_DATA, [SECGROUP],
BLOCK_DEVICE_MAPPING,
instance_count=1)
self.assertIsInstance(ret_val, api.Server)
self.assertEqual(ret_val._apiresource, TEST_RETURN)
def test_server_vnc_console(self):
fake_console = {'console': {'url': 'http://fake', 'type': ''}}
novaclient = self.stub_novaclient()
novaclient.servers = self.mox.CreateMockAnything()
novaclient.servers.get_vnc_console(
TEST_INSTANCE_ID, TEST_CONSOLE_TYPE).AndReturn(fake_console)
novaclient.servers.get_vnc_console(
TEST_INSTANCE_ID, 'novnc').AndReturn(fake_console)
self.mox.ReplayAll()
ret_val = api.server_vnc_console(self.request,
TEST_INSTANCE_ID,
TEST_CONSOLE_TYPE)
self.assertIsInstance(ret_val, api.VNCConsole)
self.assertEqual(ret_val._apidict, fake_console['console'])
ret_val = api.server_vnc_console(self.request, TEST_INSTANCE_ID)
self.assertIsInstance(ret_val, api.VNCConsole)
self.assertEqual(ret_val._apidict, fake_console['console'])
def test_flavor_list(self):
flavors = (TEST_RETURN, TEST_RETURN + '2')
novaclient = self.stub_novaclient()
novaclient.flavors = self.mox.CreateMockAnything()
novaclient.flavors.list().AndReturn(flavors)
self.mox.ReplayAll()
ret_val = api.flavor_list(self.request)
self.assertEqual(len(ret_val), len(flavors))
for flavor in ret_val:
self.assertIsInstance(flavor, api.Flavor)
self.assertIn(flavor._apiresource, flavors)
def test_server_list(self):
servers = (TEST_RETURN, TEST_RETURN + '2')
novaclient = self.stub_novaclient()
novaclient.servers = self.mox.CreateMockAnything()
novaclient.servers.list(True, {'project_id': '1'}).AndReturn(servers)
self.mox.ReplayAll()
ret_val = api.server_list(self.request)
self.assertEqual(len(ret_val), len(servers))
for server in ret_val:
self.assertIsInstance(server, api.Server)
self.assertIn(server._apiresource, servers)
def test_usage_get(self):
novaclient = self.stub_novaclient()
novaclient.usage = self.mox.CreateMockAnything()
novaclient.usage.get(TEST_TENANT_ID, 'start',
'end').AndReturn(TEST_RETURN)
self.mox.ReplayAll()
ret_val = api.usage_get(self.request, TEST_TENANT_ID, 'start', 'end')
self.assertIsInstance(ret_val, api.Usage)
self.assertEqual(ret_val._apiresource, TEST_RETURN)
def test_usage_list(self):
usages = (TEST_RETURN, TEST_RETURN + '2')
novaclient = self.stub_novaclient()
novaclient.usage = self.mox.CreateMockAnything()
novaclient.usage.list('start', 'end', True).AndReturn(usages)
self.mox.ReplayAll()
ret_val = api.usage_list(self.request, 'start', 'end')
self.assertEqual(len(ret_val), len(usages))
for usage in ret_val:
self.assertIsInstance(usage, api.Usage)
self.assertIn(usage._apiresource, usages)
def test_server_get(self):
INSTANCE_ID = '2'
novaclient = self.stub_novaclient()
novaclient.servers = self.mox.CreateMockAnything()
novaclient.servers.get(INSTANCE_ID).AndReturn(TEST_RETURN)
self.mox.ReplayAll()
ret_val = api.server_get(self.request, INSTANCE_ID)
self.assertIsInstance(ret_val, api.Server)
self.assertEqual(ret_val._apiresource, TEST_RETURN)
def test_server_snapshot_create(self):
novaclient = self.stub_novaclient()
novaclient.servers = self.mox.CreateMockAnything()
novaclient.servers.create_image(IsA(int), IsA(str)).\
AndReturn(self.server)
self.mox.ReplayAll()
server = api.snapshot_create(self.request, 1, 'test-snapshot')
self.assertIsInstance(server, api.Server)
def test_tenant_floating_ip_list(self):
novaclient = self.stub_novaclient()
novaclient.floating_ips = self.mox.CreateMockAnything()
novaclient.floating_ips.list().AndReturn(self.floating_ips)
self.mox.ReplayAll()
floating_ips = api.tenant_floating_ip_list(self.request)
self.assertEqual(len(floating_ips), len(self.floating_ips))
self.assertIsInstance(floating_ips[0], api.FloatingIp)
def test_tenant_floating_ip_get(self):
novaclient = self.stub_novaclient()
novaclient.floating_ips = self.mox.CreateMockAnything()
novaclient.floating_ips.get(IsA(int)).AndReturn(self.floating_ip)
self.mox.ReplayAll()
floating_ip = api.tenant_floating_ip_get(self.request, 1)
self.assertIsInstance(floating_ip, api.FloatingIp)
def test_tenant_floating_ip_allocate_without_pool(self):
novaclient = self.stub_novaclient()
novaclient.floating_ips = self.mox.CreateMockAnything()
novaclient.floating_ips.create(pool=IgnoreArg()).\
AndReturn(self.floating_ip)
self.mox.ReplayAll()
floating_ip = api.tenant_floating_ip_allocate(self.request)
self.assertIsInstance(floating_ip, api.FloatingIp)
def test_tenant_floating_ip_allocate_with_pool(self):
novaclient = self.stub_novaclient()
novaclient.floating_ips = self.mox.CreateMockAnything()
novaclient.floating_ips.create(pool="nova").AndReturn(self.floating_ip)
self.mox.ReplayAll()
floating_ip = api.tenant_floating_ip_allocate(self.request,
pool='nova')
self.assertIsInstance(floating_ip, api.FloatingIp)
def test_tenant_floating_ip_release(self):
novaclient = self.stub_novaclient()
novaclient.floating_ips = self.mox.CreateMockAnything()
novaclient.floating_ips.delete(1).AndReturn(self.floating_ip)
self.mox.ReplayAll()
floating_ip = api.tenant_floating_ip_release(self.request, 1)
self.assertIsInstance(floating_ip, api.FloatingIp)
def test_server_remove_floating_ip(self):
novaclient = self.stub_novaclient()
novaclient.servers = self.mox.CreateMockAnything()
novaclient.floating_ips = self.mox.CreateMockAnything()
novaclient.servers.get(IsA(int)).AndReturn(self.server)
novaclient.floating_ips.get(IsA(int)).AndReturn(self.floating_ip)
novaclient.servers.remove_floating_ip(IsA(self.server.__class__),
IsA(self.floating_ip.__class__)) \
.AndReturn(self.server)
self.mox.ReplayAll()
server = api.server_remove_floating_ip(self.request, 1, 1)
self.assertIsInstance(server, api.Server)
def test_server_add_floating_ip(self):
novaclient = self.stub_novaclient()
novaclient.floating_ips = self.mox.CreateMockAnything()
novaclient.servers = self.mox.CreateMockAnything()
novaclient.servers.get(IsA(int)).AndReturn(self.server)
novaclient.floating_ips.get(IsA(int)).AndReturn(self.floating_ip)
novaclient.servers.add_floating_ip(IsA(self.server.__class__),
IsA(self.floating_ip.__class__)) \
.AndReturn(self.server)
self.mox.ReplayAll()
server = api.server_add_floating_ip(self.request, 1, 1)
self.assertIsInstance(server, api.Server)
def test_keypair_create(self):
novaclient = self.stub_novaclient()
novaclient.keypairs = self.mox.CreateMockAnything()
novaclient.keypairs.create(IsA(str)).AndReturn(self.keypair)
self.mox.ReplayAll()
ret_val = api.keypair_create(self.request, TEST_RETURN)
self.assertIsInstance(ret_val, api.KeyPair)
self.assertEqual(ret_val.name, self.keypair.name)
def test_keypair_import(self):
novaclient = self.stub_novaclient()
novaclient.keypairs = self.mox.CreateMockAnything()
novaclient.keypairs.create(IsA(str), IsA(str)).AndReturn(self.keypair)
self.mox.ReplayAll()
ret_val = api.keypair_import(self.request, TEST_RETURN, TEST_RETURN)
self.assertIsInstance(ret_val, api.KeyPair)
self.assertEqual(ret_val.name, self.keypair.name)
def test_keypair_delete(self):
novaclient = self.stub_novaclient()
novaclient.keypairs = self.mox.CreateMockAnything()
novaclient.keypairs.delete(IsA(int))
self.mox.ReplayAll()
ret_val = api.keypair_delete(self.request, self.keypair.id)
self.assertIsNone(ret_val)
def test_keypair_list(self):
novaclient = self.stub_novaclient()
novaclient.keypairs = self.mox.CreateMockAnything()
novaclient.keypairs.list().AndReturn(self.keypairs)
self.mox.ReplayAll()
ret_val = api.keypair_list(self.request)
self.assertEqual(len(ret_val), len(self.keypairs))
for keypair in ret_val:
self.assertIsInstance(keypair, api.KeyPair)
class VolumeTests(APITestCase):
def setUp(self):
super(VolumeTests, self).setUp()
volume = api.Volume(APIResource.get_instance())
volume.id = 1
volume.displayName = "displayName"
volume.attachments = [{"device": "/dev/vdb",
"serverId": 1,
"id": 1,
"volumeId": 1}]
self.volume = volume
self.volumes = [volume, ]
self.novaclient = self.stub_novaclient()
self.novaclient.volumes = self.mox.CreateMockAnything()
def test_volume_list(self):
self.novaclient.volumes.list().AndReturn(self.volumes)
self.mox.ReplayAll()
volumes = api.volume_list(self.request)
self.assertIsInstance(volumes[0], api.Volume)
def test_volume_get(self):
self.novaclient.volumes.get(IsA(int)).AndReturn(self.volume)
self.mox.ReplayAll()
volume = api.volume_get(self.request, 1)
self.assertIsInstance(volume, api.Volume)
def test_volume_instance_list(self):
self.novaclient.volumes.get_server_volumes(IsA(int)).AndReturn(
self.volume.attachments)
self.mox.ReplayAll()
attachments = api.volume_instance_list(self.request, 1)
self.assertEqual(attachments, self.volume.attachments)
def test_volume_create(self):
self.novaclient.volumes.create(IsA(int),
display_name=IsA(str),
display_description=IsA(str)) \
.AndReturn(self.volume)
self.mox.ReplayAll()
new_volume = api.volume_create(self.request,
10,
"new volume",
"new description")
self.assertIsInstance(new_volume, api.Volume)
def test_volume_delete(self):
self.novaclient.volumes.delete(IsA(int))
self.mox.ReplayAll()
ret_val = api.volume_delete(self.request, 1)
self.assertIsNone(ret_val)
def test_volume_attach(self):
self.novaclient.volumes.create_server_volume(
IsA(int), IsA(int), IsA(str))
self.mox.ReplayAll()
ret_val = api.volume_attach(self.request, 1, 1, "/dev/vdb")
self.assertIsNone(ret_val)
def test_volume_detach(self):
self.novaclient.volumes.delete_server_volume(IsA(int), IsA(int))
self.mox.ReplayAll()
ret_val = api.volume_detach(self.request, 1, 1)
self.assertIsNone(ret_val)

View File

@ -0,0 +1,158 @@
# vim: tabstop=4 shiftwidth=4 softtabstop=4
# Copyright 2012 United States Government as represented by the
# Administrator of the National Aeronautics and Space Administration.
# All Rights Reserved.
#
# Copyright 2012 Nebula, Inc.
# Copyright (c) 2012 X.commerce, a business unit of eBay Inc.
#
# 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.
from __future__ import absolute_import
from django import http
from mox import IsA
from novaclient.v1_1 import servers
from horizon import api
from horizon import test
class ServerWrapperTests(test.TestCase):
def test_get_base_attribute(self):
server = api.nova.Server(self.servers.first(), self.request)
self.assertEqual(server.id, self.servers.first().id)
def test_image_name(self):
image = api.Image(self.images.first())
self.mox.StubOutWithMock(api.glance, 'image_get_meta')
api.glance.image_get_meta(IsA(http.HttpRequest),
image.id).AndReturn(image)
self.mox.ReplayAll()
server = api.Server(self.servers.first(), self.request)
self.assertEqual(server.image_name, image.name)
class ComputeApiTests(test.APITestCase):
def test_server_reboot(self):
server = self.servers.first()
HARDNESS = servers.REBOOT_HARD
novaclient = self.stub_novaclient()
novaclient.servers = self.mox.CreateMockAnything()
novaclient.servers.get(server.id).AndReturn(server)
novaclient.servers.reboot(server.id, HARDNESS)
self.mox.ReplayAll()
ret_val = api.nova.server_reboot(self.request, server.id)
self.assertIsNone(ret_val)
def test_server_vnc_console(self):
server = self.servers.first()
console = self.servers.console_data
console_type = console["console"]["type"]
novaclient = self.stub_novaclient()
novaclient.servers = self.mox.CreateMockAnything()
novaclient.servers.get_vnc_console(server.id,
console_type).AndReturn(console)
self.mox.ReplayAll()
ret_val = api.server_vnc_console(self.request,
server.id,
console_type)
self.assertIsInstance(ret_val, api.nova.VNCConsole)
def test_server_list(self):
servers = self.servers.list()
novaclient = self.stub_novaclient()
novaclient.servers = self.mox.CreateMockAnything()
novaclient.servers.list(True, {'all_tenants': True}).AndReturn(servers)
self.mox.ReplayAll()
ret_val = api.nova.server_list(self.request, all_tenants=True)
for server in ret_val:
self.assertIsInstance(server, api.Server)
def test_usage_get(self):
novaclient = self.stub_novaclient()
novaclient.usage = self.mox.CreateMockAnything()
novaclient.usage.get(self.tenant.id,
'start',
'end').AndReturn(self.usages.first())
self.mox.ReplayAll()
ret_val = api.usage_get(self.request, self.tenant.id, 'start', 'end')
self.assertIsInstance(ret_val, api.nova.Usage)
def test_usage_list(self):
usages = self.usages.list()
novaclient = self.stub_novaclient()
novaclient.usage = self.mox.CreateMockAnything()
novaclient.usage.list('start', 'end', True).AndReturn(usages)
self.mox.ReplayAll()
ret_val = api.usage_list(self.request, 'start', 'end')
for usage in ret_val:
self.assertIsInstance(usage, api.Usage)
def test_server_get(self):
server = self.servers.first()
novaclient = self.stub_novaclient()
novaclient.servers = self.mox.CreateMockAnything()
novaclient.servers.get(server.id).AndReturn(server)
self.mox.ReplayAll()
ret_val = api.server_get(self.request, server.id)
self.assertIsInstance(ret_val, api.nova.Server)
def test_server_remove_floating_ip(self):
server = api.nova.Server(self.servers.first(), self.request)
floating_ip = self.floating_ips.first()
novaclient = self.stub_novaclient()
novaclient.servers = self.mox.CreateMockAnything()
novaclient.floating_ips = self.mox.CreateMockAnything()
novaclient.servers.get(server.id).AndReturn(server)
novaclient.floating_ips.get(floating_ip.id).AndReturn(floating_ip)
novaclient.servers.remove_floating_ip(server.id, floating_ip.id) \
.AndReturn(server)
self.mox.ReplayAll()
server = api.server_remove_floating_ip(self.request,
server.id,
floating_ip.id)
self.assertIsInstance(server, api.nova.Server)
def test_server_add_floating_ip(self):
server = api.nova.Server(self.servers.first(), self.request)
floating_ip = self.floating_ips.first()
novaclient = self.stub_novaclient()
novaclient.floating_ips = self.mox.CreateMockAnything()
novaclient.servers = self.mox.CreateMockAnything()
novaclient.servers.get(server.id).AndReturn(server)
novaclient.floating_ips.get(floating_ip.id).AndReturn(floating_ip)
novaclient.servers.add_floating_ip(server.id, floating_ip.id) \
.AndReturn(server)
self.mox.ReplayAll()
server = api.server_add_floating_ip(self.request,
server.id,
floating_ip.id)
self.assertIsInstance(server, api.nova.Server)

View File

@ -1,247 +0,0 @@
# vim: tabstop=4 shiftwidth=4 softtabstop=4
# Copyright 2012 United States Government as represented by the
# Administrator of the National Aeronautics and Space Administration.
# All Rights Reserved.
#
# Copyright 2012 Nebula, Inc.
#
# 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.
from __future__ import absolute_import
import cloudfiles
from django import http
from mox import IsA
from horizon.tests.api_tests.utils import *
class SwiftApiTests(APITestCase):
def setUp(self):
super(SwiftApiTests, self).setUp()
self.request = http.HttpRequest()
self.request.session = dict()
self.request.session['token'] = TEST_TOKEN
def stub_swift_api(self, count=1):
self.mox.StubOutWithMock(api.swift, 'swift_api')
swift_api = self.mox.CreateMock(cloudfiles.connection.Connection)
for i in range(count):
api.swift.swift_api(IsA(http.HttpRequest)).AndReturn(swift_api)
return swift_api
def test_swift_get_containers(self):
containers = (TEST_RETURN, TEST_RETURN + '2')
swift_api = self.stub_swift_api()
swift_api.get_all_containers(limit=1001,
marker=None).AndReturn(containers)
self.mox.ReplayAll()
(conts, more) = api.swift_get_containers(self.request)
self.assertEqual(len(conts), len(containers))
self.assertFalse(more)
for container in conts:
self.assertIsInstance(container, api.Container)
self.assertIn(container._apiresource, containers)
def test_swift_create_container(self):
NAME = 'containerName'
swift_api = self.stub_swift_api()
self.mox.StubOutWithMock(api.swift, 'swift_container_exists')
api.swift.swift_container_exists(self.request,
NAME).AndReturn(False)
swift_api.create_container(NAME).AndReturn(TEST_RETURN)
self.mox.ReplayAll()
ret_val = api.swift_create_container(self.request, NAME)
self.assertIsInstance(ret_val, api.Container)
self.assertEqual(ret_val._apiresource, TEST_RETURN)
def test_swift_delete_container(self):
NAME = 'containerName'
swift_api = self.stub_swift_api()
swift_api.delete_container(NAME).AndReturn(TEST_RETURN)
self.mox.ReplayAll()
ret_val = api.swift_delete_container(self.request, NAME)
self.assertIsNone(ret_val)
def test_swift_get_objects(self):
NAME = 'containerName'
swift_objects = (TEST_RETURN, TEST_RETURN + '2')
container = self.mox.CreateMock(cloudfiles.container.Container)
container.get_objects(limit=1001,
marker=None,
prefix=None).AndReturn(swift_objects)
swift_api = self.stub_swift_api()
swift_api.get_container(NAME).AndReturn(container)
self.mox.ReplayAll()
(objects, more) = api.swift_get_objects(self.request, NAME)
self.assertEqual(len(objects), len(swift_objects))
self.assertFalse(more)
for swift_object in objects:
self.assertIsInstance(swift_object, api.SwiftObject)
self.assertIn(swift_object._apiresource, swift_objects)
def test_swift_get_objects_with_prefix(self):
NAME = 'containerName'
PREFIX = 'prefacedWith'
swift_objects = (TEST_RETURN, TEST_RETURN + '2')
container = self.mox.CreateMock(cloudfiles.container.Container)
container.get_objects(limit=1001,
marker=None,
prefix=PREFIX).AndReturn(swift_objects)
swift_api = self.stub_swift_api()
swift_api.get_container(NAME).AndReturn(container)
self.mox.ReplayAll()
(objects, more) = api.swift_get_objects(self.request,
NAME,
prefix=PREFIX)
self.assertEqual(len(objects), len(swift_objects))
self.assertFalse(more)
for swift_object in objects:
self.assertIsInstance(swift_object, api.SwiftObject)
self.assertIn(swift_object._apiresource, swift_objects)
def test_swift_upload_object(self):
CONTAINER_NAME = 'containerName'
OBJECT_NAME = 'objectName'
OBJECT_DATA = 'someData'
swift_api = self.stub_swift_api()
container = self.mox.CreateMock(cloudfiles.container.Container)
swift_object = self.mox.CreateMock(cloudfiles.storage_object.Object)
swift_api.get_container(CONTAINER_NAME).AndReturn(container)
container.create_object(OBJECT_NAME).AndReturn(swift_object)
swift_object.write(OBJECT_DATA).AndReturn(TEST_RETURN)
self.mox.ReplayAll()
ret_val = api.swift_upload_object(self.request,
CONTAINER_NAME,
OBJECT_NAME,
OBJECT_DATA)
self.assertEqual(ret_val, swift_object)
def test_swift_delete_object(self):
CONTAINER_NAME = 'containerName'
OBJECT_NAME = 'objectName'
swift_api = self.stub_swift_api()
container = self.mox.CreateMock(cloudfiles.container.Container)
swift_api.get_container(CONTAINER_NAME).AndReturn(container)
container.delete_object(OBJECT_NAME).AndReturn(TEST_RETURN)
self.mox.ReplayAll()
ret_val = api.swift_delete_object(self.request,
CONTAINER_NAME,
OBJECT_NAME)
self.assertIsNone(ret_val)
def test_swift_get_object_data(self):
CONTAINER_NAME = 'containerName'
OBJECT_NAME = 'objectName'
OBJECT_DATA = 'objectData'
swift_api = self.stub_swift_api()
container = self.mox.CreateMock(cloudfiles.container.Container)
swift_object = self.mox.CreateMock(cloudfiles.storage_object.Object)
swift_api.get_container(CONTAINER_NAME).AndReturn(container)
container.get_object(OBJECT_NAME).AndReturn(swift_object)
swift_object.stream().AndReturn(OBJECT_DATA)
self.mox.ReplayAll()
ret_val = api.swift_get_object_data(self.request,
CONTAINER_NAME,
OBJECT_NAME)
self.assertEqual(ret_val, OBJECT_DATA)
def test_swift_object_exists(self):
CONTAINER_NAME = 'containerName'
OBJECT_NAME = 'objectName'
swift_api = self.stub_swift_api()
container = self.mox.CreateMock(cloudfiles.container.Container)
swift_object = self.mox.CreateMock(cloudfiles.Object)
swift_api.get_container(CONTAINER_NAME).AndReturn(container)
container.get_object(OBJECT_NAME).AndReturn(swift_object)
self.mox.ReplayAll()
ret_val = api.swift_object_exists(self.request,
CONTAINER_NAME,
OBJECT_NAME)
self.assertTrue(ret_val)
def test_swift_copy_object(self):
CONTAINER_NAME = 'containerName'
OBJECT_NAME = 'objectName'
swift_api = self.stub_swift_api()
container = self.mox.CreateMock(cloudfiles.container.Container)
self.mox.StubOutWithMock(api.swift, 'swift_object_exists')
swift_object = self.mox.CreateMock(cloudfiles.Object)
swift_api.get_container(CONTAINER_NAME).AndReturn(container)
api.swift.swift_object_exists(self.request,
CONTAINER_NAME,
OBJECT_NAME).AndReturn(False)
container.get_object(OBJECT_NAME).AndReturn(swift_object)
swift_object.copy_to(CONTAINER_NAME, OBJECT_NAME)
self.mox.ReplayAll()
ret_val = api.swift_copy_object(self.request, CONTAINER_NAME,
OBJECT_NAME, CONTAINER_NAME,
OBJECT_NAME)
self.assertIsNone(ret_val)

View File

@ -0,0 +1,170 @@
# vim: tabstop=4 shiftwidth=4 softtabstop=4
# Copyright 2012 United States Government as represented by the
# Administrator of the National Aeronautics and Space Administration.
# All Rights Reserved.
#
# Copyright 2012 Nebula, Inc.
#
# 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.
from __future__ import absolute_import
import cloudfiles
from horizon import api
from horizon import exceptions
from horizon import test
class SwiftApiTests(test.APITestCase):
def test_swift_get_containers(self):
containers = self.containers.list()
swift_api = self.stub_swiftclient()
swift_api.get_all_containers(limit=1001,
marker=None).AndReturn(containers)
self.mox.ReplayAll()
(conts, more) = api.swift_get_containers(self.request)
self.assertEqual(len(conts), len(containers))
self.assertFalse(more)
def test_swift_create_container(self):
container = self.containers.first()
swift_api = self.stub_swiftclient(expected_calls=2)
# Check for existence, then create
exc = cloudfiles.errors.NoSuchContainer()
swift_api.get_container(container.name).AndRaise(exc)
swift_api.create_container(container.name).AndReturn(container)
self.mox.ReplayAll()
# Verification handled by mox, no assertions needed.
api.swift_create_container(self.request, container.name)
def test_swift_create_duplicate_container(self):
container = self.containers.first()
swift_api = self.stub_swiftclient()
swift_api.get_container(container.name).AndReturn(container)
self.mox.ReplayAll()
# Verification handled by mox, no assertions needed.
with self.assertRaises(exceptions.AlreadyExists):
api.swift_create_container(self.request, container.name)
def test_swift_get_objects(self):
container = self.containers.first()
objects = self.objects.list()
swift_api = self.stub_swiftclient()
swift_api.get_container(container.name).AndReturn(container)
self.mox.StubOutWithMock(container, 'get_objects')
container.get_objects(limit=1001,
marker=None,
prefix=None).AndReturn(objects)
self.mox.ReplayAll()
(objs, more) = api.swift_get_objects(self.request, container.name)
self.assertEqual(len(objs), len(objects))
self.assertFalse(more)
def test_swift_upload_object(self):
container = self.containers.first()
obj = self.objects.first()
OBJECT_DATA = 'someData'
swift_api = self.stub_swiftclient()
swift_api.get_container(container.name).AndReturn(container)
self.mox.StubOutWithMock(container, 'create_object')
container.create_object(obj.name).AndReturn(obj)
self.mox.StubOutWithMock(obj, 'write')
obj.write(OBJECT_DATA).AndReturn(obj)
self.mox.ReplayAll()
ret_val = api.swift_upload_object(self.request,
container.name,
obj.name,
OBJECT_DATA)
self.assertEqual(ret_val, obj)
def test_swift_delete_object(self):
container = self.containers.first()
obj = self.objects.first()
swift_api = self.stub_swiftclient()
swift_api.get_container(container.name).AndReturn(container)
self.mox.StubOutWithMock(container, 'delete_object')
container.delete_object(obj.name).AndReturn(obj)
self.mox.ReplayAll()
ret_val = api.swift_delete_object(self.request,
container.name,
obj.name)
self.assertIsNone(ret_val)
def test_swift_get_object_data(self):
container = self.containers.first()
obj = self.objects.first()
OBJECT_DATA = 'objectData'
swift_api = self.stub_swiftclient()
swift_api.get_container(container.name).AndReturn(container)
self.mox.StubOutWithMock(container, 'get_object')
container.get_object(obj.name).AndReturn(obj)
self.mox.StubOutWithMock(obj, 'stream')
obj.stream().AndReturn(OBJECT_DATA)
self.mox.ReplayAll()
ret_val = api.swift_get_object_data(self.request,
container.name,
obj.name)
self.assertEqual(ret_val, OBJECT_DATA)
def test_swift_object_exists(self):
container = self.containers.first()
obj = self.objects.first()
swift_api = self.stub_swiftclient(expected_calls=2)
self.mox.StubOutWithMock(container, 'get_object')
swift_api.get_container(container.name).AndReturn(container)
container.get_object(obj.name).AndReturn(obj)
swift_api.get_container(container.name).AndReturn(container)
exc = cloudfiles.errors.NoSuchObject()
container.get_object(obj.name).AndRaise(exc)
self.mox.ReplayAll()
args = self.request, container.name, obj.name
self.assertTrue(api.swift_object_exists(*args))
# Again, for a "non-existent" object
self.assertFalse(api.swift_object_exists(*args))
def test_swift_copy_object(self):
container = self.containers.get(name="container_one")
container_2 = self.containers.get(name="container_two")
obj = self.objects.first()
swift_api = self.stub_swiftclient()
self.mox.StubOutWithMock(api.swift, 'swift_object_exists')
swift_api.get_container(container.name).AndReturn(container)
api.swift.swift_object_exists(self.request,
container_2.name,
obj.name).AndReturn(False)
self.mox.StubOutWithMock(container, 'get_object')
container.get_object(obj.name).AndReturn(obj)
self.mox.StubOutWithMock(obj, 'copy_to')
obj.copy_to(container_2.name, obj.name)
self.mox.ReplayAll()
# Verification handled by mox. No assertions needed.
api.swift_copy_object(self.request,
container.name,
obj.name,
container_2.name,
obj.name)

View File

@ -1,99 +0,0 @@
# vim: tabstop=4 shiftwidth=4 softtabstop=4
# Copyright 2012 United States Government as represented by the
# Administrator of the National Aeronautics and Space Administration.
# All Rights Reserved.
#
# Copyright 2012 Nebula, Inc.
#
# 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.
from keystoneclient.v2_0 import client as keystone_client
from novaclient.v1_1 import client as nova_client
from horizon import api
from horizon import test
TEST_CONSOLE_TYPE = 'novnc'
TEST_EMAIL = 'test@test.com'
TEST_HOSTNAME = 'hostname'
TEST_INSTANCE_ID = '2'
TEST_PASSWORD = '12345'
TEST_PORT = 8000
TEST_RETURN = 'retValue'
TEST_TENANT_DESCRIPTION = 'tenantDescription'
TEST_TENANT_ID = '1234'
TEST_TENANT_NAME = 'foo'
TEST_TOKEN = 'aToken'
TEST_TOKEN_ID = 'userId'
TEST_URL = 'http://%s:%s/something/v1.0' % (TEST_HOSTNAME, TEST_PORT)
TEST_USERNAME = 'testUser'
class APIResource(api.APIResourceWrapper):
""" Simple APIResource for testing """
_attrs = ['foo', 'bar', 'baz']
@staticmethod
def get_instance(innerObject=None):
if innerObject is None:
class InnerAPIResource(object):
pass
innerObject = InnerAPIResource()
innerObject.foo = 'foo'
innerObject.bar = 'bar'
return APIResource(innerObject)
class APIDict(api.APIDictWrapper):
""" Simple APIDict for testing """
_attrs = ['foo', 'bar', 'baz']
@staticmethod
def get_instance(innerDict=None):
if innerDict is None:
innerDict = {'foo': 'foo',
'bar': 'bar'}
return APIDict(innerDict)
class APITestCase(test.TestCase):
def setUp(self):
def fake_keystoneclient(request, username=None, password=None,
tenant_id=None, token_id=None, endpoint=None):
return self.stub_keystoneclient()
super(APITestCase, self).setUp()
self._original_keystoneclient = api.keystone.keystoneclient
self._original_novaclient = api.nova.novaclient
api.keystone.keystoneclient = fake_keystoneclient
api.nova.novaclient = lambda request: self.stub_novaclient()
def stub_novaclient(self):
if not hasattr(self, "novaclient"):
self.mox.StubOutWithMock(nova_client, 'Client')
self.novaclient = self.mox.CreateMock(nova_client.Client)
return self.novaclient
def stub_keystoneclient(self):
if not hasattr(self, "keystoneclient"):
self.mox.StubOutWithMock(keystone_client, 'Client')
self.keystoneclient = self.mox.CreateMock(keystone_client.Client)
return self.keystoneclient
def tearDown(self):
super(APITestCase, self).tearDown()
api.nova.novaclient = self._original_novaclient
api.keystone.keystoneclient = self._original_keystoneclient

View File

@ -19,9 +19,7 @@
# under the License.
from django import http
from django.contrib import messages
from django.core.urlresolvers import reverse
from keystoneclient.v2_0 import tenants as keystone_tenants
from keystoneclient import exceptions as keystone_exceptions
from mox import IsA
@ -33,59 +31,41 @@ SYSPANEL_INDEX_URL = reverse('horizon:syspanel:overview:index')
DASH_INDEX_URL = reverse('horizon:nova:overview:index')
class AuthViewTests(test.BaseViewTests):
class AuthViewTests(test.TestCase):
def setUp(self):
super(AuthViewTests, self).setUp()
self.setActiveUser()
self.PASSWORD = 'secret'
self.tenant = keystone_tenants.Tenant(keystone_tenants.TenantManager,
{'id': '6',
'name': 'FAKENAME'})
self.tenants = [self.tenant]
def test_login_index(self):
res = self.client.get(reverse('horizon:auth_login'))
self.assertTemplateUsed(res, 'horizon/auth/login.html')
def test_login_user_logged_in(self):
self.setActiveUser(self.TEST_TOKEN, self.TEST_USER, self.TEST_TENANT,
False, self.TEST_SERVICE_CATALOG)
self.setActiveUser(self.tokens.first().id,
self.user.name,
self.tenant.id,
False,
self.service_catalog)
# Hitting the login URL directly should always give you a login page.
res = self.client.get(reverse('horizon:auth_login'))
self.assertTemplateUsed(res, 'horizon/auth/login.html')
def test_login_no_tenants(self):
TOKEN_ID = 1
form_data = {'method': 'Login',
'region': 'http://localhost:5000/v2.0',
'password': self.PASSWORD,
'username': self.TEST_USER}
aToken = self.tokens.first()
self.mox.StubOutWithMock(api, 'token_create')
class FakeToken(object):
id = TOKEN_ID,
user = {'roles': [{'name': 'fake'}]},
serviceCatalog = {}
aToken = api.Token(FakeToken())
api.token_create(IsA(http.HttpRequest), "", self.TEST_USER,
self.PASSWORD).AndReturn(aToken)
self.mox.StubOutWithMock(api, 'tenant_list_for_token')
api.token_create(IsA(http.HttpRequest), "", self.user.name,
self.user.password).AndReturn(aToken)
api.tenant_list_for_token(IsA(http.HttpRequest), aToken.id).\
AndReturn([])
self.mox.StubOutWithMock(messages, 'error')
messages.error(IsA(http.HttpRequest),
IsA(unicode),
extra_tags=IsA(str))
self.mox.ReplayAll()
form_data = {'method': 'Login',
'region': 'http://localhost:5000/v2.0',
'password': self.user.password,
'username': self.user.name}
res = self.client.post(reverse('horizon:auth_login'), form_data)
self.assertTemplateUsed(res, 'horizon/auth/login.html')
@ -93,28 +73,20 @@ class AuthViewTests(test.BaseViewTests):
def test_login(self):
form_data = {'method': 'Login',
'region': 'http://localhost:5000/v2.0',
'password': self.PASSWORD,
'username': self.TEST_USER}
'password': self.user.password,
'username': self.user.name}
self.mox.StubOutWithMock(api, 'token_create')
self.mox.StubOutWithMock(api, 'tenant_list_for_token')
self.mox.StubOutWithMock(api, 'token_create_scoped')
class FakeToken(object):
id = 1,
user = {"id": "1",
"roles": [{"id": "1", "name": "fake"}], "name": "user"}
serviceCatalog = {}
tenant = None
aToken = self.tokens.unscoped_token
bToken = self.tokens.scoped_token
aToken = api.Token(FakeToken())
bToken = aToken
bToken.tenant = {'id': self.tenant.id, 'name': self.tenant.name}
api.token_create(IsA(http.HttpRequest), "", self.TEST_USER,
self.PASSWORD).AndReturn(aToken)
api.token_create(IsA(http.HttpRequest), "", self.user.name,
self.user.password).AndReturn(aToken)
api.tenant_list_for_token(IsA(http.HttpRequest),
aToken.id).AndReturn(self.tenants)
aToken.id).AndReturn(self.tenants.list())
api.token_create_scoped(IsA(http.HttpRequest),
self.tenant.id,
aToken.id).AndReturn(bToken)
@ -127,15 +99,15 @@ class AuthViewTests(test.BaseViewTests):
def test_login_invalid_credentials(self):
self.mox.StubOutWithMock(api, 'token_create')
unauthorized = keystone_exceptions.Unauthorized("Invalid")
api.token_create(IsA(http.HttpRequest), "", self.TEST_USER,
self.PASSWORD).AndRaise(unauthorized)
api.token_create(IsA(http.HttpRequest), "", self.user.name,
self.user.password).AndRaise(unauthorized)
self.mox.ReplayAll()
form_data = {'method': 'Login',
'region': 'http://localhost:5000/v2.0',
'password': self.PASSWORD,
'username': self.TEST_USER}
'password': self.user.password,
'username': self.user.name}
res = self.client.post(reverse('horizon:auth_login'),
form_data,
follow=True)
@ -147,69 +119,62 @@ class AuthViewTests(test.BaseViewTests):
ex = keystone_exceptions.BadRequest('Cannot talk to keystone')
api.token_create(IsA(http.HttpRequest),
"",
self.TEST_USER,
self.PASSWORD).AndRaise(ex)
self.user.name,
self.user.password).AndRaise(ex)
self.mox.ReplayAll()
form_data = {'method': 'Login',
'region': 'http://localhost:5000/v2.0',
'password': self.PASSWORD,
'username': self.TEST_USER}
'password': self.user.password,
'username': self.user.name}
res = self.client.post(reverse('horizon:auth_login'), form_data)
self.assertTemplateUsed(res, 'horizon/auth/login.html')
def test_switch_tenants_index(self):
res = self.client.get(reverse('horizon:auth_switch',
args=[self.TEST_TENANT]))
args=[self.tenant.id]))
self.assertRedirects(res, reverse("horizon:auth_login"))
def test_switch_tenants(self):
NEW_TENANT_ID = '6'
NEW_TENANT_NAME = 'FAKENAME'
TOKEN_ID = 1
tenants = self.TEST_CONTEXT['authorized_tenants']
tenants = self.tenants.list()
aTenant = self.mox.CreateMock(api.Token)
aTenant.id = NEW_TENANT_ID
aTenant.name = NEW_TENANT_NAME
tenant = self.tenants.first()
token = self.tokens.unscoped_token
scoped_token = self.tokens.scoped_token
switch_to = scoped_token.tenant['id']
user = self.users.first()
aToken = self.mox.CreateMock(api.Token)
aToken.id = TOKEN_ID
aToken.user = {'id': self.TEST_USER_ID,
'name': self.TEST_USER, 'roles': [{'name': 'fake'}]}
aToken.serviceCatalog = {}
aToken.tenant = {'id': aTenant.id, 'name': aTenant.name}
self.setActiveUser(id=self.TEST_USER_ID,
token=self.TEST_TOKEN,
username=self.TEST_USER,
tenant_id=self.TEST_TENANT,
service_catalog=self.TEST_SERVICE_CATALOG,
self.setActiveUser(id=user.id,
token=token.id,
username=user.name,
tenant_id=tenant.id,
service_catalog=self.service_catalog,
authorized_tenants=tenants)
self.mox.StubOutWithMock(api, 'token_create')
self.mox.StubOutWithMock(api, 'tenant_list_for_token')
api.token_create(IsA(http.HttpRequest), NEW_TENANT_ID, self.TEST_USER,
self.PASSWORD).AndReturn(aToken)
api.tenant_list_for_token(IsA(http.HttpRequest), aToken.id) \
.AndReturn([aTenant])
api.token_create(IsA(http.HttpRequest),
switch_to,
user.name,
user.password).AndReturn(scoped_token)
api.tenant_list_for_token(IsA(http.HttpRequest),
token.id).AndReturn(tenants)
self.mox.ReplayAll()
form_data = {'method': 'LoginWithTenant',
'region': 'http://localhost:5000/v2.0',
'password': self.PASSWORD,
'tenant': NEW_TENANT_ID,
'username': self.TEST_USER}
res = self.client.post(reverse('horizon:auth_switch',
args=[NEW_TENANT_ID]), form_data)
'username': user.name,
'password': user.password,
'tenant': switch_to}
switch_url = reverse('horizon:auth_switch', args=[switch_to])
res = self.client.post(switch_url, form_data)
self.assertRedirectsNoFollow(res, DASH_INDEX_URL)
self.assertEqual(self.client.session['tenant'], NEW_TENANT_NAME)
self.assertEqual(self.client.session['tenant'],
scoped_token.tenant['name'])
def test_logout(self):
KEY = 'arbitraryKeyString'

View File

@ -191,8 +191,6 @@ class HorizonTests(BaseHorizonTests):
syspanel = horizon.get_dashboard("syspanel")
self.assertFalse(hasattr(syspanel, "evil"))
class HorizonBaseViewTests(BaseHorizonTests, test.BaseViewTests):
def test_public(self):
users.get_user_from_request = self._real_get_user_from_request
settings = horizon.get_dashboard("settings")
@ -220,10 +218,10 @@ class HorizonBaseViewTests(BaseHorizonTests, test.BaseViewTests):
# should get a 404.
new_catalog = [service for service in self.request.user.service_catalog
if service['type'] != MyPanel.services[0]]
tenants = self.TEST_CONTEXT['authorized_tenants']
self.setActiveUser(token=self.TEST_TOKEN,
username=self.TEST_USER,
tenant_id=self.TEST_TENANT,
tenants = self.context['authorized_tenants']
self.setActiveUser(token=self.token.id,
username=self.user.name,
tenant_id=self.tenant.id,
service_catalog=new_catalog,
authorized_tenants=tenants)
resp = self.client.get(panel.get_absolute_url())

View File

@ -38,11 +38,11 @@ class ContextProcessorTests(test.TestCase):
self.request.user.service_catalog = self._prev_catalog
def test_authorized_tenants(self):
tenant_list = self.TEST_CONTEXT['authorized_tenants']
tenant_list = self.context['authorized_tenants']
self.request.user.authorized_tenants = None # Reset from setUp
self.mox.StubOutWithMock(api, 'tenant_list_for_token')
api.tenant_list_for_token(IsA(http.HttpRequest),
self.TEST_TOKEN,
self.token.id,
endpoint_type='internalURL') \
.AndReturn(tenant_list)
self.mox.ReplayAll()
@ -51,4 +51,4 @@ class ContextProcessorTests(test.TestCase):
context = context_processors.horizon(self.request)
self.assertEqual(len(context['authorized_tenants']), 1)
tenant = context['authorized_tenants'].pop()
self.assertEqual(tenant['id'], self.TEST_TENANT)
self.assertEqual(tenant.id, self.tenant.id)

View File

@ -20,7 +20,6 @@
import re
from django import dispatch, http, template
from django.utils.text import normalize_newlines
from horizon import test
@ -34,5 +33,4 @@ def single_line(text):
class TemplateTagTests(test.TestCase):
def setUp(self):
super(TemplateTagTests, self).setUp()
pass

View File

@ -0,0 +1,46 @@
# Copyright 2012 Nebula, Inc.
#
# 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.
from horizon.api import glance
from .utils import TestDataContainer
def data(TEST):
TEST.images = TestDataContainer()
TEST.snapshots = TestDataContainer()
# Snapshots
snapshot_dict = {'name': u'snapshot',
'container_format': u'ami',
'id': 3}
snapshot = glance.Image(snapshot_dict)
snapshot_properties_dict = {'image_type': u'snapshot'}
snapshot.properties = glance.ImageProperties(snapshot_properties_dict)
TEST.snapshots.add(snapshot)
# Images
image_properties_dict = {'image_type': u'image'}
image_dict = {'id': '1',
'name': 'public_image',
'container_format': 'novaImage'}
public_image = glance.Image(image_dict)
public_image.properties = glance.ImageProperties(image_properties_dict)
image_dict = {'id': '2',
'name': 'private_image',
'container_format': 'aki'}
private_image = glance.Image(image_dict)
private_image.properties = glance.ImageProperties(image_properties_dict)
TEST.images.add(public_image, private_image)

View File

@ -0,0 +1,118 @@
# Copyright 2012 Nebula, Inc.
#
# 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.
from django.conf import settings
from keystoneclient.v2_0 import users, tenants, tokens, roles
from .utils import TestDataContainer
# Dummy service catalog with all service.
# All endpoint URLs should point to example.com.
# Try to keep them as accurate to real data as possible (ports, URIs, etc.)
SERVICE_CATALOG = [
{"type": "compute",
"name": "nova",
"endpoints": [
{"region": "RegionOne",
"adminURL": "http://admin.nova.example.com:8774/v1.0",
"internalURL": "http://internal.nova.example.com:8774/v1.0",
"publicURL": "http://public.nova.example.com:8774/v1.0/"}]},
{"type": "image",
"name": "glance",
"endpoints": [
{"region": "RegionOne",
"adminURL": "http://admin.glance.example.com:9292/v1",
"internalURL": "http://internal.glance.example.com:9292/v1",
"publicURL": "http://public.glance.example.com:9292/v1"}]},
{"type": "identity",
"name": "keystone",
"endpoints": [
{"region": "RegionOne",
"adminURL": "http://admin.keystone.example.com:35357/v2.0",
"internalURL": "http://internal.keystone.example.com:5000/v2.0",
"publicURL": "http://public.keystone.example.com:5000/v2.0"}]},
{"type": "object-store",
"name": "swift",
"endpoints": [
{"region": "RegionOne",
"adminURL": "http://admin.swift.example.com:8080/",
"internalURL": "http://internal.swift.example.com:8080/",
"publicURL": "http://public.swift.example.com:8080/"}]},
{"type": "network",
"name": "quantum",
"endpoints": [
{"region": "RegionOne",
"adminURL": "http://admin.quantum.example.com:9696/",
"internalURL": "http://internal.quantum.example.com:9696/",
"publicURL": "http://public.quantum.example.com:9696/"}]},
]
def data(TEST):
TEST.service_catalog = SERVICE_CATALOG
TEST.tokens = TestDataContainer()
TEST.users = TestDataContainer()
TEST.tenants = TestDataContainer()
TEST.roles = TestDataContainer()
admin_role_dict = {'id': '1',
'name': 'admin'}
admin_role = roles.Role(roles.RoleManager, admin_role_dict)
member_role_dict = {'id': "2",
'name': settings.OPENSTACK_KEYSTONE_DEFAULT_ROLE}
member_role = roles.Role(roles.RoleManager, member_role_dict)
TEST.roles.add(member_role, admin_role)
TEST.roles.admin = admin_role
TEST.roles.member = member_role
user_dict = {'id': "1",
'name': 'test_user',
'email': 'test@example.com',
'password': 'password'}
user = users.User(users.UserManager, user_dict)
user_dict.update({'id': "2",
'name': 'user_two',
'email': 'two@example.com'})
user2 = users.User(users.UserManager, user_dict)
TEST.users.add(user, user2)
TEST.user = user # Your "current" user
tenant_dict = {'id': "1",
'name': 'test_tenant',
'description': "a test tenant."}
tenant = tenants.Tenant(tenants.TenantManager, tenant_dict)
TEST.tenants.add(tenant)
TEST.tenant = tenant # Your "current" tenant
scoped_token = tokens.Token(tokens.TokenManager,
dict(token={"id": "test_token_id",
"expires": "#FIXME",
"tenant": tenant_dict,
"tenants": [tenant_dict]},
user={"id": "test_user_id",
"name": "test_user",
"roles": [member_role_dict]},
serviceCatalog=TEST.service_catalog))
unscoped_token = tokens.Token(tokens.TokenManager,
dict(token={"id": "test_token_id",
"expires": "#FIXME"},
user={"id": "test_user_id",
"name": "test_user",
"roles": [member_role_dict]},
serviceCatalog=TEST.service_catalog))
TEST.tokens.add(scoped_token, unscoped_token)
TEST.token = scoped_token # your "current" token.
TEST.tokens.scoped_token = scoped_token
TEST.tokens.unscoped_token = unscoped_token

View File

@ -0,0 +1,262 @@
# Copyright 2012 Nebula, Inc.
#
# 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.
import json
from novaclient.v1_1 import (flavors, keypairs, servers, volumes, quotas,
floating_ips, usage,
volume_snapshots as vol_snaps,
security_group_rules as rules,
security_groups as sec_groups)
from .utils import TestDataContainer
SERVER_DATA = """
{
"server": {
"OS-EXT-STS:task_state": null,
"addresses": {
"private": [
{
"version": 4,
"addr": "10.0.0.1"
}
]
},
"links": [
{
"href": "%(host)s/v1.1/%(tenant_id)s/servers/%(server_id)s",
"rel": "self"
},
{
"href": "%(host)s/%(tenant_id)s/servers/%(server_id)s",
"rel": "bookmark"
}
],
"image": {
"id": "%(image_id)s",
"links": [
{
"href": "%(host)s/%(tenant_id)s/images/%(image_id)s",
"rel": "bookmark"
}
]
},
"OS-EXT-STS:vm_state": "active",
"flavor": {
"id": "%(flavor_id)s",
"links": [
{
"href": "%(host)s/%(tenant_id)s/flavors/%(flavor_id)s",
"rel": "bookmark"
}
]
},
"id": "%(server_id)s",
"user_id": "%(user_id)s",
"OS-DCF:diskConfig": "MANUAL",
"accessIPv4": "",
"accessIPv6": "",
"progress": null,
"OS-EXT-STS:power_state": 1,
"config_drive": "",
"status": "%(status)s",
"updated": "2012-02-28T19:51:27Z",
"hostId": "c461ea283faa0ab5d777073c93b126c68139e4e45934d4fc37e403c2",
"key_name": "%(key_name)s",
"name": "%(name)s",
"created": "2012-02-28T19:51:17Z",
"tenant_id": "%(tenant_id)s",
"metadata": {}
}
}
"""
USAGE_DATA = """
{
"total_memory_mb_usage": 64246.89777777778,
"total_vcpus_usage": 125.48222222222223,
"total_hours": 125.48222222222223,
"total_local_gb_usage": 0,
"tenant_id": "%(tenant_id)s",
"stop": "2012-01-31 23:59:59",
"start": "2012-01-01 00:00:00",
"server_usages": [
{
"memory_mb": %(flavor_ram)s,
"uptime": 442321,
"started_at": "2012-01-26 20:38:21",
"ended_at": null,
"name": "%(instance_name)s",
"tenant_id": "%(tenant_id)s",
"state": "active",
"hours": 122.87361111111112,
"vcpus": %(flavor_vcpus)s,
"flavor": "%(flavor_name)s",
"local_gb": %(flavor_disk)s
},
{
"memory_mb": %(flavor_ram)s,
"uptime": 9367,
"started_at": "2012-01-31 20:54:15",
"ended_at": null,
"name": "%(instance_name)s",
"tenant_id": "%(tenant_id)s",
"state": "active",
"hours": 2.608611111111111,
"vcpus": %(flavor_vcpus)s,
"flavor": "%(flavor_name)s",
"local_gb": %(flavor_disk)s
}
]
}
"""
def data(TEST):
TEST.servers = TestDataContainer()
TEST.flavors = TestDataContainer()
TEST.keypairs = TestDataContainer()
TEST.security_groups = TestDataContainer()
TEST.security_group_rules = TestDataContainer()
TEST.volumes = TestDataContainer()
TEST.quotas = TestDataContainer()
TEST.floating_ips = TestDataContainer()
TEST.usages = TestDataContainer()
TEST.volume_snapshots = TestDataContainer()
# Volumes
volume = volumes.Volume(volumes.VolumeManager,
dict(id="1",
name='test_volume',
status='available',
size=40,
displayName='',
attachments={}))
TEST.volumes.add(volume)
# Flavors
flavor_1 = flavors.Flavor(flavors.FlavorManager,
dict(id="1",
name='m1.tiny',
vcpus=1,
disk=0,
ram=512))
flavor_2 = flavors.Flavor(flavors.FlavorManager,
dict(id="2",
name='m1.massive',
vcpus=1000,
disk=1024,
ram=10000))
TEST.flavors.add(flavor_1, flavor_2)
# Keypairs
keypair = keypairs.Keypair(keypairs.KeypairManager,
dict(name='keyName'))
TEST.keypairs.add(keypair)
# Security Groups
sec_group_1 = sec_groups.SecurityGroup(sec_groups.SecurityGroupManager,
{"rules": [],
"tenant_id": TEST.tenant.id,
"id": 1,
"name": u"default",
"description": u"default"})
sec_group_2 = sec_groups.SecurityGroup(sec_groups.SecurityGroupManager,
{"rules": [],
"tenant_id": TEST.tenant.id,
"id": 2,
"name": u"other_group",
"description": u"Not default."})
rule = {'id': 1,
'ip_protocol': u"tcp",
'from_port': u"80",
'to_port': u"80",
'parent_group_id': 1,
'ip_range': {'cidr': u"0.0.0.0/32"}}
rule_obj = rules.SecurityGroupRule(rules.SecurityGroupRuleManager, rule)
TEST.security_group_rules.add(rule_obj)
sec_group_1.rules = [rule_obj]
sec_group_2.rules = [rule_obj]
TEST.security_groups.add(sec_group_1, sec_group_2)
# Security Group Rules
# Quota Sets
quota_data = dict(metadata_items='1',
injected_file_content_bytes='1',
volumes='1',
gigabytes='1',
ram=1,
floating_ips='1',
instances='1',
injected_files='1',
cores='1')
quota = quotas.QuotaSet(quotas.QuotaSetManager, quota_data)
TEST.quotas.add(quota)
# Servers
vals = {"host": "http://nova.example.com:8774",
"name": "server_1",
"status": "ACTIVE",
"tenant_id": TEST.tenants.first().id,
"user_id": TEST.user.id,
"server_id": "1",
"flavor_id": flavor_1.id,
"image_id": TEST.images.first().id,
"key_name": keypair.name}
server_1 = servers.Server(servers.ServerManager,
json.loads(SERVER_DATA % vals)['server'])
vals.update({"name": "server_2",
"status": "BUILD",
"server_id": "2"})
server_2 = servers.Server(servers.ServerManager,
json.loads(SERVER_DATA % vals)['server'])
TEST.servers.add(server_1, server_2)
# VNC Console Data
console = {u'console': {u'url': u'http://example.com:6080/vnc_auto.html',
u'type': u'novnc'}}
TEST.servers.console_data = console
# Floating IPs
fip_1 = floating_ips.FloatingIP(floating_ips.FloatingIPManager,
{'id': 1,
'fixed_ip': '10.0.0.4',
'instance_id': server_1.id,
'ip': '58.58.58.58'})
TEST.floating_ips.add(fip_1)
# Usage
usage_vals = {"tenant_id": TEST.tenant.id,
"instance_name": server_1.name,
"flavor_name": flavor_1.name,
"flavor_vcpus": flavor_1.vcpus,
"flavor_disk": flavor_1.disk,
"flavor_ram": flavor_1.ram}
usage_obj = usage.Usage(usage.UsageManager,
json.loads(USAGE_DATA % usage_vals))
TEST.usages.add(usage_obj)
volume_snapshot = vol_snaps.Snapshot(vol_snaps.SnapshotManager,
{'id': 2,
'displayName': 'test snapshot',
'displayDescription': 'vol snap!',
'size': 40,
'status': 'available',
'volumeId': 1})
TEST.volume_snapshots.add(volume_snapshot)

View File

@ -0,0 +1,22 @@
# Copyright 2012 Nebula, Inc.
#
# 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.
from .utils import TestDataContainer
def data(TEST):
TEST.networks = TestDataContainer()
TEST.ports = TestDataContainer()
# TODO(gabriel): Move quantum test data into this module after it
# has been refactored with object wrappers (a la Glance).

View File

@ -0,0 +1,53 @@
# Copyright 2012 Nebula, Inc.
#
# 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.
import new
from cloudfiles import container, storage_object
from .utils import TestDataContainer
def data(TEST):
TEST.containers = TestDataContainer()
TEST.objects = TestDataContainer()
class FakeConnection(object):
def __init__(self):
self.cdn_enabled = False
conn = FakeConnection()
container_1 = container.Container(conn, name="container_one")
container_2 = container.Container(conn, name="container_two")
TEST.containers.add(container_1, container_2)
object_dict = {"name": "test_object",
"content_type": "text/plain",
"bytes": 128,
"last_modified": None,
"hash": "object_hash"}
obj_dicts = [object_dict]
for obj_dict in obj_dicts:
swift_object = storage_object.Object(container_1,
object_record=obj_dict)
TEST.objects.add(swift_object)
# Override the list method to return the type of list cloudfiles does.
def get_object_result_list(self):
return storage_object.ObjectResults(container_1,
objects=obj_dicts)
list_method = new.instancemethod(get_object_result_list, TEST.objects)
TEST.objects.list = list_method

View File

@ -0,0 +1,122 @@
# Copyright 2012 Nebula, Inc.
#
# 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.
def load_test_data(load_onto=None):
from . import glance_data
from . import keystone_data
from . import nova_data
from . import swift_data
# The order of these loaders matters, some depend on others.
loaders = (keystone_data.data,
glance_data.data,
nova_data.data,
swift_data.data)
if load_onto:
for data_func in loaders:
data_func(load_onto)
return load_onto
else:
return TestData(*loaders)
class TestData(object):
"""
Holder object for test data. Any functions passed to the init method
will be called with the ``TestData`` object as their only argument. They
can then load data onto the object as desired.
The idea is to use the instantiated object like this::
>>> import glance_data
>>> TEST = TestData(glance_data.data)
>>> TEST.images.list()
... [<Image: visible_image>, <Image: invisible_image>]
>>> TEST.images.first()
... <Image: visible_image>
You can load as little or as much data as you like as long as the loaders
don't conflict with each other.
See the :class:`~horizon.tests.test_data.utils.TestDataContainer` class
for a list of available methods.
"""
def __init__(self, *args):
for data_func in args:
data_func(self)
class TestDataContainer(object):
""" A container for test data objects.
The behavior of this class is meant to mimic a "manager" class, which
has convenient shortcuts for common actions like "list", "filter", "get",
and "add".
"""
def __init__(self):
self._objects = []
def add(self, *args):
""" Add a new object to this container.
Generally this method should only be used during data loading, since
adding data during a test can affect the results of other tests.
"""
for obj in args:
if obj not in self._objects:
self._objects.append(obj)
def list(self):
""" Returns a list of all objects in this container. """
return self._objects
def filter(self, filtered=None, **kwargs):
"""
Returns objects in this container whose attributes match the given
keyword arguments.
"""
if filtered is None:
filtered = self._objects
try:
key, value = kwargs.popitem()
except KeyError:
# We're out of filters, return
return filtered
def get_match(obj):
return hasattr(obj, key) and getattr(obj, key) == value
return self.filter(filtered=filter(get_match, filtered), **kwargs)
def get(self, **kwargs):
"""
Returns the single object in this container whose attributes match
the given keyword arguments. An error will be raised if the arguments
provided don't return exactly one match.
"""
matches = self.filter(**kwargs)
if not matches:
raise Exception("No matches found.")
elif len(matches) > 1:
raise Exception("Multiple matches found.")
else:
return matches.pop()
def first(self):
""" Returns the first object from this container. """
return self._objects[0]
def count(self):
return len(self._objects)

View File

@ -60,6 +60,8 @@ TEMPLATE_CONTEXT_PROCESSORS = (
'django.contrib.messages.context_processors.messages',
'horizon.context_processors.horizon')
MESSAGE_STORAGE = 'django.contrib.messages.storage.cookie.CookieStorage'
ROOT_URLCONF = 'horizon.tests.testurls'
TEMPLATE_DIRS = (os.path.join(ROOT_PATH, 'tests', 'templates'))
SITE_ID = 1

View File

@ -27,5 +27,4 @@ def fakeView(request):
'This is a fake httpresponse from a fake view for testing '
' purposes only'
'</p></body></html>')
return resp

View File

@ -76,9 +76,6 @@ class BaseUsage(object):
def get_usage_list(self, start, end):
raise NotImplementedError("You must define a get_usage method.")
def get_summary(self):
raise NotImplementedError("You must define a get_summary method.")
def summarize(self, start, end):
if start <= end <= time.today():
# Convert to datetime.datetime just for API call.

View File

@ -23,6 +23,8 @@ Classes and methods related to user handling in Horizon.
import logging
from django.utils.translation import ugettext as _
from horizon import exceptions
@ -135,7 +137,8 @@ class User(object):
return False
def get_and_delete_messages(self):
""" Placeholder function for parity with
"""
Placeholder function for parity with
``django.contrib.auth.models.User``.
"""
return []