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:
parent
2e914f9578
commit
aca739445e
@ -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
17
docs/source/ref/test.rst
Normal 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:
|
@ -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>`.
|
||||
|
@ -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
|
||||
=======================
|
||||
|
276
docs/source/topics/testing.rst
Normal file
276
docs/source/topics/testing.rst
Normal 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.
|
@ -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
|
||||
|
||||
|
||||
|
@ -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)]
|
||||
|
@ -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):
|
||||
|
@ -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):
|
||||
|
@ -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):
|
||||
|
@ -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)
|
||||
|
@ -25,8 +25,6 @@ import logging
|
||||
|
||||
from django.conf import settings
|
||||
|
||||
from horizon import api
|
||||
|
||||
|
||||
LOG = logging.getLogger(__name__)
|
||||
|
||||
|
@ -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)
|
||||
|
@ -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')
|
||||
|
||||
|
||||
|
@ -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)
|
||||
|
@ -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}
|
||||
|
@ -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)
|
||||
|
@ -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):
|
||||
|
@ -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']),
|
||||
|
@ -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:
|
||||
|
@ -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)
|
||||
|
@ -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)
|
||||
|
@ -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)
|
||||
|
@ -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)
|
||||
|
@ -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')
|
||||
|
@ -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)
|
||||
|
@ -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)
|
||||
|
@ -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()
|
||||
|
||||
|
@ -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"])
|
||||
|
@ -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))
|
||||
|
@ -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)
|
||||
|
@ -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)
|
||||
|
@ -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)
|
||||
|
@ -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):
|
||||
|
@ -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
|
||||
|
@ -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
|
@ -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')
|
@ -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
|
86
horizon/horizon/tests/api_tests/glance_tests.py
Normal file
86
horizon/horizon/tests/api_tests/glance_tests.py
Normal 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
|
@ -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)
|
84
horizon/horizon/tests/api_tests/keystone_tests.py
Normal file
84
horizon/horizon/tests/api_tests/keystone_tests.py
Normal 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)
|
@ -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)
|
158
horizon/horizon/tests/api_tests/nova_tests.py
Normal file
158
horizon/horizon/tests/api_tests/nova_tests.py
Normal 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)
|
@ -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)
|
170
horizon/horizon/tests/api_tests/swift_tests.py
Normal file
170
horizon/horizon/tests/api_tests/swift_tests.py
Normal 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)
|
@ -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
|
@ -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'
|
||||
|
@ -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())
|
||||
|
@ -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)
|
||||
|
@ -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
|
||||
|
0
horizon/horizon/tests/test_data/__init__.py
Normal file
0
horizon/horizon/tests/test_data/__init__.py
Normal file
46
horizon/horizon/tests/test_data/glance_data.py
Normal file
46
horizon/horizon/tests/test_data/glance_data.py
Normal 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)
|
118
horizon/horizon/tests/test_data/keystone_data.py
Normal file
118
horizon/horizon/tests/test_data/keystone_data.py
Normal 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
|
262
horizon/horizon/tests/test_data/nova_data.py
Normal file
262
horizon/horizon/tests/test_data/nova_data.py
Normal 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)
|
22
horizon/horizon/tests/test_data/quantum_data.py
Normal file
22
horizon/horizon/tests/test_data/quantum_data.py
Normal 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).
|
53
horizon/horizon/tests/test_data/swift_data.py
Normal file
53
horizon/horizon/tests/test_data/swift_data.py
Normal 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
|
122
horizon/horizon/tests/test_data/utils.py
Normal file
122
horizon/horizon/tests/test_data/utils.py
Normal 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)
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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.
|
||||
|
@ -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 []
|
||||
|
Loading…
Reference in New Issue
Block a user