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
|
intro
|
||||||
quickstart
|
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
|
Topic Guides
|
||||||
============
|
------------
|
||||||
|
|
||||||
Information on how to work with specific areas of Horizon can be found in
|
Information on how to work with specific areas of Horizon can be found in
|
||||||
the following topic guides.
|
the following topic guides.
|
||||||
@ -52,25 +70,7 @@ the following topic guides.
|
|||||||
:maxdepth: 1
|
:maxdepth: 1
|
||||||
|
|
||||||
topics/tables
|
topics/tables
|
||||||
|
topics/testing
|
||||||
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
|
|
||||||
|
|
||||||
API Reference
|
API Reference
|
||||||
-------------
|
-------------
|
||||||
@ -90,6 +90,7 @@ In-depth documentation for Horizon and it's APIs.
|
|||||||
ref/context_processors
|
ref/context_processors
|
||||||
ref/decorators
|
ref/decorators
|
||||||
ref/exceptions
|
ref/exceptions
|
||||||
|
ref/test
|
||||||
|
|
||||||
Source Code Reference
|
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
|
Horizon's tests and you
|
||||||
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
|
|
||||||
===============
|
|
||||||
|
|
||||||
How to run the tests
|
How to run the tests
|
||||||
====================
|
====================
|
||||||
@ -41,22 +25,15 @@ To run the tests::
|
|||||||
:doc:`ref/run_tests`
|
:doc:`ref/run_tests`
|
||||||
Full reference for the ``run_tests.sh`` script.
|
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``
|
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.
|
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
|
In general new code without unit tests will not be accepted, and every bugfix
|
||||||
*must* include a regression test.
|
*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
|
Change the branding of Horizon
|
||||||
------------------------------
|
==============================
|
||||||
=======================
|
|
||||||
Changing the Page Title
|
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
|
The file ``local_settings.py`` can be found at the Horizon directory path of
|
||||||
``horizon/openstack-dashboard/local/local_settings.py``.
|
``horizon/openstack-dashboard/local/local_settings.py``.
|
||||||
|
|
||||||
=======================
|
|
||||||
Changing the Page Logo
|
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):
|
def __getattr__(self, attr):
|
||||||
try:
|
try:
|
||||||
return self._apidict[attr]
|
return self._apidict[attr]
|
||||||
except KeyError, e:
|
except KeyError:
|
||||||
msg = 'Unknown attribute "%(attr)s" on APIResource object ' \
|
msg = 'Unknown attribute "%(attr)s" on APIResource object ' \
|
||||||
'of type "%(cls)s"' % {'attr': attr, 'cls': self.__class__}
|
'of type "%(cls)s"' % {'attr': attr, 'cls': self.__class__}
|
||||||
LOG.debug(msg)
|
LOG.debug(msg)
|
||||||
@ -90,6 +90,7 @@ class APIDictWrapper(object):
|
|||||||
|
|
||||||
|
|
||||||
def get_service_from_catalog(catalog, service_type):
|
def get_service_from_catalog(catalog, service_type):
|
||||||
|
if catalog:
|
||||||
for service in catalog:
|
for service in catalog:
|
||||||
if service['type'] == service_type:
|
if service['type'] == service_type:
|
||||||
return service
|
return service
|
||||||
|
@ -25,34 +25,42 @@ import urlparse
|
|||||||
|
|
||||||
from glance import client as glance_client
|
from glance import client as glance_client
|
||||||
|
|
||||||
from horizon.api.base import *
|
from horizon.api.base import APIDictWrapper, url_for
|
||||||
|
|
||||||
|
|
||||||
LOG = logging.getLogger(__name__)
|
LOG = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
class Image(APIDictWrapper):
|
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',
|
_attrs = ['checksum', 'container_format', 'created_at', 'deleted',
|
||||||
'deleted_at', 'disk_format', 'id', 'is_public', 'location',
|
'deleted_at', 'disk_format', 'id', 'is_public', 'location',
|
||||||
'name', 'properties', 'size', 'status', 'updated_at', 'owner']
|
'name', 'properties', 'size', 'status', 'updated_at', 'owner']
|
||||||
|
|
||||||
def __getattr__(self, attrname):
|
def __getattr__(self, attrname):
|
||||||
if attrname == "properties":
|
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:
|
else:
|
||||||
return super(Image, self).__getattr__(attrname)
|
return super(Image, self).__getattr__(attrname)
|
||||||
|
|
||||||
|
|
||||||
class ImageProperties(APIDictWrapper):
|
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',
|
_attrs = ['architecture', 'image_location', 'image_state', 'kernel_id',
|
||||||
'project_id', 'ramdisk_id', 'image_type']
|
'project_id', 'ramdisk_id', 'image_type']
|
||||||
|
|
||||||
|
|
||||||
def glance_api(request):
|
def glanceclient(request):
|
||||||
o = urlparse.urlparse(url_for(request, 'image'))
|
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))
|
(o.hostname, o.port))
|
||||||
return glance_client.Client(o.hostname,
|
return glance_client.Client(o.hostname,
|
||||||
o.port,
|
o.port,
|
||||||
@ -60,11 +68,11 @@ def glance_api(request):
|
|||||||
|
|
||||||
|
|
||||||
def image_create(request, image_meta, image_file):
|
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):
|
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):
|
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
|
Returns the actual image file from Glance for image with
|
||||||
supplied identifier
|
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):
|
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
|
Returns an Image object populated with metadata for image
|
||||||
with supplied identifier.
|
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):
|
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):
|
def image_update(request, image_id, image_meta=None):
|
||||||
image_meta = image_meta and image_meta or {}
|
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))
|
image_meta=image_meta))
|
||||||
|
|
||||||
|
|
||||||
@ -97,5 +105,5 @@ def snapshot_list_detailed(request):
|
|||||||
filters = {}
|
filters = {}
|
||||||
filters['property-image_type'] = 'snapshot'
|
filters['property-image_type'] = 'snapshot'
|
||||||
filters['is_public'] = 'none'
|
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)]
|
.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 keystoneclient.v2_0 import tokens
|
||||||
|
|
||||||
from horizon import exceptions
|
from horizon import exceptions
|
||||||
from horizon.api import APIResourceWrapper
|
|
||||||
|
|
||||||
|
|
||||||
LOG = logging.getLogger(__name__)
|
LOG = logging.getLogger(__name__)
|
||||||
@ -39,21 +38,6 @@ def _get_endpoint_url(request):
|
|||||||
getattr(settings, 'OPENSTACK_KEYSTONE_URL'))
|
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,
|
def keystoneclient(request, username=None, password=None, tenant_id=None,
|
||||||
token_id=None, endpoint=None, endpoint_type=None):
|
token_id=None, endpoint=None, endpoint_type=None):
|
||||||
"""Returns a client connected to the Keystone backend.
|
"""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,
|
token = c.tokens.authenticate(username=username,
|
||||||
password=password,
|
password=password,
|
||||||
tenant_id=tenant)
|
tenant_id=tenant)
|
||||||
return Token(token)
|
return token
|
||||||
|
|
||||||
|
|
||||||
def token_create_scoped(request, tenant, 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',
|
c.management_url = c.service_catalog.url_for(service_type='identity',
|
||||||
endpoint_type='publicURL')
|
endpoint_type='publicURL')
|
||||||
scoped_token = tokens.Token(tokens.TokenManager, raw_token)
|
scoped_token = tokens.Token(tokens.TokenManager, raw_token)
|
||||||
return Token(scoped_token)
|
return scoped_token
|
||||||
|
|
||||||
|
|
||||||
def user_list(request, tenant_id=None):
|
def user_list(request, tenant_id=None):
|
||||||
return [User(u) for u in
|
return keystoneclient(request).users.list(tenant_id=tenant_id)
|
||||||
keystoneclient(request).users.list(tenant_id=tenant_id)]
|
|
||||||
|
|
||||||
|
|
||||||
def user_create(request, user_id, email, password, tenant_id, enabled):
|
def user_create(request, user_id, email, password, tenant_id, enabled):
|
||||||
return User(keystoneclient(request).users.create(
|
return keystoneclient(request).users.create(user_id,
|
||||||
user_id, password, email, tenant_id, enabled))
|
password,
|
||||||
|
email,
|
||||||
|
tenant_id,
|
||||||
|
enabled)
|
||||||
|
|
||||||
|
|
||||||
def user_delete(request, user_id):
|
def user_delete(request, user_id):
|
||||||
@ -202,25 +188,23 @@ def user_delete(request, user_id):
|
|||||||
|
|
||||||
|
|
||||||
def user_get(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):
|
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):
|
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):
|
def user_update_password(request, user_id, password):
|
||||||
return User(keystoneclient(request).users \
|
return keystoneclient(request).users.update_password(user_id, password)
|
||||||
.update_password(user_id, password))
|
|
||||||
|
|
||||||
|
|
||||||
def user_update_tenant(request, user_id, tenant_id):
|
def user_update_tenant(request, user_id, tenant_id):
|
||||||
return User(keystoneclient(request).users \
|
return keystoneclient(request).users.update_tenant(user_id, tenant_id)
|
||||||
.update_tenant(user_id, tenant_id))
|
|
||||||
|
|
||||||
|
|
||||||
def role_list(request):
|
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 import security_group_rules as nova_rules
|
||||||
from novaclient.v1_1.servers import REBOOT_HARD
|
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__)
|
LOG = logging.getLogger(__name__)
|
||||||
@ -38,43 +38,16 @@ INSTANCE_ACTIVE_STATE = 'ACTIVE'
|
|||||||
VOLUME_STATE_AVAILABLE = "available"
|
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):
|
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']
|
_attrs = ['url', 'type']
|
||||||
|
|
||||||
|
|
||||||
class Quota(object):
|
class Quota(object):
|
||||||
""" Basic wrapper for individual limits in a quota."""
|
""" Wrapper for individual limits in a quota. """
|
||||||
def __init__(self, name, limit):
|
def __init__(self, name, limit):
|
||||||
self.name = name
|
self.name = name
|
||||||
self.limit = limit
|
self.limit = limit
|
||||||
@ -84,7 +57,10 @@ class Quota(object):
|
|||||||
|
|
||||||
|
|
||||||
class QuotaSet(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):
|
def __init__(self, apiresource):
|
||||||
self.items = []
|
self.items = []
|
||||||
for k in apiresource._info.keys():
|
for k in apiresource._info.keys():
|
||||||
@ -167,7 +143,10 @@ class Usage(APIResourceWrapper):
|
|||||||
|
|
||||||
|
|
||||||
class SecurityGroup(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']
|
_attrs = ['id', 'name', 'description', 'tenant_id']
|
||||||
|
|
||||||
@property
|
@property
|
||||||
@ -185,7 +164,7 @@ class SecurityGroup(APIResourceWrapper):
|
|||||||
|
|
||||||
|
|
||||||
class SecurityGroupRule(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']
|
_attrs = ['id', 'ip_protocol', 'from_port', 'to_port', 'ip_range']
|
||||||
|
|
||||||
def __unicode__(self):
|
def __unicode__(self):
|
||||||
@ -207,14 +186,14 @@ def novaclient(request):
|
|||||||
return c
|
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,
|
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):
|
def flavor_create(request, name, memory, vcpu, disk, flavor_id):
|
||||||
return Flavor(novaclient(request).flavors.create(
|
return novaclient(request).flavors.create(name, int(memory), int(vcpu),
|
||||||
name, int(memory), int(vcpu), int(disk), flavor_id))
|
int(disk), flavor_id)
|
||||||
|
|
||||||
|
|
||||||
def flavor_delete(request, flavor_id):
|
def flavor_delete(request, flavor_id):
|
||||||
@ -222,26 +201,25 @@ def flavor_delete(request, flavor_id):
|
|||||||
|
|
||||||
|
|
||||||
def flavor_get(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):
|
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):
|
def tenant_floating_ip_list(request):
|
||||||
"""
|
"""
|
||||||
Fetches a list of all floating ips.
|
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):
|
def floating_ip_pools_list(request):
|
||||||
"""
|
"""
|
||||||
Fetches a list of all floating ip pools.
|
Fetches a list of all floating ip pools.
|
||||||
"""
|
"""
|
||||||
return [FloatingIpPool(pool)
|
return novaclient(request).floating_ip_pools.list()
|
||||||
for pool in novaclient(request).floating_ip_pools.list()]
|
|
||||||
|
|
||||||
|
|
||||||
def tenant_floating_ip_get(request, floating_ip_id):
|
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):
|
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):
|
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):
|
def keypair_delete(request, keypair_id):
|
||||||
@ -283,7 +261,7 @@ def keypair_delete(request, keypair_id):
|
|||||||
|
|
||||||
|
|
||||||
def keypair_list(request):
|
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,
|
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)
|
novaclient(request).servers.resume(instance_id)
|
||||||
|
|
||||||
|
|
||||||
def server_reboot(request,
|
def server_reboot(request, instance_id, hardness=REBOOT_HARD):
|
||||||
instance_id,
|
|
||||||
hardness=REBOOT_HARD):
|
|
||||||
server = server_get(request, instance_id)
|
server = server_get(request, instance_id)
|
||||||
server.reboot(hardness)
|
server.reboot(hardness)
|
||||||
|
|
||||||
@ -347,24 +323,22 @@ def server_update(request, instance_id, name):
|
|||||||
return novaclient(request).servers.update(instance_id, name=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.
|
Associates floating IP to server's fixed IP.
|
||||||
"""
|
"""
|
||||||
server = novaclient(request).servers.get(server)
|
server = novaclient(request).servers.get(server)
|
||||||
fip = novaclient(request).floating_ips.get(address)
|
fip = novaclient(request).floating_ips.get(floating_ip)
|
||||||
|
return novaclient(request).servers.add_floating_ip(server.id, fip.id)
|
||||||
return novaclient(request).servers.add_floating_ip(server, fip)
|
|
||||||
|
|
||||||
|
|
||||||
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.
|
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)
|
server = novaclient(request).servers.get(fip.instance_id)
|
||||||
|
return novaclient(request).servers.remove_floating_ip(server.id, fip.id)
|
||||||
return novaclient(request).servers.remove_floating_ip(server, fip)
|
|
||||||
|
|
||||||
|
|
||||||
def tenant_quota_get(request, tenant_id):
|
def tenant_quota_get(request, tenant_id):
|
||||||
@ -427,11 +401,11 @@ def virtual_interfaces_list(request, instance_id):
|
|||||||
|
|
||||||
|
|
||||||
def volume_list(request):
|
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):
|
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):
|
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):
|
def volume_create(request, size, name, description):
|
||||||
return Volume(novaclient(request).volumes.create(
|
return novaclient(request).volumes.create(size,
|
||||||
size, display_name=name, display_description=description))
|
display_name=name,
|
||||||
|
display_description=description)
|
||||||
|
|
||||||
|
|
||||||
def volume_delete(request, volume_id):
|
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):
|
def volume_attach(request, volume_id, instance_id, device):
|
||||||
novaclient(request).volumes.create_server_volume(
|
novaclient(request).volumes.create_server_volume(instance_id,
|
||||||
instance_id, volume_id, device)
|
volume_id,
|
||||||
|
device)
|
||||||
|
|
||||||
|
|
||||||
def volume_detach(request, instance_id, attachment_id):
|
def volume_detach(request, instance_id, attachment_id):
|
||||||
|
@ -22,25 +22,24 @@
|
|||||||
from __future__ import absolute_import
|
from __future__ import absolute_import
|
||||||
|
|
||||||
import logging
|
import logging
|
||||||
|
import urlparse
|
||||||
|
|
||||||
from django.conf import settings
|
|
||||||
from quantum import client as quantum_client
|
from quantum import client as quantum_client
|
||||||
|
|
||||||
|
from horizon.api.base import url_for
|
||||||
from horizon.api import nova
|
from horizon.api import nova
|
||||||
from horizon.api.base import *
|
|
||||||
|
|
||||||
|
|
||||||
LOG = logging.getLogger(__name__)
|
LOG = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
def quantum_api(request):
|
# FIXME(gabriel): Add object wrappers for Quantum client. The quantum client
|
||||||
if hasattr(request, 'user'):
|
# returns plain dicts (a la Glance) which we should wrap.
|
||||||
tenant = request.user.tenant_id
|
|
||||||
else:
|
|
||||||
tenant = settings.QUANTUM_TENANT
|
|
||||||
|
|
||||||
return quantum_client.Client(settings.QUANTUM_URL, settings.QUANTUM_PORT,
|
def quantum_api(request):
|
||||||
False, tenant, 'json')
|
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):
|
def quantum_list_networks(request):
|
||||||
|
@ -23,29 +23,15 @@ import logging
|
|||||||
import cloudfiles
|
import cloudfiles
|
||||||
from django.conf import settings
|
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__)
|
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):
|
class SwiftAuthentication(object):
|
||||||
"""Auth container to pass CloudFiles storage URL and token from
|
""" Auth container in the format CloudFiles expects. """
|
||||||
session.
|
|
||||||
"""
|
|
||||||
def __init__(self, storage_url, auth_token):
|
def __init__(self, storage_url, auth_token):
|
||||||
self.storage_url = storage_url
|
self.storage_url = storage_url
|
||||||
self.auth_token = auth_token
|
self.auth_token = auth_token
|
||||||
@ -55,11 +41,10 @@ class SwiftAuthentication(object):
|
|||||||
|
|
||||||
|
|
||||||
def swift_api(request):
|
def swift_api(request):
|
||||||
LOG.debug('object store connection created using token "%s"'
|
endpoint = url_for(request, 'object-store')
|
||||||
' and url "%s"' %
|
LOG.debug('Swift connection created using token "%s" and url "%s"'
|
||||||
(request.session['token'], url_for(request, 'object-store')))
|
% (request.session['token'], endpoint))
|
||||||
auth = SwiftAuthentication(url_for(request, 'object-store'),
|
auth = SwiftAuthentication(endpoint, request.session['token'])
|
||||||
request.session['token'])
|
|
||||||
return cloudfiles.get_connection(auth=auth)
|
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):
|
def swift_get_containers(request, marker=None):
|
||||||
limit = getattr(settings, 'API_RESULT_LIMIT', 1000)
|
limit = getattr(settings, 'API_RESULT_LIMIT', 1000)
|
||||||
containers = [Container(c) for c in swift_api(request).get_all_containers(
|
containers = swift_api(request).get_all_containers(limit=limit + 1,
|
||||||
limit=limit + 1,
|
marker=marker)
|
||||||
marker=marker)]
|
|
||||||
if(len(containers) > limit):
|
if(len(containers) > limit):
|
||||||
return (containers[0:-1], True)
|
return (containers[0:-1], True)
|
||||||
else:
|
else:
|
||||||
@ -94,9 +78,8 @@ def swift_get_containers(request, marker=None):
|
|||||||
|
|
||||||
def swift_create_container(request, name):
|
def swift_create_container(request, name):
|
||||||
if swift_container_exists(request, name):
|
if swift_container_exists(request, name):
|
||||||
raise Exception('Container with name %s already exists.' % (name))
|
raise exceptions.AlreadyExists(name, 'container')
|
||||||
|
return swift_api(request).create_container(name)
|
||||||
return Container(swift_api(request).create_container(name))
|
|
||||||
|
|
||||||
|
|
||||||
def swift_delete_container(request, 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):
|
def swift_get_objects(request, container_name, prefix=None, marker=None):
|
||||||
limit = getattr(settings, 'API_RESULT_LIMIT', 1000)
|
limit = getattr(settings, 'API_RESULT_LIMIT', 1000)
|
||||||
container = swift_api(request).get_container(container_name)
|
container = swift_api(request).get_container(container_name)
|
||||||
objects = [SwiftObject(o) for o in
|
objects = container.get_objects(prefix=prefix,
|
||||||
container.get_objects(prefix=prefix, marker=marker,
|
marker=marker,
|
||||||
limit=limit + 1)]
|
limit=limit + 1)
|
||||||
if(len(objects) > limit):
|
if(len(objects) > limit):
|
||||||
return (objects[0:-1], True)
|
return (objects[0:-1], True)
|
||||||
else:
|
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)
|
container = swift_api(request).get_container(orig_container_name)
|
||||||
|
|
||||||
if swift_object_exists(request, new_container_name, new_object_name):
|
if swift_object_exists(request, new_container_name, new_object_name):
|
||||||
raise Exception('Object with name %s already exists in container %s'
|
raise exceptions.AlreadyExists(new_object_name, 'object')
|
||||||
% (new_object_name, new_container_name))
|
|
||||||
|
|
||||||
orig_obj = container.get_object(orig_object_name)
|
orig_obj = container.get_object(orig_object_name)
|
||||||
return orig_obj.copy_to(new_container_name, new_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 django.conf import settings
|
||||||
|
|
||||||
from horizon import api
|
|
||||||
|
|
||||||
|
|
||||||
LOG = logging.getLogger(__name__)
|
LOG = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
@ -48,16 +48,18 @@ class FloatingIpAssociate(forms.SelfHandlingForm):
|
|||||||
label=_("Instance"))
|
label=_("Instance"))
|
||||||
|
|
||||||
def handle(self, request, data):
|
def handle(self, request, data):
|
||||||
|
ip_id = int(data['floating_ip_id'])
|
||||||
try:
|
try:
|
||||||
api.server_add_floating_ip(request,
|
api.server_add_floating_ip(request,
|
||||||
data['instance_id'],
|
data['instance_id'],
|
||||||
data['floating_ip_id'])
|
ip_id)
|
||||||
LOG.info('Associating Floating IP "%s" with Instance "%s"'
|
LOG.info('Associating Floating IP "%s" with Instance "%s"'
|
||||||
% (data['floating_ip'], data['instance_id']))
|
% (data['floating_ip'], data['instance_id']))
|
||||||
messages.info(request, _('Successfully associated Floating IP \
|
messages.success(request,
|
||||||
%(ip)s with Instance: %(inst)s'
|
_('Successfully associated Floating IP %(ip)s '
|
||||||
|
'with Instance: %(inst)s')
|
||||||
% {"ip": data['floating_ip'],
|
% {"ip": data['floating_ip'],
|
||||||
"inst": data['instance_id']}))
|
"inst": data['instance_id']})
|
||||||
except novaclient_exceptions.ClientException, e:
|
except novaclient_exceptions.ClientException, e:
|
||||||
LOG.exception("ClientException in FloatingIpAssociate")
|
LOG.exception("ClientException in FloatingIpAssociate")
|
||||||
messages.error(request, _('Error associating Floating IP: %s') % e)
|
messages.error(request, _('Error associating Floating IP: %s') % e)
|
||||||
|
@ -20,9 +20,9 @@ import logging
|
|||||||
from django import shortcuts
|
from django import shortcuts
|
||||||
from django.contrib import messages
|
from django.contrib import messages
|
||||||
from django.utils.translation import ugettext as _
|
from django.utils.translation import ugettext as _
|
||||||
from novaclient import exceptions as novaclient_exceptions
|
|
||||||
|
|
||||||
from horizon import api
|
from horizon import api
|
||||||
|
from horizon import exceptions
|
||||||
from horizon import tables
|
from horizon import tables
|
||||||
|
|
||||||
|
|
||||||
@ -76,13 +76,12 @@ class DisassociateIP(tables.Action):
|
|||||||
fip = table.get_object_by_id(int(obj_id))
|
fip = table.get_object_by_id(int(obj_id))
|
||||||
api.server_remove_floating_ip(request, fip.instance_id, fip.id)
|
api.server_remove_floating_ip(request, fip.instance_id, fip.id)
|
||||||
LOG.info('Disassociating Floating IP "%s".' % obj_id)
|
LOG.info('Disassociating Floating IP "%s".' % obj_id)
|
||||||
messages.info(request,
|
messages.success(request,
|
||||||
_('Successfully disassociated Floating IP: %s')
|
_('Successfully disassociated Floating IP: %s')
|
||||||
% obj_id)
|
% obj_id)
|
||||||
except novaclient_exceptions.ClientException, e:
|
except:
|
||||||
LOG.exception("ClientException in FloatingIpAssociate")
|
exceptions.handle(request,
|
||||||
messages.error(request, _('Error disassociating Floating IP: %s')
|
_('Unable to disassociate floating IP.'))
|
||||||
% e.message)
|
|
||||||
return shortcuts.redirect('horizon:nova:access_and_security:index')
|
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')
|
INDEX_URL = reverse('horizon:nova:access_and_security:index')
|
||||||
|
NAMESPACE = "horizon:nova:access_and_security:floating_ips"
|
||||||
|
|
||||||
|
|
||||||
class FloatingIpViewTests(test.BaseViewTests):
|
class FloatingIpViewTests(test.TestCase):
|
||||||
|
|
||||||
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,)
|
|
||||||
|
|
||||||
def test_associate(self):
|
def test_associate(self):
|
||||||
|
floating_ip = self.floating_ips.first()
|
||||||
self.mox.StubOutWithMock(api, 'server_list')
|
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')
|
self.mox.StubOutWithMock(api, 'tenant_floating_ip_get')
|
||||||
api.tenant_floating_ip_get = self.mox.CreateMockAnything()
|
api.server_list(IsA(http.HttpRequest)).AndReturn(self.servers.list())
|
||||||
api.tenant_floating_ip_get(IsA(http.HttpRequest), str(1)).\
|
api.tenant_floating_ip_get(IsA(http.HttpRequest),
|
||||||
AndReturn(self.floating_ip)
|
floating_ip.id).AndReturn(floating_ip)
|
||||||
self.mox.ReplayAll()
|
self.mox.ReplayAll()
|
||||||
|
|
||||||
res = self.client.get(
|
url = reverse('%s:associate' % NAMESPACE, args=[floating_ip.id])
|
||||||
reverse('horizon:nova:access_and_security:floating_ips:associate',
|
res = self.client.get(url)
|
||||||
args=[1]))
|
|
||||||
self.assertTemplateUsed(res,
|
self.assertTemplateUsed(res,
|
||||||
'nova/access_and_security/floating_ips/associate.html')
|
'nova/access_and_security/floating_ips/associate.html')
|
||||||
|
|
||||||
def test_associate_post(self):
|
def test_associate_post(self):
|
||||||
|
floating_ip = self.floating_ips.first()
|
||||||
|
server = self.servers.first()
|
||||||
self.mox.StubOutWithMock(api, 'server_list')
|
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')
|
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')
|
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')
|
self.mox.StubOutWithMock(api, 'tenant_floating_ip_get')
|
||||||
api.tenant_floating_ip_get = self.mox.CreateMockAnything()
|
api.server_list(IsA(http.HttpRequest)).AndReturn(self.servers.list())
|
||||||
api.tenant_floating_ip_get(IsA(http.HttpRequest), str(1)).\
|
api.tenant_floating_ip_list(IsA(http.HttpRequest)) \
|
||||||
AndReturn(self.floating_ip)
|
.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()
|
self.mox.ReplayAll()
|
||||||
|
|
||||||
res = self.client.post(
|
form_data = {'instance_id': server.id,
|
||||||
reverse('horizon:nova:access_and_security:floating_ips:associate',
|
'floating_ip_id': floating_ip.id,
|
||||||
args=[1]),
|
'floating_ip': floating_ip.ip,
|
||||||
{'instance_id': 1,
|
'method': 'FloatingIpAssociate'}
|
||||||
'floating_ip_id': self.floating_ip.id,
|
url = reverse('%s:associate' % NAMESPACE, args=[floating_ip.id])
|
||||||
'floating_ip': self.floating_ip.ip,
|
res = self.client.post(url, form_data)
|
||||||
'method': 'FloatingIpAssociate'})
|
|
||||||
|
|
||||||
self.assertRedirects(res, INDEX_URL)
|
self.assertRedirects(res, INDEX_URL)
|
||||||
|
|
||||||
def test_associate_post_with_exception(self):
|
def test_associate_post_with_exception(self):
|
||||||
|
floating_ip = self.floating_ips.first()
|
||||||
|
server = self.servers.first()
|
||||||
self.mox.StubOutWithMock(api, 'server_list')
|
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')
|
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')
|
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')
|
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')
|
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')
|
self.mox.StubOutWithMock(api, 'tenant_floating_ip_get')
|
||||||
api.tenant_floating_ip_get = self.mox.CreateMockAnything()
|
api.server_list(IsA(http.HttpRequest)).AndReturn(self.servers.list())
|
||||||
api.tenant_floating_ip_get(IsA(http.HttpRequest), IsA(unicode)).\
|
api.tenant_floating_ip_list(IsA(http.HttpRequest)) \
|
||||||
AndReturn(self.floating_ip)
|
.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()
|
self.mox.ReplayAll()
|
||||||
|
|
||||||
res = self.client.post(reverse(
|
url = reverse('%s:associate' % NAMESPACE, args=[floating_ip.id])
|
||||||
'horizon:nova:access_and_security:floating_ips:associate',
|
res = self.client.post(url,
|
||||||
args=[1]),
|
|
||||||
{'instance_id': 1,
|
{'instance_id': 1,
|
||||||
'floating_ip_id': self.floating_ip.id,
|
'floating_ip_id': floating_ip.id,
|
||||||
'floating_ip': self.floating_ip.ip,
|
'floating_ip': floating_ip.ip,
|
||||||
'method': 'FloatingIpAssociate'})
|
'method': 'FloatingIpAssociate'})
|
||||||
self.assertRaises(novaclient_exceptions.ClientException)
|
self.assertRaises(novaclient_exceptions.ClientException)
|
||||||
|
|
||||||
self.assertRedirects(res, INDEX_URL)
|
self.assertRedirects(res, INDEX_URL)
|
||||||
|
|
||||||
def test_disassociate_post(self):
|
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.nova, 'keypair_list')
|
||||||
self.mox.StubOutWithMock(api, 'security_group_list')
|
self.mox.StubOutWithMock(api, 'security_group_list')
|
||||||
self.mox.StubOutWithMock(api, 'tenant_floating_ip_list')
|
self.mox.StubOutWithMock(api, 'tenant_floating_ip_list')
|
||||||
self.mox.StubOutWithMock(api, 'tenant_floating_ip_get')
|
self.mox.StubOutWithMock(api, 'tenant_floating_ip_get')
|
||||||
self.mox.StubOutWithMock(api, 'server_remove_floating_ip')
|
self.mox.StubOutWithMock(api, 'server_remove_floating_ip')
|
||||||
|
|
||||||
api.nova.keypair_list(IsA(http.HttpRequest)).AndReturn(self.keypairs)
|
api.nova.keypair_list(IsA(http.HttpRequest)) \
|
||||||
api.security_group_list(IsA(http.HttpRequest)).\
|
.AndReturn(self.keypairs.list())
|
||||||
AndReturn(self.security_groups)
|
api.security_group_list(IsA(http.HttpRequest)) \
|
||||||
api.tenant_floating_ip_list(IsA(http.HttpRequest)).\
|
.AndReturn(self.security_groups.list())
|
||||||
AndReturn(self.floating_ips)
|
api.tenant_floating_ip_list(IsA(http.HttpRequest)) \
|
||||||
|
.AndReturn(self.floating_ips.list())
|
||||||
api.server_remove_floating_ip(IsA(http.HttpRequest), IsA(int),
|
api.server_remove_floating_ip(IsA(http.HttpRequest),
|
||||||
IsA(int)).\
|
server.id,
|
||||||
AndReturn(None)
|
floating_ip.id)
|
||||||
self.mox.ReplayAll()
|
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})
|
res = self.client.post(INDEX_URL, {"action": action})
|
||||||
|
self.assertMessageCount(success=1)
|
||||||
self.assertRedirectsNoFollow(res, INDEX_URL)
|
self.assertRedirectsNoFollow(res, INDEX_URL)
|
||||||
|
|
||||||
def test_disassociate_post_with_exception(self):
|
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.nova, 'keypair_list')
|
||||||
self.mox.StubOutWithMock(api, 'security_group_list')
|
self.mox.StubOutWithMock(api, 'security_group_list')
|
||||||
self.mox.StubOutWithMock(api, 'tenant_floating_ip_list')
|
self.mox.StubOutWithMock(api, 'tenant_floating_ip_list')
|
||||||
self.mox.StubOutWithMock(api, 'tenant_floating_ip_get')
|
self.mox.StubOutWithMock(api, 'tenant_floating_ip_get')
|
||||||
self.mox.StubOutWithMock(api, 'server_remove_floating_ip')
|
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)
|
exc = novaclient_exceptions.ClientException('ClientException')
|
||||||
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')
|
|
||||||
api.server_remove_floating_ip(IsA(http.HttpRequest),
|
api.server_remove_floating_ip(IsA(http.HttpRequest),
|
||||||
IsA(int),
|
server.id,
|
||||||
IsA(int)).AndRaise(exception)
|
floating_ip.id).AndRaise(exc)
|
||||||
self.mox.ReplayAll()
|
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})
|
res = self.client.post(INDEX_URL, {"action": action})
|
||||||
self.assertRaises(novaclient_exceptions.ClientException)
|
self.assertRaises(novaclient_exceptions.ClientException)
|
||||||
self.assertRedirectsNoFollow(res, INDEX_URL)
|
self.assertRedirectsNoFollow(res, INDEX_URL)
|
||||||
|
@ -24,11 +24,11 @@ Views for managing Nova floating IPs.
|
|||||||
"""
|
"""
|
||||||
import logging
|
import logging
|
||||||
|
|
||||||
from django import http
|
from django.core.urlresolvers import reverse
|
||||||
from django.contrib import messages
|
|
||||||
from django.utils.translation import ugettext as _
|
from django.utils.translation import ugettext as _
|
||||||
|
|
||||||
from horizon import api
|
from horizon import api
|
||||||
|
from horizon import exceptions
|
||||||
from horizon import forms
|
from horizon import forms
|
||||||
from .forms import FloatingIpAssociate, FloatingIpAllocate
|
from .forms import FloatingIpAssociate, FloatingIpAllocate
|
||||||
|
|
||||||
@ -42,19 +42,24 @@ class AssociateView(forms.ModalFormView):
|
|||||||
context_object_name = 'floating_ip'
|
context_object_name = 'floating_ip'
|
||||||
|
|
||||||
def get_object(self, *args, **kwargs):
|
def get_object(self, *args, **kwargs):
|
||||||
ip_id = kwargs['ip_id']
|
ip_id = int(kwargs['ip_id'])
|
||||||
try:
|
try:
|
||||||
return api.tenant_floating_ip_get(self.request, ip_id)
|
return api.tenant_floating_ip_get(self.request, ip_id)
|
||||||
except Exception as e:
|
except:
|
||||||
LOG.exception('Error fetching floating ip with id "%s".' % ip_id)
|
redirect = reverse('horizon:nova:access_and_security:index')
|
||||||
messages.error(self.request,
|
exceptions.handle(self.request,
|
||||||
_('Unable to associate floating ip: %s') % e)
|
_('Unable to associate floating IP.'),
|
||||||
raise http.Http404("Floating IP %s not available." % ip_id)
|
redirect=redirect)
|
||||||
|
|
||||||
def get_initial(self):
|
def get_initial(self):
|
||||||
instances = [(server.id, 'id: %s, name: %s' %
|
try:
|
||||||
(server.id, server.name))
|
servers = api.server_list(self.request)
|
||||||
for server in 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,
|
return {'floating_ip_id': self.object.id,
|
||||||
'floating_ip': self.object.ip,
|
'floating_ip': self.object.ip,
|
||||||
'instances': instances}
|
'instances': instances}
|
||||||
@ -71,6 +76,6 @@ class AllocateView(forms.ModalFormView):
|
|||||||
pool_list = [(pool.name, pool.name)
|
pool_list = [(pool.name, pool.name)
|
||||||
for pool in api.floating_ip_pools_list(self.request)]
|
for pool in api.floating_ip_pools_list(self.request)]
|
||||||
else:
|
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,
|
return {'tenant_id': self.request.user.tenant_id,
|
||||||
'pool_list': pool_list}
|
'pool_list': pool_list}
|
||||||
|
@ -30,97 +30,67 @@ from horizon import test
|
|||||||
INDEX_VIEW_URL = reverse('horizon:nova:access_and_security:index')
|
INDEX_VIEW_URL = reverse('horizon:nova:access_and_security:index')
|
||||||
|
|
||||||
|
|
||||||
class KeyPairViewTests(test.BaseViewTests):
|
class KeyPairViewTests(test.TestCase):
|
||||||
def setUp(self):
|
|
||||||
super(KeyPairViewTests, self).setUp()
|
|
||||||
keypair = api.KeyPair(None)
|
|
||||||
keypair.name = 'keyName'
|
|
||||||
self.keypairs = (keypair,)
|
|
||||||
|
|
||||||
def test_delete_keypair(self):
|
def test_delete_keypair(self):
|
||||||
KEYPAIR_ID = self.keypairs[0].name
|
keypair = self.keypairs.first()
|
||||||
formData = {'action': 'keypairs__delete__%s' % KEYPAIR_ID}
|
|
||||||
|
|
||||||
self.mox.StubOutWithMock(api.nova, 'keypair_list')
|
self.mox.StubOutWithMock(api.nova, 'keypair_list')
|
||||||
self.mox.StubOutWithMock(api.nova, 'keypair_delete')
|
self.mox.StubOutWithMock(api.nova, 'keypair_delete')
|
||||||
|
api.nova.keypair_list(IsA(http.HttpRequest)) \
|
||||||
api.nova.keypair_list(IsA(http.HttpRequest)).AndReturn(self.keypairs)
|
.AndReturn(self.keypairs.list())
|
||||||
api.nova.keypair_delete(IsA(http.HttpRequest), unicode(KEYPAIR_ID))
|
api.nova.keypair_delete(IsA(http.HttpRequest), keypair.name)
|
||||||
|
|
||||||
self.mox.ReplayAll()
|
self.mox.ReplayAll()
|
||||||
|
|
||||||
|
formData = {'action': 'keypairs__delete__%s' % keypair.name}
|
||||||
res = self.client.post(INDEX_VIEW_URL, formData)
|
res = self.client.post(INDEX_VIEW_URL, formData)
|
||||||
|
|
||||||
self.assertRedirectsNoFollow(res, INDEX_VIEW_URL)
|
self.assertRedirectsNoFollow(res, INDEX_VIEW_URL)
|
||||||
|
|
||||||
def test_delete_keypair_exception(self):
|
def test_delete_keypair_exception(self):
|
||||||
KEYPAIR_ID = self.keypairs[0].name
|
keypair = self.keypairs.first()
|
||||||
formData = {'action': 'keypairs__delete__%s' % KEYPAIR_ID}
|
|
||||||
|
|
||||||
self.mox.StubOutWithMock(api.nova, 'keypair_list')
|
self.mox.StubOutWithMock(api.nova, 'keypair_list')
|
||||||
self.mox.StubOutWithMock(api.nova, 'keypair_delete')
|
self.mox.StubOutWithMock(api.nova, 'keypair_delete')
|
||||||
|
api.nova.keypair_list(IsA(http.HttpRequest)) \
|
||||||
api.nova.keypair_list(IsA(http.HttpRequest)).AndReturn(self.keypairs)
|
.AndReturn(self.keypairs.list())
|
||||||
exception = novaclient_exceptions.ClientException('clientException',
|
exc = novaclient_exceptions.ClientException('clientException')
|
||||||
message='clientException')
|
api.nova.keypair_delete(IsA(http.HttpRequest), keypair.name) \
|
||||||
api.nova.keypair_delete(IsA(http.HttpRequest), unicode(KEYPAIR_ID)) \
|
.AndRaise(exc)
|
||||||
.AndRaise(exception)
|
|
||||||
|
|
||||||
self.mox.ReplayAll()
|
self.mox.ReplayAll()
|
||||||
|
|
||||||
|
formData = {'action': 'keypairs__delete__%s' % keypair.name}
|
||||||
res = self.client.post(INDEX_VIEW_URL, formData)
|
res = self.client.post(INDEX_VIEW_URL, formData)
|
||||||
|
|
||||||
self.assertRedirectsNoFollow(res, INDEX_VIEW_URL)
|
self.assertRedirectsNoFollow(res, INDEX_VIEW_URL)
|
||||||
|
|
||||||
def test_create_keypair_get(self):
|
def test_create_keypair_get(self):
|
||||||
res = self.client.get(
|
res = self.client.get(
|
||||||
reverse('horizon:nova:access_and_security:keypairs:create'))
|
reverse('horizon:nova:access_and_security:keypairs:create'))
|
||||||
|
|
||||||
self.assertTemplateUsed(res,
|
self.assertTemplateUsed(res,
|
||||||
'nova/access_and_security/keypairs/create.html')
|
'nova/access_and_security/keypairs/create.html')
|
||||||
|
|
||||||
def test_create_keypair_post(self):
|
def test_create_keypair_post(self):
|
||||||
KEYPAIR_NAME = 'newKeypair'
|
keypair = self.keypairs.first()
|
||||||
PRIVATE_KEY = 'privateKey'
|
keypair.private_key = "secret"
|
||||||
|
|
||||||
newKeyPair = self.mox.CreateMock(api.KeyPair)
|
|
||||||
newKeyPair.name = KEYPAIR_NAME
|
|
||||||
newKeyPair.private_key = PRIVATE_KEY
|
|
||||||
|
|
||||||
formData = {'method': 'CreateKeypair',
|
|
||||||
'name': KEYPAIR_NAME,
|
|
||||||
}
|
|
||||||
|
|
||||||
self.mox.StubOutWithMock(api, 'keypair_create')
|
self.mox.StubOutWithMock(api, 'keypair_create')
|
||||||
api.keypair_create(IsA(http.HttpRequest),
|
api.keypair_create(IsA(http.HttpRequest),
|
||||||
KEYPAIR_NAME).AndReturn(newKeyPair)
|
keypair.name).AndReturn(keypair)
|
||||||
|
|
||||||
self.mox.ReplayAll()
|
self.mox.ReplayAll()
|
||||||
|
|
||||||
res = self.client.post(
|
formData = {'method': 'CreateKeypair',
|
||||||
reverse('horizon:nova:access_and_security:keypairs:create'),
|
'name': keypair.name}
|
||||||
formData)
|
url = reverse('horizon:nova:access_and_security:keypairs:create')
|
||||||
|
res = self.client.post(url, formData)
|
||||||
self.assertTrue(res.has_header('Content-Disposition'))
|
self.assertTrue(res.has_header('Content-Disposition'))
|
||||||
|
|
||||||
def test_create_keypair_exception(self):
|
def test_create_keypair_exception(self):
|
||||||
KEYPAIR_NAME = 'newKeypair'
|
keypair = self.keypairs.first()
|
||||||
|
exc = novaclient_exceptions.ClientException('clientException')
|
||||||
formData = {'method': 'CreateKeypair',
|
|
||||||
'name': KEYPAIR_NAME,
|
|
||||||
}
|
|
||||||
|
|
||||||
exception = novaclient_exceptions.ClientException('clientException',
|
|
||||||
message='clientException')
|
|
||||||
self.mox.StubOutWithMock(api, 'keypair_create')
|
self.mox.StubOutWithMock(api, 'keypair_create')
|
||||||
api.keypair_create(IsA(http.HttpRequest),
|
api.keypair_create(IsA(http.HttpRequest), keypair.name).AndRaise(exc)
|
||||||
KEYPAIR_NAME).AndRaise(exception)
|
|
||||||
|
|
||||||
self.mox.ReplayAll()
|
self.mox.ReplayAll()
|
||||||
|
|
||||||
res = self.client.post(
|
formData = {'method': 'CreateKeypair',
|
||||||
reverse('horizon:nova:access_and_security:keypairs:create'),
|
'name': keypair.name}
|
||||||
formData)
|
url = reverse('horizon:nova:access_and_security:keypairs:create')
|
||||||
|
res = self.client.post(url, formData)
|
||||||
|
|
||||||
self.assertRedirectsNoFollow(res,
|
self.assertRedirectsNoFollow(res, url)
|
||||||
reverse('horizon:nova:access_and_security:keypairs:create'))
|
|
||||||
|
@ -27,6 +27,7 @@ from django.utils.translation import ugettext as _
|
|||||||
from novaclient import exceptions as novaclient_exceptions
|
from novaclient import exceptions as novaclient_exceptions
|
||||||
|
|
||||||
from horizon import api
|
from horizon import api
|
||||||
|
from horizon import exceptions
|
||||||
from horizon import forms
|
from horizon import forms
|
||||||
|
|
||||||
|
|
||||||
@ -40,20 +41,15 @@ class CreateGroup(forms.SelfHandlingForm):
|
|||||||
|
|
||||||
def handle(self, request, data):
|
def handle(self, request, data):
|
||||||
try:
|
try:
|
||||||
LOG.info('Add security_group: "%s"' % data)
|
|
||||||
|
|
||||||
api.security_group_create(request,
|
api.security_group_create(request,
|
||||||
data['name'],
|
data['name'],
|
||||||
data['description'])
|
data['description'])
|
||||||
messages.success(request,
|
messages.success(request,
|
||||||
_('Successfully created security_group: %s')
|
_('Successfully created security_group: %s')
|
||||||
% data['name'])
|
% data['name'])
|
||||||
return shortcuts.redirect(
|
except:
|
||||||
'horizon:nova:access_and_security:index')
|
exceptions.handle(request, _('Unable to create security group.'))
|
||||||
except novaclient_exceptions.ClientException, e:
|
return shortcuts.redirect('horizon:nova:access_and_security:index')
|
||||||
LOG.exception("ClientException in CreateGroup")
|
|
||||||
messages.error(request, _('Error creating security group: %s') %
|
|
||||||
e.message)
|
|
||||||
|
|
||||||
|
|
||||||
class AddRule(forms.SelfHandlingForm):
|
class AddRule(forms.SelfHandlingForm):
|
||||||
@ -66,7 +62,7 @@ class AddRule(forms.SelfHandlingForm):
|
|||||||
# TODO (anthony) source group support
|
# TODO (anthony) source group support
|
||||||
# group_id = forms.CharField()
|
# 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())
|
tenant_id = forms.CharField(widget=forms.HiddenInput())
|
||||||
|
|
||||||
def handle(self, request, data):
|
def handle(self, request, data):
|
||||||
|
@ -22,257 +22,195 @@ from django import http
|
|||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
from django.core.urlresolvers import reverse
|
from django.core.urlresolvers import reverse
|
||||||
from novaclient import exceptions as novaclient_exceptions
|
from novaclient import exceptions as novaclient_exceptions
|
||||||
from novaclient.v1_1 import security_group_rules as nova_rules
|
|
||||||
from mox import IsA
|
from mox import IsA
|
||||||
|
|
||||||
from horizon import api
|
from horizon import api
|
||||||
from horizon import test
|
from horizon import test
|
||||||
from .tables import SecurityGroupsTable, RulesTable
|
from .tables import SecurityGroupsTable, RulesTable
|
||||||
|
|
||||||
SECGROUP_ID = '2'
|
|
||||||
INDEX_URL = reverse('horizon:nova:access_and_security:index')
|
INDEX_URL = reverse('horizon:nova:access_and_security:index')
|
||||||
SG_CREATE_URL = \
|
SG_CREATE_URL = \
|
||||||
reverse('horizon:nova:access_and_security:security_groups:create')
|
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):
|
def strip_absolute_base(uri):
|
||||||
return uri.split(settings.TESTSERVER, 1)[-1]
|
return uri.split(settings.TESTSERVER, 1)[-1]
|
||||||
|
|
||||||
|
|
||||||
class SecurityGroupsViewTests(test.BaseViewTests):
|
class SecurityGroupsViewTests(test.TestCase):
|
||||||
def setUp(self):
|
def setUp(self):
|
||||||
super(SecurityGroupsViewTests, self).setUp()
|
super(SecurityGroupsViewTests, self).setUp()
|
||||||
|
sec_group = self.security_groups.first()
|
||||||
sg1 = api.SecurityGroup(None)
|
self.edit_url = reverse('horizon:nova:access_and_security:'
|
||||||
sg1.id = 1
|
'security_groups:edit_rules',
|
||||||
sg1.name = 'default'
|
args=[sec_group.id])
|
||||||
|
|
||||||
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)
|
|
||||||
|
|
||||||
def test_create_security_groups_get(self):
|
def test_create_security_groups_get(self):
|
||||||
res = self.client.get(SG_CREATE_URL)
|
res = self.client.get(SG_CREATE_URL)
|
||||||
|
|
||||||
self.assertTemplateUsed(res,
|
self.assertTemplateUsed(res,
|
||||||
'nova/access_and_security/security_groups/create.html')
|
'nova/access_and_security/security_groups/create.html')
|
||||||
|
|
||||||
def test_create_security_groups_post(self):
|
def test_create_security_groups_post(self):
|
||||||
SECGROUP_NAME = 'fakegroup'
|
sec_group = self.security_groups.first()
|
||||||
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,
|
|
||||||
}
|
|
||||||
|
|
||||||
self.mox.StubOutWithMock(api, 'security_group_create')
|
self.mox.StubOutWithMock(api, 'security_group_create')
|
||||||
api.security_group_create(IsA(http.HttpRequest),
|
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()
|
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)
|
res = self.client.post(SG_CREATE_URL, formData)
|
||||||
|
|
||||||
self.assertRedirectsNoFollow(res, INDEX_URL)
|
self.assertRedirectsNoFollow(res, INDEX_URL)
|
||||||
|
|
||||||
def test_create_security_groups_post_exception(self):
|
def test_create_security_groups_post_exception(self):
|
||||||
SECGROUP_NAME = 'fakegroup'
|
sec_group = self.security_groups.first()
|
||||||
SECGROUP_DESC = 'fakegroup_desc'
|
self.mox.StubOutWithMock(api, 'security_group_create')
|
||||||
|
exc = novaclient_exceptions.ClientException('ClientException')
|
||||||
exception = novaclient_exceptions.ClientException('ClientException',
|
api.security_group_create(IsA(http.HttpRequest),
|
||||||
message='ClientException')
|
sec_group.name,
|
||||||
|
sec_group.description).AndRaise(exc)
|
||||||
|
self.mox.ReplayAll()
|
||||||
|
|
||||||
formData = {'method': 'CreateGroup',
|
formData = {'method': 'CreateGroup',
|
||||||
'tenant_id': self.TEST_TENANT,
|
'tenant_id': self.tenant.id,
|
||||||
'name': SECGROUP_NAME,
|
'name': sec_group.name,
|
||||||
'description': SECGROUP_DESC,
|
'description': sec_group.description}
|
||||||
}
|
|
||||||
|
|
||||||
self.mox.StubOutWithMock(api, 'security_group_create')
|
|
||||||
api.security_group_create(IsA(http.HttpRequest),
|
|
||||||
SECGROUP_NAME, SECGROUP_DESC).AndRaise(exception)
|
|
||||||
|
|
||||||
self.mox.ReplayAll()
|
|
||||||
|
|
||||||
res = self.client.post(SG_CREATE_URL, formData)
|
res = self.client.post(SG_CREATE_URL, formData)
|
||||||
|
self.assertMessageCount(error=1)
|
||||||
self.assertTemplateUsed(res,
|
self.assertRedirectsNoFollow(res, INDEX_URL)
|
||||||
'nova/access_and_security/security_groups/create.html')
|
|
||||||
|
|
||||||
def test_edit_rules_get(self):
|
def test_edit_rules_get(self):
|
||||||
|
sec_group = self.security_groups.first()
|
||||||
self.mox.StubOutWithMock(api, 'security_group_get')
|
self.mox.StubOutWithMock(api, 'security_group_get')
|
||||||
api.security_group_get(IsA(http.HttpRequest), SECGROUP_ID).AndReturn(
|
api.security_group_get(IsA(http.HttpRequest),
|
||||||
self.security_groups[1])
|
sec_group.id).AndReturn(sec_group)
|
||||||
|
|
||||||
self.mox.ReplayAll()
|
self.mox.ReplayAll()
|
||||||
|
|
||||||
res = self.client.get(SG_EDIT_RULE_URL)
|
res = self.client.get(self.edit_url)
|
||||||
|
|
||||||
self.assertTemplateUsed(res,
|
self.assertTemplateUsed(res,
|
||||||
'nova/access_and_security/security_groups/edit_rules.html')
|
'nova/access_and_security/security_groups/edit_rules.html')
|
||||||
self.assertItemsEqual(res.context['security_group'].name,
|
self.assertItemsEqual(res.context['security_group'].name,
|
||||||
self.security_groups[1].name)
|
sec_group.name)
|
||||||
|
|
||||||
def test_edit_rules_get_exception(self):
|
def test_edit_rules_get_exception(self):
|
||||||
exception = novaclient_exceptions.ClientException('ClientException',
|
sec_group = self.security_groups.first()
|
||||||
message='ClientException')
|
|
||||||
|
|
||||||
self.mox.StubOutWithMock(api, 'security_group_get')
|
self.mox.StubOutWithMock(api, 'security_group_get')
|
||||||
api.security_group_get(IsA(http.HttpRequest), SECGROUP_ID) \
|
exc = novaclient_exceptions.ClientException('ClientException')
|
||||||
.AndRaise(exception)
|
api.security_group_get(IsA(http.HttpRequest),
|
||||||
|
sec_group.id).AndRaise(exc)
|
||||||
self.mox.ReplayAll()
|
self.mox.ReplayAll()
|
||||||
|
|
||||||
res = self.client.get(SG_EDIT_RULE_URL)
|
res = self.client.get(self.edit_url)
|
||||||
|
|
||||||
self.assertRedirects(res, INDEX_URL)
|
self.assertRedirects(res, INDEX_URL)
|
||||||
|
|
||||||
def test_edit_rules_add_rule(self):
|
def test_edit_rules_add_rule(self):
|
||||||
RULE_ID = '1'
|
sec_group = self.security_groups.first()
|
||||||
FROM_PORT = '-1'
|
rule = self.security_group_rules.first()
|
||||||
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}
|
|
||||||
|
|
||||||
self.mox.StubOutWithMock(api, 'security_group_rule_create')
|
self.mox.StubOutWithMock(api, 'security_group_rule_create')
|
||||||
api.security_group_rule_create(IsA(http.HttpRequest),
|
api.security_group_rule_create(IsA(http.HttpRequest),
|
||||||
SECGROUP_ID, IP_PROTOCOL, FROM_PORT, TO_PORT, CIDR)\
|
sec_group.id,
|
||||||
.AndReturn(new_rule)
|
rule.ip_protocol,
|
||||||
|
rule.from_port,
|
||||||
|
rule.to_port,
|
||||||
|
rule.ip_range['cidr']).AndReturn(rule)
|
||||||
self.mox.ReplayAll()
|
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)
|
self.assertRedirectsNoFollow(res, INDEX_URL)
|
||||||
|
|
||||||
def test_edit_rules_add_rule_exception(self):
|
def test_edit_rules_add_rule_exception(self):
|
||||||
exception = novaclient_exceptions.ClientException('ClientException',
|
sec_group = self.security_groups.first()
|
||||||
message='ClientException')
|
rule = self.security_group_rules.first()
|
||||||
|
exc = novaclient_exceptions.ClientException('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}
|
|
||||||
|
|
||||||
self.mox.StubOutWithMock(api, 'security_group_rule_create')
|
self.mox.StubOutWithMock(api, 'security_group_rule_create')
|
||||||
api.security_group_rule_create(IsA(http.HttpRequest),
|
api.security_group_rule_create(IsA(http.HttpRequest),
|
||||||
SECGROUP_ID, IP_PROTOCOL, FROM_PORT,
|
sec_group.id,
|
||||||
TO_PORT, CIDR).AndRaise(exception)
|
rule.ip_protocol,
|
||||||
|
rule.from_port,
|
||||||
|
rule.to_port,
|
||||||
|
rule.ip_range['cidr']).AndRaise(exc)
|
||||||
self.mox.ReplayAll()
|
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)
|
self.assertRedirectsNoFollow(res, INDEX_URL)
|
||||||
|
|
||||||
def test_edit_rules_delete_rule(self):
|
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')
|
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()
|
self.mox.ReplayAll()
|
||||||
|
|
||||||
form_data = {"action": "rules__delete__%s" % RULE_ID}
|
form_data = {"action": "rules__delete__%s" % rule.id}
|
||||||
req = self.factory.post(SG_EDIT_RULE_URL, form_data)
|
req = self.factory.post(self.edit_url, form_data)
|
||||||
table = RulesTable(req, self.rules)
|
table = RulesTable(req, sec_group.rules)
|
||||||
handled = table.maybe_handle()
|
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):
|
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')
|
self.mox.StubOutWithMock(api, 'security_group_rule_delete')
|
||||||
|
exc = novaclient_exceptions.ClientException('ClientException')
|
||||||
exception = novaclient_exceptions.ClientException('ClientException',
|
api.security_group_rule_delete(IsA(http.HttpRequest),
|
||||||
message='ClientException')
|
rule.id).AndRaise(exc)
|
||||||
api.security_group_rule_delete(IsA(http.HttpRequest), RULE_ID) \
|
|
||||||
.AndRaise(exception)
|
|
||||||
|
|
||||||
self.mox.ReplayAll()
|
self.mox.ReplayAll()
|
||||||
|
|
||||||
form_data = {"action": "rules__delete__%s" % RULE_ID}
|
form_data = {"action": "rules__delete__%s" % rule.id}
|
||||||
req = self.factory.post(SG_EDIT_RULE_URL, form_data)
|
req = self.factory.post(self.edit_url, form_data)
|
||||||
table = RulesTable(req, self.rules)
|
table = RulesTable(req, self.security_group_rules.list())
|
||||||
handled = table.maybe_handle()
|
handled = table.maybe_handle()
|
||||||
|
|
||||||
self.assertEqual(strip_absolute_base(handled['location']),
|
self.assertEqual(strip_absolute_base(handled['location']),
|
||||||
INDEX_URL)
|
INDEX_URL)
|
||||||
|
|
||||||
def test_delete_group(self):
|
def test_delete_group(self):
|
||||||
self.mox.StubOutWithMock(api, 'security_group_delete')
|
sec_group = self.security_groups.get(name="other_group")
|
||||||
api.security_group_delete(IsA(http.HttpRequest), 2)
|
|
||||||
|
|
||||||
|
self.mox.StubOutWithMock(api, 'security_group_delete')
|
||||||
|
api.security_group_delete(IsA(http.HttpRequest), sec_group.id)
|
||||||
self.mox.ReplayAll()
|
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)
|
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()
|
handled = table.maybe_handle()
|
||||||
|
|
||||||
self.assertEqual(strip_absolute_base(handled['location']),
|
self.assertEqual(strip_absolute_base(handled['location']),
|
||||||
INDEX_URL)
|
INDEX_URL)
|
||||||
|
|
||||||
def test_delete_group_exception(self):
|
def test_delete_group_exception(self):
|
||||||
|
sec_group = self.security_groups.get(name="other_group")
|
||||||
|
|
||||||
self.mox.StubOutWithMock(api, 'security_group_delete')
|
self.mox.StubOutWithMock(api, 'security_group_delete')
|
||||||
exception = novaclient_exceptions.ClientException('ClientException',
|
exc = novaclient_exceptions.ClientException('ClientException')
|
||||||
message='ClientException')
|
api.security_group_delete(IsA(http.HttpRequest),
|
||||||
api.security_group_delete(IsA(http.HttpRequest), 2).\
|
sec_group.id).AndRaise(exc)
|
||||||
AndRaise(exception)
|
|
||||||
|
|
||||||
self.mox.ReplayAll()
|
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)
|
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()
|
handled = table.maybe_handle()
|
||||||
|
|
||||||
self.assertEqual(strip_absolute_base(handled['location']),
|
self.assertEqual(strip_absolute_base(handled['location']),
|
||||||
|
@ -43,7 +43,7 @@ class EditRulesView(tables.DataTableView):
|
|||||||
template_name = 'nova/access_and_security/security_groups/edit_rules.html'
|
template_name = 'nova/access_and_security/security_groups/edit_rules.html'
|
||||||
|
|
||||||
def get_data(self):
|
def get_data(self):
|
||||||
security_group_id = self.kwargs['security_group_id']
|
security_group_id = int(self.kwargs['security_group_id'])
|
||||||
try:
|
try:
|
||||||
self.object = api.security_group_get(self.request,
|
self.object = api.security_group_get(self.request,
|
||||||
security_group_id)
|
security_group_id)
|
||||||
|
@ -26,43 +26,19 @@ from horizon import api
|
|||||||
from horizon import test
|
from horizon import test
|
||||||
|
|
||||||
|
|
||||||
class AccessAndSecurityTests(test.BaseViewTests):
|
class AccessAndSecurityTests(test.TestCase):
|
||||||
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,)
|
|
||||||
|
|
||||||
def test_index(self):
|
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, 'tenant_floating_ip_list')
|
||||||
self.mox.StubOutWithMock(api, 'security_group_list')
|
self.mox.StubOutWithMock(api, 'security_group_list')
|
||||||
self.mox.StubOutWithMock(api.nova, 'keypair_list')
|
self.mox.StubOutWithMock(api.nova, 'keypair_list')
|
||||||
|
|
||||||
api.nova.keypair_list(IsA(http.HttpRequest)).AndReturn(self.keypairs)
|
api.nova.keypair_list(IsA(http.HttpRequest)).AndReturn(keypairs)
|
||||||
api.tenant_floating_ip_list(IsA(http.HttpRequest)).\
|
api.tenant_floating_ip_list(IsA(http.HttpRequest)) \
|
||||||
AndReturn(self.floating_ips)
|
.AndReturn(floating_ips)
|
||||||
api.security_group_list(IsA(http.HttpRequest)).\
|
api.security_group_list(IsA(http.HttpRequest)).AndReturn(sec_groups)
|
||||||
AndReturn(self.security_groups)
|
|
||||||
|
|
||||||
self.mox.ReplayAll()
|
self.mox.ReplayAll()
|
||||||
|
|
||||||
@ -70,9 +46,8 @@ class AccessAndSecurityTests(test.BaseViewTests):
|
|||||||
reverse('horizon:nova:access_and_security:index'))
|
reverse('horizon:nova:access_and_security:index'))
|
||||||
|
|
||||||
self.assertTemplateUsed(res, 'nova/access_and_security/index.html')
|
self.assertTemplateUsed(res, 'nova/access_and_security/index.html')
|
||||||
self.assertItemsEqual(res.context['keypairs_table'].data,
|
self.assertItemsEqual(res.context['keypairs_table'].data, keypairs)
|
||||||
self.keypairs)
|
|
||||||
self.assertItemsEqual(res.context['security_groups_table'].data,
|
self.assertItemsEqual(res.context['security_groups_table'].data,
|
||||||
self.security_groups)
|
sec_groups)
|
||||||
self.assertItemsEqual(res.context['floating_ips_table'].data,
|
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 api
|
||||||
from horizon import test
|
from horizon import test
|
||||||
from .tables import ContainersTable, ObjectsTable
|
from .tables import ContainersTable, ObjectsTable
|
||||||
|
from . import forms
|
||||||
|
|
||||||
|
|
||||||
CONTAINER_INDEX_URL = reverse('horizon:nova:containers:index')
|
CONTAINER_INDEX_URL = reverse('horizon:nova:containers:index')
|
||||||
|
|
||||||
|
|
||||||
class ContainerViewTests(test.BaseViewTests):
|
class ContainerViewTests(test.TestCase):
|
||||||
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,)
|
|
||||||
|
|
||||||
def test_index(self):
|
def test_index(self):
|
||||||
|
containers = self.containers.list()
|
||||||
self.mox.StubOutWithMock(api, 'swift_get_containers')
|
self.mox.StubOutWithMock(api, 'swift_get_containers')
|
||||||
api.swift_get_containers(
|
api.swift_get_containers(IsA(http.HttpRequest), marker=None) \
|
||||||
IsA(http.HttpRequest), marker=None).AndReturn(
|
.AndReturn((containers, False))
|
||||||
([self.container], False))
|
|
||||||
|
|
||||||
self.mox.ReplayAll()
|
self.mox.ReplayAll()
|
||||||
|
|
||||||
res = self.client.get(CONTAINER_INDEX_URL)
|
res = self.client.get(CONTAINER_INDEX_URL)
|
||||||
|
|
||||||
self.assertTemplateUsed(res, 'nova/containers/index.html')
|
self.assertTemplateUsed(res, 'nova/containers/index.html')
|
||||||
self.assertIn('table', res.context)
|
self.assertIn('table', res.context)
|
||||||
containers = res.context['table'].data
|
resp_containers = res.context['table'].data
|
||||||
|
self.assertEqual(len(resp_containers), len(containers))
|
||||||
self.assertEqual(len(containers), 1)
|
|
||||||
self.assertEqual(containers[0].name, 'containerName')
|
|
||||||
|
|
||||||
def test_delete_container(self):
|
def test_delete_container(self):
|
||||||
|
container = self.containers.get(name="container_two")
|
||||||
self.mox.StubOutWithMock(api, 'swift_delete_container')
|
self.mox.StubOutWithMock(api, 'swift_delete_container')
|
||||||
api.swift_delete_container(IsA(http.HttpRequest),
|
api.swift_delete_container(IsA(http.HttpRequest), container.name)
|
||||||
'containerName')
|
|
||||||
|
|
||||||
self.mox.ReplayAll()
|
self.mox.ReplayAll()
|
||||||
|
|
||||||
action_string = "containers__delete__%s" % self.container.name
|
action_string = "containers__delete__%s" % container.name
|
||||||
form_data = {"action": action_string}
|
form_data = {"action": action_string}
|
||||||
req = self.factory.post(CONTAINER_INDEX_URL, form_data)
|
req = self.factory.post(CONTAINER_INDEX_URL, form_data)
|
||||||
table = ContainersTable(req, self.containers)
|
table = ContainersTable(req, self.containers.list())
|
||||||
handled = table.maybe_handle()
|
handled = table.maybe_handle()
|
||||||
|
|
||||||
self.assertEqual(handled['location'], CONTAINER_INDEX_URL)
|
self.assertEqual(handled['location'], CONTAINER_INDEX_URL)
|
||||||
|
|
||||||
def test_delete_container_nonempty(self):
|
def test_delete_container_nonempty(self):
|
||||||
|
container = self.containers.first()
|
||||||
self.mox.StubOutWithMock(api, 'swift_delete_container')
|
self.mox.StubOutWithMock(api, 'swift_delete_container')
|
||||||
|
exc = ContainerNotEmpty('containerNotEmpty')
|
||||||
exception = ContainerNotEmpty('containerNotEmpty')
|
api.swift_delete_container(IsA(http.HttpRequest),
|
||||||
api.swift_delete_container(
|
container.name).AndRaise(exc)
|
||||||
IsA(http.HttpRequest),
|
|
||||||
'containerName').AndRaise(exception)
|
|
||||||
|
|
||||||
self.mox.ReplayAll()
|
self.mox.ReplayAll()
|
||||||
|
|
||||||
action_string = "containers__delete__%s" % self.container.name
|
action_string = "containers__delete__%s" % container.name
|
||||||
form_data = {"action": action_string}
|
form_data = {"action": action_string}
|
||||||
req = self.factory.post(CONTAINER_INDEX_URL, form_data)
|
req = self.factory.post(CONTAINER_INDEX_URL, form_data)
|
||||||
table = ContainersTable(req, self.containers)
|
table = ContainersTable(req, self.containers.list())
|
||||||
handled = table.maybe_handle()
|
handled = table.maybe_handle()
|
||||||
|
|
||||||
self.assertEqual(handled['location'], CONTAINER_INDEX_URL)
|
self.assertEqual(handled['location'], CONTAINER_INDEX_URL)
|
||||||
|
|
||||||
def test_create_container_get(self):
|
def test_create_container_get(self):
|
||||||
res = self.client.get(reverse('horizon:nova:containers:create'))
|
res = self.client.get(reverse('horizon:nova:containers:create'))
|
||||||
|
|
||||||
self.assertTemplateUsed(res, 'nova/containers/create.html')
|
self.assertTemplateUsed(res, 'nova/containers/create.html')
|
||||||
|
|
||||||
def test_create_container_post(self):
|
def test_create_container_post(self):
|
||||||
formData = {'name': 'containerName',
|
|
||||||
'method': 'CreateContainer'}
|
|
||||||
|
|
||||||
self.mox.StubOutWithMock(api, 'swift_create_container')
|
self.mox.StubOutWithMock(api, 'swift_create_container')
|
||||||
api.swift_create_container(
|
api.swift_create_container(IsA(http.HttpRequest),
|
||||||
IsA(http.HttpRequest), u'containerName')
|
self.containers.first().name)
|
||||||
|
|
||||||
self.mox.ReplayAll()
|
self.mox.ReplayAll()
|
||||||
|
|
||||||
|
formData = {'name': self.containers.first().name,
|
||||||
|
'method': forms.CreateContainer.__name__}
|
||||||
res = self.client.post(reverse('horizon:nova:containers:create'),
|
res = self.client.post(reverse('horizon:nova:containers:create'),
|
||||||
formData)
|
formData)
|
||||||
|
|
||||||
self.assertRedirectsNoFollow(res, CONTAINER_INDEX_URL)
|
self.assertRedirectsNoFollow(res, CONTAINER_INDEX_URL)
|
||||||
|
|
||||||
|
|
||||||
class ObjectViewTests(test.BaseViewTests):
|
class ObjectViewTests(test.TestCase):
|
||||||
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]
|
|
||||||
|
|
||||||
def test_index(self):
|
def test_index(self):
|
||||||
self.mox.StubOutWithMock(api, 'swift_get_objects')
|
self.mox.StubOutWithMock(api, 'swift_get_objects')
|
||||||
api.swift_get_objects(
|
ret = (self.objects.list(), False)
|
||||||
IsA(http.HttpRequest),
|
api.swift_get_objects(IsA(http.HttpRequest),
|
||||||
self.CONTAINER_NAME,
|
self.containers.first().name,
|
||||||
marker=None).AndReturn((self.swift_objects, False))
|
marker=None).AndReturn(ret)
|
||||||
|
|
||||||
self.mox.ReplayAll()
|
self.mox.ReplayAll()
|
||||||
|
|
||||||
res = self.client.get(reverse('horizon:nova:containers:object_index',
|
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.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):
|
def test_upload_index(self):
|
||||||
res = self.client.get(reverse('horizon:nova:containers:object_upload',
|
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')
|
self.assertTemplateUsed(res, 'nova/objects/upload.html')
|
||||||
|
|
||||||
def test_upload(self):
|
def test_upload(self):
|
||||||
|
container = self.containers.first()
|
||||||
|
obj = self.objects.first()
|
||||||
OBJECT_DATA = 'objectData'
|
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',
|
temp_file = tempfile.TemporaryFile()
|
||||||
'container_name': self.CONTAINER_NAME,
|
temp_file.write(OBJECT_DATA)
|
||||||
'name': OBJECT_NAME,
|
temp_file.flush()
|
||||||
'object_file': OBJECT_FILE}
|
temp_file.seek(0)
|
||||||
|
|
||||||
self.mox.StubOutWithMock(api, 'swift_upload_object')
|
self.mox.StubOutWithMock(api, 'swift_upload_object')
|
||||||
api.swift_upload_object(IsA(http.HttpRequest),
|
api.swift_upload_object(IsA(http.HttpRequest),
|
||||||
unicode(self.CONTAINER_NAME),
|
container.name,
|
||||||
unicode(OBJECT_NAME),
|
obj.name,
|
||||||
OBJECT_DATA).AndReturn(self.swift_objects[0])
|
OBJECT_DATA).AndReturn(obj)
|
||||||
|
self.mox.StubOutWithMock(obj, 'sync_metadata')
|
||||||
|
obj.sync_metadata()
|
||||||
self.mox.ReplayAll()
|
self.mox.ReplayAll()
|
||||||
|
upload_url = reverse('horizon:nova:containers:object_upload',
|
||||||
res = self.client.get(reverse('horizon:nova:containers:object_upload',
|
args=[container.name])
|
||||||
args=[self.CONTAINER_NAME]))
|
res = self.client.get(upload_url)
|
||||||
|
|
||||||
self.assertContains(res, 'enctype="multipart/form-data"')
|
self.assertContains(res, 'enctype="multipart/form-data"')
|
||||||
|
|
||||||
res = self.client.post(reverse('horizon:nova:containers:object_upload',
|
formData = {'method': forms.UploadObject.__name__,
|
||||||
args=[self.CONTAINER_NAME]),
|
'container_name': container.name,
|
||||||
formData)
|
'name': obj.name,
|
||||||
|
'object_file': temp_file}
|
||||||
|
res = self.client.post(upload_url, formData)
|
||||||
|
|
||||||
self.assertRedirectsNoFollow(res,
|
index_url = reverse('horizon:nova:containers:object_index',
|
||||||
reverse('horizon:nova:containers:object_index',
|
args=[container.name])
|
||||||
args=[self.CONTAINER_NAME]))
|
self.assertRedirectsNoFollow(res, index_url)
|
||||||
|
|
||||||
def test_delete(self):
|
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')
|
self.mox.StubOutWithMock(api, 'swift_delete_object')
|
||||||
api.swift_delete_object(
|
api.swift_delete_object(IsA(http.HttpRequest),
|
||||||
IsA(http.HttpRequest),
|
container.name,
|
||||||
self.CONTAINER_NAME, self.swift_objects[0].name)
|
obj.name)
|
||||||
|
|
||||||
self.mox.ReplayAll()
|
self.mox.ReplayAll()
|
||||||
|
|
||||||
OBJECT_INDEX_URL = reverse('horizon:nova:containers:object_index',
|
action_string = "objects__delete__%s" % obj.name
|
||||||
args=[self.CONTAINER_NAME])
|
|
||||||
action_string = "objects__delete__%s" % self.swift_objects[0].name
|
|
||||||
form_data = {"action": action_string}
|
form_data = {"action": action_string}
|
||||||
req = self.factory.post(OBJECT_INDEX_URL, form_data)
|
req = self.factory.post(index_url, form_data)
|
||||||
kwargs = {"container_name": self.CONTAINER_NAME}
|
kwargs = {"container_name": container.name}
|
||||||
table = ObjectsTable(req, self.swift_objects, **kwargs)
|
table = ObjectsTable(req, self.objects.list(), **kwargs)
|
||||||
handled = table.maybe_handle()
|
handled = table.maybe_handle()
|
||||||
|
self.assertEqual(handled['location'], index_url)
|
||||||
self.assertEqual(handled['location'], OBJECT_INDEX_URL)
|
|
||||||
|
|
||||||
def test_download(self):
|
def test_download(self):
|
||||||
|
container = self.containers.first()
|
||||||
|
obj = self.objects.first()
|
||||||
OBJECT_DATA = 'objectData'
|
OBJECT_DATA = 'objectData'
|
||||||
OBJECT_NAME = 'objectName'
|
|
||||||
|
|
||||||
self.mox.StubOutWithMock(api, 'swift_get_object_data')
|
self.mox.StubOutWithMock(api, 'swift_get_object_data')
|
||||||
self.mox.StubOutWithMock(api.swift, 'swift_get_object')
|
self.mox.StubOutWithMock(api.swift, 'swift_get_object')
|
||||||
|
|
||||||
api.swift.swift_get_object(IsA(http.HttpRequest),
|
api.swift.swift_get_object(IsA(http.HttpRequest),
|
||||||
unicode(self.CONTAINER_NAME),
|
container.name,
|
||||||
unicode(OBJECT_NAME)) \
|
obj.name).AndReturn(obj)
|
||||||
.AndReturn(self.swift_objects[0])
|
|
||||||
api.swift_get_object_data(IsA(http.HttpRequest),
|
api.swift_get_object_data(IsA(http.HttpRequest),
|
||||||
unicode(self.CONTAINER_NAME),
|
container.name,
|
||||||
unicode(OBJECT_NAME)).AndReturn(OBJECT_DATA)
|
obj.name).AndReturn(OBJECT_DATA)
|
||||||
|
|
||||||
self.mox.ReplayAll()
|
self.mox.ReplayAll()
|
||||||
|
|
||||||
res = self.client.get(reverse(
|
download_url = reverse('horizon:nova:containers:object_download',
|
||||||
'horizon:nova:containers:object_download',
|
args=[container.name, obj.name])
|
||||||
args=[self.CONTAINER_NAME, OBJECT_NAME]))
|
res = self.client.get(download_url)
|
||||||
|
|
||||||
self.assertEqual(res.content, OBJECT_DATA)
|
self.assertEqual(res.content, OBJECT_DATA)
|
||||||
self.assertTrue(res.has_header('Content-Disposition'))
|
self.assertTrue(res.has_header('Content-Disposition'))
|
||||||
|
|
||||||
def test_copy_index(self):
|
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')
|
self.mox.StubOutWithMock(api, 'swift_get_containers')
|
||||||
api.swift_get_containers(
|
ret = (self.containers.list(), False)
|
||||||
IsA(http.HttpRequest)).AndReturn(([container], False))
|
api.swift_get_containers(IsA(http.HttpRequest)).AndReturn(ret)
|
||||||
|
|
||||||
self.mox.ReplayAll()
|
self.mox.ReplayAll()
|
||||||
|
|
||||||
res = self.client.get(reverse('horizon:nova:containers:object_copy',
|
res = self.client.get(reverse('horizon:nova:containers:object_copy',
|
||||||
args=[self.CONTAINER_NAME,
|
args=[self.containers.first().name,
|
||||||
OBJECT_NAME]))
|
self.objects.first().name]))
|
||||||
|
|
||||||
self.assertTemplateUsed(res, 'nova/objects/copy.html')
|
self.assertTemplateUsed(res, 'nova/objects/copy.html')
|
||||||
|
|
||||||
def test_copy(self):
|
def test_copy(self):
|
||||||
NEW_CONTAINER_NAME = self.CONTAINER_NAME
|
container_1 = self.containers.get(name="container_one")
|
||||||
NEW_OBJECT_NAME = 'newObjectName'
|
container_2 = self.containers.get(name="container_two")
|
||||||
ORIG_CONTAINER_NAME = 'origContainerName'
|
obj = self.objects.first()
|
||||||
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
|
|
||||||
|
|
||||||
self.mox.StubOutWithMock(api, 'swift_get_containers')
|
self.mox.StubOutWithMock(api, 'swift_get_containers')
|
||||||
api.swift_get_containers(
|
|
||||||
IsA(http.HttpRequest)).AndReturn(([container], False))
|
|
||||||
|
|
||||||
self.mox.StubOutWithMock(api, 'swift_copy_object')
|
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),
|
api.swift_copy_object(IsA(http.HttpRequest),
|
||||||
ORIG_CONTAINER_NAME,
|
container_1.name,
|
||||||
ORIG_OBJECT_NAME,
|
obj.name,
|
||||||
NEW_CONTAINER_NAME,
|
container_2.name,
|
||||||
NEW_OBJECT_NAME)
|
obj.name)
|
||||||
|
|
||||||
self.mox.ReplayAll()
|
self.mox.ReplayAll()
|
||||||
|
|
||||||
res = self.client.post(reverse('horizon:nova:containers:object_copy',
|
formData = {'method': forms.CopyObject.__name__,
|
||||||
args=[ORIG_CONTAINER_NAME,
|
'new_container_name': container_2.name,
|
||||||
ORIG_OBJECT_NAME]),
|
'new_object_name': obj.name,
|
||||||
formData)
|
'orig_container_name': container_1.name,
|
||||||
|
'orig_object_name': obj.name}
|
||||||
self.assertRedirectsNoFollow(res,
|
copy_url = reverse('horizon:nova:containers:object_copy',
|
||||||
reverse('horizon:nova:containers:object_index',
|
args=[container_1.name, obj.name])
|
||||||
args=[NEW_CONTAINER_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.
|
# under the License.
|
||||||
|
|
||||||
from django import http
|
from django import http
|
||||||
from django.contrib import messages
|
|
||||||
from django.core.urlresolvers import reverse
|
from django.core.urlresolvers import reverse
|
||||||
from keystoneclient import exceptions as keystone_exceptions
|
from keystoneclient import exceptions as keystone_exceptions
|
||||||
from novaclient.v1_1 import client as nova_client, volume_snapshots
|
|
||||||
from mox import IgnoreArg, IsA
|
from mox import IgnoreArg, IsA
|
||||||
|
|
||||||
from horizon import api
|
from horizon import api
|
||||||
@ -32,116 +30,48 @@ from horizon import test
|
|||||||
IMAGES_INDEX_URL = reverse('horizon:nova:images_and_snapshots:index')
|
IMAGES_INDEX_URL = reverse('horizon:nova:images_and_snapshots:index')
|
||||||
|
|
||||||
|
|
||||||
class FakeQuota:
|
class ImageViewTests(test.TestCase):
|
||||||
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]
|
|
||||||
|
|
||||||
def test_launch_get(self):
|
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, 'image_get_meta')
|
||||||
self.mox.StubOutWithMock(api, 'tenant_quota_get')
|
self.mox.StubOutWithMock(api, 'tenant_quota_get')
|
||||||
self.mox.StubOutWithMock(api, 'flavor_list')
|
self.mox.StubOutWithMock(api, 'flavor_list')
|
||||||
self.mox.StubOutWithMock(api, 'keypair_list')
|
self.mox.StubOutWithMock(api, 'keypair_list')
|
||||||
self.mox.StubOutWithMock(api, 'security_group_list')
|
self.mox.StubOutWithMock(api, 'security_group_list')
|
||||||
|
api.image_get_meta(IsA(http.HttpRequest), image.id).AndReturn(image)
|
||||||
api.image_get_meta(IsA(http.HttpRequest), str(IMAGE_ID)) \
|
api.tenant_quota_get(IsA(http.HttpRequest), tenant.id).AndReturn(quota)
|
||||||
.AndReturn(self.visibleImage)
|
api.flavor_list(IsA(http.HttpRequest)).AndReturn(self.flavors.list())
|
||||||
|
api.keypair_list(IsA(http.HttpRequest)).AndReturn(self.keypairs.list())
|
||||||
api.tenant_quota_get(IsA(http.HttpRequest),
|
api.security_group_list(IsA(http.HttpRequest)) \
|
||||||
self.TEST_TENANT).AndReturn(FakeQuota)
|
.AndReturn(self.security_groups.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)
|
|
||||||
|
|
||||||
self.mox.ReplayAll()
|
self.mox.ReplayAll()
|
||||||
|
|
||||||
res = self.client.get(
|
url = reverse('horizon:nova:images_and_snapshots:images:launch',
|
||||||
reverse('horizon:nova:images_and_snapshots:images:launch',
|
args=[image.id])
|
||||||
args=[IMAGE_ID]))
|
res = self.client.get(url)
|
||||||
form = res.context['form']
|
form = res.context['form']
|
||||||
|
|
||||||
self.assertTemplateUsed(res,
|
self.assertTemplateUsed(res,
|
||||||
'nova/images_and_snapshots/images/launch.html')
|
'nova/images_and_snapshots/images/launch.html')
|
||||||
self.assertEqual(res.context['image'].name, self.visibleImage.name)
|
self.assertEqual(res.context['image'].name, image.name)
|
||||||
self.assertIn('m1.massive', form.fields['flavor'].choices[0][1])
|
self.assertIn(self.flavors.first().name,
|
||||||
self.assertEqual(form.fields['keypair'].choices[0][0],
|
form.fields['flavor'].choices[0][1])
|
||||||
self.keypairs[0].name)
|
self.assertEqual(self.keypairs.first().name,
|
||||||
|
form.fields['keypair'].choices[0][0])
|
||||||
|
|
||||||
def test_launch_post(self):
|
def test_launch_post(self):
|
||||||
FLAVOR_ID = unicode(self.flavors[0].id)
|
flavor = self.flavors.first()
|
||||||
IMAGE_ID = u'1'
|
image = self.images.first()
|
||||||
keypair = unicode(self.keypairs[0].name)
|
keypair = self.keypairs.first()
|
||||||
SERVER_NAME = u'serverName'
|
server = self.servers.first()
|
||||||
USER_DATA = u'userData'
|
volume = self.volumes.first()
|
||||||
volume = u'%s:vol' % self.volumes[0].id
|
sec_group = self.security_groups.first()
|
||||||
|
USER_DATA = 'user data'
|
||||||
device_name = u'vda'
|
device_name = u'vda'
|
||||||
BLOCK_DEVICE_MAPPING = {device_name: u"1:vol::0"}
|
volume_choice = "%s:vol" % volume.id
|
||||||
|
block_device_mapping = {device_name: u"%s::0" % volume_choice}
|
||||||
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}
|
|
||||||
|
|
||||||
self.mox.StubOutWithMock(api, 'image_get_meta')
|
self.mox.StubOutWithMock(api, 'image_get_meta')
|
||||||
self.mox.StubOutWithMock(api, 'flavor_list')
|
self.mox.StubOutWithMock(api, 'flavor_list')
|
||||||
@ -150,98 +80,97 @@ class ImageViewTests(test.BaseViewTests):
|
|||||||
self.mox.StubOutWithMock(api, 'server_create')
|
self.mox.StubOutWithMock(api, 'server_create')
|
||||||
self.mox.StubOutWithMock(api, 'volume_list')
|
self.mox.StubOutWithMock(api, 'volume_list')
|
||||||
|
|
||||||
api.flavor_list(IsA(http.HttpRequest)).AndReturn(self.flavors)
|
api.flavor_list(IsA(http.HttpRequest)).AndReturn(self.flavors.list())
|
||||||
api.keypair_list(IsA(http.HttpRequest)).AndReturn(self.keypairs)
|
api.keypair_list(IsA(http.HttpRequest)).AndReturn(self.keypairs.list())
|
||||||
api.security_group_list(IsA(http.HttpRequest)).AndReturn(
|
api.security_group_list(IsA(http.HttpRequest)) \
|
||||||
self.security_groups)
|
.AndReturn(self.security_groups.list())
|
||||||
api.image_get_meta(IsA(http.HttpRequest), IMAGE_ID).AndReturn(
|
api.image_get_meta(IsA(http.HttpRequest), image.id).AndReturn(image)
|
||||||
self.visibleImage)
|
api.volume_list(IsA(http.HttpRequest)).AndReturn(self.volumes.list())
|
||||||
api.volume_list(IsA(http.HttpRequest)).AndReturn(self.volumes)
|
api.server_create(IsA(http.HttpRequest),
|
||||||
api.server_create(IsA(http.HttpRequest), SERVER_NAME,
|
server.name,
|
||||||
str(IMAGE_ID), str(FLAVOR_ID),
|
image.id,
|
||||||
keypair, USER_DATA, [self.security_groups[0].name],
|
flavor.id,
|
||||||
BLOCK_DEVICE_MAPPING,
|
keypair.name,
|
||||||
|
USER_DATA,
|
||||||
|
[sec_group.name],
|
||||||
|
block_device_mapping,
|
||||||
instance_count=IsA(int))
|
instance_count=IsA(int))
|
||||||
|
|
||||||
self.mox.ReplayAll()
|
self.mox.ReplayAll()
|
||||||
|
|
||||||
res = self.client.post(
|
form_data = {'method': 'LaunchForm',
|
||||||
reverse('horizon:nova:images_and_snapshots:images:launch',
|
'flavor': flavor.id,
|
||||||
args=[IMAGE_ID]),
|
'image_id': image.id,
|
||||||
form_data)
|
'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,
|
self.assertRedirectsNoFollow(res,
|
||||||
reverse('horizon:nova:instances_and_volumes:index'))
|
reverse('horizon:nova:instances_and_volumes:index'))
|
||||||
|
|
||||||
def test_launch_flavorlist_error(self):
|
def test_launch_flavorlist_error(self):
|
||||||
IMAGE_ID = '1'
|
image = self.images.first()
|
||||||
|
|
||||||
self.mox.StubOutWithMock(api, 'image_get_meta')
|
self.mox.StubOutWithMock(api, 'image_get_meta')
|
||||||
self.mox.StubOutWithMock(api, 'tenant_quota_get')
|
self.mox.StubOutWithMock(api, 'tenant_quota_get')
|
||||||
self.mox.StubOutWithMock(api, 'flavor_list')
|
self.mox.StubOutWithMock(api, 'flavor_list')
|
||||||
self.mox.StubOutWithMock(api, 'keypair_list')
|
self.mox.StubOutWithMock(api, 'keypair_list')
|
||||||
self.mox.StubOutWithMock(api, 'security_group_list')
|
self.mox.StubOutWithMock(api, 'security_group_list')
|
||||||
|
|
||||||
api.image_get_meta(IsA(http.HttpRequest),
|
api.image_get_meta(IsA(http.HttpRequest),
|
||||||
IMAGE_ID).AndReturn(self.visibleImage)
|
image.id).AndReturn(image)
|
||||||
api.tenant_quota_get(IsA(http.HttpRequest),
|
api.tenant_quota_get(IsA(http.HttpRequest),
|
||||||
self.TEST_TENANT).AndReturn(FakeQuota)
|
self.tenant.id).AndReturn(self.quotas.first())
|
||||||
exception = keystone_exceptions.ClientException('Failed.')
|
exc = keystone_exceptions.ClientException('Failed.')
|
||||||
api.flavor_list(IsA(http.HttpRequest)).AndRaise(exception)
|
api.flavor_list(IsA(http.HttpRequest)).AndRaise(exc)
|
||||||
api.keypair_list(IsA(http.HttpRequest)).AndReturn(self.keypairs)
|
api.keypair_list(IsA(http.HttpRequest)).AndReturn(self.keypairs.list())
|
||||||
api.security_group_list(IsA(http.HttpRequest)).AndReturn(
|
api.security_group_list(IsA(http.HttpRequest)) \
|
||||||
self.security_groups)
|
.AndReturn(self.security_groups.list())
|
||||||
|
|
||||||
self.mox.ReplayAll()
|
self.mox.ReplayAll()
|
||||||
|
|
||||||
res = self.client.get(
|
url = reverse('horizon:nova:images_and_snapshots:images:launch',
|
||||||
reverse('horizon:nova:images_and_snapshots:images:launch',
|
args=[image.id])
|
||||||
args=[IMAGE_ID]))
|
res = self.client.get(url)
|
||||||
|
|
||||||
self.assertTemplateUsed(res,
|
self.assertTemplateUsed(res,
|
||||||
'nova/images_and_snapshots/images/launch.html')
|
'nova/images_and_snapshots/images/launch.html')
|
||||||
|
|
||||||
def test_launch_keypairlist_error(self):
|
def test_launch_keypairlist_error(self):
|
||||||
IMAGE_ID = '2'
|
image = self.images.first()
|
||||||
|
|
||||||
self.mox.StubOutWithMock(api, 'image_get_meta')
|
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')
|
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')
|
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')
|
self.mox.StubOutWithMock(api, 'keypair_list')
|
||||||
api.keypair_list(IsA(http.HttpRequest)).AndRaise(exception)
|
|
||||||
|
|
||||||
self.mox.StubOutWithMock(api, 'security_group_list')
|
self.mox.StubOutWithMock(api, 'security_group_list')
|
||||||
api.security_group_list(IsA(http.HttpRequest)).AndReturn(
|
api.image_get_meta(IsA(http.HttpRequest), image.id).AndReturn(image)
|
||||||
self.security_groups)
|
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()
|
self.mox.ReplayAll()
|
||||||
|
|
||||||
res = self.client.get(
|
url = reverse('horizon:nova:images_and_snapshots:images:launch',
|
||||||
reverse('horizon:nova:images_and_snapshots:images:launch',
|
args=[image.id])
|
||||||
args=[IMAGE_ID]))
|
res = self.client.get(url)
|
||||||
|
|
||||||
self.assertTemplateUsed(res,
|
self.assertTemplateUsed(res,
|
||||||
'nova/images_and_snapshots/images/launch.html')
|
'nova/images_and_snapshots/images/launch.html')
|
||||||
|
self.assertEqual(len(res.context['form'].fields['keypair'].choices), 0)
|
||||||
form = res.context['form']
|
|
||||||
|
|
||||||
form_keyfield = form.fields['keypair']
|
|
||||||
self.assertEqual(len(form_keyfield.choices), 0)
|
|
||||||
|
|
||||||
def test_launch_form_keystone_exception(self):
|
def test_launch_form_keystone_exception(self):
|
||||||
FLAVOR_ID = self.flavors[0].id
|
flavor = self.flavors.first()
|
||||||
IMAGE_ID = '1'
|
image = self.images.first()
|
||||||
keypair = self.keypairs[0].name
|
keypair = self.keypairs.first()
|
||||||
SERVER_NAME = 'serverName'
|
server = self.servers.first()
|
||||||
|
sec_group = self.security_groups.first()
|
||||||
USER_DATA = 'userData'
|
USER_DATA = 'userData'
|
||||||
|
|
||||||
self.mox.StubOutWithMock(api, 'image_get_meta')
|
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, 'server_create')
|
||||||
self.mox.StubOutWithMock(api, 'volume_list')
|
self.mox.StubOutWithMock(api, 'volume_list')
|
||||||
|
|
||||||
form_data = {'method': 'LaunchForm',
|
api.flavor_list(IgnoreArg()).AndReturn(self.flavors.list())
|
||||||
'flavor': FLAVOR_ID,
|
api.keypair_list(IgnoreArg()).AndReturn(self.keypairs.list())
|
||||||
'image_id': IMAGE_ID,
|
api.security_group_list(IsA(http.HttpRequest)) \
|
||||||
'keypair': keypair,
|
.AndReturn(self.security_groups.list())
|
||||||
'name': SERVER_NAME,
|
api.image_get_meta(IgnoreArg(), image.id).AndReturn(image)
|
||||||
'tenant_id': self.TEST_TENANT,
|
api.volume_list(IgnoreArg()).AndReturn(self.volumes.list())
|
||||||
'user_data': USER_DATA,
|
exc = keystone_exceptions.ClientException('Failed')
|
||||||
'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.server_create(IsA(http.HttpRequest),
|
api.server_create(IsA(http.HttpRequest),
|
||||||
SERVER_NAME,
|
server.name,
|
||||||
IMAGE_ID,
|
image.id,
|
||||||
str(FLAVOR_ID),
|
flavor.id,
|
||||||
keypair,
|
keypair.name,
|
||||||
USER_DATA,
|
USER_DATA,
|
||||||
[group.name for group in self.security_groups],
|
[sec_group.name],
|
||||||
None,
|
None,
|
||||||
instance_count=IsA(int)).AndRaise(exception)
|
instance_count=IsA(int)).AndRaise(exc)
|
||||||
|
|
||||||
self.mox.ReplayAll()
|
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)
|
self.assertRedirectsNoFollow(res, IMAGES_INDEX_URL)
|
||||||
|
@ -30,144 +30,84 @@ from horizon import test
|
|||||||
INDEX_URL = reverse('horizon:nova:images_and_snapshots:index')
|
INDEX_URL = reverse('horizon:nova:images_and_snapshots:index')
|
||||||
|
|
||||||
|
|
||||||
class SnapshotsViewTests(test.BaseViewTests):
|
class SnapshotsViewTests(test.TestCase):
|
||||||
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,)
|
|
||||||
|
|
||||||
def test_create_snapshot_get(self):
|
def test_create_snapshot_get(self):
|
||||||
|
server = self.servers.first()
|
||||||
self.mox.StubOutWithMock(api, 'server_get')
|
self.mox.StubOutWithMock(api, 'server_get')
|
||||||
api.server_get(IsA(http.HttpRequest),
|
api.server_get(IsA(http.HttpRequest), server.id).AndReturn(server)
|
||||||
str(self.good_server.id)).AndReturn(self.good_server)
|
|
||||||
|
|
||||||
self.mox.ReplayAll()
|
self.mox.ReplayAll()
|
||||||
|
|
||||||
res = self.client.get(
|
url = reverse('horizon:nova:images_and_snapshots:snapshots:create',
|
||||||
reverse('horizon:nova:images_and_snapshots:snapshots:create',
|
args=[server.id])
|
||||||
args=[self.good_server.id]))
|
res = self.client.get(url)
|
||||||
|
|
||||||
self.assertTemplateUsed(res,
|
self.assertTemplateUsed(res,
|
||||||
'nova/images_and_snapshots/snapshots/create.html')
|
'nova/images_and_snapshots/snapshots/create.html')
|
||||||
|
|
||||||
def test_create_snapshot_get_with_invalid_status(self):
|
def test_create_snapshot_get_with_invalid_status(self):
|
||||||
|
server = self.servers.get(status='BUILD')
|
||||||
self.mox.StubOutWithMock(api, 'server_get')
|
self.mox.StubOutWithMock(api, 'server_get')
|
||||||
api.server_get(IsA(http.HttpRequest),
|
api.server_get(IsA(http.HttpRequest), server.id).AndReturn(server)
|
||||||
str(self.bad_server.id)).AndReturn(self.bad_server)
|
|
||||||
|
|
||||||
self.mox.ReplayAll()
|
self.mox.ReplayAll()
|
||||||
|
|
||||||
res = self.client.get(
|
url = reverse('horizon:nova:images_and_snapshots:snapshots:create',
|
||||||
reverse('horizon:nova:images_and_snapshots:snapshots:create',
|
args=[server.id])
|
||||||
args=[self.bad_server.id]))
|
res = self.client.get(url)
|
||||||
|
redirect = reverse("horizon:nova:instances_and_volumes:index")
|
||||||
url = reverse("horizon:nova:instances_and_volumes:index")
|
self.assertRedirectsNoFollow(res, redirect)
|
||||||
self.assertRedirectsNoFollow(res, url)
|
|
||||||
|
|
||||||
def test_create_get_server_exception(self):
|
def test_create_get_server_exception(self):
|
||||||
|
server = self.servers.first()
|
||||||
self.mox.StubOutWithMock(api, 'server_get')
|
self.mox.StubOutWithMock(api, 'server_get')
|
||||||
exception = novaclient_exceptions.ClientException('apiException')
|
exc = novaclient_exceptions.ClientException('apiException')
|
||||||
api.server_get(IsA(http.HttpRequest),
|
api.server_get(IsA(http.HttpRequest), server.id).AndRaise(exc)
|
||||||
str(self.good_server.id)).AndRaise(exception)
|
|
||||||
|
|
||||||
self.mox.ReplayAll()
|
self.mox.ReplayAll()
|
||||||
|
|
||||||
res = self.client.get(
|
url = reverse('horizon:nova:images_and_snapshots:snapshots:create',
|
||||||
reverse('horizon:nova:images_and_snapshots:snapshots:create',
|
args=[server.id])
|
||||||
args=[self.good_server.id]))
|
res = self.client.get(url)
|
||||||
|
redirect = reverse("horizon:nova:instances_and_volumes:index")
|
||||||
url = reverse("horizon:nova:instances_and_volumes:index")
|
self.assertRedirectsNoFollow(res, redirect)
|
||||||
self.assertRedirectsNoFollow(res, url)
|
|
||||||
|
|
||||||
def test_create_snapshot_post(self):
|
def test_create_snapshot_post(self):
|
||||||
SNAPSHOT_NAME = 'snappy'
|
server = self.servers.first()
|
||||||
|
snapshot = self.snapshots.first()
|
||||||
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}
|
|
||||||
|
|
||||||
self.mox.StubOutWithMock(api, 'server_get')
|
self.mox.StubOutWithMock(api, 'server_get')
|
||||||
self.mox.StubOutWithMock(api, 'snapshot_create')
|
self.mox.StubOutWithMock(api, 'snapshot_create')
|
||||||
|
api.server_get(IsA(http.HttpRequest), server.id).AndReturn(server)
|
||||||
api.server_get(IsA(http.HttpRequest),
|
api.snapshot_create(IsA(http.HttpRequest), server.id, snapshot.name) \
|
||||||
str(self.good_server.id)).AndReturn(self.good_server)
|
.AndReturn(snapshot)
|
||||||
api.snapshot_create(IsA(http.HttpRequest),
|
api.server_get(IsA(http.HttpRequest), server.id).AndReturn(server)
|
||||||
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)
|
|
||||||
|
|
||||||
self.mox.ReplayAll()
|
self.mox.ReplayAll()
|
||||||
|
|
||||||
res = self.client.post(
|
formData = {'method': 'CreateSnapshot',
|
||||||
reverse('horizon:nova:images_and_snapshots:snapshots:create',
|
'tenant_id': self.tenant.id,
|
||||||
args=[self.good_server.id]),
|
'instance_id': server.id,
|
||||||
formData)
|
'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)
|
self.assertRedirectsNoFollow(res, INDEX_URL)
|
||||||
|
|
||||||
def test_create_snapshot_post_exception(self):
|
def test_create_snapshot_post_exception(self):
|
||||||
SNAPSHOT_NAME = 'snappy'
|
server = self.servers.first()
|
||||||
|
snapshot = self.snapshots.first()
|
||||||
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}
|
|
||||||
|
|
||||||
self.mox.StubOutWithMock(api, 'server_get')
|
self.mox.StubOutWithMock(api, 'server_get')
|
||||||
self.mox.StubOutWithMock(api, 'snapshot_create')
|
self.mox.StubOutWithMock(api, 'snapshot_create')
|
||||||
|
api.server_get(IsA(http.HttpRequest), server.id).AndReturn(server)
|
||||||
api.server_get(IsA(http.HttpRequest),
|
exc = novaclient_exceptions.ClientException('apiException')
|
||||||
str(self.good_server.id)).AndReturn(self.good_server)
|
api.snapshot_create(IsA(http.HttpRequest), server.id, snapshot.name) \
|
||||||
exception = novaclient_exceptions.ClientException('apiException',
|
.AndRaise(exc)
|
||||||
message='apiException')
|
|
||||||
api.snapshot_create(IsA(http.HttpRequest),
|
|
||||||
str(self.good_server.id), SNAPSHOT_NAME).\
|
|
||||||
AndRaise(exception)
|
|
||||||
|
|
||||||
self.mox.ReplayAll()
|
self.mox.ReplayAll()
|
||||||
|
|
||||||
res = self.client.post(
|
formData = {'method': 'CreateSnapshot',
|
||||||
reverse('horizon:nova:images_and_snapshots:snapshots:create',
|
'tenant_id': self.tenant.id,
|
||||||
args=[self.good_server.id]),
|
'instance_id': server.id,
|
||||||
formData)
|
'name': snapshot.name}
|
||||||
|
url = reverse('horizon:nova:images_and_snapshots:snapshots:create',
|
||||||
url = reverse("horizon:nova:instances_and_volumes:index")
|
args=[server.id])
|
||||||
self.assertRedirectsNoFollow(res, url)
|
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')
|
INDEX_URL = reverse('horizon:nova:images_and_snapshots:index')
|
||||||
|
|
||||||
|
|
||||||
class ImagesAndSnapshotsTests(test.BaseViewTests):
|
class ImagesAndSnapshotsTests(test.TestCase):
|
||||||
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,)
|
|
||||||
|
|
||||||
def test_index(self):
|
def test_index(self):
|
||||||
|
images = self.images.list()
|
||||||
|
snapshots = self.snapshots.list()
|
||||||
self.mox.StubOutWithMock(api, 'image_list_detailed')
|
self.mox.StubOutWithMock(api, 'image_list_detailed')
|
||||||
api.image_list_detailed(IsA(http.HttpRequest)).AndReturn(self.images)
|
|
||||||
|
|
||||||
self.mox.StubOutWithMock(api, 'snapshot_list_detailed')
|
self.mox.StubOutWithMock(api, 'snapshot_list_detailed')
|
||||||
api.snapshot_list_detailed(IsA(http.HttpRequest)).AndReturn(
|
api.image_list_detailed(IsA(http.HttpRequest)).AndReturn(images)
|
||||||
self.snapshots)
|
api.snapshot_list_detailed(IsA(http.HttpRequest)).AndReturn(snapshots)
|
||||||
|
|
||||||
self.mox.ReplayAll()
|
self.mox.ReplayAll()
|
||||||
|
|
||||||
res = self.client.get(INDEX_URL)
|
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)
|
self.assertIn('images_table', res.context)
|
||||||
images = res.context['images_table'].data
|
images = res.context['images_table'].data
|
||||||
self.assertEqual(len(images), 1)
|
filter_func = lambda im: im.container_format not in ['aki', 'ari']
|
||||||
self.assertEqual(images[0].name, 'visibleImage')
|
filtered_images = filter(filter_func, images)
|
||||||
|
self.assertItemsEqual(images, filtered_images)
|
||||||
|
|
||||||
def test_index_no_images(self):
|
def test_index_no_images(self):
|
||||||
self.mox.StubOutWithMock(api, 'snapshot_list_detailed')
|
self.mox.StubOutWithMock(api, 'snapshot_list_detailed')
|
||||||
self.mox.StubOutWithMock(api, 'image_list_detailed')
|
self.mox.StubOutWithMock(api, 'image_list_detailed')
|
||||||
|
|
||||||
api.image_list_detailed(IsA(http.HttpRequest)).AndReturn([])
|
api.image_list_detailed(IsA(http.HttpRequest)).AndReturn([])
|
||||||
api.snapshot_list_detailed(IsA(http.HttpRequest)).\
|
api.snapshot_list_detailed(IsA(http.HttpRequest)) \
|
||||||
AndReturn(self.snapshots)
|
.AndReturn(self.snapshots.list())
|
||||||
|
|
||||||
self.mox.ReplayAll()
|
self.mox.ReplayAll()
|
||||||
|
|
||||||
res = self.client.get(INDEX_URL)
|
res = self.client.get(INDEX_URL)
|
||||||
|
|
||||||
self.assertTemplateUsed(res, 'nova/images_and_snapshots/index.html')
|
self.assertTemplateUsed(res, 'nova/images_and_snapshots/index.html')
|
||||||
|
|
||||||
def test_index_client_conn_error(self):
|
def test_index_client_conn_error(self):
|
||||||
|
|
||||||
self.mox.StubOutWithMock(api, 'image_list_detailed')
|
self.mox.StubOutWithMock(api, 'image_list_detailed')
|
||||||
self.mox.StubOutWithMock(api, 'snapshot_list_detailed')
|
self.mox.StubOutWithMock(api, 'snapshot_list_detailed')
|
||||||
|
exc = glance_exception.ClientConnectionError('clientConnError')
|
||||||
exception = glance_exception.ClientConnectionError('clientConnError')
|
api.image_list_detailed(IsA(http.HttpRequest)).AndRaise(exc)
|
||||||
api.image_list_detailed(IsA(http.HttpRequest)).AndRaise(exception)
|
api.snapshot_list_detailed(IsA(http.HttpRequest)) \
|
||||||
api.snapshot_list_detailed(IsA(http.HttpRequest)).\
|
.AndReturn(self.snapshots.list())
|
||||||
AndReturn(self.snapshots)
|
|
||||||
|
|
||||||
self.mox.ReplayAll()
|
self.mox.ReplayAll()
|
||||||
|
|
||||||
res = self.client.get(INDEX_URL)
|
res = self.client.get(INDEX_URL)
|
||||||
|
|
||||||
self.assertTemplateUsed(res, 'nova/images_and_snapshots/index.html')
|
self.assertTemplateUsed(res, 'nova/images_and_snapshots/index.html')
|
||||||
|
@ -20,7 +20,6 @@
|
|||||||
|
|
||||||
from django import http
|
from django import http
|
||||||
from django.core.urlresolvers import reverse
|
from django.core.urlresolvers import reverse
|
||||||
from novaclient.v1_1 import volume_snapshots
|
|
||||||
from mox import IsA
|
from mox import IsA
|
||||||
|
|
||||||
from horizon import api
|
from horizon import api
|
||||||
@ -30,49 +29,34 @@ from horizon import test
|
|||||||
INDEX_URL = reverse('horizon:nova:images_and_snapshots:index')
|
INDEX_URL = reverse('horizon:nova:images_and_snapshots:index')
|
||||||
|
|
||||||
|
|
||||||
class SnapshotsViewTests(test.BaseViewTests):
|
class VolumeSnapshotsViewTests(test.TestCase):
|
||||||
def test_create_snapshot_get(self):
|
def test_create_snapshot_get(self):
|
||||||
VOLUME_ID = u'1'
|
volume = self.volumes.first()
|
||||||
|
|
||||||
res = self.client.get(reverse('horizon:nova:instances_and_volumes:'
|
res = self.client.get(reverse('horizon:nova:instances_and_volumes:'
|
||||||
'volumes:create_snapshot',
|
'volumes:create_snapshot',
|
||||||
args=[VOLUME_ID]))
|
args=[volume.id]))
|
||||||
|
|
||||||
self.assertTemplateUsed(res, 'nova/instances_and_volumes/'
|
self.assertTemplateUsed(res, 'nova/instances_and_volumes/'
|
||||||
'volumes/create_snapshot.html')
|
'volumes/create_snapshot.html')
|
||||||
|
|
||||||
def test_create_snapshot_post(self):
|
def test_create_snapshot_post(self):
|
||||||
VOLUME_ID = u'1'
|
volume = self.volumes.first()
|
||||||
SNAPSHOT_NAME = u'vol snap'
|
snapshot = self.volume_snapshots.first()
|
||||||
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}
|
|
||||||
|
|
||||||
self.mox.StubOutWithMock(api, 'volume_snapshot_create')
|
self.mox.StubOutWithMock(api, 'volume_snapshot_create')
|
||||||
|
api.volume_snapshot_create(IsA(http.HttpRequest),
|
||||||
api.volume_snapshot_create(
|
volume.id,
|
||||||
IsA(http.HttpRequest), str(VOLUME_ID), SNAPSHOT_NAME,
|
snapshot.displayName,
|
||||||
SNAPSHOT_DESCRIPTION).AndReturn(volume_snapshot)
|
snapshot.displayDescription) \
|
||||||
|
.AndReturn(snapshot)
|
||||||
self.mox.ReplayAll()
|
self.mox.ReplayAll()
|
||||||
|
|
||||||
res = self.client.post(
|
formData = {'method': 'CreateSnapshotForm',
|
||||||
reverse('horizon:nova:instances_and_volumes:volumes:'
|
'tenant_id': self.tenant.id,
|
||||||
'create_snapshot',
|
'volume_id': volume.id,
|
||||||
args=[VOLUME_ID]),
|
'name': snapshot.displayName,
|
||||||
formData)
|
'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)
|
self.assertRedirectsNoFollow(res, INDEX_URL)
|
||||||
|
@ -30,114 +30,87 @@ from horizon import test
|
|||||||
INDEX_URL = reverse('horizon:nova:instances_and_volumes:index')
|
INDEX_URL = reverse('horizon:nova:instances_and_volumes:index')
|
||||||
|
|
||||||
|
|
||||||
class InstanceViewTests(test.BaseViewTests):
|
class InstanceViewTests(test.TestCase):
|
||||||
def setUp(self):
|
def setUp(self):
|
||||||
super(InstanceViewTests, self).setUp()
|
super(InstanceViewTests, self).setUp()
|
||||||
self.now = self.override_times()
|
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):
|
def tearDown(self):
|
||||||
super(InstanceViewTests, self).tearDown()
|
super(InstanceViewTests, self).tearDown()
|
||||||
self.reset_times()
|
self.reset_times()
|
||||||
|
|
||||||
def test_terminate_instance(self):
|
def test_terminate_instance(self):
|
||||||
|
server = self.servers.first()
|
||||||
self.mox.StubOutWithMock(api, 'server_list')
|
self.mox.StubOutWithMock(api, 'server_list')
|
||||||
self.mox.StubOutWithMock(api, 'flavor_list')
|
self.mox.StubOutWithMock(api, 'flavor_list')
|
||||||
self.mox.StubOutWithMock(api, 'server_delete')
|
self.mox.StubOutWithMock(api, 'server_delete')
|
||||||
|
api.server_list(IsA(http.HttpRequest)).AndReturn(self.servers.list())
|
||||||
api.server_list(IsA(http.HttpRequest)).AndReturn(self.servers)
|
api.flavor_list(IgnoreArg()).AndReturn(self.flavors.list())
|
||||||
api.flavor_list(IgnoreArg()).AndReturn(self.flavors)
|
api.server_delete(IsA(http.HttpRequest), server.id)
|
||||||
api.server_delete(IsA(http.HttpRequest),
|
|
||||||
self.servers[0].id)
|
|
||||||
|
|
||||||
self.mox.ReplayAll()
|
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)
|
res = self.client.post(INDEX_URL, formData)
|
||||||
|
|
||||||
self.assertRedirectsNoFollow(res, INDEX_URL)
|
self.assertRedirectsNoFollow(res, INDEX_URL)
|
||||||
|
|
||||||
def test_terminate_instance_exception(self):
|
def test_terminate_instance_exception(self):
|
||||||
|
server = self.servers.first()
|
||||||
self.mox.StubOutWithMock(api, 'server_list')
|
self.mox.StubOutWithMock(api, 'server_list')
|
||||||
self.mox.StubOutWithMock(api, 'flavor_list')
|
self.mox.StubOutWithMock(api, 'flavor_list')
|
||||||
self.mox.StubOutWithMock(api, 'server_delete')
|
self.mox.StubOutWithMock(api, 'server_delete')
|
||||||
|
api.server_list(IsA(http.HttpRequest)).AndReturn(self.servers.list())
|
||||||
api.server_list(IsA(http.HttpRequest)).AndReturn(self.servers)
|
api.flavor_list(IgnoreArg()).AndReturn(self.flavors.list())
|
||||||
api.flavor_list(IgnoreArg()).AndReturn(self.flavors)
|
exc = nova_exceptions.ClientException(500)
|
||||||
exception = nova_exceptions.ClientException(500)
|
api.server_delete(IsA(http.HttpRequest), server.id).AndRaise(exc)
|
||||||
api.server_delete(IsA(http.HttpRequest),
|
|
||||||
self.servers[0].id).AndRaise(exception)
|
|
||||||
|
|
||||||
self.mox.ReplayAll()
|
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)
|
res = self.client.post(INDEX_URL, formData)
|
||||||
|
|
||||||
self.assertRedirectsNoFollow(res, INDEX_URL)
|
self.assertRedirectsNoFollow(res, INDEX_URL)
|
||||||
|
|
||||||
def test_reboot_instance(self):
|
def test_reboot_instance(self):
|
||||||
|
server = self.servers.first()
|
||||||
self.mox.StubOutWithMock(api, 'server_reboot')
|
self.mox.StubOutWithMock(api, 'server_reboot')
|
||||||
self.mox.StubOutWithMock(api, 'server_list')
|
self.mox.StubOutWithMock(api, 'server_list')
|
||||||
api.server_list(IsA(http.HttpRequest)).AndReturn(self.servers)
|
api.server_list(IsA(http.HttpRequest)).AndReturn(self.servers.list())
|
||||||
api.server_reboot(IsA(http.HttpRequest), unicode(self.servers[0].id))
|
api.server_reboot(IsA(http.HttpRequest), server.id)
|
||||||
|
|
||||||
self.mox.ReplayAll()
|
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)
|
res = self.client.post(INDEX_URL, formData)
|
||||||
|
|
||||||
self.assertRedirectsNoFollow(res, INDEX_URL)
|
self.assertRedirectsNoFollow(res, INDEX_URL)
|
||||||
|
|
||||||
def test_reboot_instance_exception(self):
|
def test_reboot_instance_exception(self):
|
||||||
|
server = self.servers.first()
|
||||||
self.mox.StubOutWithMock(api, 'server_reboot')
|
self.mox.StubOutWithMock(api, 'server_reboot')
|
||||||
self.mox.StubOutWithMock(api, 'server_list')
|
self.mox.StubOutWithMock(api, 'server_list')
|
||||||
api.server_list(IsA(http.HttpRequest)).AndReturn(self.servers)
|
api.server_list(IsA(http.HttpRequest)).AndReturn(self.servers.list())
|
||||||
exception = nova_exceptions.ClientException(500)
|
exc = nova_exceptions.ClientException(500)
|
||||||
api.server_reboot(IsA(http.HttpRequest),
|
api.server_reboot(IsA(http.HttpRequest), server.id).AndRaise(exc)
|
||||||
unicode(self.servers[0].id)).AndRaise(exception)
|
|
||||||
|
|
||||||
self.mox.ReplayAll()
|
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)
|
res = self.client.post(INDEX_URL, formData)
|
||||||
|
|
||||||
self.assertRedirectsNoFollow(res, INDEX_URL)
|
self.assertRedirectsNoFollow(res, INDEX_URL)
|
||||||
|
|
||||||
def test_instance_console(self):
|
def test_instance_console(self):
|
||||||
|
server = self.servers.first()
|
||||||
CONSOLE_OUTPUT = 'output'
|
CONSOLE_OUTPUT = 'output'
|
||||||
INSTANCE_ID = self.servers[0].id
|
|
||||||
|
|
||||||
self.mox.StubOutWithMock(api, 'server_console_output')
|
self.mox.StubOutWithMock(api, 'server_console_output')
|
||||||
api.server_console_output(IsA(http.HttpRequest),
|
api.server_console_output(IsA(http.HttpRequest),
|
||||||
unicode(INSTANCE_ID),
|
server.id,
|
||||||
tail_length=None).AndReturn(CONSOLE_OUTPUT)
|
tail_length=None).AndReturn(CONSOLE_OUTPUT)
|
||||||
|
|
||||||
self.mox.ReplayAll()
|
self.mox.ReplayAll()
|
||||||
|
|
||||||
res = self.client.get(
|
url = reverse('horizon:nova:instances_and_volumes:instances:console',
|
||||||
reverse('horizon:nova:instances_and_volumes:instances:console',
|
args=[server.id])
|
||||||
args=[INSTANCE_ID]))
|
res = self.client.get(url)
|
||||||
|
|
||||||
self.assertIsInstance(res, http.HttpResponse)
|
self.assertIsInstance(res, http.HttpResponse)
|
||||||
self.assertContains(res, CONSOLE_OUTPUT)
|
self.assertContains(res, CONSOLE_OUTPUT)
|
||||||
|
|
||||||
def test_instance_vnc(self):
|
def test_instance_vnc(self):
|
||||||
INSTANCE_ID = self.servers[0].id
|
server = self.servers.first()
|
||||||
CONSOLE_OUTPUT = '/vncserver'
|
CONSOLE_OUTPUT = '/vncserver'
|
||||||
|
|
||||||
console_mock = self.mox.CreateMock(api.VNCConsole)
|
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_vnc_console')
|
||||||
self.mox.StubOutWithMock(api, 'server_get')
|
self.mox.StubOutWithMock(api, 'server_get')
|
||||||
api.server_get(IsA(http.HttpRequest),
|
api.server_get(IsA(http.HttpRequest), server.id).AndReturn(server)
|
||||||
str(self.servers[0].id)).AndReturn(self.servers[0])
|
api.server_vnc_console(IgnoreArg(), server.id).AndReturn(console_mock)
|
||||||
api.server_vnc_console(IgnoreArg(),
|
|
||||||
unicode(INSTANCE_ID)).AndReturn(console_mock)
|
|
||||||
|
|
||||||
self.mox.ReplayAll()
|
self.mox.ReplayAll()
|
||||||
|
|
||||||
res = self.client.get(
|
url = reverse('horizon:nova:instances_and_volumes:instances:vnc',
|
||||||
reverse('horizon:nova:instances_and_volumes:instances:vnc',
|
args=[server.id])
|
||||||
args=[INSTANCE_ID]))
|
res = self.client.get(url)
|
||||||
|
redirect = CONSOLE_OUTPUT + '&title=%s(1)' % server.name
|
||||||
self.assertRedirectsNoFollow(res,
|
self.assertRedirectsNoFollow(res, redirect)
|
||||||
CONSOLE_OUTPUT + '&title=serverName(1)')
|
|
||||||
|
|
||||||
def test_instance_vnc_exception(self):
|
def test_instance_vnc_exception(self):
|
||||||
INSTANCE_ID = self.servers[0].id
|
server = self.servers.first()
|
||||||
|
|
||||||
exception = nova_exceptions.ClientException(500)
|
|
||||||
|
|
||||||
self.mox.StubOutWithMock(api, 'server_vnc_console')
|
self.mox.StubOutWithMock(api, 'server_vnc_console')
|
||||||
api.server_vnc_console(IsA(http.HttpRequest),
|
exc = nova_exceptions.ClientException(500)
|
||||||
unicode(INSTANCE_ID)).AndRaise(exception)
|
api.server_vnc_console(IsA(http.HttpRequest), server.id).AndRaise(exc)
|
||||||
|
|
||||||
self.mox.ReplayAll()
|
self.mox.ReplayAll()
|
||||||
|
|
||||||
res = self.client.get(
|
url = reverse('horizon:nova:instances_and_volumes:instances:vnc',
|
||||||
reverse('horizon:nova:instances_and_volumes:instances:vnc',
|
args=[server.id])
|
||||||
args=[INSTANCE_ID]))
|
res = self.client.get(url)
|
||||||
|
|
||||||
self.assertRedirectsNoFollow(res, INDEX_URL)
|
self.assertRedirectsNoFollow(res, INDEX_URL)
|
||||||
|
|
||||||
def test_instance_update_get(self):
|
def test_instance_update_get(self):
|
||||||
INSTANCE_ID = self.servers[0].id
|
server = self.servers.first()
|
||||||
|
|
||||||
self.mox.StubOutWithMock(api, 'server_get')
|
self.mox.StubOutWithMock(api, 'server_get')
|
||||||
api.server_get(IsA(http.HttpRequest),
|
api.server_get(IsA(http.HttpRequest), server.id).AndReturn(server)
|
||||||
unicode(INSTANCE_ID)).AndReturn(self.servers[0])
|
|
||||||
|
|
||||||
self.mox.ReplayAll()
|
self.mox.ReplayAll()
|
||||||
|
|
||||||
res = self.client.get(
|
url = reverse('horizon:nova:instances_and_volumes:instances:update',
|
||||||
reverse('horizon:nova:instances_and_volumes:instances:update',
|
args=[server.id])
|
||||||
args=[INSTANCE_ID]))
|
res = self.client.get(url)
|
||||||
|
|
||||||
self.assertTemplateUsed(res,
|
self.assertTemplateUsed(res,
|
||||||
'nova/instances_and_volumes/instances/update.html')
|
'nova/instances_and_volumes/instances/update.html')
|
||||||
|
|
||||||
def test_instance_update_get_server_get_exception(self):
|
def test_instance_update_get_server_get_exception(self):
|
||||||
INSTANCE_ID = self.servers[0].id
|
server = self.servers.first()
|
||||||
|
|
||||||
exception = nova_exceptions.ClientException(500)
|
|
||||||
self.mox.StubOutWithMock(api, 'server_get')
|
self.mox.StubOutWithMock(api, 'server_get')
|
||||||
api.server_get(IsA(http.HttpRequest),
|
exc = nova_exceptions.ClientException(500)
|
||||||
unicode(INSTANCE_ID)).AndRaise(exception)
|
api.server_get(IsA(http.HttpRequest), server.id).AndRaise(exc)
|
||||||
|
|
||||||
self.mox.ReplayAll()
|
self.mox.ReplayAll()
|
||||||
|
|
||||||
res = self.client.get(
|
url = reverse('horizon:nova:instances_and_volumes:instances:update',
|
||||||
reverse('horizon:nova:instances_and_volumes:instances:update',
|
args=[server.id])
|
||||||
args=[INSTANCE_ID]))
|
res = self.client.get(url)
|
||||||
|
|
||||||
self.assertRedirectsNoFollow(res, INDEX_URL)
|
self.assertRedirectsNoFollow(res, INDEX_URL)
|
||||||
|
|
||||||
def test_instance_update_post(self):
|
def test_instance_update_post(self):
|
||||||
INSTANCE_ID = self.servers[0].id
|
server = self.servers.first()
|
||||||
NAME = 'myname'
|
|
||||||
formData = {'method': 'UpdateInstance',
|
|
||||||
'instance': self.servers[0].id,
|
|
||||||
'name': NAME,
|
|
||||||
'tenant_id': self.TEST_TENANT}
|
|
||||||
|
|
||||||
self.mox.StubOutWithMock(api, 'server_get')
|
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')
|
self.mox.StubOutWithMock(api, 'server_update')
|
||||||
api.server_update(IsA(http.HttpRequest),
|
api.server_get(IsA(http.HttpRequest), server.id).AndReturn(server)
|
||||||
str(INSTANCE_ID), NAME)
|
api.server_update(IsA(http.HttpRequest), server.id, server.name)
|
||||||
|
|
||||||
self.mox.ReplayAll()
|
self.mox.ReplayAll()
|
||||||
|
|
||||||
res = self.client.post(
|
formData = {'method': 'UpdateInstance',
|
||||||
reverse('horizon:nova:instances_and_volumes:instances:update',
|
'instance': server.id,
|
||||||
args=[INSTANCE_ID]), formData)
|
'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)
|
self.assertRedirectsNoFollow(res, INDEX_URL)
|
||||||
|
|
||||||
def test_instance_update_post_api_exception(self):
|
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_get')
|
||||||
self.mox.StubOutWithMock(api, 'server_update')
|
self.mox.StubOutWithMock(api, 'server_update')
|
||||||
|
api.server_get(IsA(http.HttpRequest), server.id).AndReturn(server)
|
||||||
api.server_get(IsA(http.HttpRequest), unicode(SERVER.id)) \
|
exc = nova_exceptions.ClientException(500)
|
||||||
.AndReturn(self.servers[0])
|
api.server_update(IsA(http.HttpRequest), server.id, server.name) \
|
||||||
exception = nova_exceptions.ClientException(500)
|
.AndRaise(exc)
|
||||||
api.server_update(IsA(http.HttpRequest), str(SERVER.id), SERVER.name) \
|
|
||||||
.AndRaise(exception)
|
|
||||||
|
|
||||||
self.mox.ReplayAll()
|
self.mox.ReplayAll()
|
||||||
|
|
||||||
formData = {'method': 'UpdateInstance',
|
formData = {'method': 'UpdateInstance',
|
||||||
'instance': SERVER.id,
|
'instance': server.id,
|
||||||
'name': SERVER.name,
|
'name': server.name,
|
||||||
'tenant_id': self.TEST_TENANT}
|
'tenant_id': self.tenant.id}
|
||||||
url = reverse('horizon:nova:instances_and_volumes:instances:update',
|
url = reverse('horizon:nova:instances_and_volumes:instances:update',
|
||||||
args=[SERVER.id])
|
args=[server.id])
|
||||||
res = self.client.post(url, formData)
|
res = self.client.post(url, formData)
|
||||||
|
|
||||||
self.assertRedirectsNoFollow(res, INDEX_URL)
|
self.assertRedirectsNoFollow(res, INDEX_URL)
|
||||||
|
@ -27,27 +27,12 @@ from horizon import api
|
|||||||
from horizon import test
|
from horizon import test
|
||||||
|
|
||||||
|
|
||||||
class InstancesAndVolumesViewTest(test.BaseViewTests):
|
class InstancesAndVolumesViewTest(test.TestCase):
|
||||||
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,)
|
|
||||||
|
|
||||||
def test_index(self):
|
def test_index(self):
|
||||||
self.mox.StubOutWithMock(api, 'server_list')
|
self.mox.StubOutWithMock(api, 'server_list')
|
||||||
self.mox.StubOutWithMock(api, 'volume_list')
|
self.mox.StubOutWithMock(api, 'volume_list')
|
||||||
api.server_list(IsA(http.HttpRequest)).AndReturn(self.servers)
|
api.server_list(IsA(http.HttpRequest)).AndReturn(self.servers.list())
|
||||||
api.volume_list(IsA(http.HttpRequest)).AndReturn(self.volumes)
|
api.volume_list(IsA(http.HttpRequest)).AndReturn(self.volumes.list())
|
||||||
|
|
||||||
self.mox.ReplayAll()
|
self.mox.ReplayAll()
|
||||||
|
|
||||||
@ -57,14 +42,14 @@ class InstancesAndVolumesViewTest(test.BaseViewTests):
|
|||||||
self.assertTemplateUsed(res,
|
self.assertTemplateUsed(res,
|
||||||
'nova/instances_and_volumes/index.html')
|
'nova/instances_and_volumes/index.html')
|
||||||
instances = res.context['instances_table'].data
|
instances = res.context['instances_table'].data
|
||||||
self.assertItemsEqual(instances, self.servers)
|
self.assertItemsEqual(instances, self.servers.list())
|
||||||
|
|
||||||
def test_index_server_list_exception(self):
|
def test_index_server_list_exception(self):
|
||||||
self.mox.StubOutWithMock(api, 'server_list')
|
self.mox.StubOutWithMock(api, 'server_list')
|
||||||
self.mox.StubOutWithMock(api, 'volume_list')
|
self.mox.StubOutWithMock(api, 'volume_list')
|
||||||
exception = novaclient_exceptions.ClientException('apiException')
|
exception = novaclient_exceptions.ClientException('apiException')
|
||||||
api.server_list(IsA(http.HttpRequest)).AndRaise(exception)
|
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()
|
self.mox.ReplayAll()
|
||||||
|
|
||||||
|
@ -19,17 +19,18 @@
|
|||||||
# under the License.
|
# under the License.
|
||||||
|
|
||||||
from django import http
|
from django import http
|
||||||
from django.contrib import messages
|
|
||||||
from django.core.urlresolvers import reverse
|
from django.core.urlresolvers import reverse
|
||||||
from mox import IgnoreArg, IsA
|
from mox import IsA
|
||||||
|
|
||||||
from horizon import api
|
from horizon import api
|
||||||
from horizon import test
|
from horizon import test
|
||||||
|
|
||||||
|
|
||||||
class NetworkViewTests(test.BaseViewTests):
|
class NetworkViewTests(test.TestCase):
|
||||||
def setUp(self):
|
def setUp(self):
|
||||||
super(NetworkViewTests, self).setUp()
|
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 = {}
|
||||||
self.network['networks'] = []
|
self.network['networks'] = []
|
||||||
self.network['networks'].append({'id': 'n1'})
|
self.network['networks'].append({'id': 'n1'})
|
||||||
@ -186,9 +187,6 @@ class NetworkViewTests(test.BaseViewTests):
|
|||||||
'network': 'n1',
|
'network': 'n1',
|
||||||
'method': 'CreatePort'}
|
'method': 'CreatePort'}
|
||||||
|
|
||||||
self.mox.StubOutWithMock(messages, 'success')
|
|
||||||
messages.success(IgnoreArg(), IsA(basestring))
|
|
||||||
|
|
||||||
self.mox.ReplayAll()
|
self.mox.ReplayAll()
|
||||||
|
|
||||||
res = self.client.post(reverse('horizon:nova:networks:port_create',
|
res = self.client.post(reverse('horizon:nova:networks:port_create',
|
||||||
@ -226,9 +224,6 @@ class NetworkViewTests(test.BaseViewTests):
|
|||||||
|
|
||||||
formData = {'action': 'network_details__delete__p1'}
|
formData = {'action': 'network_details__delete__p1'}
|
||||||
|
|
||||||
self.mox.StubOutWithMock(messages, 'success')
|
|
||||||
messages.success(IgnoreArg(), IsA(basestring))
|
|
||||||
|
|
||||||
self.mox.ReplayAll()
|
self.mox.ReplayAll()
|
||||||
|
|
||||||
detail_url = reverse('horizon:nova:networks:detail', args=["n1"])
|
detail_url = reverse('horizon:nova:networks:detail', args=["n1"])
|
||||||
@ -292,9 +287,6 @@ class NetworkViewTests(test.BaseViewTests):
|
|||||||
|
|
||||||
formData = {'action': "network_details__detach_port__p1"}
|
formData = {'action': "network_details__detach_port__p1"}
|
||||||
|
|
||||||
self.mox.StubOutWithMock(messages, 'success')
|
|
||||||
messages.success(IgnoreArg(), IsA(basestring))
|
|
||||||
|
|
||||||
self.mox.ReplayAll()
|
self.mox.ReplayAll()
|
||||||
|
|
||||||
detail_url = reverse('horizon:nova:networks:detail', args=["n1"])
|
detail_url = reverse('horizon:nova:networks:detail', args=["n1"])
|
||||||
|
@ -24,7 +24,6 @@ from django import http
|
|||||||
from django.core.urlresolvers import reverse
|
from django.core.urlresolvers import reverse
|
||||||
from mox import IsA
|
from mox import IsA
|
||||||
from novaclient import exceptions as nova_exceptions
|
from novaclient import exceptions as nova_exceptions
|
||||||
from novaclient.v1_1 import usage as nova_usage
|
|
||||||
|
|
||||||
from horizon import api
|
from horizon import api
|
||||||
from horizon import test
|
from horizon import test
|
||||||
@ -32,137 +31,83 @@ from horizon import usage
|
|||||||
|
|
||||||
|
|
||||||
INDEX_URL = reverse('horizon:nova:overview:index')
|
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):
|
class UsageViewTests(test.TestCase):
|
||||||
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,)
|
|
||||||
|
|
||||||
def tearDown(self):
|
def tearDown(self):
|
||||||
super(UsageViewTests, self).tearDown()
|
super(UsageViewTests, self).tearDown()
|
||||||
self.reset_times()
|
self.reset_times() # override_times is called in the tests
|
||||||
|
|
||||||
def test_usage(self):
|
def test_usage(self):
|
||||||
now = self.override_times()
|
now = self.override_times()
|
||||||
|
usage_obj = api.nova.Usage(self.usages.first())
|
||||||
self.mox.StubOutWithMock(api, 'usage_get')
|
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,
|
datetime.datetime(now.year, now.month, 1,
|
||||||
now.hour, now.minute, now.second),
|
now.hour, now.minute, now.second),
|
||||||
datetime.datetime(now.year, now.month, now.day, now.hour,
|
datetime.datetime(now.year, now.month, now.day, now.hour,
|
||||||
now.minute, now.second)) \
|
now.minute, now.second)) \
|
||||||
.AndReturn(self.usage)
|
.AndReturn(usage_obj)
|
||||||
|
|
||||||
self.mox.ReplayAll()
|
self.mox.ReplayAll()
|
||||||
|
|
||||||
res = self.client.get(reverse('horizon:nova:overview:index'))
|
res = self.client.get(reverse('horizon:nova:overview:index'))
|
||||||
|
|
||||||
self.assertTemplateUsed(res, 'nova/overview/usage.html')
|
self.assertTemplateUsed(res, 'nova/overview/usage.html')
|
||||||
|
|
||||||
self.assertTrue(isinstance(res.context['usage'], usage.TenantUsage))
|
self.assertTrue(isinstance(res.context['usage'], usage.TenantUsage))
|
||||||
self.assertContains(res, 'form-horizontal')
|
self.assertContains(res, 'form-horizontal')
|
||||||
|
|
||||||
def test_usage_csv(self):
|
def test_usage_csv(self):
|
||||||
now = self.override_times()
|
now = self.override_times()
|
||||||
|
usage_obj = api.nova.Usage(self.usages.first())
|
||||||
self.mox.StubOutWithMock(api, 'usage_get')
|
self.mox.StubOutWithMock(api, 'usage_get')
|
||||||
timestamp = datetime.datetime(now.year, now.month, 1,
|
timestamp = datetime.datetime(now.year, now.month, 1,
|
||||||
now.hour, now.minute,
|
now.hour, now.minute,
|
||||||
now.second)
|
now.second)
|
||||||
api.usage_get(IsA(http.HttpRequest),
|
api.usage_get(IsA(http.HttpRequest),
|
||||||
self.TEST_TENANT,
|
self.tenant.id,
|
||||||
timestamp,
|
timestamp,
|
||||||
datetime.datetime(now.year, now.month, now.day, now.hour,
|
datetime.datetime(now.year, now.month, now.day, now.hour,
|
||||||
now.minute, now.second)) \
|
now.minute, now.second)) \
|
||||||
.AndReturn(self.usage)
|
.AndReturn(usage_obj)
|
||||||
|
|
||||||
self.mox.ReplayAll()
|
self.mox.ReplayAll()
|
||||||
|
|
||||||
res = self.client.get(reverse('horizon:nova:overview:index') +
|
res = self.client.get(reverse('horizon:nova:overview:index') +
|
||||||
"?format=csv")
|
"?format=csv")
|
||||||
|
|
||||||
self.assertTemplateUsed(res, 'nova/overview/usage.csv')
|
self.assertTemplateUsed(res, 'nova/overview/usage.csv')
|
||||||
|
|
||||||
self.assertTrue(isinstance(res.context['usage'], usage.TenantUsage))
|
self.assertTrue(isinstance(res.context['usage'], usage.TenantUsage))
|
||||||
|
|
||||||
def test_usage_exception(self):
|
def test_usage_exception(self):
|
||||||
now = self.override_times()
|
now = self.override_times()
|
||||||
|
|
||||||
self.mox.StubOutWithMock(api, 'usage_get')
|
self.mox.StubOutWithMock(api, 'usage_get')
|
||||||
timestamp = datetime.datetime(now.year, now.month, 1, now.hour,
|
timestamp = datetime.datetime(now.year, now.month, 1, now.hour,
|
||||||
now.minute, now.second)
|
now.minute, now.second)
|
||||||
exception = nova_exceptions.ClientException(500)
|
exception = nova_exceptions.ClientException(500)
|
||||||
api.usage_get(IsA(http.HttpRequest),
|
api.usage_get(IsA(http.HttpRequest),
|
||||||
self.TEST_TENANT,
|
self.tenant.id,
|
||||||
timestamp,
|
timestamp,
|
||||||
datetime.datetime(now.year, now.month, now.day, now.hour,
|
datetime.datetime(now.year, now.month, now.day, now.hour,
|
||||||
now.minute, now.second)) \
|
now.minute, now.second)) \
|
||||||
.AndRaise(exception)
|
.AndRaise(exception)
|
||||||
|
|
||||||
self.mox.ReplayAll()
|
self.mox.ReplayAll()
|
||||||
|
|
||||||
res = self.client.get(reverse('horizon:nova:overview:index'))
|
res = self.client.get(reverse('horizon:nova:overview:index'))
|
||||||
|
|
||||||
self.assertTemplateUsed(res, 'nova/overview/usage.html')
|
self.assertTemplateUsed(res, 'nova/overview/usage.html')
|
||||||
self.assertEqual(res.context['usage'].usage_list, [])
|
self.assertEqual(res.context['usage'].usage_list, [])
|
||||||
|
|
||||||
def test_usage_default_tenant(self):
|
def test_usage_default_tenant(self):
|
||||||
now = self.override_times()
|
now = self.override_times()
|
||||||
|
usage_obj = api.nova.Usage(self.usages.first())
|
||||||
self.mox.StubOutWithMock(api, 'usage_get')
|
self.mox.StubOutWithMock(api, 'usage_get')
|
||||||
timestamp = datetime.datetime(now.year, now.month, 1,
|
timestamp = datetime.datetime(now.year, now.month, 1,
|
||||||
now.hour, now.minute,
|
now.hour, now.minute,
|
||||||
now.second)
|
now.second)
|
||||||
api.usage_get(IsA(http.HttpRequest),
|
api.usage_get(IsA(http.HttpRequest),
|
||||||
self.TEST_TENANT,
|
self.tenant.id,
|
||||||
timestamp,
|
timestamp,
|
||||||
datetime.datetime(now.year, now.month, now.day, now.hour,
|
datetime.datetime(now.year, now.month, now.day, now.hour,
|
||||||
now.minute, now.second)) \
|
now.minute, now.second)) \
|
||||||
.AndReturn(self.usage)
|
.AndReturn(usage_obj)
|
||||||
|
|
||||||
self.mox.ReplayAll()
|
self.mox.ReplayAll()
|
||||||
|
|
||||||
res = self.client.get(reverse('horizon:nova:overview:index'))
|
res = self.client.get(reverse('horizon:nova:overview:index'))
|
||||||
|
|
||||||
self.assertTemplateUsed(res, 'nova/overview/usage.html')
|
self.assertTemplateUsed(res, 'nova/overview/usage.html')
|
||||||
self.assertTrue(isinstance(res.context['usage'], usage.TenantUsage))
|
self.assertTrue(isinstance(res.context['usage'], usage.TenantUsage))
|
||||||
|
@ -24,37 +24,20 @@ from horizon import test
|
|||||||
|
|
||||||
|
|
||||||
class InstanceViewTest(test.BaseAdminViewTests):
|
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):
|
def test_index(self):
|
||||||
|
servers = self.servers.list()
|
||||||
|
flavors = self.flavors.list()
|
||||||
self.mox.StubOutWithMock(api.nova, 'server_list')
|
self.mox.StubOutWithMock(api.nova, 'server_list')
|
||||||
self.mox.StubOutWithMock(api.nova, 'flavor_list')
|
self.mox.StubOutWithMock(api.nova, 'flavor_list')
|
||||||
api.nova.server_list(IsA(http.HttpRequest),
|
api.nova.server_list(IsA(http.HttpRequest),
|
||||||
all_tenants=True).AndReturn(self.servers)
|
all_tenants=True).AndReturn(servers)
|
||||||
api.nova.flavor_list(IsA(http.HttpRequest)).AndReturn(self.flavors)
|
api.nova.flavor_list(IsA(http.HttpRequest)).AndReturn(flavors)
|
||||||
|
|
||||||
self.mox.ReplayAll()
|
self.mox.ReplayAll()
|
||||||
|
|
||||||
res = self.client.get(reverse('horizon:syspanel:instances:index'))
|
res = self.client.get(reverse('horizon:syspanel:instances:index'))
|
||||||
|
|
||||||
self.assertTemplateUsed(res, 'syspanel/instances/index.html')
|
self.assertTemplateUsed(res, 'syspanel/instances/index.html')
|
||||||
instances = res.context['table'].data
|
instances = res.context['table'].data
|
||||||
self.assertItemsEqual(instances, self.servers)
|
self.assertItemsEqual(instances, servers)
|
||||||
|
|
||||||
def test_index_server_list_exception(self):
|
def test_index_server_list_exception(self):
|
||||||
self.mox.StubOutWithMock(api.nova, 'server_list')
|
self.mox.StubOutWithMock(api.nova, 'server_list')
|
||||||
@ -66,6 +49,5 @@ class InstanceViewTest(test.BaseAdminViewTests):
|
|||||||
self.mox.ReplayAll()
|
self.mox.ReplayAll()
|
||||||
|
|
||||||
res = self.client.get(reverse('horizon:syspanel:instances:index'))
|
res = self.client.get(reverse('horizon:syspanel:instances:index'))
|
||||||
|
|
||||||
self.assertTemplateUsed(res, 'syspanel/instances/index.html')
|
self.assertTemplateUsed(res, 'syspanel/instances/index.html')
|
||||||
self.assertEqual(len(res.context['instances_table'].data), 0)
|
self.assertEqual(len(res.context['instances_table'].data), 0)
|
||||||
|
@ -25,64 +25,20 @@ from horizon import test
|
|||||||
INDEX_URL = reverse('horizon:syspanel:projects:index')
|
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):
|
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):
|
def test_index(self):
|
||||||
self.mox.StubOutWithMock(api, 'tenant_list')
|
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()
|
self.mox.ReplayAll()
|
||||||
|
|
||||||
res = self.client.get(INDEX_URL)
|
res = self.client.get(INDEX_URL)
|
||||||
|
|
||||||
self.assertTemplateUsed(res, 'syspanel/projects/index.html')
|
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):
|
def test_modify_quota(self):
|
||||||
self.mox.StubOutWithMock(api.keystone, 'tenant_get')
|
tenant = self.tenants.first()
|
||||||
self.mox.StubOutWithMock(api.nova, 'tenant_quota_get')
|
quota = self.quotas.first()
|
||||||
self.mox.StubOutWithMock(api.nova, 'tenant_quota_update')
|
quota_data = {"metadata_items": '1',
|
||||||
|
|
||||||
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)
|
|
||||||
|
|
||||||
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_files": '1',
|
||||||
"injected_file_content_bytes": '1',
|
"injected_file_content_bytes": '1',
|
||||||
"cores": '1',
|
"cores": '1',
|
||||||
@ -91,5 +47,17 @@ class TenantsViewTests(test.BaseAdminViewTests):
|
|||||||
"gigabytes": '1',
|
"gigabytes": '1',
|
||||||
"ram": 1,
|
"ram": 1,
|
||||||
"floating_ips": '1'}
|
"floating_ips": '1'}
|
||||||
res = self.client.post(url, data)
|
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(), 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.tenant.id])
|
||||||
|
quota_data.update({"method": "UpdateQuotas",
|
||||||
|
"tenant_id": self.tenant.id})
|
||||||
|
res = self.client.post(url, quota_data)
|
||||||
self.assertRedirectsNoFollow(res, INDEX_URL)
|
self.assertRedirectsNoFollow(res, INDEX_URL)
|
||||||
|
@ -19,8 +19,8 @@
|
|||||||
# under the License.
|
# under the License.
|
||||||
|
|
||||||
from django.core.urlresolvers import reverse
|
from django.core.urlresolvers import reverse
|
||||||
from mox import IgnoreArg
|
|
||||||
from keystoneclient import exceptions as keystone_exceptions
|
from keystoneclient import exceptions as keystone_exceptions
|
||||||
|
from mox import IgnoreArg
|
||||||
|
|
||||||
from horizon import api
|
from horizon import api
|
||||||
from horizon import test
|
from horizon import test
|
||||||
@ -30,68 +30,50 @@ USERS_INDEX_URL = reverse('horizon:syspanel:users:index')
|
|||||||
|
|
||||||
|
|
||||||
class UsersViewTests(test.BaseAdminViewTests):
|
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):
|
def test_index(self):
|
||||||
self.mox.StubOutWithMock(api, 'user_list')
|
self.mox.StubOutWithMock(api, 'user_list')
|
||||||
api.user_list(IgnoreArg()).AndReturn(self.users)
|
api.user_list(IgnoreArg()).AndReturn(self.users.list())
|
||||||
|
|
||||||
self.mox.ReplayAll()
|
self.mox.ReplayAll()
|
||||||
|
|
||||||
res = self.client.get(USERS_INDEX_URL)
|
res = self.client.get(USERS_INDEX_URL)
|
||||||
|
|
||||||
self.assertTemplateUsed(res, 'syspanel/users/index.html')
|
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):
|
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')
|
self.mox.StubOutWithMock(api.keystone, 'user_update_enabled')
|
||||||
api.keystone.user_update_enabled(IgnoreArg(), self.user.id, True) \
|
api.keystone.user_update_enabled(IgnoreArg(),
|
||||||
.AndReturn(self.mox.CreateMock(api.User))
|
user.id,
|
||||||
|
True).AndReturn(user)
|
||||||
self.mox.ReplayAll()
|
self.mox.ReplayAll()
|
||||||
|
|
||||||
|
formData = {'action': 'users__enable__%s' % user.id}
|
||||||
res = self.client.post(USERS_INDEX_URL, formData)
|
res = self.client.post(USERS_INDEX_URL, formData)
|
||||||
|
|
||||||
self.assertRedirects(res, USERS_INDEX_URL)
|
self.assertRedirects(res, USERS_INDEX_URL)
|
||||||
|
|
||||||
def test_disable_user(self):
|
def test_disable_user(self):
|
||||||
OTHER_USER_ID = '5'
|
user = self.users.get(id="2")
|
||||||
formData = {'action': 'users__disable__%s' % OTHER_USER_ID}
|
|
||||||
|
|
||||||
self.mox.StubOutWithMock(api.keystone, 'user_update_enabled')
|
self.mox.StubOutWithMock(api.keystone, 'user_update_enabled')
|
||||||
api.keystone.user_update_enabled(IgnoreArg(), OTHER_USER_ID, False) \
|
api.keystone.user_update_enabled(IgnoreArg(),
|
||||||
.AndReturn(self.mox.CreateMock(api.User))
|
user.id,
|
||||||
|
False).AndReturn(user)
|
||||||
self.mox.ReplayAll()
|
self.mox.ReplayAll()
|
||||||
|
|
||||||
|
formData = {'action': 'users__disable__%s' % user.id}
|
||||||
res = self.client.post(USERS_INDEX_URL, formData)
|
res = self.client.post(USERS_INDEX_URL, formData)
|
||||||
|
|
||||||
self.assertRedirects(res, USERS_INDEX_URL)
|
self.assertRedirects(res, USERS_INDEX_URL)
|
||||||
|
|
||||||
def test_enable_disable_user_exception(self):
|
def test_enable_disable_user_exception(self):
|
||||||
OTHER_USER_ID = '5'
|
user = self.users.get(id="2")
|
||||||
formData = {'action': 'users__enable__%s' % OTHER_USER_ID}
|
|
||||||
|
|
||||||
self.mox.StubOutWithMock(api.keystone, 'user_update_enabled')
|
self.mox.StubOutWithMock(api.keystone, 'user_update_enabled')
|
||||||
api_exception = keystone_exceptions.ClientException('apiException',
|
api_exception = keystone_exceptions.ClientException('apiException',
|
||||||
message='apiException')
|
message='apiException')
|
||||||
api.keystone.user_update_enabled(IgnoreArg(), OTHER_USER_ID, True) \
|
api.keystone.user_update_enabled(IgnoreArg(),
|
||||||
.AndRaise(api_exception)
|
user.id,
|
||||||
|
True).AndRaise(api_exception)
|
||||||
self.mox.ReplayAll()
|
self.mox.ReplayAll()
|
||||||
|
|
||||||
|
formData = {'action': 'users__enable__%s' % user.id}
|
||||||
res = self.client.post(USERS_INDEX_URL, formData)
|
res = self.client.post(USERS_INDEX_URL, formData)
|
||||||
|
|
||||||
self.assertRedirects(res, USERS_INDEX_URL)
|
self.assertRedirects(res, USERS_INDEX_URL)
|
||||||
@ -99,10 +81,10 @@ class UsersViewTests(test.BaseAdminViewTests):
|
|||||||
def test_shoot_yourself_in_the_foot(self):
|
def test_shoot_yourself_in_the_foot(self):
|
||||||
self.mox.StubOutWithMock(api, 'user_list')
|
self.mox.StubOutWithMock(api, 'user_list')
|
||||||
# Four times... one for each post and one for each followed redirect
|
# 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.list())
|
||||||
api.user_list(IgnoreArg()).AndReturn(self.users)
|
api.user_list(IgnoreArg()).AndReturn(self.users.list())
|
||||||
api.user_list(IgnoreArg()).AndReturn(self.users)
|
api.user_list(IgnoreArg()).AndReturn(self.users.list())
|
||||||
api.user_list(IgnoreArg()).AndReturn(self.users)
|
api.user_list(IgnoreArg()).AndReturn(self.users.list())
|
||||||
|
|
||||||
self.mox.ReplayAll()
|
self.mox.ReplayAll()
|
||||||
|
|
||||||
@ -115,4 +97,5 @@ class UsersViewTests(test.BaseAdminViewTests):
|
|||||||
formData = {'action': 'users__delete__%s' % self.request.user.id}
|
formData = {'action': 'users__delete__%s' % self.request.user.id}
|
||||||
res = self.client.post(USERS_INDEX_URL, formData, follow=True)
|
res = self.client.post(USERS_INDEX_URL, formData, follow=True)
|
||||||
self.assertEqual(list(res.context['messages'])[0].message,
|
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__)
|
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,
|
UNAUTHORIZED = (keystoneclient.Unauthorized,
|
||||||
keystoneclient.Forbidden,
|
keystoneclient.Forbidden,
|
||||||
novaclient.Unauthorized,
|
novaclient.Unauthorized,
|
||||||
@ -51,61 +126,8 @@ NOT_FOUND = (keystoneclient.NotFound,
|
|||||||
RECOVERABLE = (keystoneclient.ClientException,
|
RECOVERABLE = (keystoneclient.ClientException,
|
||||||
novaclient.ClientException,
|
novaclient.ClientException,
|
||||||
glanceclient.GlanceException,
|
glanceclient.GlanceException,
|
||||||
swiftclient.Error)
|
swiftclient.Error,
|
||||||
|
AlreadyExists)
|
||||||
|
|
||||||
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
|
|
||||||
|
|
||||||
|
|
||||||
def handle(request, message=None, redirect=None, ignore=False, escalate=False):
|
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
|
exc_type, exc_value, exc_traceback = exc_value.wrapped
|
||||||
wrap = True
|
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 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}
|
message = message % {"exc": exc_value}
|
||||||
|
|
||||||
if issubclass(exc_type, UNAUTHORIZED):
|
if issubclass(exc_type, UNAUTHORIZED):
|
||||||
|
@ -20,18 +20,24 @@
|
|||||||
|
|
||||||
import datetime
|
import datetime
|
||||||
|
|
||||||
|
import cloudfiles as swift_client
|
||||||
from django import http
|
from django import http
|
||||||
from django import test as django_test
|
from django import test as django_test
|
||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
from django.contrib.messages.storage import default_storage
|
from django.contrib.messages.storage import default_storage
|
||||||
from django.core.handlers import wsgi
|
from django.core.handlers import wsgi
|
||||||
from django.test.client import RequestFactory
|
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 httplib2
|
||||||
import mox
|
import mox
|
||||||
|
|
||||||
|
from horizon import api
|
||||||
from horizon import context_processors
|
from horizon import context_processors
|
||||||
from horizon import middleware
|
from horizon import middleware
|
||||||
from horizon import users
|
from horizon import users
|
||||||
|
from horizon.tests.test_data.utils import load_test_data
|
||||||
|
|
||||||
from .time import time
|
from .time import time
|
||||||
from .time import today
|
from .time import today
|
||||||
@ -51,85 +57,49 @@ class RequestFactoryWithMessages(RequestFactory):
|
|||||||
|
|
||||||
|
|
||||||
class TestCase(django_test.TestCase):
|
class TestCase(django_test.TestCase):
|
||||||
TEST_STAFF_USER = 'staffUser'
|
"""
|
||||||
TEST_TENANT = '1'
|
Specialized base test case class for Horizon which gives access to
|
||||||
TEST_TENANT_NAME = 'aTenant'
|
numerous additional features:
|
||||||
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"}]
|
|
||||||
|
|
||||||
|
* 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):
|
def setUp(self):
|
||||||
|
load_test_data(self)
|
||||||
self.mox = mox.Mox()
|
self.mox = mox.Mox()
|
||||||
self.factory = RequestFactoryWithMessages()
|
self.factory = RequestFactoryWithMessages()
|
||||||
|
self.context = {'authorized_tenants': self.tenants.list()}
|
||||||
|
|
||||||
def fake_conn_request(*args, **kwargs):
|
def fake_conn_request(*args, **kwargs):
|
||||||
raise Exception("An external URI request tried to escape through "
|
raise Exception("An external URI request tried to escape through "
|
||||||
"an httplib2 client. Args: %s, kwargs: %s"
|
"an httplib2 client. Args: %s, kwargs: %s"
|
||||||
% (args, kwargs))
|
% (args, kwargs))
|
||||||
|
|
||||||
self._real_conn_request = httplib2.Http._conn_request
|
self._real_conn_request = httplib2.Http._conn_request
|
||||||
httplib2.Http._conn_request = fake_conn_request
|
httplib2.Http._conn_request = fake_conn_request
|
||||||
|
|
||||||
self._real_horizon_context_processor = context_processors.horizon
|
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
|
self._real_get_user_from_request = users.get_user_from_request
|
||||||
tenants = self.TEST_CONTEXT['authorized_tenants']
|
tenants = self.context['authorized_tenants']
|
||||||
self.setActiveUser(token=self.TEST_TOKEN,
|
self.setActiveUser(token=self.token.id,
|
||||||
username=self.TEST_USER,
|
username=self.user.name,
|
||||||
tenant_id=self.TEST_TENANT,
|
tenant_id=self.tenant.id,
|
||||||
service_catalog=self.TEST_SERVICE_CATALOG,
|
service_catalog=self.service_catalog,
|
||||||
authorized_tenants=tenants)
|
authorized_tenants=tenants)
|
||||||
self.request = http.HttpRequest()
|
self.request = http.HttpRequest()
|
||||||
self.request.session = self.client._session()
|
self.request.session = self.client._session()
|
||||||
|
self.request.session['token'] = self.token.id
|
||||||
middleware.HorizonMiddleware().process_request(self.request)
|
middleware.HorizonMiddleware().process_request(self.request)
|
||||||
|
|
||||||
def tearDown(self):
|
def tearDown(self):
|
||||||
@ -151,41 +121,150 @@ class TestCase(django_test.TestCase):
|
|||||||
authorized_tenants=authorized_tenants)
|
authorized_tenants=authorized_tenants)
|
||||||
|
|
||||||
def override_times(self):
|
def override_times(self):
|
||||||
|
""" Overrides the "current" time with immutable values. """
|
||||||
now = datetime.datetime.utcnow()
|
now = datetime.datetime.utcnow()
|
||||||
time.override_time = \
|
time.override_time = \
|
||||||
datetime.time(now.hour, now.minute, now.second)
|
datetime.time(now.hour, now.minute, now.second)
|
||||||
today.override_time = datetime.date(now.year, now.month, now.day)
|
today.override_time = datetime.date(now.year, now.month, now.day)
|
||||||
utcnow.override_time = now
|
utcnow.override_time = now
|
||||||
|
|
||||||
return now
|
return now
|
||||||
|
|
||||||
def reset_times(self):
|
def reset_times(self):
|
||||||
|
""" Undoes the changes made by ``override_times``. """
|
||||||
time.override_time = None
|
time.override_time = None
|
||||||
today.override_time = None
|
today.override_time = None
|
||||||
utcnow.override_time = None
|
utcnow.override_time = None
|
||||||
|
|
||||||
|
|
||||||
class BaseViewTests(TestCase):
|
|
||||||
"""
|
|
||||||
Base class for view based unit tests.
|
|
||||||
"""
|
|
||||||
def assertRedirectsNoFollow(self, response, expected_url):
|
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:
|
if response.status_code / 100 != 3:
|
||||||
assert("The response did not return a redirect.")
|
assert("The response did not return a redirect.")
|
||||||
self.assertEqual(response._headers.get('location', None),
|
self.assertEqual(response._headers.get('location', None),
|
||||||
('Location', settings.TESTSERVER + expected_url))
|
('Location', settings.TESTSERVER + expected_url))
|
||||||
self.assertEqual(response.status_code, 302)
|
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,
|
def setActiveUser(self, id=None, token=None, username=None, tenant_id=None,
|
||||||
service_catalog=None, tenant_name=None, roles=None,
|
service_catalog=None, tenant_name=None, roles=None,
|
||||||
authorized_tenants=None):
|
authorized_tenants=None):
|
||||||
users.get_user_from_request = lambda x: \
|
users.get_user_from_request = lambda x: \
|
||||||
users.User(id=self.TEST_USER_ID,
|
users.User(id=self.user.id,
|
||||||
token=self.TEST_TOKEN,
|
token=self.token.id,
|
||||||
user=self.TEST_USER,
|
user=self.user.name,
|
||||||
tenant_id=self.TEST_TENANT,
|
tenant_id=self.tenant.id,
|
||||||
service_catalog=self.TEST_SERVICE_CATALOG,
|
service_catalog=self.service_catalog,
|
||||||
roles=self.TEST_ROLES,
|
roles=[self.roles.admin._info],
|
||||||
authorized_tenants=None)
|
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 __future__ import absolute_import
|
||||||
|
|
||||||
from django import http
|
|
||||||
from django.conf import settings
|
|
||||||
from mox import IsA
|
|
||||||
|
|
||||||
from horizon import exceptions
|
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.
|
# 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 """
|
""" Tests for functions that don't use one of the api objects """
|
||||||
|
|
||||||
def test_url_for(self):
|
def test_url_for(self):
|
||||||
GLANCE_URL = 'http://glance/glanceapi/'
|
url = api_base.url_for(self.request, 'image')
|
||||||
NOVA_URL = 'http://nova/novapi/'
|
self.assertEqual(url, 'http://internal.glance.example.com:9292/v1')
|
||||||
|
|
||||||
url = api.url_for(self.request, 'image')
|
url = api_base.url_for(self.request, 'image', admin=False)
|
||||||
self.assertEqual(url, GLANCE_URL + 'internal')
|
self.assertEqual(url, 'http://internal.glance.example.com:9292/v1')
|
||||||
|
|
||||||
url = api.url_for(self.request, 'image', admin=False)
|
url = api_base.url_for(self.request, 'image', admin=True)
|
||||||
self.assertEqual(url, GLANCE_URL + 'internal')
|
self.assertEqual(url, 'http://admin.glance.example.com:9292/v1')
|
||||||
|
|
||||||
url = api.url_for(self.request, 'image', admin=True)
|
url = api_base.url_for(self.request, 'compute')
|
||||||
self.assertEqual(url, GLANCE_URL + 'admin')
|
self.assertEqual(url, 'http://internal.nova.example.com:8774/v1.0')
|
||||||
|
|
||||||
url = api.url_for(self.request, 'compute')
|
url = api_base.url_for(self.request, 'compute', admin=False)
|
||||||
self.assertEqual(url, NOVA_URL + 'internal')
|
self.assertEqual(url, 'http://internal.nova.example.com:8774/v1.0')
|
||||||
|
|
||||||
url = api.url_for(self.request, 'compute', admin=False)
|
url = api_base.url_for(self.request, 'compute', admin=True)
|
||||||
self.assertEqual(url, NOVA_URL + 'internal')
|
self.assertEqual(url, 'http://admin.nova.example.com:8774/v1.0')
|
||||||
|
|
||||||
url = api.url_for(self.request, 'compute', admin=True)
|
|
||||||
self.assertEqual(url, NOVA_URL + 'admin')
|
|
||||||
|
|
||||||
self.assertNotIn('notAnApi', self.request.user.service_catalog,
|
self.assertNotIn('notAnApi', self.request.user.service_catalog,
|
||||||
'Select a new nonexistent service catalog key')
|
'Select a new nonexistent service catalog key')
|
||||||
with self.assertRaises(exceptions.ServiceCatalogException):
|
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.
|
# under the License.
|
||||||
|
|
||||||
from django import http
|
from django import http
|
||||||
from django.contrib import messages
|
|
||||||
from django.core.urlresolvers import reverse
|
from django.core.urlresolvers import reverse
|
||||||
from keystoneclient.v2_0 import tenants as keystone_tenants
|
|
||||||
from keystoneclient import exceptions as keystone_exceptions
|
from keystoneclient import exceptions as keystone_exceptions
|
||||||
from mox import IsA
|
from mox import IsA
|
||||||
|
|
||||||
@ -33,59 +31,41 @@ SYSPANEL_INDEX_URL = reverse('horizon:syspanel:overview:index')
|
|||||||
DASH_INDEX_URL = reverse('horizon:nova:overview:index')
|
DASH_INDEX_URL = reverse('horizon:nova:overview:index')
|
||||||
|
|
||||||
|
|
||||||
class AuthViewTests(test.BaseViewTests):
|
class AuthViewTests(test.TestCase):
|
||||||
def setUp(self):
|
def setUp(self):
|
||||||
super(AuthViewTests, self).setUp()
|
super(AuthViewTests, self).setUp()
|
||||||
self.setActiveUser()
|
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):
|
def test_login_index(self):
|
||||||
res = self.client.get(reverse('horizon:auth_login'))
|
res = self.client.get(reverse('horizon:auth_login'))
|
||||||
self.assertTemplateUsed(res, 'horizon/auth/login.html')
|
self.assertTemplateUsed(res, 'horizon/auth/login.html')
|
||||||
|
|
||||||
def test_login_user_logged_in(self):
|
def test_login_user_logged_in(self):
|
||||||
self.setActiveUser(self.TEST_TOKEN, self.TEST_USER, self.TEST_TENANT,
|
self.setActiveUser(self.tokens.first().id,
|
||||||
False, self.TEST_SERVICE_CATALOG)
|
self.user.name,
|
||||||
|
self.tenant.id,
|
||||||
|
False,
|
||||||
|
self.service_catalog)
|
||||||
# Hitting the login URL directly should always give you a login page.
|
# Hitting the login URL directly should always give you a login page.
|
||||||
res = self.client.get(reverse('horizon:auth_login'))
|
res = self.client.get(reverse('horizon:auth_login'))
|
||||||
self.assertTemplateUsed(res, 'horizon/auth/login.html')
|
self.assertTemplateUsed(res, 'horizon/auth/login.html')
|
||||||
|
|
||||||
def test_login_no_tenants(self):
|
def test_login_no_tenants(self):
|
||||||
|
aToken = self.tokens.first()
|
||||||
TOKEN_ID = 1
|
|
||||||
|
|
||||||
form_data = {'method': 'Login',
|
|
||||||
'region': 'http://localhost:5000/v2.0',
|
|
||||||
'password': self.PASSWORD,
|
|
||||||
'username': self.TEST_USER}
|
|
||||||
|
|
||||||
self.mox.StubOutWithMock(api, 'token_create')
|
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')
|
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).\
|
api.tenant_list_for_token(IsA(http.HttpRequest), aToken.id).\
|
||||||
AndReturn([])
|
AndReturn([])
|
||||||
|
|
||||||
self.mox.StubOutWithMock(messages, 'error')
|
|
||||||
messages.error(IsA(http.HttpRequest),
|
|
||||||
IsA(unicode),
|
|
||||||
extra_tags=IsA(str))
|
|
||||||
|
|
||||||
self.mox.ReplayAll()
|
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)
|
res = self.client.post(reverse('horizon:auth_login'), form_data)
|
||||||
|
|
||||||
self.assertTemplateUsed(res, 'horizon/auth/login.html')
|
self.assertTemplateUsed(res, 'horizon/auth/login.html')
|
||||||
@ -93,28 +73,20 @@ class AuthViewTests(test.BaseViewTests):
|
|||||||
def test_login(self):
|
def test_login(self):
|
||||||
form_data = {'method': 'Login',
|
form_data = {'method': 'Login',
|
||||||
'region': 'http://localhost:5000/v2.0',
|
'region': 'http://localhost:5000/v2.0',
|
||||||
'password': self.PASSWORD,
|
'password': self.user.password,
|
||||||
'username': self.TEST_USER}
|
'username': self.user.name}
|
||||||
|
|
||||||
self.mox.StubOutWithMock(api, 'token_create')
|
self.mox.StubOutWithMock(api, 'token_create')
|
||||||
self.mox.StubOutWithMock(api, 'tenant_list_for_token')
|
self.mox.StubOutWithMock(api, 'tenant_list_for_token')
|
||||||
self.mox.StubOutWithMock(api, 'token_create_scoped')
|
self.mox.StubOutWithMock(api, 'token_create_scoped')
|
||||||
|
|
||||||
class FakeToken(object):
|
aToken = self.tokens.unscoped_token
|
||||||
id = 1,
|
bToken = self.tokens.scoped_token
|
||||||
user = {"id": "1",
|
|
||||||
"roles": [{"id": "1", "name": "fake"}], "name": "user"}
|
|
||||||
serviceCatalog = {}
|
|
||||||
tenant = None
|
|
||||||
|
|
||||||
aToken = api.Token(FakeToken())
|
api.token_create(IsA(http.HttpRequest), "", self.user.name,
|
||||||
bToken = aToken
|
self.user.password).AndReturn(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.tenant_list_for_token(IsA(http.HttpRequest),
|
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),
|
api.token_create_scoped(IsA(http.HttpRequest),
|
||||||
self.tenant.id,
|
self.tenant.id,
|
||||||
aToken.id).AndReturn(bToken)
|
aToken.id).AndReturn(bToken)
|
||||||
@ -127,15 +99,15 @@ class AuthViewTests(test.BaseViewTests):
|
|||||||
def test_login_invalid_credentials(self):
|
def test_login_invalid_credentials(self):
|
||||||
self.mox.StubOutWithMock(api, 'token_create')
|
self.mox.StubOutWithMock(api, 'token_create')
|
||||||
unauthorized = keystone_exceptions.Unauthorized("Invalid")
|
unauthorized = keystone_exceptions.Unauthorized("Invalid")
|
||||||
api.token_create(IsA(http.HttpRequest), "", self.TEST_USER,
|
api.token_create(IsA(http.HttpRequest), "", self.user.name,
|
||||||
self.PASSWORD).AndRaise(unauthorized)
|
self.user.password).AndRaise(unauthorized)
|
||||||
|
|
||||||
self.mox.ReplayAll()
|
self.mox.ReplayAll()
|
||||||
|
|
||||||
form_data = {'method': 'Login',
|
form_data = {'method': 'Login',
|
||||||
'region': 'http://localhost:5000/v2.0',
|
'region': 'http://localhost:5000/v2.0',
|
||||||
'password': self.PASSWORD,
|
'password': self.user.password,
|
||||||
'username': self.TEST_USER}
|
'username': self.user.name}
|
||||||
res = self.client.post(reverse('horizon:auth_login'),
|
res = self.client.post(reverse('horizon:auth_login'),
|
||||||
form_data,
|
form_data,
|
||||||
follow=True)
|
follow=True)
|
||||||
@ -147,69 +119,62 @@ class AuthViewTests(test.BaseViewTests):
|
|||||||
ex = keystone_exceptions.BadRequest('Cannot talk to keystone')
|
ex = keystone_exceptions.BadRequest('Cannot talk to keystone')
|
||||||
api.token_create(IsA(http.HttpRequest),
|
api.token_create(IsA(http.HttpRequest),
|
||||||
"",
|
"",
|
||||||
self.TEST_USER,
|
self.user.name,
|
||||||
self.PASSWORD).AndRaise(ex)
|
self.user.password).AndRaise(ex)
|
||||||
|
|
||||||
self.mox.ReplayAll()
|
self.mox.ReplayAll()
|
||||||
|
|
||||||
form_data = {'method': 'Login',
|
form_data = {'method': 'Login',
|
||||||
'region': 'http://localhost:5000/v2.0',
|
'region': 'http://localhost:5000/v2.0',
|
||||||
'password': self.PASSWORD,
|
'password': self.user.password,
|
||||||
'username': self.TEST_USER}
|
'username': self.user.name}
|
||||||
res = self.client.post(reverse('horizon:auth_login'), form_data)
|
res = self.client.post(reverse('horizon:auth_login'), form_data)
|
||||||
|
|
||||||
self.assertTemplateUsed(res, 'horizon/auth/login.html')
|
self.assertTemplateUsed(res, 'horizon/auth/login.html')
|
||||||
|
|
||||||
def test_switch_tenants_index(self):
|
def test_switch_tenants_index(self):
|
||||||
res = self.client.get(reverse('horizon:auth_switch',
|
res = self.client.get(reverse('horizon:auth_switch',
|
||||||
args=[self.TEST_TENANT]))
|
args=[self.tenant.id]))
|
||||||
|
|
||||||
self.assertRedirects(res, reverse("horizon:auth_login"))
|
self.assertRedirects(res, reverse("horizon:auth_login"))
|
||||||
|
|
||||||
def test_switch_tenants(self):
|
def test_switch_tenants(self):
|
||||||
NEW_TENANT_ID = '6'
|
tenants = self.tenants.list()
|
||||||
NEW_TENANT_NAME = 'FAKENAME'
|
|
||||||
TOKEN_ID = 1
|
|
||||||
tenants = self.TEST_CONTEXT['authorized_tenants']
|
|
||||||
|
|
||||||
aTenant = self.mox.CreateMock(api.Token)
|
tenant = self.tenants.first()
|
||||||
aTenant.id = NEW_TENANT_ID
|
token = self.tokens.unscoped_token
|
||||||
aTenant.name = NEW_TENANT_NAME
|
scoped_token = self.tokens.scoped_token
|
||||||
|
switch_to = scoped_token.tenant['id']
|
||||||
|
user = self.users.first()
|
||||||
|
|
||||||
aToken = self.mox.CreateMock(api.Token)
|
self.setActiveUser(id=user.id,
|
||||||
aToken.id = TOKEN_ID
|
token=token.id,
|
||||||
aToken.user = {'id': self.TEST_USER_ID,
|
username=user.name,
|
||||||
'name': self.TEST_USER, 'roles': [{'name': 'fake'}]}
|
tenant_id=tenant.id,
|
||||||
aToken.serviceCatalog = {}
|
service_catalog=self.service_catalog,
|
||||||
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,
|
|
||||||
authorized_tenants=tenants)
|
authorized_tenants=tenants)
|
||||||
|
|
||||||
self.mox.StubOutWithMock(api, 'token_create')
|
self.mox.StubOutWithMock(api, 'token_create')
|
||||||
self.mox.StubOutWithMock(api, 'tenant_list_for_token')
|
self.mox.StubOutWithMock(api, 'tenant_list_for_token')
|
||||||
|
|
||||||
api.token_create(IsA(http.HttpRequest), NEW_TENANT_ID, self.TEST_USER,
|
api.token_create(IsA(http.HttpRequest),
|
||||||
self.PASSWORD).AndReturn(aToken)
|
switch_to,
|
||||||
api.tenant_list_for_token(IsA(http.HttpRequest), aToken.id) \
|
user.name,
|
||||||
.AndReturn([aTenant])
|
user.password).AndReturn(scoped_token)
|
||||||
|
api.tenant_list_for_token(IsA(http.HttpRequest),
|
||||||
|
token.id).AndReturn(tenants)
|
||||||
self.mox.ReplayAll()
|
self.mox.ReplayAll()
|
||||||
|
|
||||||
form_data = {'method': 'LoginWithTenant',
|
form_data = {'method': 'LoginWithTenant',
|
||||||
'region': 'http://localhost:5000/v2.0',
|
'region': 'http://localhost:5000/v2.0',
|
||||||
'password': self.PASSWORD,
|
'username': user.name,
|
||||||
'tenant': NEW_TENANT_ID,
|
'password': user.password,
|
||||||
'username': self.TEST_USER}
|
'tenant': switch_to}
|
||||||
res = self.client.post(reverse('horizon:auth_switch',
|
switch_url = reverse('horizon:auth_switch', args=[switch_to])
|
||||||
args=[NEW_TENANT_ID]), form_data)
|
res = self.client.post(switch_url, form_data)
|
||||||
|
|
||||||
self.assertRedirectsNoFollow(res, DASH_INDEX_URL)
|
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):
|
def test_logout(self):
|
||||||
KEY = 'arbitraryKeyString'
|
KEY = 'arbitraryKeyString'
|
||||||
|
@ -191,8 +191,6 @@ class HorizonTests(BaseHorizonTests):
|
|||||||
syspanel = horizon.get_dashboard("syspanel")
|
syspanel = horizon.get_dashboard("syspanel")
|
||||||
self.assertFalse(hasattr(syspanel, "evil"))
|
self.assertFalse(hasattr(syspanel, "evil"))
|
||||||
|
|
||||||
|
|
||||||
class HorizonBaseViewTests(BaseHorizonTests, test.BaseViewTests):
|
|
||||||
def test_public(self):
|
def test_public(self):
|
||||||
users.get_user_from_request = self._real_get_user_from_request
|
users.get_user_from_request = self._real_get_user_from_request
|
||||||
settings = horizon.get_dashboard("settings")
|
settings = horizon.get_dashboard("settings")
|
||||||
@ -220,10 +218,10 @@ class HorizonBaseViewTests(BaseHorizonTests, test.BaseViewTests):
|
|||||||
# should get a 404.
|
# should get a 404.
|
||||||
new_catalog = [service for service in self.request.user.service_catalog
|
new_catalog = [service for service in self.request.user.service_catalog
|
||||||
if service['type'] != MyPanel.services[0]]
|
if service['type'] != MyPanel.services[0]]
|
||||||
tenants = self.TEST_CONTEXT['authorized_tenants']
|
tenants = self.context['authorized_tenants']
|
||||||
self.setActiveUser(token=self.TEST_TOKEN,
|
self.setActiveUser(token=self.token.id,
|
||||||
username=self.TEST_USER,
|
username=self.user.name,
|
||||||
tenant_id=self.TEST_TENANT,
|
tenant_id=self.tenant.id,
|
||||||
service_catalog=new_catalog,
|
service_catalog=new_catalog,
|
||||||
authorized_tenants=tenants)
|
authorized_tenants=tenants)
|
||||||
resp = self.client.get(panel.get_absolute_url())
|
resp = self.client.get(panel.get_absolute_url())
|
||||||
|
@ -38,11 +38,11 @@ class ContextProcessorTests(test.TestCase):
|
|||||||
self.request.user.service_catalog = self._prev_catalog
|
self.request.user.service_catalog = self._prev_catalog
|
||||||
|
|
||||||
def test_authorized_tenants(self):
|
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.request.user.authorized_tenants = None # Reset from setUp
|
||||||
self.mox.StubOutWithMock(api, 'tenant_list_for_token')
|
self.mox.StubOutWithMock(api, 'tenant_list_for_token')
|
||||||
api.tenant_list_for_token(IsA(http.HttpRequest),
|
api.tenant_list_for_token(IsA(http.HttpRequest),
|
||||||
self.TEST_TOKEN,
|
self.token.id,
|
||||||
endpoint_type='internalURL') \
|
endpoint_type='internalURL') \
|
||||||
.AndReturn(tenant_list)
|
.AndReturn(tenant_list)
|
||||||
self.mox.ReplayAll()
|
self.mox.ReplayAll()
|
||||||
@ -51,4 +51,4 @@ class ContextProcessorTests(test.TestCase):
|
|||||||
context = context_processors.horizon(self.request)
|
context = context_processors.horizon(self.request)
|
||||||
self.assertEqual(len(context['authorized_tenants']), 1)
|
self.assertEqual(len(context['authorized_tenants']), 1)
|
||||||
tenant = context['authorized_tenants'].pop()
|
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
|
import re
|
||||||
|
|
||||||
from django import dispatch, http, template
|
|
||||||
from django.utils.text import normalize_newlines
|
from django.utils.text import normalize_newlines
|
||||||
|
|
||||||
from horizon import test
|
from horizon import test
|
||||||
@ -34,5 +33,4 @@ def single_line(text):
|
|||||||
|
|
||||||
|
|
||||||
class TemplateTagTests(test.TestCase):
|
class TemplateTagTests(test.TestCase):
|
||||||
def setUp(self):
|
pass
|
||||||
super(TemplateTagTests, self).setUp()
|
|
||||||
|
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',
|
'django.contrib.messages.context_processors.messages',
|
||||||
'horizon.context_processors.horizon')
|
'horizon.context_processors.horizon')
|
||||||
|
|
||||||
|
MESSAGE_STORAGE = 'django.contrib.messages.storage.cookie.CookieStorage'
|
||||||
|
|
||||||
ROOT_URLCONF = 'horizon.tests.testurls'
|
ROOT_URLCONF = 'horizon.tests.testurls'
|
||||||
TEMPLATE_DIRS = (os.path.join(ROOT_PATH, 'tests', 'templates'))
|
TEMPLATE_DIRS = (os.path.join(ROOT_PATH, 'tests', 'templates'))
|
||||||
SITE_ID = 1
|
SITE_ID = 1
|
||||||
|
@ -27,5 +27,4 @@ def fakeView(request):
|
|||||||
'This is a fake httpresponse from a fake view for testing '
|
'This is a fake httpresponse from a fake view for testing '
|
||||||
' purposes only'
|
' purposes only'
|
||||||
'</p></body></html>')
|
'</p></body></html>')
|
||||||
|
|
||||||
return resp
|
return resp
|
||||||
|
@ -76,9 +76,6 @@ class BaseUsage(object):
|
|||||||
def get_usage_list(self, start, end):
|
def get_usage_list(self, start, end):
|
||||||
raise NotImplementedError("You must define a get_usage method.")
|
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):
|
def summarize(self, start, end):
|
||||||
if start <= end <= time.today():
|
if start <= end <= time.today():
|
||||||
# Convert to datetime.datetime just for API call.
|
# Convert to datetime.datetime just for API call.
|
||||||
|
@ -23,6 +23,8 @@ Classes and methods related to user handling in Horizon.
|
|||||||
|
|
||||||
import logging
|
import logging
|
||||||
|
|
||||||
|
from django.utils.translation import ugettext as _
|
||||||
|
|
||||||
from horizon import exceptions
|
from horizon import exceptions
|
||||||
|
|
||||||
|
|
||||||
@ -135,7 +137,8 @@ class User(object):
|
|||||||
return False
|
return False
|
||||||
|
|
||||||
def get_and_delete_messages(self):
|
def get_and_delete_messages(self):
|
||||||
""" Placeholder function for parity with
|
"""
|
||||||
|
Placeholder function for parity with
|
||||||
``django.contrib.auth.models.User``.
|
``django.contrib.auth.models.User``.
|
||||||
"""
|
"""
|
||||||
return []
|
return []
|
||||||
|
Loading…
Reference in New Issue
Block a user