Merge lp:~termie/nova/move_tests into lp:~hudson-openstack/nova/trunk

Proposed by termie
Status: Rejected
Rejected by: Eric Day
Proposed branch: lp:~termie/nova/move_tests
Merge into: lp:~hudson-openstack/nova/trunk
Diff against target: 4302 lines
To merge this branch: bzr merge lp:~termie/nova/move_tests
Reviewer Review Type Date Requested Status
Eric Day (community) Disapprove
Soren Hansen (community) Disapprove
Review via email: mp+30739@code.launchpad.net

Description of the change

Move tests out of nova directory.

Mainly so that they aren't packaged with the nova code,
but also just for cleanliness.

Had to fix a small bug in the Network unittests where it was using relative paths.

To post a comment you must log in.
Revision history for this message
Soren Hansen (soren) wrote :

I actually prefer the tests right where they are, i.e. below the namespace to which they pertain. Looking through the python packages I have installed on my system, it also seems to be the prevailing approach, so -1 from me, I'm afraid.

review: Disapprove
Revision history for this message
Soren Hansen (soren) wrote :

Oh, you should set your bzr whomi properly, by the way. "bzr help whoami" for more info.

Revision history for this message
Soren Hansen (soren) wrote :

And one last thing: bzr supports moving files (using "bzr move" or "bzr rename" (one is an alias for the other)). Please use that when moving files in the future.

Revision history for this message
termie (termie) wrote :

If that seems to be the common approach no objection here, I'm used to seeing it the other way around. A quick scan through the python repositories I have copies of on my laptop shows:

PIL --
boto -- out
bzr -- in
bzr-fastimport -- out
django -- out
eventlet -- out
google-app-engine-django -- in
gunicorn -- out
mercurial -- out
nose -- out
redis-py -- out
pylint -- ?? wtf are they doing
pyosc -- n/a
python-cloudfiles -- out
python-daemon -- n/a
python-gflags -- n/a
twisted -- in
virtualenv -- n/a
zope -- in

TOTAL:
tests in package: 4
tests outside of package: 9
no package / no test: 5

... which actually makes me inclined to believe that having it out is actually the more common way.

Reconsider?

Revision history for this message
termie (termie) wrote :

oh, forgot to delete PIL from the list, I had a source zip file.

Revision history for this message
termie (termie) wrote :

swift also has them out.

If you will approve I will remake a branch using move/rename to clean this up.

Revision history for this message
Monty Taylor (mordred) wrote :

-----BEGIN PGP SIGNED MESSAGE-----
Hash: SHA1

On 07/26/2010 03:10 PM, termie wrote:
> If that seems to be the common approach no objection here, I'm used to seeing it the other way around. A quick scan through the python repositories I have copies of on my laptop shows:
>
> PIL --
> boto -- out
> bzr -- in
> bzr-fastimport -- out
> django -- out
> eventlet -- out
> google-app-engine-django -- in
> gunicorn -- out
> mercurial -- out
> nose -- out
> redis-py -- out
> pylint -- ?? wtf are they doing
> pyosc -- n/a
> python-cloudfiles -- out
> python-daemon -- n/a
> python-gflags -- n/a
> twisted -- in
> virtualenv -- n/a
> zope -- in
>
>
> TOTAL:
> tests in package: 4
> tests outside of package: 9
> no package / no test: 5
>
> ... which actually makes me inclined to believe that having it out is actually the more common way.
>
> Reconsider?

I can make good arguments for either... my personal preference is out
though. My arguments for out:

When I run pep8/pylint, I run it on nova/ ... if you have tests out,
then you don't have to keep them clean. :)

They don't get installed.

Tests are in a location, which means you can find them - and then they
consume the package to be tested just like anyone else. If they're in,
then they're sort of _part_ of that package, which means they may have a
slightly different view of things than things which are not part of that
package.

HOWEVER - I would vote that whichever we pick we should apply to both
swift and nova and then make that our best-practice for openstack
projects. Swift is currently out. Nova is currently in.
-----BEGIN PGP SIGNATURE-----
Version: GnuPG v1.4.10 (GNU/Linux)
Comment: Using GnuPG with Mozilla - http://enigmail.mozdev.org/

iEYEARECAAYFAkxOCW4ACgkQ2Jv7/VK1RgHgXQCguzts8sb7B2iTalDx/LHgFXPC
V0YAnRtvnwWn5b3829GmfcXaAi+FoLTP
=Pgtq
-----END PGP SIGNATURE-----

Revision history for this message
Eric Day (eday) wrote :

My preference would probably by out as well, and don't install them. I would still require pylint/pep8 to pass for everything in tests/ though, no reason to be sloppy there. :)

If we do go this way, definitely need to recreate this branch with proper move/rename though.

review: Needs Fixing
Revision history for this message
Soren Hansen (soren) wrote :

On Mon, Jul 26, 2010 at 10:18:59PM -0000, Monty Taylor wrote:
> I can make good arguments for either... my personal preference is out
> though. My arguments for out:
>
> When I run pep8/pylint, I run it on nova/ ... if you have tests out,
> then you don't have to keep them clean. :)
>
> They don't get installed.

To me, both of those are good reasons why we should keep them in. :)

Revision history for this message
Soren Hansen (soren) wrote :

To elaborate a bit: Tests are code. It should be just as pretty, neat, and wonderful as any other piece of code.

I want them to be installed so that people can run tests on the installed code as well. There might be environmental things that factor into the tests, so I think it's very valuable to be able to tell people who report bugs to run the test suite, for instance.

In fact, I think I'd much rather move the tests for e.g. the objectstore into nova/objectstore/tests. It feels perfectly natural to me that tests pertaining to nova.objectstore are in nova.objectstore.tests.

Revision history for this message
termie (termie) wrote :

> To elaborate a bit: Tests are code. It should be just as pretty, neat, and
> wonderful as any other piece of code.
>

I agree on the cleanliness, but that has little to do with where the tests live.

> I want them to be installed so that people can run tests on the installed code
> as well. There might be environmental things that factor into the tests, so I
> think it's very valuable to be able to tell people who report bugs to run the
> test suite, for instance.

I think this is a bogus statement, I don't think anybody has ever convinced a 'regular' user to run a test suite and I think any developer who is going to go through the trouble is fine downloading a package or checkout (or more likely going back to the package or checkout they've already downloaded and left in a folder somewhere) to get at the tests.

>
> In fact, I think I'd much rather move the tests for e.g. the objectstore into
> nova/objectstore/tests. It feels perfectly natural to me that tests pertaining
> to nova.objectstore are in nova.objectstore.tests.

That to me feels obscene, my opinion on subdirectories/submodules is that they exist to hide things, putting tests further down a hole only makes you forget it is there and makes it that much more difficult to keep track of and to get to when you are changing something: making things separate that have no need to be separate for the sake of having little bins to put everything in just means more bins to sort through when you need to find something.

Test-code is self-similar, much more so than it is similar to, say, objectstore's code, and more than it is to anything else in the nova directory. Test code should generally look the same as other test code and, I think, should be thought of as an entity outside of nova twisting the switches and pulling the nobs to make sure all the lights come on at the right times.

In general I don't want people thinking that the test code is part of the nova install, I don't want people referencing the test code from their code, it is stuff that should never be run on a production machine and has no place there. We also already have 'smoketests' that are outside the repo, so there is some precedent.

Revision history for this message
Soren Hansen (soren) wrote :
Download full text (4.1 KiB)

On Tue, Jul 27, 2010 at 12:59:43PM -0000, termie wrote:
>> To elaborate a bit: Tests are code. It should be just as pretty,
>> neat, and wonderful as any other piece of code.
> I agree on the cleanliness, but that has little to do with where the
> tests live.

I agree. It was a response to Monty's comment about not having pylint
shout at him about tests if they were not inside nova/.

>> I want them to be installed so that people can run tests on the
>> installed code as well. There might be environmental things that
>> factor into the tests, so I think it's very valuable to be able to
>> tell people who report bugs to run the test suite, for instance.
> I think this is a bogus statement, I don't think anybody has ever
> convinced a 'regular' user to run a test suite

I don't think I understand why you find it so far-fetched to tell a user
to "please run nova-run-tests" if they've reported a bug that looks odd
in some way. It can help identify broken dependencies, user-modified
code (people tend to tinker with stuff if they can, and since this is
all Python, bugs are just a simple $EDITOR away).

> and I think any developer who is going to go through the trouble is
> fine downloading a package or checkout (or more likely going back to
> the package or checkout they've already downloaded and left in a
> folder somewhere) to get at the tests.

I expect people doing vcs checkouts or otherwise installing from source
will be a rarity compared to people who install from packages from their
respective Linux distro.

>> In fact, I think I'd much rather move the tests for e.g. the
>> objectstore into nova/objectstore/tests. It feels perfectly natural
>> to me that tests pertaining to nova.objectstore are in
>> nova.objectstore.tests.
> That to me feels obscene, my opinion on subdirectories/submodules is
> that they exist to hide things,

In that case, I think this conversation is going to difficult :) I treat
subdirectories much like shelves in my book case. I use both to keep
things orderly and arrange stuff according to some set of criteria. I
try to put stuff on the shelves instead of throwing them all on the
floor in front of bookcase. Sure, if the directory structure was
random, it'd be counterproductive, but if it's consistent and
predictable, it's a huge help in organising stuff.

> putting tests further down a hole only makes you forget it is there
> and makes it that much more difficult to keep track of and to get to
> when you are changing something: making things separate that have no
> need to be separate for the sake of having little bins to put
> everything in just means more bins to sort through when you need to
> find something.

I'm completely missing how this is an argument for putting the tests
_all the way outside the tree_.

> Test-code is self-similar, much more so than it is similar to, say,
> objectstore's code, and more than it is to anything else in the nova
> directory. Test code should generally look the same as other test code
> and, I think, should be thought of as an entity outside of nova
> twisting the switches and pulling the nobs to make sure all the lights
> come on at the right times.

I fundamentally disagree. I...

Read more...

Revision history for this message
termie (termie) wrote :
Download full text (8.5 KiB)

> >> I want them to be installed so that people can run tests on the
> >> installed code as well. There might be environmental things that
> >> factor into the tests, so I think it's very valuable to be able to
> >> tell people who report bugs to run the test suite, for instance.
> > I think this is a bogus statement, I don't think anybody has ever
> > convinced a 'regular' user to run a test suite
>
> I don't think I understand why you find it so far-fetched to tell a user
> to "please run nova-run-tests" if they've reported a bug that looks odd
> in some way. It can help identify broken dependencies, user-modified
> code (people tend to tinker with stuff if they can, and since this is
> all Python, bugs are just a simple $EDITOR away).

I agree with the sentiment, but

(a) I don't think unit tests should be testing system compatibility or touching the system much at all, though they still detect missing / invalid python dependencies from time to time. While it is true that we have a mixed bag of tests at the moment, I hope that in the future we will eventually manage to separate the integration tests from the unit tests.

(b) In the event that we have some tests that were relevant towards actually exercising the system they would also have to be pretty sure to "above all, do no harm" if we are expecting some poor user in an already unknown broken state to run them and not have his system worse off afterwards, at the moment the tests litter rather heavily.

(c) I think this use case is much better targeted by a specific set of checks that look for system compatibility / sanity and make sure they are non-invasive. And we would write tests for those checks to make sure they work that wouldn't be run when you run the checks. And since they non-invasive we can run them on install, or any other time.

>
> > and I think any developer who is going to go through the trouble is
> > fine downloading a package or checkout (or more likely going back to
> > the package or checkout they've already downloaded and left in a
> > folder somewhere) to get at the tests.
>
> I expect people doing vcs checkouts or otherwise installing from source
> will be a rarity compared to people who install from packages from their
> respective Linux distro.

Agreed that they are the the rarity, my point was that other users are less likely to be useful candidates for running a test suite.

>
> >> In fact, I think I'd much rather move the tests for e.g. the
> >> objectstore into nova/objectstore/tests. It feels perfectly natural
> >> to me that tests pertaining to nova.objectstore are in
> >> nova.objectstore.tests.
> > That to me feels obscene, my opinion on subdirectories/submodules is
> > that they exist to hide things,
>
> In that case, I think this conversation is going to difficult :) I treat
> subdirectories much like shelves in my book case. I use both to keep
> things orderly and arrange stuff according to some set of criteria. I
> try to put stuff on the shelves instead of throwing them all on the
> floor in front of bookcase. Sure, if the directory structure was
> random, it'd be counterproductive, but if it's consistent and
> predictable, it's a huge h...

Read more...

Revision history for this message
Soren Hansen (soren) wrote :

On Tue, Jul 27, 2010 at 11:36:49PM -0000, termie wrote:
> A better one is your office, you keep the things you use often out in
> the open and you put the things you use less often and want out of
> sight in a tray or on a shelf (like a book, because you are unlikely
> to be using a book often). On my desk you will generally find a phone,
> a usb cable, my wallet, my keys and a variety of junk that could be
> put in a box but I would just have to take it back out of the box
> every couple days when I want to use it. Somewhere in the room will be
> drawers (what do you have in your drawers? do you even open them? I
> have ping pong paddles) and shelves. It's messy but if you were to
> attempt to find something on my desk it would be obvious how to
> proceed and you'd likely do it seconds.

Would your rather we put e.g. objectstore tests in
nova/objectstore/tests.py? That should also address your prominence
concern.

--
Soren Hansen
Ubuntu Developer
http://www.ubuntu.com/

Revision history for this message
Eric Day (eday) wrote :

So, this thread has died. We should decide one way or another so we don't have an outstanding merge request. Anyone feel strongly enough to decide one way or another? The difference between 'nova/tests' vs just 'tests' seems pretty minor.

I actually like the idea of having the tests right alongside the files they test as Soren suggests. For example:

nova/objectstire/test_handler.py
...
nova/objectstore/test.py # tests integration between all objectstore components
nova/rpc/test_rpc.py
...

I can see the argument that this litters tests too much, but the test code is just as real as the production code. :) Having a directory with the same structure for tests (nova/tests/...) seems redundant.

Thoughts?

Revision history for this message
Soren Hansen (soren) wrote :

Yes, test code is just as real as production code, but I also group production code into directories to keep things orderly. Such is my taste :)

...but sure, if this is what it takes to keep it inside nova/, let's go for it.

Revision history for this message
Eric Day (eday) wrote :

Marking this as rejected. We'll keep tests as is since this stalled, and if we did want to move the tests out, we would need a new patch that uses bzr renames instead of rm/add anyways.

review: Disapprove

Unmerged revisions

155. By termie

Move tests out of nova directory.

Mainly so that they aren't packaged with the nova code,
but also just for cleanliness.

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== removed directory 'nova/tests'
2=== removed directory 'nova/tests/CA'
3=== removed file 'nova/tests/CA/cacert.pem'
4--- nova/tests/CA/cacert.pem 2010-05-28 06:05:26 +0000
5+++ nova/tests/CA/cacert.pem 1970-01-01 00:00:00 +0000
6@@ -1,17 +0,0 @@
7------BEGIN CERTIFICATE-----
8-MIICyzCCAjSgAwIBAgIJANiqHZUcbScCMA0GCSqGSIb3DQEBBAUAME4xEjAQBgNV
9-BAoTCU5PVkEgUk9PVDEWMBQGA1UEBxMNTW91bnRhaW4gVmlldzETMBEGA1UECBMK
10-Q2FsaWZvcm5pYTELMAkGA1UEBhMCVVMwHhcNMTAwNTI4MDExOTI1WhcNMTEwNTI4
11-MDExOTI1WjBOMRIwEAYDVQQKEwlOT1ZBIFJPT1QxFjAUBgNVBAcTDU1vdW50YWlu
12-IFZpZXcxEzARBgNVBAgTCkNhbGlmb3JuaWExCzAJBgNVBAYTAlVTMIGfMA0GCSqG
13-SIb3DQEBAQUAA4GNADCBiQKBgQDobUnq8rpXA/HQZ2Uu9Me3SlqCayz3ws2wtvFQ
14-koWPUzpriIYPkpprz2EaVu07Zb9uJHvjcoY07nYntl4jR8S7PH4XZhlVFn8AQWzs
15-iThU4KJF71UfVM00dDrarSgVpyOIcFXO3iUvLoJj7+RUPjrWdLuJoMqnhicgLeHZ
16-LAZ8ewIDAQABo4GwMIGtMAwGA1UdEwQFMAMBAf8wHQYDVR0OBBYEFMh1RMlTVtt8
17-EdESYpsTU08r0FnpMH4GA1UdIwR3MHWAFMh1RMlTVtt8EdESYpsTU08r0FnpoVKk
18-UDBOMRIwEAYDVQQKEwlOT1ZBIFJPT1QxFjAUBgNVBAcTDU1vdW50YWluIFZpZXcx
19-EzARBgNVBAgTCkNhbGlmb3JuaWExCzAJBgNVBAYTAlVTggkA2KodlRxtJwIwDQYJ
20-KoZIhvcNAQEEBQADgYEAq+YCgflK36HCdodNu2ya3O6UDRUE2dW8n96tAOmvHqmR
21-v38k8GIW0pjWDo+lZYnFmeJYd+QGcJl9fLzXxffV5k+rNCfr/gEYtznWLNUX7AZB
22-b/VC7L+yK9qz08C8n51TslXaf3fUGkfkQxsvEP7+hi0qavdd/8eTbdheWahYwWg=
23------END CERTIFICATE-----
24
25=== removed directory 'nova/tests/CA/private'
26=== removed file 'nova/tests/CA/private/cakey.pem'
27--- nova/tests/CA/private/cakey.pem 2010-05-28 06:05:26 +0000
28+++ nova/tests/CA/private/cakey.pem 1970-01-01 00:00:00 +0000
29@@ -1,15 +0,0 @@
30------BEGIN RSA PRIVATE KEY-----
31-MIICXQIBAAKBgQDobUnq8rpXA/HQZ2Uu9Me3SlqCayz3ws2wtvFQkoWPUzpriIYP
32-kpprz2EaVu07Zb9uJHvjcoY07nYntl4jR8S7PH4XZhlVFn8AQWzsiThU4KJF71Uf
33-VM00dDrarSgVpyOIcFXO3iUvLoJj7+RUPjrWdLuJoMqnhicgLeHZLAZ8ewIDAQAB
34-AoGBANQonmZ2Nh2jniFrn/LiwULP/ho6Fov6J6N8+n1focaYZCUwM58XZRmv7KUM
35-X/PuBnVVnDibm2HJodTSJM/zfODnGO15kdmJ9X23FkkdTyuvphO5tYF0ONARXdfX
36-9LbPcLYA14VSCZCKCye6mbv/xi0C/s7q6ZBoMl7XaeD9hgUxAkEA9lxQY/ZxcLV0
37-Ae5I2spBbtuXEGns11YnKnppc59RrAono1gaDeYY2WZRwztIcD6VtUv7qkzH6ubo
38-shAG4fvnPQJBAPGFaDODs2ckPvxnILEbjpnZXGQqDCpQ3sVJ6nfu+qdAWS92ESNo
39-Y6DC8zFjFaQFbKy6Jxr1VsvYDXhF8cmy7hcCQHkLElSLGWGPRdhNA268QTn+mlJu
40-OPf0VHoCex1cAfzNYHxZJTP/AeaO501NK2I63cOd+aDK6M75dQtH5JnT8uECQQCg
41-jVydkhk6oV+1jiCvW3BKWbIPa9w2bRgJ8n8JRzYc5Kvk3wm5jfVcsvvTgtip9mkt
42-0XmZdCpEy9T4dRasTGP1AkBMhShiVP7+P+SIQlZtSn8ckTt9G6cefEjxsv0kVFZe
43-SjkUO0ZifahF8r3Q1eEUSzdXEvicEwONvcpc7MLwfSD7
44------END RSA PRIVATE KEY-----
45
46=== removed file 'nova/tests/__init__.py'
47--- nova/tests/__init__.py 2010-07-15 15:52:11 +0000
48+++ nova/tests/__init__.py 1970-01-01 00:00:00 +0000
49@@ -1,31 +0,0 @@
50-# vim: tabstop=4 shiftwidth=4 softtabstop=4
51-
52-# Copyright 2010 United States Government as represented by the
53-# Administrator of the National Aeronautics and Space Administration.
54-# All Rights Reserved.
55-#
56-# Licensed under the Apache License, Version 2.0 (the "License"); you may
57-# not use this file except in compliance with the License. You may obtain
58-# a copy of the License at
59-#
60-# http://www.apache.org/licenses/LICENSE-2.0
61-#
62-# Unless required by applicable law or agreed to in writing, software
63-# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
64-# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
65-# License for the specific language governing permissions and limitations
66-# under the License.
67-
68-"""
69-:mod:`nova.tests` -- Nova Unittests
70-=====================================================
71-
72-.. automodule:: nova.tests
73- :platform: Unix
74-.. moduleauthor:: Jesse Andrews <jesse@ansolabs.com>
75-.. moduleauthor:: Devin Carlen <devin.carlen@gmail.com>
76-.. moduleauthor:: Vishvananda Ishaya <vishvananda@yahoo.com>
77-.. moduleauthor:: Joshua McKenty <joshua@cognition.ca>
78-.. moduleauthor:: Manish Singh <yosh@gimp.org>
79-.. moduleauthor:: Andy Smith <andy@anarkystic.com>
80-"""
81
82=== removed file 'nova/tests/access_unittest.py'
83--- nova/tests/access_unittest.py 2010-07-15 23:13:48 +0000
84+++ nova/tests/access_unittest.py 1970-01-01 00:00:00 +0000
85@@ -1,165 +0,0 @@
86-# vim: tabstop=4 shiftwidth=4 softtabstop=4
87-
88-# Copyright 2010 United States Government as represented by the
89-# Administrator of the National Aeronautics and Space Administration.
90-# All Rights Reserved.
91-#
92-# Licensed under the Apache License, Version 2.0 (the "License"); you may
93-# not use this file except in compliance with the License. You may obtain
94-# a copy of the License at
95-#
96-# http://www.apache.org/licenses/LICENSE-2.0
97-#
98-# Unless required by applicable law or agreed to in writing, software
99-# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
100-# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
101-# License for the specific language governing permissions and limitations
102-# under the License.
103-
104-import unittest
105-import logging
106-
107-from nova import exception
108-from nova import flags
109-from nova import test
110-from nova.auth.users import UserManager
111-from nova.auth import rbac
112-
113-
114-FLAGS = flags.FLAGS
115-class Context(object):
116- pass
117-
118-class AccessTestCase(test.BaseTestCase):
119- def setUp(self):
120- super(AccessTestCase, self).setUp()
121- FLAGS.fake_libvirt = True
122- FLAGS.fake_storage = True
123- um = UserManager.instance()
124- # Make test users
125- try:
126- self.testadmin = um.create_user('testadmin')
127- except Exception, err:
128- logging.error(str(err))
129- try:
130- self.testpmsys = um.create_user('testpmsys')
131- except: pass
132- try:
133- self.testnet = um.create_user('testnet')
134- except: pass
135- try:
136- self.testsys = um.create_user('testsys')
137- except: pass
138- # Assign some rules
139- try:
140- um.add_role('testadmin', 'cloudadmin')
141- except: pass
142- try:
143- um.add_role('testpmsys', 'sysadmin')
144- except: pass
145- try:
146- um.add_role('testnet', 'netadmin')
147- except: pass
148- try:
149- um.add_role('testsys', 'sysadmin')
150- except: pass
151-
152- # Make a test project
153- try:
154- self.project = um.create_project('testproj', 'testpmsys', 'a test project', ['testpmsys', 'testnet', 'testsys'])
155- except: pass
156- try:
157- self.project.add_role(self.testnet, 'netadmin')
158- except: pass
159- try:
160- self.project.add_role(self.testsys, 'sysadmin')
161- except: pass
162- self.context = Context()
163- self.context.project = self.project
164- #user is set in each test
165-
166- def tearDown(self):
167- um = UserManager.instance()
168- # Delete the test project
169- um.delete_project('testproj')
170- # Delete the test user
171- um.delete_user('testadmin')
172- um.delete_user('testpmsys')
173- um.delete_user('testnet')
174- um.delete_user('testsys')
175- super(AccessTestCase, self).tearDown()
176-
177- def test_001_allow_all(self):
178- self.context.user = self.testadmin
179- self.assertTrue(self._allow_all(self.context))
180- self.context.user = self.testpmsys
181- self.assertTrue(self._allow_all(self.context))
182- self.context.user = self.testnet
183- self.assertTrue(self._allow_all(self.context))
184- self.context.user = self.testsys
185- self.assertTrue(self._allow_all(self.context))
186-
187- def test_002_allow_none(self):
188- self.context.user = self.testadmin
189- self.assertTrue(self._allow_none(self.context))
190- self.context.user = self.testpmsys
191- self.assertRaises(exception.NotAuthorized, self._allow_none, self.context)
192- self.context.user = self.testnet
193- self.assertRaises(exception.NotAuthorized, self._allow_none, self.context)
194- self.context.user = self.testsys
195- self.assertRaises(exception.NotAuthorized, self._allow_none, self.context)
196-
197- def test_003_allow_project_manager(self):
198- self.context.user = self.testadmin
199- self.assertTrue(self._allow_project_manager(self.context))
200- self.context.user = self.testpmsys
201- self.assertTrue(self._allow_project_manager(self.context))
202- self.context.user = self.testnet
203- self.assertRaises(exception.NotAuthorized, self._allow_project_manager, self.context)
204- self.context.user = self.testsys
205- self.assertRaises(exception.NotAuthorized, self._allow_project_manager, self.context)
206-
207- def test_004_allow_sys_and_net(self):
208- self.context.user = self.testadmin
209- self.assertTrue(self._allow_sys_and_net(self.context))
210- self.context.user = self.testpmsys # doesn't have the per project sysadmin
211- self.assertRaises(exception.NotAuthorized, self._allow_sys_and_net, self.context)
212- self.context.user = self.testnet
213- self.assertTrue(self._allow_sys_and_net(self.context))
214- self.context.user = self.testsys
215- self.assertTrue(self._allow_sys_and_net(self.context))
216-
217- def test_005_allow_sys_no_pm(self):
218- self.context.user = self.testadmin
219- self.assertTrue(self._allow_sys_no_pm(self.context))
220- self.context.user = self.testpmsys
221- self.assertRaises(exception.NotAuthorized, self._allow_sys_no_pm, self.context)
222- self.context.user = self.testnet
223- self.assertRaises(exception.NotAuthorized, self._allow_sys_no_pm, self.context)
224- self.context.user = self.testsys
225- self.assertTrue(self._allow_sys_no_pm(self.context))
226-
227- @rbac.allow('all')
228- def _allow_all(self, context):
229- return True
230-
231- @rbac.allow('none')
232- def _allow_none(self, context):
233- return True
234-
235- @rbac.allow('projectmanager')
236- def _allow_project_manager(self, context):
237- return True
238-
239- @rbac.allow('sysadmin', 'netadmin')
240- def _allow_sys_and_net(self, context):
241- return True
242-
243- @rbac.allow('sysadmin')
244- @rbac.deny('projectmanager')
245- def _allow_sys_no_pm(self, context):
246- return True
247-
248-if __name__ == "__main__":
249- # TODO: Implement use_fake as an option
250- unittest.main()
251
252=== removed file 'nova/tests/api_integration.py'
253--- nova/tests/api_integration.py 2010-07-15 23:13:48 +0000
254+++ nova/tests/api_integration.py 1970-01-01 00:00:00 +0000
255@@ -1,54 +0,0 @@
256-# vim: tabstop=4 shiftwidth=4 softtabstop=4
257-
258-# Copyright 2010 United States Government as represented by the
259-# Administrator of the National Aeronautics and Space Administration.
260-# All Rights Reserved.
261-#
262-# Licensed under the Apache License, Version 2.0 (the "License"); you may
263-# not use this file except in compliance with the License. You may obtain
264-# a copy of the License at
265-#
266-# http://www.apache.org/licenses/LICENSE-2.0
267-#
268-# Unless required by applicable law or agreed to in writing, software
269-# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
270-# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
271-# License for the specific language governing permissions and limitations
272-# under the License.
273-
274-
275-import boto
276-from boto.ec2.regioninfo import RegionInfo
277-import unittest
278-
279-
280-ACCESS_KEY = 'fake'
281-SECRET_KEY = 'fake'
282-CLC_IP = '127.0.0.1'
283-CLC_PORT = 8773
284-REGION = 'test'
285-
286-def get_connection():
287- return boto.connect_ec2 (
288- aws_access_key_id=ACCESS_KEY,
289- aws_secret_access_key=SECRET_KEY,
290- is_secure=False,
291- region=RegionInfo(None, REGION, CLC_IP),
292- port=CLC_PORT,
293- path='/services/Cloud',
294- debug=99
295- )
296-
297-class APIIntegrationTests(unittest.TestCase):
298- def test_001_get_all_images(self):
299- conn = get_connection()
300- res = conn.get_all_images()
301-
302-
303-if __name__ == '__main__':
304- unittest.main()
305-
306-#print conn.get_all_key_pairs()
307-#print conn.create_key_pair
308-#print conn.create_security_group('name', 'description')
309-
310
311=== removed file 'nova/tests/api_unittest.py'
312--- nova/tests/api_unittest.py 2010-07-15 23:13:48 +0000
313+++ nova/tests/api_unittest.py 1970-01-01 00:00:00 +0000
314@@ -1,199 +0,0 @@
315-# vim: tabstop=4 shiftwidth=4 softtabstop=4
316-
317-# Copyright 2010 United States Government as represented by the
318-# Administrator of the National Aeronautics and Space Administration.
319-# All Rights Reserved.
320-#
321-# Licensed under the Apache License, Version 2.0 (the "License"); you may
322-# not use this file except in compliance with the License. You may obtain
323-# a copy of the License at
324-#
325-# http://www.apache.org/licenses/LICENSE-2.0
326-#
327-# Unless required by applicable law or agreed to in writing, software
328-# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
329-# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
330-# License for the specific language governing permissions and limitations
331-# under the License.
332-
333-import boto
334-from boto.ec2 import regioninfo
335-import httplib
336-import random
337-import StringIO
338-from tornado import httpserver
339-from twisted.internet import defer
340-
341-from nova import flags
342-from nova import test
343-from nova.auth import users
344-from nova.endpoint import api
345-from nova.endpoint import cloud
346-
347-
348-FLAGS = flags.FLAGS
349-
350-
351-# NOTE(termie): These are a bunch of helper methods and classes to short
352-# circuit boto calls and feed them into our tornado handlers,
353-# it's pretty damn circuitous so apologies if you have to fix
354-# a bug in it
355-def boto_to_tornado(method, path, headers, data, host, connection=None):
356- """ translate boto requests into tornado requests
357-
358- connection should be a FakeTornadoHttpConnection instance
359- """
360- headers = httpserver.HTTPHeaders()
361- for k, v in headers.iteritems():
362- headers[k] = v
363-
364- req = httpserver.HTTPRequest(method=method,
365- uri=path,
366- headers=headers,
367- body=data,
368- host=host,
369- remote_ip='127.0.0.1',
370- connection=connection)
371- return req
372-
373-
374-def raw_to_httpresponse(s):
375- """ translate a raw tornado http response into an httplib.HTTPResponse """
376- sock = FakeHttplibSocket(s)
377- resp = httplib.HTTPResponse(sock)
378- resp.begin()
379- return resp
380-
381-
382-class FakeHttplibSocket(object):
383- """ a fake socket implementation for httplib.HTTPResponse, trivial """
384- def __init__(self, s):
385- self.fp = StringIO.StringIO(s)
386-
387- def makefile(self, mode, other):
388- return self.fp
389-
390-
391-class FakeTornadoStream(object):
392- """ a fake stream to satisfy tornado's assumptions, trivial """
393- def set_close_callback(self, f):
394- pass
395-
396-
397-class FakeTornadoConnection(object):
398- """ a fake connection object for tornado to pass to its handlers
399-
400- web requests are expected to write to this as they get data and call
401- finish when they are done with the request, we buffer the writes and
402- kick off a callback when it is done so that we can feed the result back
403- into boto.
404- """
405- def __init__(self, d):
406- self.d = d
407- self._buffer = StringIO.StringIO()
408-
409- def write(self, chunk):
410- self._buffer.write(chunk)
411-
412- def finish(self):
413- s = self._buffer.getvalue()
414- self.d.callback(s)
415-
416- xheaders = None
417-
418- @property
419- def stream(self):
420- return FakeTornadoStream()
421-
422-
423-class FakeHttplibConnection(object):
424- """ a fake httplib.HTTPConnection for boto to use
425-
426- requests made via this connection actually get translated and routed into
427- our tornado app, we then wait for the response and turn it back into
428- the httplib.HTTPResponse that boto expects.
429- """
430- def __init__(self, app, host, is_secure=False):
431- self.app = app
432- self.host = host
433- self.deferred = defer.Deferred()
434-
435- def request(self, method, path, data, headers):
436- req = boto_to_tornado
437- conn = FakeTornadoConnection(self.deferred)
438- request = boto_to_tornado(connection=conn,
439- method=method,
440- path=path,
441- headers=headers,
442- data=data,
443- host=self.host)
444- handler = self.app(request)
445- self.deferred.addCallback(raw_to_httpresponse)
446-
447- def getresponse(self):
448- @defer.inlineCallbacks
449- def _waiter():
450- result = yield self.deferred
451- defer.returnValue(result)
452- d = _waiter()
453- # NOTE(termie): defer.returnValue above should ensure that
454- # this deferred has already been called by the time
455- # we get here, we are going to cheat and return
456- # the result of the callback
457- return d.result
458-
459- def close(self):
460- pass
461-
462-
463-class ApiEc2TestCase(test.BaseTestCase):
464- def setUp(self):
465- super(ApiEc2TestCase, self).setUp()
466-
467- self.users = users.UserManager.instance()
468- self.cloud = cloud.CloudController()
469-
470- self.host = '127.0.0.1'
471-
472- self.app = api.APIServerApplication({'Cloud': self.cloud})
473- self.ec2 = boto.connect_ec2(
474- aws_access_key_id='fake',
475- aws_secret_access_key='fake',
476- is_secure=False,
477- region=regioninfo.RegionInfo(None, 'test', self.host),
478- port=FLAGS.cc_port,
479- path='/services/Cloud')
480-
481- self.mox.StubOutWithMock(self.ec2, 'new_http_connection')
482-
483- def expect_http(self, host=None, is_secure=False):
484- http = FakeHttplibConnection(
485- self.app, '%s:%d' % (self.host, FLAGS.cc_port), False)
486- self.ec2.new_http_connection(host, is_secure).AndReturn(http)
487- return http
488-
489- def test_describe_instances(self):
490- self.expect_http()
491- self.mox.ReplayAll()
492- try:
493- self.users.create_user('fake', 'fake', 'fake')
494- except Exception, _err:
495- pass # User may already exist
496- self.assertEqual(self.ec2.get_all_instances(), [])
497- self.users.delete_user('fake')
498-
499-
500- def test_get_all_key_pairs(self):
501- self.expect_http()
502- self.mox.ReplayAll()
503- keyname = "".join(random.choice("sdiuisudfsdcnpaqwertasd") for x in range(random.randint(4, 8)))
504- try:
505- self.users.create_user('fake', 'fake', 'fake')
506- except Exception, _err:
507- pass # User may already exist
508- self.users.generate_key_pair('fake', keyname)
509-
510- rv = self.ec2.get_all_key_pairs()
511- self.assertTrue(filter(lambda k: k.name == keyname, rv))
512- self.users.delete_user('fake')
513-
514
515=== removed directory 'nova/tests/bundle'
516=== removed file 'nova/tests/bundle/1mb.manifest.xml'
517--- nova/tests/bundle/1mb.manifest.xml 2010-05-28 06:05:26 +0000
518+++ nova/tests/bundle/1mb.manifest.xml 1970-01-01 00:00:00 +0000
519@@ -1,1 +0,0 @@
520-<?xml version="1.0" ?><manifest><version>2007-10-10</version><bundler><name>euca-tools</name><version>1.2</version><release>31337</release></bundler><machine_configuration><architecture>x86_64</architecture></machine_configuration><image><name>1mb</name><user>42</user><type>machine</type><digest algorithm="SHA1">da39a3ee5e6b4b0d3255bfef95601890afd80709</digest><size>1048576</size><bundled_size>1136</bundled_size><ec2_encrypted_key algorithm="AES-128-CBC">33a2ea00dc64083dd9a10eb5e233635b42a7beb1670ab75452087d9de74c60aba1cd27c136fda56f62beb581de128fb1f10d072b9e556fd25e903107a57827c21f6ee8a93a4ff55b11311fcef217e3eefb07e81f71e88216f43b4b54029c1f2549f2925a839a73947d2d5aeecec4a62ece4af9156d557ae907978298296d9915</ec2_encrypted_key><user_encrypted_key algorithm="AES-128-CBC">4c11147fd8caf92447e90ce339928933d7579244c2f8ffb07cc0ea35f8738da8b90eff6c7a49671a84500e993e9462e4c36d5c19c0b3a2b397d035b4c0cce742b58e12552175d81d129b0425e9f71ebacb9aeb539fa9dd2ac36749fb82876f6902e5fb24b6ec19f35ec4c20acd50437fd30966e99c4d9a0647577970a8fa3023</user_encrypted_key><ec2_encrypted_iv>14bd082c9715f071160c69bbfb070f51d2ba1076775f1d988ccde150e515088156b248e4b5a64e46c4fe064feeeedfe14511f7fde478a51acb89f9b2f6c84b60593e5c3f792ba6b01fed9bf2158fdac03086374883b39d13a3ca74497eeaaf579fc3f26effc73bfd9446a2a8c4061f0874bfaca058905180e22d3d8881551cb3</ec2_encrypted_iv><user_encrypted_iv>8f7606f19f00e4e19535dd234b66b31b77e9c7bad3885d9c9efa75c863631fd4f82a009e17d789066d9cc6032a436f05384832f6d9a3283d3e63eab04fa0da5c8c87db9b17e854e842c3fb416507d067a266b44538125ce732e486098e8ebd1ca91fa3079f007fce7d14957a9b7e57282407ead3c6eb68fe975df3d83190021b</user_encrypted_iv><parts count="2"><part index="0"><filename>1mb.part.0</filename><digest algorithm="SHA1">c4413423cf7a57e71187e19bfd5cd4b514a64283</digest></part><part index="1"><filename>1mb.part.1</filename><digest algorithm="SHA1">9d4262e6589393d09a11a0332af169887bc2e57d</digest></part></parts></image><signature>4e00b5ba28114dda4a9df7eeae94be847ec46117a09a1cbe41e578660642f0660dda1776b39fb3bf826b6cfec019e2a5e9c566728d186b7400ebc989a30670eb1db26ce01e68bd9d3f31290370077a85b81c66b63c1e0d5499bac115c06c17a21a81b6d3a67ebbce6c17019095af7ab07f3796c708cc843e58efc12ddc788c5e</signature></manifest>
521\ No newline at end of file
522
523=== removed file 'nova/tests/bundle/1mb.part.0'
524Binary files nova/tests/bundle/1mb.part.0 2010-05-28 06:05:26 +0000 and nova/tests/bundle/1mb.part.0 1970-01-01 00:00:00 +0000 differ
525=== removed file 'nova/tests/bundle/1mb.part.1'
526--- nova/tests/bundle/1mb.part.1 2010-05-28 06:05:26 +0000
527+++ nova/tests/bundle/1mb.part.1 1970-01-01 00:00:00 +0000
528@@ -1,1 +0,0 @@
529-­´ˆà«€ç‰°Ƴ
530¡ÀiDHW̽×JÈ8ïrV¼³h§X’·@Yj“~Ø
531·Gû5û 3Nt«˜•H6Ñ$§Ëgö™é Lá¢+³æ¤X†pm¬@,øŽ>7ÚÊ×užp¼ aü`¥V2X@£#á¶
532\ No newline at end of file
533
534=== removed file 'nova/tests/cloud_unittest.py'
535--- nova/tests/cloud_unittest.py 2010-07-15 23:13:48 +0000
536+++ nova/tests/cloud_unittest.py 1970-01-01 00:00:00 +0000
537@@ -1,163 +0,0 @@
538-# vim: tabstop=4 shiftwidth=4 softtabstop=4
539-
540-# Copyright 2010 United States Government as represented by the
541-# Administrator of the National Aeronautics and Space Administration.
542-# All Rights Reserved.
543-#
544-# Licensed under the Apache License, Version 2.0 (the "License"); you may
545-# not use this file except in compliance with the License. You may obtain
546-# a copy of the License at
547-#
548-# http://www.apache.org/licenses/LICENSE-2.0
549-#
550-# Unless required by applicable law or agreed to in writing, software
551-# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
552-# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
553-# License for the specific language governing permissions and limitations
554-# under the License.
555-
556-import logging
557-import StringIO
558-import time
559-from tornado import ioloop
560-from twisted.internet import defer
561-import unittest
562-from xml.etree import ElementTree
563-
564-from nova import flags
565-from nova import rpc
566-from nova import test
567-from nova.auth import users
568-from nova.compute import node
569-from nova.endpoint import api
570-from nova.endpoint import cloud
571-
572-
573-FLAGS = flags.FLAGS
574-
575-
576-class CloudTestCase(test.BaseTestCase):
577- def setUp(self):
578- super(CloudTestCase, self).setUp()
579- self.flags(fake_libvirt=True,
580- fake_storage=True,
581- fake_users=True)
582-
583- self.conn = rpc.Connection.instance()
584- logging.getLogger().setLevel(logging.DEBUG)
585-
586- # set up our cloud
587- self.cloud = cloud.CloudController()
588- self.cloud_consumer = rpc.AdapterConsumer(connection=self.conn,
589- topic=FLAGS.cloud_topic,
590- proxy=self.cloud)
591- self.injected.append(self.cloud_consumer.attach_to_tornado(self.ioloop))
592-
593- # set up a node
594- self.node = node.Node()
595- self.node_consumer = rpc.AdapterConsumer(connection=self.conn,
596- topic=FLAGS.compute_topic,
597- proxy=self.node)
598- self.injected.append(self.node_consumer.attach_to_tornado(self.ioloop))
599-
600- try:
601- users.UserManager.instance().create_user('admin', 'admin', 'admin')
602- except: pass
603- admin = users.UserManager.instance().get_user('admin')
604- project = users.UserManager.instance().create_project('proj', 'admin', 'proj')
605- self.context = api.APIRequestContext(handler=None,project=project,user=admin)
606-
607- def tearDown(self):
608- users.UserManager.instance().delete_project('proj')
609- users.UserManager.instance().delete_user('admin')
610-
611- def test_console_output(self):
612- if FLAGS.fake_libvirt:
613- logging.debug("Can't test instances without a real virtual env.")
614- return
615- instance_id = 'foo'
616- inst = yield self.node.run_instance(instance_id)
617- output = yield self.cloud.get_console_output(self.context, [instance_id])
618- logging.debug(output)
619- self.assert_(output)
620- rv = yield self.node.terminate_instance(instance_id)
621-
622- def test_run_instances(self):
623- if FLAGS.fake_libvirt:
624- logging.debug("Can't test instances without a real virtual env.")
625- return
626- image_id = FLAGS.default_image
627- instance_type = FLAGS.default_instance_type
628- max_count = 1
629- kwargs = {'image_id': image_id,
630- 'instance_type': instance_type,
631- 'max_count': max_count}
632- rv = yield self.cloud.run_instances(self.context, **kwargs)
633- # TODO: check for proper response
634- instance = rv['reservationSet'][0][rv['reservationSet'][0].keys()[0]][0]
635- logging.debug("Need to watch instance %s until it's running..." % instance['instance_id'])
636- while True:
637- rv = yield defer.succeed(time.sleep(1))
638- info = self.cloud._get_instance(instance['instance_id'])
639- logging.debug(info['state'])
640- if info['state'] == node.Instance.RUNNING:
641- break
642- self.assert_(rv)
643-
644- if not FLAGS.fake_libvirt:
645- time.sleep(45) # Should use boto for polling here
646- for reservations in rv['reservationSet']:
647- # for res_id in reservations.keys():
648- # logging.debug(reservations[res_id])
649- # for instance in reservations[res_id]:
650- for instance in reservations[reservations.keys()[0]]:
651- logging.debug("Terminating instance %s" % instance['instance_id'])
652- rv = yield self.node.terminate_instance(instance['instance_id'])
653-
654- def test_instance_update_state(self):
655- def instance(num):
656- return {
657- 'reservation_id': 'r-1',
658- 'instance_id': 'i-%s' % num,
659- 'image_id': 'ami-%s' % num,
660- 'private_dns_name': '10.0.0.%s' % num,
661- 'dns_name': '10.0.0%s' % num,
662- 'ami_launch_index': str(num),
663- 'instance_type': 'fake',
664- 'availability_zone': 'fake',
665- 'key_name': None,
666- 'kernel_id': 'fake',
667- 'ramdisk_id': 'fake',
668- 'groups': ['default'],
669- 'product_codes': None,
670- 'state': 0x01,
671- 'user_data': ''
672- }
673- rv = self.cloud._format_instances(self.context)
674- self.assert_(len(rv['reservationSet']) == 0)
675-
676- # simulate launch of 5 instances
677- # self.cloud.instances['pending'] = {}
678- #for i in xrange(5):
679- # inst = instance(i)
680- # self.cloud.instances['pending'][inst['instance_id']] = inst
681-
682- #rv = self.cloud._format_instances(self.admin)
683- #self.assert_(len(rv['reservationSet']) == 1)
684- #self.assert_(len(rv['reservationSet'][0]['instances_set']) == 5)
685- # report 4 nodes each having 1 of the instances
686- #for i in xrange(4):
687- # self.cloud.update_state('instances', {('node-%s' % i): {('i-%s' % i): instance(i)}})
688-
689- # one instance should be pending still
690- #self.assert_(len(self.cloud.instances['pending'].keys()) == 1)
691-
692- # check that the reservations collapse
693- #rv = self.cloud._format_instances(self.admin)
694- #self.assert_(len(rv['reservationSet']) == 1)
695- #self.assert_(len(rv['reservationSet'][0]['instances_set']) == 5)
696-
697- # check that we can get metadata for each instance
698- #for i in xrange(4):
699- # data = self.cloud.get_metadata(instance(i)['private_dns_name'])
700- # self.assert_(data['meta-data']['ami-id'] == 'ami-%s' % i)
701
702=== removed file 'nova/tests/fake_flags.py'
703--- nova/tests/fake_flags.py 2010-07-15 15:52:11 +0000
704+++ nova/tests/fake_flags.py 1970-01-01 00:00:00 +0000
705@@ -1,28 +0,0 @@
706-# vim: tabstop=4 shiftwidth=4 softtabstop=4
707-
708-# Copyright 2010 United States Government as represented by the
709-# Administrator of the National Aeronautics and Space Administration.
710-# All Rights Reserved.
711-#
712-# Licensed under the Apache License, Version 2.0 (the "License"); you may
713-# not use this file except in compliance with the License. You may obtain
714-# a copy of the License at
715-#
716-# http://www.apache.org/licenses/LICENSE-2.0
717-#
718-# Unless required by applicable law or agreed to in writing, software
719-# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
720-# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
721-# License for the specific language governing permissions and limitations
722-# under the License.
723-
724-from nova import flags
725-
726-FLAGS = flags.FLAGS
727-
728-FLAGS.fake_libvirt = True
729-FLAGS.fake_storage = True
730-FLAGS.fake_rabbit = True
731-FLAGS.fake_network = True
732-FLAGS.fake_users = True
733-FLAGS.verbose = True
734
735=== removed file 'nova/tests/future_unittest.py'
736--- nova/tests/future_unittest.py 2010-07-15 23:13:48 +0000
737+++ nova/tests/future_unittest.py 1970-01-01 00:00:00 +0000
738@@ -1,75 +0,0 @@
739-# vim: tabstop=4 shiftwidth=4 softtabstop=4
740-
741-# Copyright 2010 United States Government as represented by the
742-# Administrator of the National Aeronautics and Space Administration.
743-# All Rights Reserved.
744-#
745-# Licensed under the Apache License, Version 2.0 (the "License"); you may
746-# not use this file except in compliance with the License. You may obtain
747-# a copy of the License at
748-#
749-# http://www.apache.org/licenses/LICENSE-2.0
750-#
751-# Unless required by applicable law or agreed to in writing, software
752-# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
753-# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
754-# License for the specific language governing permissions and limitations
755-# under the License.
756-
757-import logging
758-import mox
759-import StringIO
760-import time
761-from tornado import ioloop
762-from twisted.internet import defer
763-import unittest
764-from xml.etree import ElementTree
765-
766-from nova import cloud
767-from nova import exception
768-from nova import flags
769-from nova import node
770-from nova import rpc
771-from nova import test
772-
773-
774-FLAGS = flags.FLAGS
775-
776-
777-class AdminTestCase(test.BaseTestCase):
778- def setUp(self):
779- super(AdminTestCase, self).setUp()
780- self.flags(fake_libvirt=True,
781- fake_rabbit=True)
782-
783- self.conn = rpc.Connection.instance()
784-
785- logging.getLogger().setLevel(logging.INFO)
786-
787- # set up our cloud
788- self.cloud = cloud.CloudController()
789- self.cloud_consumer = rpc.AdapterConsumer(connection=self.conn,
790- topic=FLAGS.cloud_topic,
791- proxy=self.cloud)
792- self.injected.append(self.cloud_consumer.attach_to_tornado(self.ioloop))
793-
794- # set up a node
795- self.node = node.Node()
796- self.node_consumer = rpc.AdapterConsumer(connection=self.conn,
797- topic=FLAGS.compute_topic,
798- proxy=self.node)
799- self.injected.append(self.node_consumer.attach_to_tornado(self.ioloop))
800-
801- def test_flush_terminated(self):
802- # Launch an instance
803-
804- # Wait until it's running
805-
806- # Terminate it
807-
808- # Wait until it's terminated
809-
810- # Flush terminated nodes
811-
812- # ASSERT that it's gone
813- pass
814
815=== removed file 'nova/tests/model_unittest.py'
816--- nova/tests/model_unittest.py 2010-07-15 23:13:48 +0000
817+++ nova/tests/model_unittest.py 1970-01-01 00:00:00 +0000
818@@ -1,206 +0,0 @@
819-# vim: tabstop=4 shiftwidth=4 softtabstop=4
820-
821-# Copyright 2010 United States Government as represented by the
822-# Administrator of the National Aeronautics and Space Administration.
823-# All Rights Reserved.
824-#
825-# Licensed under the Apache License, Version 2.0 (the "License"); you may
826-# not use this file except in compliance with the License. You may obtain
827-# a copy of the License at
828-#
829-# http://www.apache.org/licenses/LICENSE-2.0
830-#
831-# Unless required by applicable law or agreed to in writing, software
832-# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
833-# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
834-# License for the specific language governing permissions and limitations
835-# under the License.
836-
837-import logging
838-import time
839-from twisted.internet import defer
840-
841-from nova import exception
842-from nova import flags
843-from nova import test
844-from nova import utils
845-from nova.compute import model
846-from nova.compute import node
847-
848-
849-FLAGS = flags.FLAGS
850-
851-
852-class ModelTestCase(test.TrialTestCase):
853- def setUp(self):
854- super(ModelTestCase, self).setUp()
855- self.flags(fake_libvirt=True,
856- fake_storage=True,
857- fake_users=True)
858-
859- def tearDown(self):
860- model.Instance('i-test').destroy()
861- model.Host('testhost').destroy()
862- model.Daemon('testhost', 'nova-testdaemon').destroy()
863-
864- def create_instance(self):
865- inst = model.Instance('i-test')
866- inst['reservation_id'] = 'r-test'
867- inst['launch_time'] = '10'
868- inst['user_id'] = 'fake'
869- inst['project_id'] = 'fake'
870- inst['instance_type'] = 'm1.tiny'
871- inst['node_name'] = FLAGS.node_name
872- inst['mac_address'] = utils.generate_mac()
873- inst['ami_launch_index'] = 0
874- inst.save()
875- return inst
876-
877- def create_host(self):
878- host = model.Host('testhost')
879- host.save()
880- return host
881-
882- def create_daemon(self):
883- daemon = model.Daemon('testhost', 'nova-testdaemon')
884- daemon.save()
885- return daemon
886-
887- @defer.inlineCallbacks
888- def test_create_instance(self):
889- """store with create_instace, then test that a load finds it"""
890- instance = yield self.create_instance()
891- old = yield model.Instance(instance.identifier)
892- self.assertFalse(old.is_new_record())
893-
894- @defer.inlineCallbacks
895- def test_delete_instance(self):
896- """create, then destroy, then make sure loads a new record"""
897- instance = yield self.create_instance()
898- yield instance.destroy()
899- newinst = yield model.Instance('i-test')
900- self.assertTrue(newinst.is_new_record())
901-
902- @defer.inlineCallbacks
903- def test_instance_added_to_set(self):
904- """create, then check that it is listed for the project"""
905- instance = yield self.create_instance()
906- found = False
907- for x in model.InstanceDirectory().all:
908- if x.identifier == 'i-test':
909- found = True
910- self.assert_(found)
911-
912- @defer.inlineCallbacks
913- def test_instance_associates_project(self):
914- """create, then check that it is listed for the project"""
915- instance = yield self.create_instance()
916- found = False
917- for x in model.InstanceDirectory().by_project(instance.project):
918- if x.identifier == 'i-test':
919- found = True
920- self.assert_(found)
921-
922- @defer.inlineCallbacks
923- def test_host_class_finds_hosts(self):
924- host = yield self.create_host()
925- self.assertEqual('testhost', model.Host.lookup('testhost').identifier)
926-
927- @defer.inlineCallbacks
928- def test_host_class_doesnt_find_missing_hosts(self):
929- rv = yield model.Host.lookup('woahnelly')
930- self.assertEqual(None, rv)
931-
932- @defer.inlineCallbacks
933- def test_create_host(self):
934- """store with create_host, then test that a load finds it"""
935- host = yield self.create_host()
936- old = yield model.Host(host.identifier)
937- self.assertFalse(old.is_new_record())
938-
939- @defer.inlineCallbacks
940- def test_delete_host(self):
941- """create, then destroy, then make sure loads a new record"""
942- instance = yield self.create_host()
943- yield instance.destroy()
944- newinst = yield model.Host('testhost')
945- self.assertTrue(newinst.is_new_record())
946-
947- @defer.inlineCallbacks
948- def test_host_added_to_set(self):
949- """create, then check that it is included in list"""
950- instance = yield self.create_host()
951- found = False
952- for x in model.Host.all():
953- if x.identifier == 'testhost':
954- found = True
955- self.assert_(found)
956-
957- @defer.inlineCallbacks
958- def test_create_daemon_two_args(self):
959- """create a daemon with two arguments"""
960- d = yield self.create_daemon()
961- d = model.Daemon('testhost', 'nova-testdaemon')
962- self.assertFalse(d.is_new_record())
963-
964- @defer.inlineCallbacks
965- def test_create_daemon_single_arg(self):
966- """Create a daemon using the combined host:bin format"""
967- d = yield model.Daemon("testhost:nova-testdaemon")
968- d.save()
969- d = model.Daemon('testhost:nova-testdaemon')
970- self.assertFalse(d.is_new_record())
971-
972- @defer.inlineCallbacks
973- def test_equality_of_daemon_single_and_double_args(self):
974- """Create a daemon using the combined host:bin arg, find with 2"""
975- d = yield model.Daemon("testhost:nova-testdaemon")
976- d.save()
977- d = model.Daemon('testhost', 'nova-testdaemon')
978- self.assertFalse(d.is_new_record())
979-
980- @defer.inlineCallbacks
981- def test_equality_daemon_of_double_and_single_args(self):
982- """Create a daemon using the combined host:bin arg, find with 2"""
983- d = yield self.create_daemon()
984- d = model.Daemon('testhost:nova-testdaemon')
985- self.assertFalse(d.is_new_record())
986-
987- @defer.inlineCallbacks
988- def test_delete_daemon(self):
989- """create, then destroy, then make sure loads a new record"""
990- instance = yield self.create_daemon()
991- yield instance.destroy()
992- newinst = yield model.Daemon('testhost', 'nova-testdaemon')
993- self.assertTrue(newinst.is_new_record())
994-
995- @defer.inlineCallbacks
996- def test_daemon_heartbeat(self):
997- """Create a daemon, sleep, heartbeat, check for update"""
998- d = yield self.create_daemon()
999- ts = d['updated_at']
1000- time.sleep(2)
1001- d.heartbeat()
1002- d2 = model.Daemon('testhost', 'nova-testdaemon')
1003- ts2 = d2['updated_at']
1004- self.assert_(ts2 > ts)
1005-
1006- @defer.inlineCallbacks
1007- def test_daemon_added_to_set(self):
1008- """create, then check that it is included in list"""
1009- instance = yield self.create_daemon()
1010- found = False
1011- for x in model.Daemon.all():
1012- if x.identifier == 'testhost:nova-testdaemon':
1013- found = True
1014- self.assert_(found)
1015-
1016- @defer.inlineCallbacks
1017- def test_daemon_associates_host(self):
1018- """create, then check that it is listed for the host"""
1019- instance = yield self.create_daemon()
1020- found = False
1021- for x in model.Daemon.by_host('testhost'):
1022- if x.identifier == 'testhost:nova-testdaemon':
1023- found = True
1024- self.assertTrue(found)
1025
1026=== removed file 'nova/tests/network_unittest.py'
1027--- nova/tests/network_unittest.py 2010-07-21 02:57:31 +0000
1028+++ nova/tests/network_unittest.py 1970-01-01 00:00:00 +0000
1029@@ -1,219 +0,0 @@
1030-# vim: tabstop=4 shiftwidth=4 softtabstop=4
1031-
1032-# Copyright 2010 United States Government as represented by the
1033-# Administrator of the National Aeronautics and Space Administration.
1034-# All Rights Reserved.
1035-#
1036-# Licensed under the Apache License, Version 2.0 (the "License"); you may
1037-# not use this file except in compliance with the License. You may obtain
1038-# a copy of the License at
1039-#
1040-# http://www.apache.org/licenses/LICENSE-2.0
1041-#
1042-# Unless required by applicable law or agreed to in writing, software
1043-# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
1044-# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
1045-# License for the specific language governing permissions and limitations
1046-# under the License.
1047-
1048-import IPy
1049-import os
1050-import logging
1051-
1052-from nova import flags
1053-from nova import test
1054-from nova import utils
1055-from nova.auth import users
1056-from nova.compute import network
1057-from nova.compute.exception import NoMoreAddresses
1058-
1059-FLAGS = flags.FLAGS
1060-
1061-class NetworkTestCase(test.TrialTestCase):
1062- def setUp(self):
1063- super(NetworkTestCase, self).setUp()
1064- self.flags(fake_libvirt=True,
1065- fake_storage=True,
1066- fake_network=True,
1067- network_size=32)
1068- logging.getLogger().setLevel(logging.DEBUG)
1069- self.manager = users.UserManager.instance()
1070- self.dnsmasq = FakeDNSMasq()
1071- try:
1072- self.manager.create_user('netuser', 'netuser', 'netuser')
1073- except: pass
1074- for i in range(0, 6):
1075- name = 'project%s' % i
1076- if not self.manager.get_project(name):
1077- self.manager.create_project(name, 'netuser', name)
1078- self.network = network.PublicNetworkController()
1079-
1080- def tearDown(self):
1081- super(NetworkTestCase, self).tearDown()
1082- for i in range(0, 6):
1083- name = 'project%s' % i
1084- self.manager.delete_project(name)
1085- self.manager.delete_user('netuser')
1086-
1087- def test_public_network_allocation(self):
1088- pubnet = IPy.IP(flags.FLAGS.public_range)
1089- address = self.network.allocate_ip("netuser", "project0", "public")
1090- self.assertTrue(IPy.IP(address) in pubnet)
1091- self.assertTrue(IPy.IP(address) in self.network.network)
1092-
1093- def test_allocate_deallocate_ip(self):
1094- address = network.allocate_ip(
1095- "netuser", "project0", utils.generate_mac())
1096- logging.debug("Was allocated %s" % (address))
1097- net = network.get_project_network("project0", "default")
1098- self.assertEqual(True, is_in_project(address, "project0"))
1099- mac = utils.generate_mac()
1100- hostname = "test-host"
1101- self.dnsmasq.issue_ip(mac, address, hostname, net.bridge_name)
1102- rv = network.deallocate_ip(address)
1103-
1104- # Doesn't go away until it's dhcp released
1105- self.assertEqual(True, is_in_project(address, "project0"))
1106-
1107- self.dnsmasq.release_ip(mac, address, hostname, net.bridge_name)
1108- self.assertEqual(False, is_in_project(address, "project0"))
1109-
1110- def test_range_allocation(self):
1111- mac = utils.generate_mac()
1112- secondmac = utils.generate_mac()
1113- hostname = "test-host"
1114- address = network.allocate_ip(
1115- "netuser", "project0", mac)
1116- secondaddress = network.allocate_ip(
1117- "netuser", "project1", secondmac)
1118- net = network.get_project_network("project0", "default")
1119- secondnet = network.get_project_network("project1", "default")
1120-
1121- self.assertEqual(True, is_in_project(address, "project0"))
1122- self.assertEqual(True, is_in_project(secondaddress, "project1"))
1123- self.assertEqual(False, is_in_project(address, "project1"))
1124-
1125- # Addresses are allocated before they're issued
1126- self.dnsmasq.issue_ip(mac, address, hostname, net.bridge_name)
1127- self.dnsmasq.issue_ip(secondmac, secondaddress,
1128- hostname, secondnet.bridge_name)
1129-
1130- rv = network.deallocate_ip(address)
1131- self.dnsmasq.release_ip(mac, address, hostname, net.bridge_name)
1132- self.assertEqual(False, is_in_project(address, "project0"))
1133-
1134- # First address release shouldn't affect the second
1135- self.assertEqual(True, is_in_project(secondaddress, "project1"))
1136-
1137- rv = network.deallocate_ip(secondaddress)
1138- self.dnsmasq.release_ip(secondmac, secondaddress,
1139- hostname, secondnet.bridge_name)
1140- self.assertEqual(False, is_in_project(secondaddress, "project1"))
1141-
1142- def test_subnet_edge(self):
1143- secondaddress = network.allocate_ip("netuser", "project0",
1144- utils.generate_mac())
1145- hostname = "toomany-hosts"
1146- for project in range(1,5):
1147- project_id = "project%s" % (project)
1148- mac = utils.generate_mac()
1149- mac2 = utils.generate_mac()
1150- mac3 = utils.generate_mac()
1151- address = network.allocate_ip(
1152- "netuser", project_id, mac)
1153- address2 = network.allocate_ip(
1154- "netuser", project_id, mac2)
1155- address3 = network.allocate_ip(
1156- "netuser", project_id, mac3)
1157- self.assertEqual(False, is_in_project(address, "project0"))
1158- self.assertEqual(False, is_in_project(address2, "project0"))
1159- self.assertEqual(False, is_in_project(address3, "project0"))
1160- rv = network.deallocate_ip(address)
1161- rv = network.deallocate_ip(address2)
1162- rv = network.deallocate_ip(address3)
1163- net = network.get_project_network(project_id, "default")
1164- self.dnsmasq.release_ip(mac, address, hostname, net.bridge_name)
1165- self.dnsmasq.release_ip(mac2, address2, hostname, net.bridge_name)
1166- self.dnsmasq.release_ip(mac3, address3, hostname, net.bridge_name)
1167- net = network.get_project_network("project0", "default")
1168- rv = network.deallocate_ip(secondaddress)
1169- self.dnsmasq.release_ip(mac, address, hostname, net.bridge_name)
1170-
1171- def test_release_before_deallocate(self):
1172- pass
1173-
1174- def test_deallocate_before_issued(self):
1175- pass
1176-
1177- def test_too_many_addresses(self):
1178- """
1179- Here, we test that a proper NoMoreAddresses exception is raised.
1180-
1181- However, the number of available IP addresses depends on the test
1182- environment's setup.
1183-
1184- Network size is set in test fixture's setUp method.
1185-
1186- There are FLAGS.cnt_vpn_clients addresses reserved for VPN (NUM_RESERVED_VPN_IPS)
1187-
1188- And there are NUM_STATIC_IPS that are always reserved by Nova for the necessary
1189- services (gateway, CloudPipe, etc)
1190-
1191- So we should get flags.network_size - (NUM_STATIC_IPS +
1192- NUM_PREALLOCATED_IPS +
1193- NUM_RESERVED_VPN_IPS)
1194- usable addresses
1195- """
1196- net = network.get_project_network("project0", "default")
1197-
1198- # Determine expected number of available IP addresses
1199- num_static_ips = net.num_static_ips
1200- num_preallocated_ips = len(net.hosts.keys())
1201- num_reserved_vpn_ips = flags.FLAGS.cnt_vpn_clients
1202- num_available_ips = flags.FLAGS.network_size - (num_static_ips + num_preallocated_ips + num_reserved_vpn_ips)
1203-
1204- hostname = "toomany-hosts"
1205- macs = {}
1206- addresses = {}
1207- for i in range(0, (num_available_ips - 1)):
1208- macs[i] = utils.generate_mac()
1209- addresses[i] = network.allocate_ip("netuser", "project0", macs[i])
1210- self.dnsmasq.issue_ip(macs[i], addresses[i], hostname, net.bridge_name)
1211-
1212- self.assertRaises(NoMoreAddresses, network.allocate_ip, "netuser", "project0", utils.generate_mac())
1213-
1214- for i in range(0, (num_available_ips - 1)):
1215- rv = network.deallocate_ip(addresses[i])
1216- self.dnsmasq.release_ip(macs[i], addresses[i], hostname, net.bridge_name)
1217-
1218-def is_in_project(address, project_id):
1219- return address in network.get_project_network(project_id).list_addresses()
1220-
1221-def _get_project_addresses(project_id):
1222- project_addresses = []
1223- for addr in network.get_project_network(project_id).list_addresses():
1224- project_addresses.append(addr)
1225- return project_addresses
1226-
1227-def binpath(script):
1228- return os.path.abspath(os.path.join(__file__, "../../../bin", script))
1229-
1230-class FakeDNSMasq(object):
1231- def issue_ip(self, mac, ip, hostname, interface):
1232- cmd = "%s add %s %s %s" % (binpath('nova-dhcpbridge'),
1233- mac, ip, hostname)
1234- env = {'DNSMASQ_INTERFACE': interface,
1235- 'TESTING' : '1',
1236- 'FLAGFILE' : FLAGS.dhcpbridge_flagfile}
1237- (out, err) = utils.execute(cmd, addl_env=env)
1238- logging.debug("ISSUE_IP: %s, %s " % (out, err))
1239-
1240- def release_ip(self, mac, ip, hostname, interface):
1241- cmd = "%s del %s %s %s" % (binpath('nova-dhcpbridge'),
1242- mac, ip, hostname)
1243- env = {'DNSMASQ_INTERFACE': interface,
1244- 'TESTING' : '1',
1245- 'FLAGFILE' : FLAGS.dhcpbridge_flagfile}
1246- (out, err) = utils.execute(cmd, addl_env=env)
1247- logging.debug("RELEASE_IP: %s, %s " % (out, err))
1248-
1249
1250=== removed file 'nova/tests/node_unittest.py'
1251--- nova/tests/node_unittest.py 2010-07-15 23:13:48 +0000
1252+++ nova/tests/node_unittest.py 1970-01-01 00:00:00 +0000
1253@@ -1,128 +0,0 @@
1254-# vim: tabstop=4 shiftwidth=4 softtabstop=4
1255-
1256-# Copyright 2010 United States Government as represented by the
1257-# Administrator of the National Aeronautics and Space Administration.
1258-# All Rights Reserved.
1259-#
1260-# Licensed under the Apache License, Version 2.0 (the "License"); you may
1261-# not use this file except in compliance with the License. You may obtain
1262-# a copy of the License at
1263-#
1264-# http://www.apache.org/licenses/LICENSE-2.0
1265-#
1266-# Unless required by applicable law or agreed to in writing, software
1267-# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
1268-# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
1269-# License for the specific language governing permissions and limitations
1270-# under the License.
1271-
1272-import logging
1273-import time
1274-from twisted.internet import defer
1275-from xml.etree import ElementTree
1276-
1277-from nova import exception
1278-from nova import flags
1279-from nova import test
1280-from nova import utils
1281-from nova.compute import model
1282-from nova.compute import node
1283-
1284-
1285-FLAGS = flags.FLAGS
1286-
1287-
1288-class InstanceXmlTestCase(test.TrialTestCase):
1289- # @defer.inlineCallbacks
1290- def test_serialization(self):
1291- # TODO: Reimplement this, it doesn't make sense in redis-land
1292- return
1293-
1294- # instance_id = 'foo'
1295- # first_node = node.Node()
1296- # inst = yield first_node.run_instance(instance_id)
1297- #
1298- # # force the state so that we can verify that it changes
1299- # inst._s['state'] = node.Instance.NOSTATE
1300- # xml = inst.toXml()
1301- # self.assert_(ElementTree.parse(StringIO.StringIO(xml)))
1302- #
1303- # second_node = node.Node()
1304- # new_inst = node.Instance.fromXml(second_node._conn, pool=second_node._pool, xml=xml)
1305- # self.assertEqual(new_inst.state, node.Instance.RUNNING)
1306- # rv = yield first_node.terminate_instance(instance_id)
1307-
1308-
1309-class NodeConnectionTestCase(test.TrialTestCase):
1310- def setUp(self):
1311- logging.getLogger().setLevel(logging.DEBUG)
1312- super(NodeConnectionTestCase, self).setUp()
1313- self.flags(fake_libvirt=True,
1314- fake_storage=True,
1315- fake_users=True)
1316- self.node = node.Node()
1317-
1318- def create_instance(self):
1319- instdir = model.InstanceDirectory()
1320- inst = instdir.new()
1321- # TODO(ja): add ami, ari, aki, user_data
1322- inst['reservation_id'] = 'r-fakeres'
1323- inst['launch_time'] = '10'
1324- inst['user_id'] = 'fake'
1325- inst['project_id'] = 'fake'
1326- inst['instance_type'] = 'm1.tiny'
1327- inst['node_name'] = FLAGS.node_name
1328- inst['mac_address'] = utils.generate_mac()
1329- inst['ami_launch_index'] = 0
1330- inst.save()
1331- return inst['instance_id']
1332-
1333- @defer.inlineCallbacks
1334- def test_run_describe_terminate(self):
1335- instance_id = self.create_instance()
1336-
1337- rv = yield self.node.run_instance(instance_id)
1338-
1339- rv = yield self.node.describe_instances()
1340- logging.info("Running instances: %s", rv)
1341- self.assertEqual(rv[instance_id].name, instance_id)
1342-
1343- rv = yield self.node.terminate_instance(instance_id)
1344-
1345- rv = yield self.node.describe_instances()
1346- logging.info("After terminating instances: %s", rv)
1347- self.assertEqual(rv, {})
1348-
1349- @defer.inlineCallbacks
1350- def test_reboot(self):
1351- instance_id = self.create_instance()
1352- rv = yield self.node.run_instance(instance_id)
1353-
1354- rv = yield self.node.describe_instances()
1355- self.assertEqual(rv[instance_id].name, instance_id)
1356-
1357- yield self.node.reboot_instance(instance_id)
1358-
1359- rv = yield self.node.describe_instances()
1360- self.assertEqual(rv[instance_id].name, instance_id)
1361- rv = yield self.node.terminate_instance(instance_id)
1362-
1363- @defer.inlineCallbacks
1364- def test_console_output(self):
1365- instance_id = self.create_instance()
1366- rv = yield self.node.run_instance(instance_id)
1367-
1368- console = yield self.node.get_console_output(instance_id)
1369- self.assert_(console)
1370- rv = yield self.node.terminate_instance(instance_id)
1371-
1372- @defer.inlineCallbacks
1373- def test_run_instance_existing(self):
1374- instance_id = self.create_instance()
1375- rv = yield self.node.run_instance(instance_id)
1376-
1377- rv = yield self.node.describe_instances()
1378- self.assertEqual(rv[instance_id].name, instance_id)
1379-
1380- self.assertRaises(exception.Error, self.node.run_instance, instance_id)
1381- rv = yield self.node.terminate_instance(instance_id)
1382
1383=== removed file 'nova/tests/objectstore_unittest.py'
1384--- nova/tests/objectstore_unittest.py 2010-07-15 23:13:48 +0000
1385+++ nova/tests/objectstore_unittest.py 1970-01-01 00:00:00 +0000
1386@@ -1,203 +0,0 @@
1387-# vim: tabstop=4 shiftwidth=4 softtabstop=4
1388-
1389-# Copyright 2010 United States Government as represented by the
1390-# Administrator of the National Aeronautics and Space Administration.
1391-# All Rights Reserved.
1392-#
1393-# Licensed under the Apache License, Version 2.0 (the "License"); you may
1394-# not use this file except in compliance with the License. You may obtain
1395-# a copy of the License at
1396-#
1397-# http://www.apache.org/licenses/LICENSE-2.0
1398-#
1399-# Unless required by applicable law or agreed to in writing, software
1400-# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
1401-# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
1402-# License for the specific language governing permissions and limitations
1403-# under the License.
1404-
1405-import glob
1406-import hashlib
1407-import logging
1408-import os
1409-import shutil
1410-import tempfile
1411-
1412-from nova import flags
1413-from nova import objectstore
1414-from nova import test
1415-from nova.auth import users
1416-
1417-
1418-FLAGS = flags.FLAGS
1419-
1420-oss_tempdir = tempfile.mkdtemp(prefix='test_oss-')
1421-
1422-
1423-# delete tempdirs from previous runs (we don't delete after test to allow
1424-# checking the contents after running tests)
1425-# TODO: This fails on the test box with a permission denied error
1426-# Also, doing these things in a global tempdir means that different runs of
1427-# the test suite on the same box could clobber each other.
1428-#for path in glob.glob(os.path.abspath(os.path.join(oss_tempdir, '../test_oss-*'))):
1429-# if path != oss_tempdir:
1430-# shutil.rmtree(path)
1431-
1432-
1433-# create bucket/images path
1434-os.makedirs(os.path.join(oss_tempdir, 'images'))
1435-os.makedirs(os.path.join(oss_tempdir, 'buckets'))
1436-
1437-class ObjectStoreTestCase(test.BaseTestCase):
1438- def setUp(self):
1439- super(ObjectStoreTestCase, self).setUp()
1440- self.flags(fake_users=True,
1441- buckets_path=os.path.join(oss_tempdir, 'buckets'),
1442- images_path=os.path.join(oss_tempdir, 'images'),
1443- ca_path=os.path.join(os.path.dirname(__file__), 'CA'))
1444- logging.getLogger().setLevel(logging.DEBUG)
1445-
1446- self.um = users.UserManager.instance()
1447- try:
1448- self.um.create_user('user1')
1449- except: pass
1450- try:
1451- self.um.create_user('user2')
1452- except: pass
1453- try:
1454- self.um.create_user('admin_user', admin=True)
1455- except: pass
1456- try:
1457- self.um.create_project('proj1', 'user1', 'a proj', ['user1'])
1458- except: pass
1459- try:
1460- self.um.create_project('proj2', 'user2', 'a proj', ['user2'])
1461- except: pass
1462- class Context(object): pass
1463- self.context = Context()
1464-
1465- def tearDown(self):
1466- self.um.delete_project('proj1')
1467- self.um.delete_project('proj2')
1468- self.um.delete_user('user1')
1469- self.um.delete_user('user2')
1470- self.um.delete_user('admin_user')
1471- super(ObjectStoreTestCase, self).tearDown()
1472-
1473- def test_buckets(self):
1474- self.context.user = self.um.get_user('user1')
1475- self.context.project = self.um.get_project('proj1')
1476- objectstore.bucket.Bucket.create('new_bucket', self.context)
1477- bucket = objectstore.bucket.Bucket('new_bucket')
1478-
1479- # creator is authorized to use bucket
1480- self.assert_(bucket.is_authorized(self.context))
1481-
1482- # another user is not authorized
1483- self.context.user = self.um.get_user('user2')
1484- self.context.project = self.um.get_project('proj2')
1485- self.assert_(bucket.is_authorized(self.context) == False)
1486-
1487- # admin is authorized to use bucket
1488- self.context.user = self.um.get_user('admin_user')
1489- self.context.project = None
1490- self.assert_(bucket.is_authorized(self.context))
1491-
1492- # new buckets are empty
1493- self.assert_(bucket.list_keys()['Contents'] == [])
1494-
1495- # storing keys works
1496- bucket['foo'] = "bar"
1497-
1498- self.assert_(len(bucket.list_keys()['Contents']) == 1)
1499-
1500- self.assert_(bucket['foo'].read() == 'bar')
1501-
1502- # md5 of key works
1503- self.assert_(bucket['foo'].md5 == hashlib.md5('bar').hexdigest())
1504-
1505- # deleting non-empty bucket throws exception
1506- exception = False
1507- try:
1508- bucket.delete()
1509- except:
1510- exception = True
1511-
1512- self.assert_(exception)
1513-
1514- # deleting key
1515- del bucket['foo']
1516-
1517- # deleting empty button
1518- bucket.delete()
1519-
1520- # accessing deleted bucket throws exception
1521- exception = False
1522- try:
1523- objectstore.bucket.Bucket('new_bucket')
1524- except:
1525- exception = True
1526-
1527- self.assert_(exception)
1528-
1529- def test_images(self):
1530- self.context.user = self.um.get_user('user1')
1531- self.context.project = self.um.get_project('proj1')
1532-
1533- # create a bucket for our bundle
1534- objectstore.bucket.Bucket.create('image_bucket', self.context)
1535- bucket = objectstore.bucket.Bucket('image_bucket')
1536-
1537- # upload an image manifest/parts
1538- bundle_path = os.path.join(os.path.dirname(__file__), 'bundle')
1539- for path in glob.glob(bundle_path + '/*'):
1540- bucket[os.path.basename(path)] = open(path, 'rb').read()
1541-
1542- # register an image
1543- objectstore.image.Image.register_aws_image('i-testing', 'image_bucket/1mb.manifest.xml', self.context)
1544-
1545- # verify image
1546- my_img = objectstore.image.Image('i-testing')
1547- result_image_file = os.path.join(my_img.path, 'image')
1548- self.assertEqual(os.stat(result_image_file).st_size, 1048576)
1549-
1550- sha = hashlib.sha1(open(result_image_file).read()).hexdigest()
1551- self.assertEqual(sha, '3b71f43ff30f4b15b5cd85dd9e95ebc7e84eb5a3')
1552-
1553- # verify image permissions
1554- self.context.user = self.um.get_user('user2')
1555- self.context.project = self.um.get_project('proj2')
1556- self.assert_(my_img.is_authorized(self.context) == False)
1557-
1558-# class ApiObjectStoreTestCase(test.BaseTestCase):
1559-# def setUp(self):
1560-# super(ApiObjectStoreTestCase, self).setUp()
1561-# FLAGS.fake_users = True
1562-# FLAGS.buckets_path = os.path.join(tempdir, 'buckets')
1563-# FLAGS.images_path = os.path.join(tempdir, 'images')
1564-# FLAGS.ca_path = os.path.join(os.path.dirname(__file__), 'CA')
1565-#
1566-# self.users = users.UserManager.instance()
1567-# self.app = handler.Application(self.users)
1568-#
1569-# self.host = '127.0.0.1'
1570-#
1571-# self.conn = boto.s3.connection.S3Connection(
1572-# aws_access_key_id=user.access,
1573-# aws_secret_access_key=user.secret,
1574-# is_secure=False,
1575-# calling_format=boto.s3.connection.OrdinaryCallingFormat(),
1576-# port=FLAGS.s3_port,
1577-# host=FLAGS.s3_host)
1578-#
1579-# self.mox.StubOutWithMock(self.ec2, 'new_http_connection')
1580-#
1581-# def tearDown(self):
1582-# FLAGS.Reset()
1583-# super(ApiObjectStoreTestCase, self).tearDown()
1584-#
1585-# def test_describe_instances(self):
1586-# self.expect_http()
1587-# self.mox.ReplayAll()
1588-#
1589-# self.assertEqual(self.ec2.get_all_instances(), [])
1590
1591=== removed file 'nova/tests/process_unittest.py'
1592--- nova/tests/process_unittest.py 2010-07-16 19:12:36 +0000
1593+++ nova/tests/process_unittest.py 1970-01-01 00:00:00 +0000
1594@@ -1,122 +0,0 @@
1595-# vim: tabstop=4 shiftwidth=4 softtabstop=4
1596-
1597-# Copyright 2010 United States Government as represented by the
1598-# Administrator of the National Aeronautics and Space Administration.
1599-# All Rights Reserved.
1600-#
1601-# Licensed under the Apache License, Version 2.0 (the "License"); you may
1602-# not use this file except in compliance with the License. You may obtain
1603-# a copy of the License at
1604-#
1605-# http://www.apache.org/licenses/LICENSE-2.0
1606-#
1607-# Unless required by applicable law or agreed to in writing, software
1608-# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
1609-# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
1610-# License for the specific language governing permissions and limitations
1611-# under the License.
1612-
1613-import logging
1614-from twisted.internet import defer
1615-from twisted.internet import reactor
1616-from xml.etree import ElementTree
1617-
1618-from nova import exception
1619-from nova import flags
1620-from nova import process
1621-from nova import test
1622-from nova import utils
1623-
1624-FLAGS = flags.FLAGS
1625-
1626-
1627-class ProcessTestCase(test.TrialTestCase):
1628- def setUp(self):
1629- logging.getLogger().setLevel(logging.DEBUG)
1630- super(ProcessTestCase, self).setUp()
1631-
1632- def test_execute_stdout(self):
1633- pool = process.ProcessPool(2)
1634- d = pool.simple_execute('echo test')
1635- def _check(rv):
1636- self.assertEqual(rv[0], 'test\n')
1637- self.assertEqual(rv[1], '')
1638-
1639- d.addCallback(_check)
1640- d.addErrback(self.fail)
1641- return d
1642-
1643- def test_execute_stderr(self):
1644- pool = process.ProcessPool(2)
1645- d = pool.simple_execute('cat BAD_FILE', error_ok=1)
1646- def _check(rv):
1647- self.assertEqual(rv[0], '')
1648- self.assert_('No such file' in rv[1])
1649-
1650- d.addCallback(_check)
1651- d.addErrback(self.fail)
1652- return d
1653-
1654- def test_execute_unexpected_stderr(self):
1655- pool = process.ProcessPool(2)
1656- d = pool.simple_execute('cat BAD_FILE')
1657- d.addCallback(lambda x: self.fail('should have raised an error'))
1658- d.addErrback(lambda failure: failure.trap(IOError))
1659- return d
1660-
1661- def test_max_processes(self):
1662- pool = process.ProcessPool(2)
1663- d1 = pool.simple_execute('sleep 0.01')
1664- d2 = pool.simple_execute('sleep 0.01')
1665- d3 = pool.simple_execute('sleep 0.005')
1666- d4 = pool.simple_execute('sleep 0.005')
1667-
1668- called = []
1669- def _called(rv, name):
1670- called.append(name)
1671-
1672- d1.addCallback(_called, 'd1')
1673- d2.addCallback(_called, 'd2')
1674- d3.addCallback(_called, 'd3')
1675- d4.addCallback(_called, 'd4')
1676-
1677- # Make sure that d3 and d4 had to wait on the other two and were called
1678- # in order
1679- # NOTE(termie): there may be a race condition in this test if for some
1680- # reason one of the sleeps takes longer to complete
1681- # than it should
1682- d4.addCallback(lambda x: self.assertEqual(called[2], 'd3'))
1683- d4.addCallback(lambda x: self.assertEqual(called[3], 'd4'))
1684- d4.addErrback(self.fail)
1685- return d4
1686-
1687- def test_kill_long_process(self):
1688- pool = process.ProcessPool(2)
1689-
1690- d1 = pool.simple_execute('sleep 1')
1691- d2 = pool.simple_execute('sleep 0.005')
1692-
1693- timeout = reactor.callLater(0.1, self.fail, 'should have been killed')
1694-
1695- # kill d1 and wait on it to end then cancel the timeout
1696- d2.addCallback(lambda _: d1.process.signalProcess('KILL'))
1697- d2.addCallback(lambda _: d1)
1698- d2.addBoth(lambda _: timeout.active() and timeout.cancel())
1699- d2.addErrback(self.fail)
1700- return d2
1701-
1702- def test_process_exit_is_contained(self):
1703- pool = process.ProcessPool(2)
1704-
1705- d1 = pool.simple_execute('sleep 1')
1706- d1.addCallback(lambda x: self.fail('should have errbacked'))
1707- d1.addErrback(lambda fail: fail.trap(IOError))
1708- reactor.callLater(0.05, d1.process.signalProcess, 'KILL')
1709-
1710- return d1
1711-
1712- def test_shared_pool_is_singleton(self):
1713- pool1 = process.SharedPool()
1714- pool2 = process.SharedPool()
1715- self.assert_(id(pool1) == id(pool2))
1716-
1717
1718=== removed file 'nova/tests/real_flags.py'
1719--- nova/tests/real_flags.py 2010-07-15 15:52:11 +0000
1720+++ nova/tests/real_flags.py 1970-01-01 00:00:00 +0000
1721@@ -1,28 +0,0 @@
1722-# vim: tabstop=4 shiftwidth=4 softtabstop=4
1723-
1724-# Copyright 2010 United States Government as represented by the
1725-# Administrator of the National Aeronautics and Space Administration.
1726-# All Rights Reserved.
1727-#
1728-# Licensed under the Apache License, Version 2.0 (the "License"); you may
1729-# not use this file except in compliance with the License. You may obtain
1730-# a copy of the License at
1731-#
1732-# http://www.apache.org/licenses/LICENSE-2.0
1733-#
1734-# Unless required by applicable law or agreed to in writing, software
1735-# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
1736-# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
1737-# License for the specific language governing permissions and limitations
1738-# under the License.
1739-
1740-from nova import flags
1741-
1742-FLAGS = flags.FLAGS
1743-
1744-FLAGS.fake_libvirt = False
1745-FLAGS.fake_storage = False
1746-FLAGS.fake_rabbit = False
1747-FLAGS.fake_network = False
1748-FLAGS.fake_users = False
1749-FLAGS.verbose = False
1750
1751=== removed file 'nova/tests/storage_unittest.py'
1752--- nova/tests/storage_unittest.py 2010-07-15 23:16:09 +0000
1753+++ nova/tests/storage_unittest.py 1970-01-01 00:00:00 +0000
1754@@ -1,115 +0,0 @@
1755-# vim: tabstop=4 shiftwidth=4 softtabstop=4
1756-
1757-# Copyright 2010 United States Government as represented by the
1758-# Administrator of the National Aeronautics and Space Administration.
1759-# All Rights Reserved.
1760-#
1761-# Licensed under the Apache License, Version 2.0 (the "License"); you may
1762-# not use this file except in compliance with the License. You may obtain
1763-# a copy of the License at
1764-#
1765-# http://www.apache.org/licenses/LICENSE-2.0
1766-#
1767-# Unless required by applicable law or agreed to in writing, software
1768-# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
1769-# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
1770-# License for the specific language governing permissions and limitations
1771-# under the License.
1772-
1773-import logging
1774-
1775-from nova import exception
1776-from nova import flags
1777-from nova import test
1778-from nova.compute import node
1779-from nova.volume import storage
1780-
1781-
1782-FLAGS = flags.FLAGS
1783-
1784-
1785-class StorageTestCase(test.TrialTestCase):
1786- def setUp(self):
1787- logging.getLogger().setLevel(logging.DEBUG)
1788- super(StorageTestCase, self).setUp()
1789- self.mynode = node.Node()
1790- self.mystorage = None
1791- self.flags(fake_libvirt=True,
1792- fake_storage=True)
1793- self.mystorage = storage.BlockStore()
1794-
1795- def test_run_create_volume(self):
1796- vol_size = '0'
1797- user_id = 'fake'
1798- project_id = 'fake'
1799- volume_id = self.mystorage.create_volume(vol_size, user_id, project_id)
1800- # TODO(termie): get_volume returns differently than create_volume
1801- self.assertEqual(volume_id,
1802- storage.get_volume(volume_id)['volume_id'])
1803-
1804- rv = self.mystorage.delete_volume(volume_id)
1805- self.assertRaises(exception.Error,
1806- storage.get_volume,
1807- volume_id)
1808-
1809- def test_too_big_volume(self):
1810- vol_size = '1001'
1811- user_id = 'fake'
1812- project_id = 'fake'
1813- self.assertRaises(TypeError,
1814- self.mystorage.create_volume,
1815- vol_size, user_id, project_id)
1816-
1817- def test_too_many_volumes(self):
1818- vol_size = '1'
1819- user_id = 'fake'
1820- project_id = 'fake'
1821- num_shelves = FLAGS.last_shelf_id - FLAGS.first_shelf_id + 1
1822- total_slots = FLAGS.slots_per_shelf * num_shelves
1823- vols = []
1824- for i in xrange(total_slots):
1825- vid = self.mystorage.create_volume(vol_size, user_id, project_id)
1826- vols.append(vid)
1827- self.assertRaises(storage.NoMoreVolumes,
1828- self.mystorage.create_volume,
1829- vol_size, user_id, project_id)
1830- for id in vols:
1831- self.mystorage.delete_volume(id)
1832-
1833- def test_run_attach_detach_volume(self):
1834- # Create one volume and one node to test with
1835- instance_id = "storage-test"
1836- vol_size = "5"
1837- user_id = "fake"
1838- project_id = 'fake'
1839- mountpoint = "/dev/sdf"
1840- volume_id = self.mystorage.create_volume(vol_size, user_id, project_id)
1841-
1842- volume_obj = storage.get_volume(volume_id)
1843- volume_obj.start_attach(instance_id, mountpoint)
1844- rv = yield self.mynode.attach_volume(volume_id,
1845- instance_id,
1846- mountpoint)
1847- self.assertEqual(volume_obj['status'], "in-use")
1848- self.assertEqual(volume_obj['attachStatus'], "attached")
1849- self.assertEqual(volume_obj['instance_id'], instance_id)
1850- self.assertEqual(volume_obj['mountpoint'], mountpoint)
1851-
1852- self.assertRaises(exception.Error,
1853- self.mystorage.delete_volume,
1854- volume_id)
1855-
1856- rv = yield self.mystorage.detach_volume(volume_id)
1857- volume_obj = storage.get_volume(volume_id)
1858- self.assertEqual(volume_obj['status'], "available")
1859-
1860- rv = self.mystorage.delete_volume(volume_id)
1861- self.assertRaises(exception.Error,
1862- storage.get_volume,
1863- volume_id)
1864-
1865- def test_multi_node(self):
1866- # TODO(termie): Figure out how to test with two nodes,
1867- # each of them having a different FLAG for storage_node
1868- # This will allow us to test cross-node interactions
1869- pass
1870
1871=== removed file 'nova/tests/users_unittest.py'
1872--- nova/tests/users_unittest.py 2010-07-15 23:13:48 +0000
1873+++ nova/tests/users_unittest.py 1970-01-01 00:00:00 +0000
1874@@ -1,207 +0,0 @@
1875-# vim: tabstop=4 shiftwidth=4 softtabstop=4
1876-
1877-# Copyright 2010 United States Government as represented by the
1878-# Administrator of the National Aeronautics and Space Administration.
1879-# All Rights Reserved.
1880-#
1881-# Licensed under the Apache License, Version 2.0 (the "License"); you may
1882-# not use this file except in compliance with the License. You may obtain
1883-# a copy of the License at
1884-#
1885-# http://www.apache.org/licenses/LICENSE-2.0
1886-#
1887-# Unless required by applicable law or agreed to in writing, software
1888-# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
1889-# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
1890-# License for the specific language governing permissions and limitations
1891-# under the License.
1892-
1893-import logging
1894-from M2Crypto import BIO
1895-from M2Crypto import RSA
1896-from M2Crypto import X509
1897-import unittest
1898-
1899-from nova import crypto
1900-from nova import flags
1901-from nova import test
1902-from nova.auth import users
1903-from nova.endpoint import cloud
1904-
1905-FLAGS = flags.FLAGS
1906-
1907-
1908-class UserTestCase(test.BaseTestCase):
1909- flush_db = False
1910- def setUp(self):
1911- super(UserTestCase, self).setUp()
1912- self.flags(fake_libvirt=True,
1913- fake_storage=True)
1914- self.users = users.UserManager.instance()
1915-
1916- def test_001_can_create_users(self):
1917- self.users.create_user('test1', 'access', 'secret')
1918- self.users.create_user('test2')
1919-
1920- def test_002_can_get_user(self):
1921- user = self.users.get_user('test1')
1922-
1923- def test_003_can_retreive_properties(self):
1924- user = self.users.get_user('test1')
1925- self.assertEqual('test1', user.id)
1926- self.assertEqual('access', user.access)
1927- self.assertEqual('secret', user.secret)
1928-
1929- def test_004_signature_is_valid(self):
1930- #self.assertTrue(self.users.authenticate( **boto.generate_url ... ? ? ? ))
1931- pass
1932- #raise NotImplementedError
1933-
1934- def test_005_can_get_credentials(self):
1935- return
1936- credentials = self.users.get_user('test1').get_credentials()
1937- self.assertEqual(credentials,
1938- 'export EC2_ACCESS_KEY="access"\n' +
1939- 'export EC2_SECRET_KEY="secret"\n' +
1940- 'export EC2_URL="http://127.0.0.1:8773/services/Cloud"\n' +
1941- 'export S3_URL="http://127.0.0.1:3333/"\n' +
1942- 'export EC2_USER_ID="test1"\n')
1943-
1944- def test_006_test_key_storage(self):
1945- user = self.users.get_user('test1')
1946- user.create_key_pair('public', 'key', 'fingerprint')
1947- key = user.get_key_pair('public')
1948- self.assertEqual('key', key.public_key)
1949- self.assertEqual('fingerprint', key.fingerprint)
1950-
1951- def test_007_test_key_generation(self):
1952- user = self.users.get_user('test1')
1953- private_key, fingerprint = user.generate_key_pair('public2')
1954- key = RSA.load_key_string(private_key, callback=lambda: None)
1955- bio = BIO.MemoryBuffer()
1956- public_key = user.get_key_pair('public2').public_key
1957- key.save_pub_key_bio(bio)
1958- converted = crypto.ssl_pub_to_ssh_pub(bio.read())
1959- # assert key fields are equal
1960- self.assertEqual(public_key.split(" ")[1].strip(),
1961- converted.split(" ")[1].strip())
1962-
1963- def test_008_can_list_key_pairs(self):
1964- keys = self.users.get_user('test1').get_key_pairs()
1965- self.assertTrue(filter(lambda k: k.name == 'public', keys))
1966- self.assertTrue(filter(lambda k: k.name == 'public2', keys))
1967-
1968- def test_009_can_delete_key_pair(self):
1969- self.users.get_user('test1').delete_key_pair('public')
1970- keys = self.users.get_user('test1').get_key_pairs()
1971- self.assertFalse(filter(lambda k: k.name == 'public', keys))
1972-
1973- def test_010_can_list_users(self):
1974- users = self.users.get_users()
1975- logging.warn(users)
1976- self.assertTrue(filter(lambda u: u.id == 'test1', users))
1977-
1978- def test_101_can_add_user_role(self):
1979- self.assertFalse(self.users.has_role('test1', 'itsec'))
1980- self.users.add_role('test1', 'itsec')
1981- self.assertTrue(self.users.has_role('test1', 'itsec'))
1982-
1983- def test_199_can_remove_user_role(self):
1984- self.assertTrue(self.users.has_role('test1', 'itsec'))
1985- self.users.remove_role('test1', 'itsec')
1986- self.assertFalse(self.users.has_role('test1', 'itsec'))
1987-
1988- def test_201_can_create_project(self):
1989- project = self.users.create_project('testproj', 'test1', 'A test project', ['test1'])
1990- self.assertTrue(filter(lambda p: p.name == 'testproj', self.users.get_projects()))
1991- self.assertEqual(project.name, 'testproj')
1992- self.assertEqual(project.description, 'A test project')
1993- self.assertEqual(project.project_manager_id, 'test1')
1994- self.assertTrue(project.has_member('test1'))
1995-
1996- def test_202_user1_is_project_member(self):
1997- self.assertTrue(self.users.get_user('test1').is_project_member('testproj'))
1998-
1999- def test_203_user2_is_not_project_member(self):
2000- self.assertFalse(self.users.get_user('test2').is_project_member('testproj'))
2001-
2002- def test_204_user1_is_project_manager(self):
2003- self.assertTrue(self.users.get_user('test1').is_project_manager('testproj'))
2004-
2005- def test_205_user2_is_not_project_manager(self):
2006- self.assertFalse(self.users.get_user('test2').is_project_manager('testproj'))
2007-
2008- def test_206_can_add_user_to_project(self):
2009- self.users.add_to_project('test2', 'testproj')
2010- self.assertTrue(self.users.get_project('testproj').has_member('test2'))
2011-
2012- def test_208_can_remove_user_from_project(self):
2013- self.users.remove_from_project('test2', 'testproj')
2014- self.assertFalse(self.users.get_project('testproj').has_member('test2'))
2015-
2016- def test_209_can_generate_x509(self):
2017- # MUST HAVE RUN CLOUD SETUP BY NOW
2018- self.cloud = cloud.CloudController()
2019- self.cloud.setup()
2020- private_key, signed_cert_string = self.users.get_project('testproj').generate_x509_cert('test1')
2021- logging.debug(signed_cert_string)
2022-
2023- # Need to verify that it's signed by the right intermediate CA
2024- full_chain = crypto.fetch_ca(project_id='testproj', chain=True)
2025- int_cert = crypto.fetch_ca(project_id='testproj', chain=False)
2026- cloud_cert = crypto.fetch_ca()
2027- logging.debug("CA chain:\n\n =====\n%s\n\n=====" % full_chain)
2028- signed_cert = X509.load_cert_string(signed_cert_string)
2029- chain_cert = X509.load_cert_string(full_chain)
2030- int_cert = X509.load_cert_string(int_cert)
2031- cloud_cert = X509.load_cert_string(cloud_cert)
2032- self.assertTrue(signed_cert.verify(chain_cert.get_pubkey()))
2033- self.assertTrue(signed_cert.verify(int_cert.get_pubkey()))
2034-
2035- if not FLAGS.use_intermediate_ca:
2036- self.assertTrue(signed_cert.verify(cloud_cert.get_pubkey()))
2037- else:
2038- self.assertFalse(signed_cert.verify(cloud_cert.get_pubkey()))
2039-
2040- def test_210_can_add_project_role(self):
2041- project = self.users.get_project('testproj')
2042- self.assertFalse(project.has_role('test1', 'sysadmin'))
2043- self.users.add_role('test1', 'sysadmin')
2044- self.assertFalse(project.has_role('test1', 'sysadmin'))
2045- project.add_role('test1', 'sysadmin')
2046- self.assertTrue(project.has_role('test1', 'sysadmin'))
2047-
2048- def test_211_can_remove_project_role(self):
2049- project = self.users.get_project('testproj')
2050- self.assertTrue(project.has_role('test1', 'sysadmin'))
2051- project.remove_role('test1', 'sysadmin')
2052- self.assertFalse(project.has_role('test1', 'sysadmin'))
2053- self.users.remove_role('test1', 'sysadmin')
2054- self.assertFalse(project.has_role('test1', 'sysadmin'))
2055-
2056- def test_212_vpn_ip_and_port_looks_valid(self):
2057- project = self.users.get_project('testproj')
2058- self.assert_(project.vpn_ip)
2059- self.assert_(project.vpn_port >= FLAGS.vpn_start_port)
2060- self.assert_(project.vpn_port <= FLAGS.vpn_end_port)
2061-
2062- def test_213_too_many_vpns(self):
2063- for i in xrange(users.Vpn.num_ports_for_ip(FLAGS.vpn_ip)):
2064- users.Vpn.create("vpnuser%s" % i)
2065- self.assertRaises(users.NoMorePorts, users.Vpn.create, "boom")
2066-
2067- def test_299_can_delete_project(self):
2068- self.users.delete_project('testproj')
2069- self.assertFalse(filter(lambda p: p.name == 'testproj', self.users.get_projects()))
2070-
2071- def test_999_can_delete_users(self):
2072- self.users.delete_user('test1')
2073- users = self.users.get_users()
2074- self.assertFalse(filter(lambda u: u.id == 'test1', users))
2075- self.users.delete_user('test2')
2076- self.assertEqual(self.users.get_user('test2'), None)
2077-
2078-
2079-if __name__ == "__main__":
2080- # TODO: Implement use_fake as an option
2081- unittest.main()
2082
2083=== removed file 'nova/tests/validator_unittest.py'
2084--- nova/tests/validator_unittest.py 2010-07-15 23:13:48 +0000
2085+++ nova/tests/validator_unittest.py 1970-01-01 00:00:00 +0000
2086@@ -1,41 +0,0 @@
2087-# vim: tabstop=4 shiftwidth=4 softtabstop=4
2088-
2089-# Copyright 2010 United States Government as represented by the
2090-# Administrator of the National Aeronautics and Space Administration.
2091-# All Rights Reserved.
2092-#
2093-# Licensed under the Apache License, Version 2.0 (the "License"); you may
2094-# not use this file except in compliance with the License. You may obtain
2095-# a copy of the License at
2096-#
2097-# http://www.apache.org/licenses/LICENSE-2.0
2098-#
2099-# Unless required by applicable law or agreed to in writing, software
2100-# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
2101-# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
2102-# License for the specific language governing permissions and limitations
2103-# under the License.
2104-
2105-import logging
2106-import unittest
2107-
2108-from nova import flags
2109-from nova import test
2110-from nova import validate
2111-
2112-
2113-class ValidationTestCase(test.TrialTestCase):
2114- def setUp(self):
2115- super(ValidationTestCase, self).setUp()
2116-
2117- def tearDown(self):
2118- super(ValidationTestCase, self).tearDown()
2119-
2120- def test_type_validation(self):
2121- self.assertTrue(type_case("foo", 5, 1))
2122- self.assertRaises(TypeError, type_case, "bar", "5", 1)
2123- self.assertRaises(TypeError, type_case, None, 5, 1)
2124-
2125-@validate.typetest(instanceid=str, size=int, number_of_instances=int)
2126-def type_case(instanceid, size, number_of_instances):
2127- return True
2128
2129=== modified file 'run_tests.py'
2130--- run_tests.py 2010-07-19 08:37:28 +0000
2131+++ run_tests.py 2010-07-23 03:41:08 +0000
2132@@ -49,17 +49,17 @@
2133 from nova import flags
2134 from nova import twistd
2135
2136-from nova.tests.access_unittest import *
2137-from nova.tests.api_unittest import *
2138-from nova.tests.cloud_unittest import *
2139-from nova.tests.model_unittest import *
2140-from nova.tests.network_unittest import *
2141-from nova.tests.node_unittest import *
2142-from nova.tests.objectstore_unittest import *
2143-from nova.tests.process_unittest import *
2144-from nova.tests.storage_unittest import *
2145-from nova.tests.users_unittest import *
2146-from nova.tests.validator_unittest import *
2147+from tests.access_unittest import *
2148+from tests.api_unittest import *
2149+from tests.cloud_unittest import *
2150+from tests.model_unittest import *
2151+from tests.network_unittest import *
2152+from tests.node_unittest import *
2153+from tests.objectstore_unittest import *
2154+from tests.process_unittest import *
2155+from tests.storage_unittest import *
2156+from tests.users_unittest import *
2157+from tests.validator_unittest import *
2158
2159
2160 FLAGS = flags.FLAGS
2161@@ -79,7 +79,7 @@
2162
2163 # TODO(termie): these should make a call instead of doing work on import
2164 if FLAGS.fake_tests:
2165- from nova.tests.fake_flags import *
2166+ from tests.fake_flags import *
2167 # use db 8 for fake tests
2168 FLAGS.redis_db = 8
2169 if FLAGS.flush_db:
2170@@ -87,7 +87,7 @@
2171 r = datastore.Redis.instance()
2172 r.flushdb()
2173 else:
2174- from nova.tests.real_flags import *
2175+ from tests.real_flags import *
2176
2177 # Establish redirect for STDERR
2178 sys.stderr.flush()
2179
2180=== added directory 'tests'
2181=== added directory 'tests/CA'
2182=== added file 'tests/CA/cacert.pem'
2183--- tests/CA/cacert.pem 1970-01-01 00:00:00 +0000
2184+++ tests/CA/cacert.pem 2010-07-23 03:41:08 +0000
2185@@ -0,0 +1,17 @@
2186+-----BEGIN CERTIFICATE-----
2187+MIICyzCCAjSgAwIBAgIJANiqHZUcbScCMA0GCSqGSIb3DQEBBAUAME4xEjAQBgNV
2188+BAoTCU5PVkEgUk9PVDEWMBQGA1UEBxMNTW91bnRhaW4gVmlldzETMBEGA1UECBMK
2189+Q2FsaWZvcm5pYTELMAkGA1UEBhMCVVMwHhcNMTAwNTI4MDExOTI1WhcNMTEwNTI4
2190+MDExOTI1WjBOMRIwEAYDVQQKEwlOT1ZBIFJPT1QxFjAUBgNVBAcTDU1vdW50YWlu
2191+IFZpZXcxEzARBgNVBAgTCkNhbGlmb3JuaWExCzAJBgNVBAYTAlVTMIGfMA0GCSqG
2192+SIb3DQEBAQUAA4GNADCBiQKBgQDobUnq8rpXA/HQZ2Uu9Me3SlqCayz3ws2wtvFQ
2193+koWPUzpriIYPkpprz2EaVu07Zb9uJHvjcoY07nYntl4jR8S7PH4XZhlVFn8AQWzs
2194+iThU4KJF71UfVM00dDrarSgVpyOIcFXO3iUvLoJj7+RUPjrWdLuJoMqnhicgLeHZ
2195+LAZ8ewIDAQABo4GwMIGtMAwGA1UdEwQFMAMBAf8wHQYDVR0OBBYEFMh1RMlTVtt8
2196+EdESYpsTU08r0FnpMH4GA1UdIwR3MHWAFMh1RMlTVtt8EdESYpsTU08r0FnpoVKk
2197+UDBOMRIwEAYDVQQKEwlOT1ZBIFJPT1QxFjAUBgNVBAcTDU1vdW50YWluIFZpZXcx
2198+EzARBgNVBAgTCkNhbGlmb3JuaWExCzAJBgNVBAYTAlVTggkA2KodlRxtJwIwDQYJ
2199+KoZIhvcNAQEEBQADgYEAq+YCgflK36HCdodNu2ya3O6UDRUE2dW8n96tAOmvHqmR
2200+v38k8GIW0pjWDo+lZYnFmeJYd+QGcJl9fLzXxffV5k+rNCfr/gEYtznWLNUX7AZB
2201+b/VC7L+yK9qz08C8n51TslXaf3fUGkfkQxsvEP7+hi0qavdd/8eTbdheWahYwWg=
2202+-----END CERTIFICATE-----
2203
2204=== added directory 'tests/CA/private'
2205=== added file 'tests/CA/private/cakey.pem'
2206--- tests/CA/private/cakey.pem 1970-01-01 00:00:00 +0000
2207+++ tests/CA/private/cakey.pem 2010-07-23 03:41:08 +0000
2208@@ -0,0 +1,15 @@
2209+-----BEGIN RSA PRIVATE KEY-----
2210+MIICXQIBAAKBgQDobUnq8rpXA/HQZ2Uu9Me3SlqCayz3ws2wtvFQkoWPUzpriIYP
2211+kpprz2EaVu07Zb9uJHvjcoY07nYntl4jR8S7PH4XZhlVFn8AQWzsiThU4KJF71Uf
2212+VM00dDrarSgVpyOIcFXO3iUvLoJj7+RUPjrWdLuJoMqnhicgLeHZLAZ8ewIDAQAB
2213+AoGBANQonmZ2Nh2jniFrn/LiwULP/ho6Fov6J6N8+n1focaYZCUwM58XZRmv7KUM
2214+X/PuBnVVnDibm2HJodTSJM/zfODnGO15kdmJ9X23FkkdTyuvphO5tYF0ONARXdfX
2215+9LbPcLYA14VSCZCKCye6mbv/xi0C/s7q6ZBoMl7XaeD9hgUxAkEA9lxQY/ZxcLV0
2216+Ae5I2spBbtuXEGns11YnKnppc59RrAono1gaDeYY2WZRwztIcD6VtUv7qkzH6ubo
2217+shAG4fvnPQJBAPGFaDODs2ckPvxnILEbjpnZXGQqDCpQ3sVJ6nfu+qdAWS92ESNo
2218+Y6DC8zFjFaQFbKy6Jxr1VsvYDXhF8cmy7hcCQHkLElSLGWGPRdhNA268QTn+mlJu
2219+OPf0VHoCex1cAfzNYHxZJTP/AeaO501NK2I63cOd+aDK6M75dQtH5JnT8uECQQCg
2220+jVydkhk6oV+1jiCvW3BKWbIPa9w2bRgJ8n8JRzYc5Kvk3wm5jfVcsvvTgtip9mkt
2221+0XmZdCpEy9T4dRasTGP1AkBMhShiVP7+P+SIQlZtSn8ckTt9G6cefEjxsv0kVFZe
2222+SjkUO0ZifahF8r3Q1eEUSzdXEvicEwONvcpc7MLwfSD7
2223+-----END RSA PRIVATE KEY-----
2224
2225=== added file 'tests/__init__.py'
2226--- tests/__init__.py 1970-01-01 00:00:00 +0000
2227+++ tests/__init__.py 2010-07-23 03:41:08 +0000
2228@@ -0,0 +1,31 @@
2229+# vim: tabstop=4 shiftwidth=4 softtabstop=4
2230+
2231+# Copyright 2010 United States Government as represented by the
2232+# Administrator of the National Aeronautics and Space Administration.
2233+# All Rights Reserved.
2234+#
2235+# Licensed under the Apache License, Version 2.0 (the "License"); you may
2236+# not use this file except in compliance with the License. You may obtain
2237+# a copy of the License at
2238+#
2239+# http://www.apache.org/licenses/LICENSE-2.0
2240+#
2241+# Unless required by applicable law or agreed to in writing, software
2242+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
2243+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
2244+# License for the specific language governing permissions and limitations
2245+# under the License.
2246+
2247+"""
2248+:mod:`tests` -- Nova Unittests
2249+=====================================================
2250+
2251+.. automodule:: tests
2252+ :platform: Unix
2253+.. moduleauthor:: Jesse Andrews <jesse@ansolabs.com>
2254+.. moduleauthor:: Devin Carlen <devin.carlen@gmail.com>
2255+.. moduleauthor:: Vishvananda Ishaya <vishvananda@yahoo.com>
2256+.. moduleauthor:: Joshua McKenty <joshua@cognition.ca>
2257+.. moduleauthor:: Manish Singh <yosh@gimp.org>
2258+.. moduleauthor:: Andy Smith <andy@anarkystic.com>
2259+"""
2260
2261=== added file 'tests/access_unittest.py'
2262--- tests/access_unittest.py 1970-01-01 00:00:00 +0000
2263+++ tests/access_unittest.py 2010-07-23 03:41:08 +0000
2264@@ -0,0 +1,165 @@
2265+# vim: tabstop=4 shiftwidth=4 softtabstop=4
2266+
2267+# Copyright 2010 United States Government as represented by the
2268+# Administrator of the National Aeronautics and Space Administration.
2269+# All Rights Reserved.
2270+#
2271+# Licensed under the Apache License, Version 2.0 (the "License"); you may
2272+# not use this file except in compliance with the License. You may obtain
2273+# a copy of the License at
2274+#
2275+# http://www.apache.org/licenses/LICENSE-2.0
2276+#
2277+# Unless required by applicable law or agreed to in writing, software
2278+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
2279+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
2280+# License for the specific language governing permissions and limitations
2281+# under the License.
2282+
2283+import unittest
2284+import logging
2285+
2286+from nova import exception
2287+from nova import flags
2288+from nova import test
2289+from nova.auth.users import UserManager
2290+from nova.auth import rbac
2291+
2292+
2293+FLAGS = flags.FLAGS
2294+class Context(object):
2295+ pass
2296+
2297+class AccessTestCase(test.BaseTestCase):
2298+ def setUp(self):
2299+ super(AccessTestCase, self).setUp()
2300+ FLAGS.fake_libvirt = True
2301+ FLAGS.fake_storage = True
2302+ um = UserManager.instance()
2303+ # Make test users
2304+ try:
2305+ self.testadmin = um.create_user('testadmin')
2306+ except Exception, err:
2307+ logging.error(str(err))
2308+ try:
2309+ self.testpmsys = um.create_user('testpmsys')
2310+ except: pass
2311+ try:
2312+ self.testnet = um.create_user('testnet')
2313+ except: pass
2314+ try:
2315+ self.testsys = um.create_user('testsys')
2316+ except: pass
2317+ # Assign some rules
2318+ try:
2319+ um.add_role('testadmin', 'cloudadmin')
2320+ except: pass
2321+ try:
2322+ um.add_role('testpmsys', 'sysadmin')
2323+ except: pass
2324+ try:
2325+ um.add_role('testnet', 'netadmin')
2326+ except: pass
2327+ try:
2328+ um.add_role('testsys', 'sysadmin')
2329+ except: pass
2330+
2331+ # Make a test project
2332+ try:
2333+ self.project = um.create_project('testproj', 'testpmsys', 'a test project', ['testpmsys', 'testnet', 'testsys'])
2334+ except: pass
2335+ try:
2336+ self.project.add_role(self.testnet, 'netadmin')
2337+ except: pass
2338+ try:
2339+ self.project.add_role(self.testsys, 'sysadmin')
2340+ except: pass
2341+ self.context = Context()
2342+ self.context.project = self.project
2343+ #user is set in each test
2344+
2345+ def tearDown(self):
2346+ um = UserManager.instance()
2347+ # Delete the test project
2348+ um.delete_project('testproj')
2349+ # Delete the test user
2350+ um.delete_user('testadmin')
2351+ um.delete_user('testpmsys')
2352+ um.delete_user('testnet')
2353+ um.delete_user('testsys')
2354+ super(AccessTestCase, self).tearDown()
2355+
2356+ def test_001_allow_all(self):
2357+ self.context.user = self.testadmin
2358+ self.assertTrue(self._allow_all(self.context))
2359+ self.context.user = self.testpmsys
2360+ self.assertTrue(self._allow_all(self.context))
2361+ self.context.user = self.testnet
2362+ self.assertTrue(self._allow_all(self.context))
2363+ self.context.user = self.testsys
2364+ self.assertTrue(self._allow_all(self.context))
2365+
2366+ def test_002_allow_none(self):
2367+ self.context.user = self.testadmin
2368+ self.assertTrue(self._allow_none(self.context))
2369+ self.context.user = self.testpmsys
2370+ self.assertRaises(exception.NotAuthorized, self._allow_none, self.context)
2371+ self.context.user = self.testnet
2372+ self.assertRaises(exception.NotAuthorized, self._allow_none, self.context)
2373+ self.context.user = self.testsys
2374+ self.assertRaises(exception.NotAuthorized, self._allow_none, self.context)
2375+
2376+ def test_003_allow_project_manager(self):
2377+ self.context.user = self.testadmin
2378+ self.assertTrue(self._allow_project_manager(self.context))
2379+ self.context.user = self.testpmsys
2380+ self.assertTrue(self._allow_project_manager(self.context))
2381+ self.context.user = self.testnet
2382+ self.assertRaises(exception.NotAuthorized, self._allow_project_manager, self.context)
2383+ self.context.user = self.testsys
2384+ self.assertRaises(exception.NotAuthorized, self._allow_project_manager, self.context)
2385+
2386+ def test_004_allow_sys_and_net(self):
2387+ self.context.user = self.testadmin
2388+ self.assertTrue(self._allow_sys_and_net(self.context))
2389+ self.context.user = self.testpmsys # doesn't have the per project sysadmin
2390+ self.assertRaises(exception.NotAuthorized, self._allow_sys_and_net, self.context)
2391+ self.context.user = self.testnet
2392+ self.assertTrue(self._allow_sys_and_net(self.context))
2393+ self.context.user = self.testsys
2394+ self.assertTrue(self._allow_sys_and_net(self.context))
2395+
2396+ def test_005_allow_sys_no_pm(self):
2397+ self.context.user = self.testadmin
2398+ self.assertTrue(self._allow_sys_no_pm(self.context))
2399+ self.context.user = self.testpmsys
2400+ self.assertRaises(exception.NotAuthorized, self._allow_sys_no_pm, self.context)
2401+ self.context.user = self.testnet
2402+ self.assertRaises(exception.NotAuthorized, self._allow_sys_no_pm, self.context)
2403+ self.context.user = self.testsys
2404+ self.assertTrue(self._allow_sys_no_pm(self.context))
2405+
2406+ @rbac.allow('all')
2407+ def _allow_all(self, context):
2408+ return True
2409+
2410+ @rbac.allow('none')
2411+ def _allow_none(self, context):
2412+ return True
2413+
2414+ @rbac.allow('projectmanager')
2415+ def _allow_project_manager(self, context):
2416+ return True
2417+
2418+ @rbac.allow('sysadmin', 'netadmin')
2419+ def _allow_sys_and_net(self, context):
2420+ return True
2421+
2422+ @rbac.allow('sysadmin')
2423+ @rbac.deny('projectmanager')
2424+ def _allow_sys_no_pm(self, context):
2425+ return True
2426+
2427+if __name__ == "__main__":
2428+ # TODO: Implement use_fake as an option
2429+ unittest.main()
2430
2431=== added file 'tests/api_integration.py'
2432--- tests/api_integration.py 1970-01-01 00:00:00 +0000
2433+++ tests/api_integration.py 2010-07-23 03:41:08 +0000
2434@@ -0,0 +1,54 @@
2435+# vim: tabstop=4 shiftwidth=4 softtabstop=4
2436+
2437+# Copyright 2010 United States Government as represented by the
2438+# Administrator of the National Aeronautics and Space Administration.
2439+# All Rights Reserved.
2440+#
2441+# Licensed under the Apache License, Version 2.0 (the "License"); you may
2442+# not use this file except in compliance with the License. You may obtain
2443+# a copy of the License at
2444+#
2445+# http://www.apache.org/licenses/LICENSE-2.0
2446+#
2447+# Unless required by applicable law or agreed to in writing, software
2448+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
2449+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
2450+# License for the specific language governing permissions and limitations
2451+# under the License.
2452+
2453+
2454+import boto
2455+from boto.ec2.regioninfo import RegionInfo
2456+import unittest
2457+
2458+
2459+ACCESS_KEY = 'fake'
2460+SECRET_KEY = 'fake'
2461+CLC_IP = '127.0.0.1'
2462+CLC_PORT = 8773
2463+REGION = 'test'
2464+
2465+def get_connection():
2466+ return boto.connect_ec2 (
2467+ aws_access_key_id=ACCESS_KEY,
2468+ aws_secret_access_key=SECRET_KEY,
2469+ is_secure=False,
2470+ region=RegionInfo(None, REGION, CLC_IP),
2471+ port=CLC_PORT,
2472+ path='/services/Cloud',
2473+ debug=99
2474+ )
2475+
2476+class APIIntegrationTests(unittest.TestCase):
2477+ def test_001_get_all_images(self):
2478+ conn = get_connection()
2479+ res = conn.get_all_images()
2480+
2481+
2482+if __name__ == '__main__':
2483+ unittest.main()
2484+
2485+#print conn.get_all_key_pairs()
2486+#print conn.create_key_pair
2487+#print conn.create_security_group('name', 'description')
2488+
2489
2490=== added file 'tests/api_unittest.py'
2491--- tests/api_unittest.py 1970-01-01 00:00:00 +0000
2492+++ tests/api_unittest.py 2010-07-23 03:41:08 +0000
2493@@ -0,0 +1,199 @@
2494+# vim: tabstop=4 shiftwidth=4 softtabstop=4
2495+
2496+# Copyright 2010 United States Government as represented by the
2497+# Administrator of the National Aeronautics and Space Administration.
2498+# All Rights Reserved.
2499+#
2500+# Licensed under the Apache License, Version 2.0 (the "License"); you may
2501+# not use this file except in compliance with the License. You may obtain
2502+# a copy of the License at
2503+#
2504+# http://www.apache.org/licenses/LICENSE-2.0
2505+#
2506+# Unless required by applicable law or agreed to in writing, software
2507+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
2508+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
2509+# License for the specific language governing permissions and limitations
2510+# under the License.
2511+
2512+import boto
2513+from boto.ec2 import regioninfo
2514+import httplib
2515+import random
2516+import StringIO
2517+from tornado import httpserver
2518+from twisted.internet import defer
2519+
2520+from nova import flags
2521+from nova import test
2522+from nova.auth import users
2523+from nova.endpoint import api
2524+from nova.endpoint import cloud
2525+
2526+
2527+FLAGS = flags.FLAGS
2528+
2529+
2530+# NOTE(termie): These are a bunch of helper methods and classes to short
2531+# circuit boto calls and feed them into our tornado handlers,
2532+# it's pretty damn circuitous so apologies if you have to fix
2533+# a bug in it
2534+def boto_to_tornado(method, path, headers, data, host, connection=None):
2535+ """ translate boto requests into tornado requests
2536+
2537+ connection should be a FakeTornadoHttpConnection instance
2538+ """
2539+ headers = httpserver.HTTPHeaders()
2540+ for k, v in headers.iteritems():
2541+ headers[k] = v
2542+
2543+ req = httpserver.HTTPRequest(method=method,
2544+ uri=path,
2545+ headers=headers,
2546+ body=data,
2547+ host=host,
2548+ remote_ip='127.0.0.1',
2549+ connection=connection)
2550+ return req
2551+
2552+
2553+def raw_to_httpresponse(s):
2554+ """ translate a raw tornado http response into an httplib.HTTPResponse """
2555+ sock = FakeHttplibSocket(s)
2556+ resp = httplib.HTTPResponse(sock)
2557+ resp.begin()
2558+ return resp
2559+
2560+
2561+class FakeHttplibSocket(object):
2562+ """ a fake socket implementation for httplib.HTTPResponse, trivial """
2563+ def __init__(self, s):
2564+ self.fp = StringIO.StringIO(s)
2565+
2566+ def makefile(self, mode, other):
2567+ return self.fp
2568+
2569+
2570+class FakeTornadoStream(object):
2571+ """ a fake stream to satisfy tornado's assumptions, trivial """
2572+ def set_close_callback(self, f):
2573+ pass
2574+
2575+
2576+class FakeTornadoConnection(object):
2577+ """ a fake connection object for tornado to pass to its handlers
2578+
2579+ web requests are expected to write to this as they get data and call
2580+ finish when they are done with the request, we buffer the writes and
2581+ kick off a callback when it is done so that we can feed the result back
2582+ into boto.
2583+ """
2584+ def __init__(self, d):
2585+ self.d = d
2586+ self._buffer = StringIO.StringIO()
2587+
2588+ def write(self, chunk):
2589+ self._buffer.write(chunk)
2590+
2591+ def finish(self):
2592+ s = self._buffer.getvalue()
2593+ self.d.callback(s)
2594+
2595+ xheaders = None
2596+
2597+ @property
2598+ def stream(self):
2599+ return FakeTornadoStream()
2600+
2601+
2602+class FakeHttplibConnection(object):
2603+ """ a fake httplib.HTTPConnection for boto to use
2604+
2605+ requests made via this connection actually get translated and routed into
2606+ our tornado app, we then wait for the response and turn it back into
2607+ the httplib.HTTPResponse that boto expects.
2608+ """
2609+ def __init__(self, app, host, is_secure=False):
2610+ self.app = app
2611+ self.host = host
2612+ self.deferred = defer.Deferred()
2613+
2614+ def request(self, method, path, data, headers):
2615+ req = boto_to_tornado
2616+ conn = FakeTornadoConnection(self.deferred)
2617+ request = boto_to_tornado(connection=conn,
2618+ method=method,
2619+ path=path,
2620+ headers=headers,
2621+ data=data,
2622+ host=self.host)
2623+ handler = self.app(request)
2624+ self.deferred.addCallback(raw_to_httpresponse)
2625+
2626+ def getresponse(self):
2627+ @defer.inlineCallbacks
2628+ def _waiter():
2629+ result = yield self.deferred
2630+ defer.returnValue(result)
2631+ d = _waiter()
2632+ # NOTE(termie): defer.returnValue above should ensure that
2633+ # this deferred has already been called by the time
2634+ # we get here, we are going to cheat and return
2635+ # the result of the callback
2636+ return d.result
2637+
2638+ def close(self):
2639+ pass
2640+
2641+
2642+class ApiEc2TestCase(test.BaseTestCase):
2643+ def setUp(self):
2644+ super(ApiEc2TestCase, self).setUp()
2645+
2646+ self.users = users.UserManager.instance()
2647+ self.cloud = cloud.CloudController()
2648+
2649+ self.host = '127.0.0.1'
2650+
2651+ self.app = api.APIServerApplication({'Cloud': self.cloud})
2652+ self.ec2 = boto.connect_ec2(
2653+ aws_access_key_id='fake',
2654+ aws_secret_access_key='fake',
2655+ is_secure=False,
2656+ region=regioninfo.RegionInfo(None, 'test', self.host),
2657+ port=FLAGS.cc_port,
2658+ path='/services/Cloud')
2659+
2660+ self.mox.StubOutWithMock(self.ec2, 'new_http_connection')
2661+
2662+ def expect_http(self, host=None, is_secure=False):
2663+ http = FakeHttplibConnection(
2664+ self.app, '%s:%d' % (self.host, FLAGS.cc_port), False)
2665+ self.ec2.new_http_connection(host, is_secure).AndReturn(http)
2666+ return http
2667+
2668+ def test_describe_instances(self):
2669+ self.expect_http()
2670+ self.mox.ReplayAll()
2671+ try:
2672+ self.users.create_user('fake', 'fake', 'fake')
2673+ except Exception, _err:
2674+ pass # User may already exist
2675+ self.assertEqual(self.ec2.get_all_instances(), [])
2676+ self.users.delete_user('fake')
2677+
2678+
2679+ def test_get_all_key_pairs(self):
2680+ self.expect_http()
2681+ self.mox.ReplayAll()
2682+ keyname = "".join(random.choice("sdiuisudfsdcnpaqwertasd") for x in range(random.randint(4, 8)))
2683+ try:
2684+ self.users.create_user('fake', 'fake', 'fake')
2685+ except Exception, _err:
2686+ pass # User may already exist
2687+ self.users.generate_key_pair('fake', keyname)
2688+
2689+ rv = self.ec2.get_all_key_pairs()
2690+ self.assertTrue(filter(lambda k: k.name == keyname, rv))
2691+ self.users.delete_user('fake')
2692+
2693
2694=== added directory 'tests/bundle'
2695=== added file 'tests/bundle/1mb.manifest.xml'
2696--- tests/bundle/1mb.manifest.xml 1970-01-01 00:00:00 +0000
2697+++ tests/bundle/1mb.manifest.xml 2010-07-23 03:41:08 +0000
2698@@ -0,0 +1,1 @@
2699+<?xml version="1.0" ?><manifest><version>2007-10-10</version><bundler><name>euca-tools</name><version>1.2</version><release>31337</release></bundler><machine_configuration><architecture>x86_64</architecture></machine_configuration><image><name>1mb</name><user>42</user><type>machine</type><digest algorithm="SHA1">da39a3ee5e6b4b0d3255bfef95601890afd80709</digest><size>1048576</size><bundled_size>1136</bundled_size><ec2_encrypted_key algorithm="AES-128-CBC">33a2ea00dc64083dd9a10eb5e233635b42a7beb1670ab75452087d9de74c60aba1cd27c136fda56f62beb581de128fb1f10d072b9e556fd25e903107a57827c21f6ee8a93a4ff55b11311fcef217e3eefb07e81f71e88216f43b4b54029c1f2549f2925a839a73947d2d5aeecec4a62ece4af9156d557ae907978298296d9915</ec2_encrypted_key><user_encrypted_key algorithm="AES-128-CBC">4c11147fd8caf92447e90ce339928933d7579244c2f8ffb07cc0ea35f8738da8b90eff6c7a49671a84500e993e9462e4c36d5c19c0b3a2b397d035b4c0cce742b58e12552175d81d129b0425e9f71ebacb9aeb539fa9dd2ac36749fb82876f6902e5fb24b6ec19f35ec4c20acd50437fd30966e99c4d9a0647577970a8fa3023</user_encrypted_key><ec2_encrypted_iv>14bd082c9715f071160c69bbfb070f51d2ba1076775f1d988ccde150e515088156b248e4b5a64e46c4fe064feeeedfe14511f7fde478a51acb89f9b2f6c84b60593e5c3f792ba6b01fed9bf2158fdac03086374883b39d13a3ca74497eeaaf579fc3f26effc73bfd9446a2a8c4061f0874bfaca058905180e22d3d8881551cb3</ec2_encrypted_iv><user_encrypted_iv>8f7606f19f00e4e19535dd234b66b31b77e9c7bad3885d9c9efa75c863631fd4f82a009e17d789066d9cc6032a436f05384832f6d9a3283d3e63eab04fa0da5c8c87db9b17e854e842c3fb416507d067a266b44538125ce732e486098e8ebd1ca91fa3079f007fce7d14957a9b7e57282407ead3c6eb68fe975df3d83190021b</user_encrypted_iv><parts count="2"><part index="0"><filename>1mb.part.0</filename><digest algorithm="SHA1">c4413423cf7a57e71187e19bfd5cd4b514a64283</digest></part><part index="1"><filename>1mb.part.1</filename><digest algorithm="SHA1">9d4262e6589393d09a11a0332af169887bc2e57d</digest></part></parts></image><signature>4e00b5ba28114dda4a9df7eeae94be847ec46117a09a1cbe41e578660642f0660dda1776b39fb3bf826b6cfec019e2a5e9c566728d186b7400ebc989a30670eb1db26ce01e68bd9d3f31290370077a85b81c66b63c1e0d5499bac115c06c17a21a81b6d3a67ebbce6c17019095af7ab07f3796c708cc843e58efc12ddc788c5e</signature></manifest>
2700\ No newline at end of file
2701
2702=== added file 'tests/bundle/1mb.part.0'
2703Binary files tests/bundle/1mb.part.0 1970-01-01 00:00:00 +0000 and tests/bundle/1mb.part.0 2010-07-23 03:41:08 +0000 differ
2704=== added file 'tests/bundle/1mb.part.1'
2705--- tests/bundle/1mb.part.1 1970-01-01 00:00:00 +0000
2706+++ tests/bundle/1mb.part.1 2010-07-23 03:41:08 +0000
2707@@ -0,0 +1,1 @@
2708+­´ˆà«€ç‰°Ƴ
2709¡ÀiDHW̽×JÈ8ïrV¼³h§X’·@Yj“~Ø
2710·Gû5û 3Nt«˜•H6Ñ$§Ëgö™é Lá¢+³æ¤X†pm¬@,øŽ>7ÚÊ×užp¼ aü`¥V2X@£#á¶
2711\ No newline at end of file
2712
2713=== added file 'tests/cloud_unittest.py'
2714--- tests/cloud_unittest.py 1970-01-01 00:00:00 +0000
2715+++ tests/cloud_unittest.py 2010-07-23 03:41:08 +0000
2716@@ -0,0 +1,163 @@
2717+# vim: tabstop=4 shiftwidth=4 softtabstop=4
2718+
2719+# Copyright 2010 United States Government as represented by the
2720+# Administrator of the National Aeronautics and Space Administration.
2721+# All Rights Reserved.
2722+#
2723+# Licensed under the Apache License, Version 2.0 (the "License"); you may
2724+# not use this file except in compliance with the License. You may obtain
2725+# a copy of the License at
2726+#
2727+# http://www.apache.org/licenses/LICENSE-2.0
2728+#
2729+# Unless required by applicable law or agreed to in writing, software
2730+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
2731+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
2732+# License for the specific language governing permissions and limitations
2733+# under the License.
2734+
2735+import logging
2736+import StringIO
2737+import time
2738+from tornado import ioloop
2739+from twisted.internet import defer
2740+import unittest
2741+from xml.etree import ElementTree
2742+
2743+from nova import flags
2744+from nova import rpc
2745+from nova import test
2746+from nova.auth import users
2747+from nova.compute import node
2748+from nova.endpoint import api
2749+from nova.endpoint import cloud
2750+
2751+
2752+FLAGS = flags.FLAGS
2753+
2754+
2755+class CloudTestCase(test.BaseTestCase):
2756+ def setUp(self):
2757+ super(CloudTestCase, self).setUp()
2758+ self.flags(fake_libvirt=True,
2759+ fake_storage=True,
2760+ fake_users=True)
2761+
2762+ self.conn = rpc.Connection.instance()
2763+ logging.getLogger().setLevel(logging.DEBUG)
2764+
2765+ # set up our cloud
2766+ self.cloud = cloud.CloudController()
2767+ self.cloud_consumer = rpc.AdapterConsumer(connection=self.conn,
2768+ topic=FLAGS.cloud_topic,
2769+ proxy=self.cloud)
2770+ self.injected.append(self.cloud_consumer.attach_to_tornado(self.ioloop))
2771+
2772+ # set up a node
2773+ self.node = node.Node()
2774+ self.node_consumer = rpc.AdapterConsumer(connection=self.conn,
2775+ topic=FLAGS.compute_topic,
2776+ proxy=self.node)
2777+ self.injected.append(self.node_consumer.attach_to_tornado(self.ioloop))
2778+
2779+ try:
2780+ users.UserManager.instance().create_user('admin', 'admin', 'admin')
2781+ except: pass
2782+ admin = users.UserManager.instance().get_user('admin')
2783+ project = users.UserManager.instance().create_project('proj', 'admin', 'proj')
2784+ self.context = api.APIRequestContext(handler=None,project=project,user=admin)
2785+
2786+ def tearDown(self):
2787+ users.UserManager.instance().delete_project('proj')
2788+ users.UserManager.instance().delete_user('admin')
2789+
2790+ def test_console_output(self):
2791+ if FLAGS.fake_libvirt:
2792+ logging.debug("Can't test instances without a real virtual env.")
2793+ return
2794+ instance_id = 'foo'
2795+ inst = yield self.node.run_instance(instance_id)
2796+ output = yield self.cloud.get_console_output(self.context, [instance_id])
2797+ logging.debug(output)
2798+ self.assert_(output)
2799+ rv = yield self.node.terminate_instance(instance_id)
2800+
2801+ def test_run_instances(self):
2802+ if FLAGS.fake_libvirt:
2803+ logging.debug("Can't test instances without a real virtual env.")
2804+ return
2805+ image_id = FLAGS.default_image
2806+ instance_type = FLAGS.default_instance_type
2807+ max_count = 1
2808+ kwargs = {'image_id': image_id,
2809+ 'instance_type': instance_type,
2810+ 'max_count': max_count}
2811+ rv = yield self.cloud.run_instances(self.context, **kwargs)
2812+ # TODO: check for proper response
2813+ instance = rv['reservationSet'][0][rv['reservationSet'][0].keys()[0]][0]
2814+ logging.debug("Need to watch instance %s until it's running..." % instance['instance_id'])
2815+ while True:
2816+ rv = yield defer.succeed(time.sleep(1))
2817+ info = self.cloud._get_instance(instance['instance_id'])
2818+ logging.debug(info['state'])
2819+ if info['state'] == node.Instance.RUNNING:
2820+ break
2821+ self.assert_(rv)
2822+
2823+ if not FLAGS.fake_libvirt:
2824+ time.sleep(45) # Should use boto for polling here
2825+ for reservations in rv['reservationSet']:
2826+ # for res_id in reservations.keys():
2827+ # logging.debug(reservations[res_id])
2828+ # for instance in reservations[res_id]:
2829+ for instance in reservations[reservations.keys()[0]]:
2830+ logging.debug("Terminating instance %s" % instance['instance_id'])
2831+ rv = yield self.node.terminate_instance(instance['instance_id'])
2832+
2833+ def test_instance_update_state(self):
2834+ def instance(num):
2835+ return {
2836+ 'reservation_id': 'r-1',
2837+ 'instance_id': 'i-%s' % num,
2838+ 'image_id': 'ami-%s' % num,
2839+ 'private_dns_name': '10.0.0.%s' % num,
2840+ 'dns_name': '10.0.0%s' % num,
2841+ 'ami_launch_index': str(num),
2842+ 'instance_type': 'fake',
2843+ 'availability_zone': 'fake',
2844+ 'key_name': None,
2845+ 'kernel_id': 'fake',
2846+ 'ramdisk_id': 'fake',
2847+ 'groups': ['default'],
2848+ 'product_codes': None,
2849+ 'state': 0x01,
2850+ 'user_data': ''
2851+ }
2852+ rv = self.cloud._format_instances(self.context)
2853+ self.assert_(len(rv['reservationSet']) == 0)
2854+
2855+ # simulate launch of 5 instances
2856+ # self.cloud.instances['pending'] = {}
2857+ #for i in xrange(5):
2858+ # inst = instance(i)
2859+ # self.cloud.instances['pending'][inst['instance_id']] = inst
2860+
2861+ #rv = self.cloud._format_instances(self.admin)
2862+ #self.assert_(len(rv['reservationSet']) == 1)
2863+ #self.assert_(len(rv['reservationSet'][0]['instances_set']) == 5)
2864+ # report 4 nodes each having 1 of the instances
2865+ #for i in xrange(4):
2866+ # self.cloud.update_state('instances', {('node-%s' % i): {('i-%s' % i): instance(i)}})
2867+
2868+ # one instance should be pending still
2869+ #self.assert_(len(self.cloud.instances['pending'].keys()) == 1)
2870+
2871+ # check that the reservations collapse
2872+ #rv = self.cloud._format_instances(self.admin)
2873+ #self.assert_(len(rv['reservationSet']) == 1)
2874+ #self.assert_(len(rv['reservationSet'][0]['instances_set']) == 5)
2875+
2876+ # check that we can get metadata for each instance
2877+ #for i in xrange(4):
2878+ # data = self.cloud.get_metadata(instance(i)['private_dns_name'])
2879+ # self.assert_(data['meta-data']['ami-id'] == 'ami-%s' % i)
2880
2881=== added file 'tests/fake_flags.py'
2882--- tests/fake_flags.py 1970-01-01 00:00:00 +0000
2883+++ tests/fake_flags.py 2010-07-23 03:41:08 +0000
2884@@ -0,0 +1,28 @@
2885+# vim: tabstop=4 shiftwidth=4 softtabstop=4
2886+
2887+# Copyright 2010 United States Government as represented by the
2888+# Administrator of the National Aeronautics and Space Administration.
2889+# All Rights Reserved.
2890+#
2891+# Licensed under the Apache License, Version 2.0 (the "License"); you may
2892+# not use this file except in compliance with the License. You may obtain
2893+# a copy of the License at
2894+#
2895+# http://www.apache.org/licenses/LICENSE-2.0
2896+#
2897+# Unless required by applicable law or agreed to in writing, software
2898+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
2899+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
2900+# License for the specific language governing permissions and limitations
2901+# under the License.
2902+
2903+from nova import flags
2904+
2905+FLAGS = flags.FLAGS
2906+
2907+FLAGS.fake_libvirt = True
2908+FLAGS.fake_storage = True
2909+FLAGS.fake_rabbit = True
2910+FLAGS.fake_network = True
2911+FLAGS.fake_users = True
2912+FLAGS.verbose = True
2913
2914=== added file 'tests/future_unittest.py'
2915--- tests/future_unittest.py 1970-01-01 00:00:00 +0000
2916+++ tests/future_unittest.py 2010-07-23 03:41:08 +0000
2917@@ -0,0 +1,75 @@
2918+# vim: tabstop=4 shiftwidth=4 softtabstop=4
2919+
2920+# Copyright 2010 United States Government as represented by the
2921+# Administrator of the National Aeronautics and Space Administration.
2922+# All Rights Reserved.
2923+#
2924+# Licensed under the Apache License, Version 2.0 (the "License"); you may
2925+# not use this file except in compliance with the License. You may obtain
2926+# a copy of the License at
2927+#
2928+# http://www.apache.org/licenses/LICENSE-2.0
2929+#
2930+# Unless required by applicable law or agreed to in writing, software
2931+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
2932+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
2933+# License for the specific language governing permissions and limitations
2934+# under the License.
2935+
2936+import logging
2937+import mox
2938+import StringIO
2939+import time
2940+from tornado import ioloop
2941+from twisted.internet import defer
2942+import unittest
2943+from xml.etree import ElementTree
2944+
2945+from nova import cloud
2946+from nova import exception
2947+from nova import flags
2948+from nova import node
2949+from nova import rpc
2950+from nova import test
2951+
2952+
2953+FLAGS = flags.FLAGS
2954+
2955+
2956+class AdminTestCase(test.BaseTestCase):
2957+ def setUp(self):
2958+ super(AdminTestCase, self).setUp()
2959+ self.flags(fake_libvirt=True,
2960+ fake_rabbit=True)
2961+
2962+ self.conn = rpc.Connection.instance()
2963+
2964+ logging.getLogger().setLevel(logging.INFO)
2965+
2966+ # set up our cloud
2967+ self.cloud = cloud.CloudController()
2968+ self.cloud_consumer = rpc.AdapterConsumer(connection=self.conn,
2969+ topic=FLAGS.cloud_topic,
2970+ proxy=self.cloud)
2971+ self.injected.append(self.cloud_consumer.attach_to_tornado(self.ioloop))
2972+
2973+ # set up a node
2974+ self.node = node.Node()
2975+ self.node_consumer = rpc.AdapterConsumer(connection=self.conn,
2976+ topic=FLAGS.compute_topic,
2977+ proxy=self.node)
2978+ self.injected.append(self.node_consumer.attach_to_tornado(self.ioloop))
2979+
2980+ def test_flush_terminated(self):
2981+ # Launch an instance
2982+
2983+ # Wait until it's running
2984+
2985+ # Terminate it
2986+
2987+ # Wait until it's terminated
2988+
2989+ # Flush terminated nodes
2990+
2991+ # ASSERT that it's gone
2992+ pass
2993
2994=== added file 'tests/model_unittest.py'
2995--- tests/model_unittest.py 1970-01-01 00:00:00 +0000
2996+++ tests/model_unittest.py 2010-07-23 03:41:08 +0000
2997@@ -0,0 +1,206 @@
2998+# vim: tabstop=4 shiftwidth=4 softtabstop=4
2999+
3000+# Copyright 2010 United States Government as represented by the
3001+# Administrator of the National Aeronautics and Space Administration.
3002+# All Rights Reserved.
3003+#
3004+# Licensed under the Apache License, Version 2.0 (the "License"); you may
3005+# not use this file except in compliance with the License. You may obtain
3006+# a copy of the License at
3007+#
3008+# http://www.apache.org/licenses/LICENSE-2.0
3009+#
3010+# Unless required by applicable law or agreed to in writing, software
3011+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
3012+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
3013+# License for the specific language governing permissions and limitations
3014+# under the License.
3015+
3016+import logging
3017+import time
3018+from twisted.internet import defer
3019+
3020+from nova import exception
3021+from nova import flags
3022+from nova import test
3023+from nova import utils
3024+from nova.compute import model
3025+from nova.compute import node
3026+
3027+
3028+FLAGS = flags.FLAGS
3029+
3030+
3031+class ModelTestCase(test.TrialTestCase):
3032+ def setUp(self):
3033+ super(ModelTestCase, self).setUp()
3034+ self.flags(fake_libvirt=True,
3035+ fake_storage=True,
3036+ fake_users=True)
3037+
3038+ def tearDown(self):
3039+ model.Instance('i-test').destroy()
3040+ model.Host('testhost').destroy()
3041+ model.Daemon('testhost', 'nova-testdaemon').destroy()
3042+
3043+ def create_instance(self):
3044+ inst = model.Instance('i-test')
3045+ inst['reservation_id'] = 'r-test'
3046+ inst['launch_time'] = '10'
3047+ inst['user_id'] = 'fake'
3048+ inst['project_id'] = 'fake'
3049+ inst['instance_type'] = 'm1.tiny'
3050+ inst['node_name'] = FLAGS.node_name
3051+ inst['mac_address'] = utils.generate_mac()
3052+ inst['ami_launch_index'] = 0
3053+ inst.save()
3054+ return inst
3055+
3056+ def create_host(self):
3057+ host = model.Host('testhost')
3058+ host.save()
3059+ return host
3060+
3061+ def create_daemon(self):
3062+ daemon = model.Daemon('testhost', 'nova-testdaemon')
3063+ daemon.save()
3064+ return daemon
3065+
3066+ @defer.inlineCallbacks
3067+ def test_create_instance(self):
3068+ """store with create_instace, then test that a load finds it"""
3069+ instance = yield self.create_instance()
3070+ old = yield model.Instance(instance.identifier)
3071+ self.assertFalse(old.is_new_record())
3072+
3073+ @defer.inlineCallbacks
3074+ def test_delete_instance(self):
3075+ """create, then destroy, then make sure loads a new record"""
3076+ instance = yield self.create_instance()
3077+ yield instance.destroy()
3078+ newinst = yield model.Instance('i-test')
3079+ self.assertTrue(newinst.is_new_record())
3080+
3081+ @defer.inlineCallbacks
3082+ def test_instance_added_to_set(self):
3083+ """create, then check that it is listed for the project"""
3084+ instance = yield self.create_instance()
3085+ found = False
3086+ for x in model.InstanceDirectory().all:
3087+ if x.identifier == 'i-test':
3088+ found = True
3089+ self.assert_(found)
3090+
3091+ @defer.inlineCallbacks
3092+ def test_instance_associates_project(self):
3093+ """create, then check that it is listed for the project"""
3094+ instance = yield self.create_instance()
3095+ found = False
3096+ for x in model.InstanceDirectory().by_project(instance.project):
3097+ if x.identifier == 'i-test':
3098+ found = True
3099+ self.assert_(found)
3100+
3101+ @defer.inlineCallbacks
3102+ def test_host_class_finds_hosts(self):
3103+ host = yield self.create_host()
3104+ self.assertEqual('testhost', model.Host.lookup('testhost').identifier)
3105+
3106+ @defer.inlineCallbacks
3107+ def test_host_class_doesnt_find_missing_hosts(self):
3108+ rv = yield model.Host.lookup('woahnelly')
3109+ self.assertEqual(None, rv)
3110+
3111+ @defer.inlineCallbacks
3112+ def test_create_host(self):
3113+ """store with create_host, then test that a load finds it"""
3114+ host = yield self.create_host()
3115+ old = yield model.Host(host.identifier)
3116+ self.assertFalse(old.is_new_record())
3117+
3118+ @defer.inlineCallbacks
3119+ def test_delete_host(self):
3120+ """create, then destroy, then make sure loads a new record"""
3121+ instance = yield self.create_host()
3122+ yield instance.destroy()
3123+ newinst = yield model.Host('testhost')
3124+ self.assertTrue(newinst.is_new_record())
3125+
3126+ @defer.inlineCallbacks
3127+ def test_host_added_to_set(self):
3128+ """create, then check that it is included in list"""
3129+ instance = yield self.create_host()
3130+ found = False
3131+ for x in model.Host.all():
3132+ if x.identifier == 'testhost':
3133+ found = True
3134+ self.assert_(found)
3135+
3136+ @defer.inlineCallbacks
3137+ def test_create_daemon_two_args(self):
3138+ """create a daemon with two arguments"""
3139+ d = yield self.create_daemon()
3140+ d = model.Daemon('testhost', 'nova-testdaemon')
3141+ self.assertFalse(d.is_new_record())
3142+
3143+ @defer.inlineCallbacks
3144+ def test_create_daemon_single_arg(self):
3145+ """Create a daemon using the combined host:bin format"""
3146+ d = yield model.Daemon("testhost:nova-testdaemon")
3147+ d.save()
3148+ d = model.Daemon('testhost:nova-testdaemon')
3149+ self.assertFalse(d.is_new_record())
3150+
3151+ @defer.inlineCallbacks
3152+ def test_equality_of_daemon_single_and_double_args(self):
3153+ """Create a daemon using the combined host:bin arg, find with 2"""
3154+ d = yield model.Daemon("testhost:nova-testdaemon")
3155+ d.save()
3156+ d = model.Daemon('testhost', 'nova-testdaemon')
3157+ self.assertFalse(d.is_new_record())
3158+
3159+ @defer.inlineCallbacks
3160+ def test_equality_daemon_of_double_and_single_args(self):
3161+ """Create a daemon using the combined host:bin arg, find with 2"""
3162+ d = yield self.create_daemon()
3163+ d = model.Daemon('testhost:nova-testdaemon')
3164+ self.assertFalse(d.is_new_record())
3165+
3166+ @defer.inlineCallbacks
3167+ def test_delete_daemon(self):
3168+ """create, then destroy, then make sure loads a new record"""
3169+ instance = yield self.create_daemon()
3170+ yield instance.destroy()
3171+ newinst = yield model.Daemon('testhost', 'nova-testdaemon')
3172+ self.assertTrue(newinst.is_new_record())
3173+
3174+ @defer.inlineCallbacks
3175+ def test_daemon_heartbeat(self):
3176+ """Create a daemon, sleep, heartbeat, check for update"""
3177+ d = yield self.create_daemon()
3178+ ts = d['updated_at']
3179+ time.sleep(2)
3180+ d.heartbeat()
3181+ d2 = model.Daemon('testhost', 'nova-testdaemon')
3182+ ts2 = d2['updated_at']
3183+ self.assert_(ts2 > ts)
3184+
3185+ @defer.inlineCallbacks
3186+ def test_daemon_added_to_set(self):
3187+ """create, then check that it is included in list"""
3188+ instance = yield self.create_daemon()
3189+ found = False
3190+ for x in model.Daemon.all():
3191+ if x.identifier == 'testhost:nova-testdaemon':
3192+ found = True
3193+ self.assert_(found)
3194+
3195+ @defer.inlineCallbacks
3196+ def test_daemon_associates_host(self):
3197+ """create, then check that it is listed for the host"""
3198+ instance = yield self.create_daemon()
3199+ found = False
3200+ for x in model.Daemon.by_host('testhost'):
3201+ if x.identifier == 'testhost:nova-testdaemon':
3202+ found = True
3203+ self.assertTrue(found)
3204
3205=== added file 'tests/network_unittest.py'
3206--- tests/network_unittest.py 1970-01-01 00:00:00 +0000
3207+++ tests/network_unittest.py 2010-07-23 03:41:08 +0000
3208@@ -0,0 +1,219 @@
3209+# vim: tabstop=4 shiftwidth=4 softtabstop=4
3210+
3211+# Copyright 2010 United States Government as represented by the
3212+# Administrator of the National Aeronautics and Space Administration.
3213+# All Rights Reserved.
3214+#
3215+# Licensed under the Apache License, Version 2.0 (the "License"); you may
3216+# not use this file except in compliance with the License. You may obtain
3217+# a copy of the License at
3218+#
3219+# http://www.apache.org/licenses/LICENSE-2.0
3220+#
3221+# Unless required by applicable law or agreed to in writing, software
3222+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
3223+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
3224+# License for the specific language governing permissions and limitations
3225+# under the License.
3226+
3227+import IPy
3228+import os
3229+import logging
3230+
3231+from nova import flags
3232+from nova import test
3233+from nova import utils
3234+from nova.auth import users
3235+from nova.compute import network
3236+from nova.compute.exception import NoMoreAddresses
3237+
3238+FLAGS = flags.FLAGS
3239+
3240+class NetworkTestCase(test.TrialTestCase):
3241+ def setUp(self):
3242+ super(NetworkTestCase, self).setUp()
3243+ self.flags(fake_libvirt=True,
3244+ fake_storage=True,
3245+ fake_network=True,
3246+ network_size=32)
3247+ logging.getLogger().setLevel(logging.DEBUG)
3248+ self.manager = users.UserManager.instance()
3249+ self.dnsmasq = FakeDNSMasq()
3250+ try:
3251+ self.manager.create_user('netuser', 'netuser', 'netuser')
3252+ except: pass
3253+ for i in range(0, 6):
3254+ name = 'project%s' % i
3255+ if not self.manager.get_project(name):
3256+ self.manager.create_project(name, 'netuser', name)
3257+ self.network = network.PublicNetworkController()
3258+
3259+ def tearDown(self):
3260+ super(NetworkTestCase, self).tearDown()
3261+ for i in range(0, 6):
3262+ name = 'project%s' % i
3263+ self.manager.delete_project(name)
3264+ self.manager.delete_user('netuser')
3265+
3266+ def test_public_network_allocation(self):
3267+ pubnet = IPy.IP(flags.FLAGS.public_range)
3268+ address = self.network.allocate_ip("netuser", "project0", "public")
3269+ self.assertTrue(IPy.IP(address) in pubnet)
3270+ self.assertTrue(IPy.IP(address) in self.network.network)
3271+
3272+ def test_allocate_deallocate_ip(self):
3273+ address = network.allocate_ip(
3274+ "netuser", "project0", utils.generate_mac())
3275+ logging.debug("Was allocated %s" % (address))
3276+ net = network.get_project_network("project0", "default")
3277+ self.assertEqual(True, is_in_project(address, "project0"))
3278+ mac = utils.generate_mac()
3279+ hostname = "test-host"
3280+ self.dnsmasq.issue_ip(mac, address, hostname, net.bridge_name)
3281+ rv = network.deallocate_ip(address)
3282+
3283+ # Doesn't go away until it's dhcp released
3284+ self.assertEqual(True, is_in_project(address, "project0"))
3285+
3286+ self.dnsmasq.release_ip(mac, address, hostname, net.bridge_name)
3287+ self.assertEqual(False, is_in_project(address, "project0"))
3288+
3289+ def test_range_allocation(self):
3290+ mac = utils.generate_mac()
3291+ secondmac = utils.generate_mac()
3292+ hostname = "test-host"
3293+ address = network.allocate_ip(
3294+ "netuser", "project0", mac)
3295+ secondaddress = network.allocate_ip(
3296+ "netuser", "project1", secondmac)
3297+ net = network.get_project_network("project0", "default")
3298+ secondnet = network.get_project_network("project1", "default")
3299+
3300+ self.assertEqual(True, is_in_project(address, "project0"))
3301+ self.assertEqual(True, is_in_project(secondaddress, "project1"))
3302+ self.assertEqual(False, is_in_project(address, "project1"))
3303+
3304+ # Addresses are allocated before they're issued
3305+ self.dnsmasq.issue_ip(mac, address, hostname, net.bridge_name)
3306+ self.dnsmasq.issue_ip(secondmac, secondaddress,
3307+ hostname, secondnet.bridge_name)
3308+
3309+ rv = network.deallocate_ip(address)
3310+ self.dnsmasq.release_ip(mac, address, hostname, net.bridge_name)
3311+ self.assertEqual(False, is_in_project(address, "project0"))
3312+
3313+ # First address release shouldn't affect the second
3314+ self.assertEqual(True, is_in_project(secondaddress, "project1"))
3315+
3316+ rv = network.deallocate_ip(secondaddress)
3317+ self.dnsmasq.release_ip(secondmac, secondaddress,
3318+ hostname, secondnet.bridge_name)
3319+ self.assertEqual(False, is_in_project(secondaddress, "project1"))
3320+
3321+ def test_subnet_edge(self):
3322+ secondaddress = network.allocate_ip("netuser", "project0",
3323+ utils.generate_mac())
3324+ hostname = "toomany-hosts"
3325+ for project in range(1,5):
3326+ project_id = "project%s" % (project)
3327+ mac = utils.generate_mac()
3328+ mac2 = utils.generate_mac()
3329+ mac3 = utils.generate_mac()
3330+ address = network.allocate_ip(
3331+ "netuser", project_id, mac)
3332+ address2 = network.allocate_ip(
3333+ "netuser", project_id, mac2)
3334+ address3 = network.allocate_ip(
3335+ "netuser", project_id, mac3)
3336+ self.assertEqual(False, is_in_project(address, "project0"))
3337+ self.assertEqual(False, is_in_project(address2, "project0"))
3338+ self.assertEqual(False, is_in_project(address3, "project0"))
3339+ rv = network.deallocate_ip(address)
3340+ rv = network.deallocate_ip(address2)
3341+ rv = network.deallocate_ip(address3)
3342+ net = network.get_project_network(project_id, "default")
3343+ self.dnsmasq.release_ip(mac, address, hostname, net.bridge_name)
3344+ self.dnsmasq.release_ip(mac2, address2, hostname, net.bridge_name)
3345+ self.dnsmasq.release_ip(mac3, address3, hostname, net.bridge_name)
3346+ net = network.get_project_network("project0", "default")
3347+ rv = network.deallocate_ip(secondaddress)
3348+ self.dnsmasq.release_ip(mac, address, hostname, net.bridge_name)
3349+
3350+ def test_release_before_deallocate(self):
3351+ pass
3352+
3353+ def test_deallocate_before_issued(self):
3354+ pass
3355+
3356+ def test_too_many_addresses(self):
3357+ """
3358+ Here, we test that a proper NoMoreAddresses exception is raised.
3359+
3360+ However, the number of available IP addresses depends on the test
3361+ environment's setup.
3362+
3363+ Network size is set in test fixture's setUp method.
3364+
3365+ There are FLAGS.cnt_vpn_clients addresses reserved for VPN (NUM_RESERVED_VPN_IPS)
3366+
3367+ And there are NUM_STATIC_IPS that are always reserved by Nova for the necessary
3368+ services (gateway, CloudPipe, etc)
3369+
3370+ So we should get flags.network_size - (NUM_STATIC_IPS +
3371+ NUM_PREALLOCATED_IPS +
3372+ NUM_RESERVED_VPN_IPS)
3373+ usable addresses
3374+ """
3375+ net = network.get_project_network("project0", "default")
3376+
3377+ # Determine expected number of available IP addresses
3378+ num_static_ips = net.num_static_ips
3379+ num_preallocated_ips = len(net.hosts.keys())
3380+ num_reserved_vpn_ips = flags.FLAGS.cnt_vpn_clients
3381+ num_available_ips = flags.FLAGS.network_size - (num_static_ips + num_preallocated_ips + num_reserved_vpn_ips)
3382+
3383+ hostname = "toomany-hosts"
3384+ macs = {}
3385+ addresses = {}
3386+ for i in range(0, (num_available_ips - 1)):
3387+ macs[i] = utils.generate_mac()
3388+ addresses[i] = network.allocate_ip("netuser", "project0", macs[i])
3389+ self.dnsmasq.issue_ip(macs[i], addresses[i], hostname, net.bridge_name)
3390+
3391+ self.assertRaises(NoMoreAddresses, network.allocate_ip, "netuser", "project0", utils.generate_mac())
3392+
3393+ for i in range(0, (num_available_ips - 1)):
3394+ rv = network.deallocate_ip(addresses[i])
3395+ self.dnsmasq.release_ip(macs[i], addresses[i], hostname, net.bridge_name)
3396+
3397+def is_in_project(address, project_id):
3398+ return address in network.get_project_network(project_id).list_addresses()
3399+
3400+def _get_project_addresses(project_id):
3401+ project_addresses = []
3402+ for addr in network.get_project_network(project_id).list_addresses():
3403+ project_addresses.append(addr)
3404+ return project_addresses
3405+
3406+def binpath(script):
3407+ return os.path.join(utils.abspath("../bin"), script)
3408+
3409+class FakeDNSMasq(object):
3410+ def issue_ip(self, mac, ip, hostname, interface):
3411+ cmd = "%s add %s %s %s" % (binpath('nova-dhcpbridge'),
3412+ mac, ip, hostname)
3413+ env = {'DNSMASQ_INTERFACE': interface,
3414+ 'TESTING' : '1',
3415+ 'FLAGFILE' : FLAGS.dhcpbridge_flagfile}
3416+ (out, err) = utils.execute(cmd, addl_env=env)
3417+ logging.debug("ISSUE_IP: %s, %s " % (out, err))
3418+
3419+ def release_ip(self, mac, ip, hostname, interface):
3420+ cmd = "%s del %s %s %s" % (binpath('nova-dhcpbridge'),
3421+ mac, ip, hostname)
3422+ env = {'DNSMASQ_INTERFACE': interface,
3423+ 'TESTING' : '1',
3424+ 'FLAGFILE' : FLAGS.dhcpbridge_flagfile}
3425+ (out, err) = utils.execute(cmd, addl_env=env)
3426+ logging.debug("RELEASE_IP: %s, %s " % (out, err))
3427+
3428
3429=== added file 'tests/node_unittest.py'
3430--- tests/node_unittest.py 1970-01-01 00:00:00 +0000
3431+++ tests/node_unittest.py 2010-07-23 03:41:08 +0000
3432@@ -0,0 +1,128 @@
3433+# vim: tabstop=4 shiftwidth=4 softtabstop=4
3434+
3435+# Copyright 2010 United States Government as represented by the
3436+# Administrator of the National Aeronautics and Space Administration.
3437+# All Rights Reserved.
3438+#
3439+# Licensed under the Apache License, Version 2.0 (the "License"); you may
3440+# not use this file except in compliance with the License. You may obtain
3441+# a copy of the License at
3442+#
3443+# http://www.apache.org/licenses/LICENSE-2.0
3444+#
3445+# Unless required by applicable law or agreed to in writing, software
3446+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
3447+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
3448+# License for the specific language governing permissions and limitations
3449+# under the License.
3450+
3451+import logging
3452+import time
3453+from twisted.internet import defer
3454+from xml.etree import ElementTree
3455+
3456+from nova import exception
3457+from nova import flags
3458+from nova import test
3459+from nova import utils
3460+from nova.compute import model
3461+from nova.compute import node
3462+
3463+
3464+FLAGS = flags.FLAGS
3465+
3466+
3467+class InstanceXmlTestCase(test.TrialTestCase):
3468+ # @defer.inlineCallbacks
3469+ def test_serialization(self):
3470+ # TODO: Reimplement this, it doesn't make sense in redis-land
3471+ return
3472+
3473+ # instance_id = 'foo'
3474+ # first_node = node.Node()
3475+ # inst = yield first_node.run_instance(instance_id)
3476+ #
3477+ # # force the state so that we can verify that it changes
3478+ # inst._s['state'] = node.Instance.NOSTATE
3479+ # xml = inst.toXml()
3480+ # self.assert_(ElementTree.parse(StringIO.StringIO(xml)))
3481+ #
3482+ # second_node = node.Node()
3483+ # new_inst = node.Instance.fromXml(second_node._conn, pool=second_node._pool, xml=xml)
3484+ # self.assertEqual(new_inst.state, node.Instance.RUNNING)
3485+ # rv = yield first_node.terminate_instance(instance_id)
3486+
3487+
3488+class NodeConnectionTestCase(test.TrialTestCase):
3489+ def setUp(self):
3490+ logging.getLogger().setLevel(logging.DEBUG)
3491+ super(NodeConnectionTestCase, self).setUp()
3492+ self.flags(fake_libvirt=True,
3493+ fake_storage=True,
3494+ fake_users=True)
3495+ self.node = node.Node()
3496+
3497+ def create_instance(self):
3498+ instdir = model.InstanceDirectory()
3499+ inst = instdir.new()
3500+ # TODO(ja): add ami, ari, aki, user_data
3501+ inst['reservation_id'] = 'r-fakeres'
3502+ inst['launch_time'] = '10'
3503+ inst['user_id'] = 'fake'
3504+ inst['project_id'] = 'fake'
3505+ inst['instance_type'] = 'm1.tiny'
3506+ inst['node_name'] = FLAGS.node_name
3507+ inst['mac_address'] = utils.generate_mac()
3508+ inst['ami_launch_index'] = 0
3509+ inst.save()
3510+ return inst['instance_id']
3511+
3512+ @defer.inlineCallbacks
3513+ def test_run_describe_terminate(self):
3514+ instance_id = self.create_instance()
3515+
3516+ rv = yield self.node.run_instance(instance_id)
3517+
3518+ rv = yield self.node.describe_instances()
3519+ logging.info("Running instances: %s", rv)
3520+ self.assertEqual(rv[instance_id].name, instance_id)
3521+
3522+ rv = yield self.node.terminate_instance(instance_id)
3523+
3524+ rv = yield self.node.describe_instances()
3525+ logging.info("After terminating instances: %s", rv)
3526+ self.assertEqual(rv, {})
3527+
3528+ @defer.inlineCallbacks
3529+ def test_reboot(self):
3530+ instance_id = self.create_instance()
3531+ rv = yield self.node.run_instance(instance_id)
3532+
3533+ rv = yield self.node.describe_instances()
3534+ self.assertEqual(rv[instance_id].name, instance_id)
3535+
3536+ yield self.node.reboot_instance(instance_id)
3537+
3538+ rv = yield self.node.describe_instances()
3539+ self.assertEqual(rv[instance_id].name, instance_id)
3540+ rv = yield self.node.terminate_instance(instance_id)
3541+
3542+ @defer.inlineCallbacks
3543+ def test_console_output(self):
3544+ instance_id = self.create_instance()
3545+ rv = yield self.node.run_instance(instance_id)
3546+
3547+ console = yield self.node.get_console_output(instance_id)
3548+ self.assert_(console)
3549+ rv = yield self.node.terminate_instance(instance_id)
3550+
3551+ @defer.inlineCallbacks
3552+ def test_run_instance_existing(self):
3553+ instance_id = self.create_instance()
3554+ rv = yield self.node.run_instance(instance_id)
3555+
3556+ rv = yield self.node.describe_instances()
3557+ self.assertEqual(rv[instance_id].name, instance_id)
3558+
3559+ self.assertRaises(exception.Error, self.node.run_instance, instance_id)
3560+ rv = yield self.node.terminate_instance(instance_id)
3561
3562=== added file 'tests/objectstore_unittest.py'
3563--- tests/objectstore_unittest.py 1970-01-01 00:00:00 +0000
3564+++ tests/objectstore_unittest.py 2010-07-23 03:41:08 +0000
3565@@ -0,0 +1,203 @@
3566+# vim: tabstop=4 shiftwidth=4 softtabstop=4
3567+
3568+# Copyright 2010 United States Government as represented by the
3569+# Administrator of the National Aeronautics and Space Administration.
3570+# All Rights Reserved.
3571+#
3572+# Licensed under the Apache License, Version 2.0 (the "License"); you may
3573+# not use this file except in compliance with the License. You may obtain
3574+# a copy of the License at
3575+#
3576+# http://www.apache.org/licenses/LICENSE-2.0
3577+#
3578+# Unless required by applicable law or agreed to in writing, software
3579+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
3580+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
3581+# License for the specific language governing permissions and limitations
3582+# under the License.
3583+
3584+import glob
3585+import hashlib
3586+import logging
3587+import os
3588+import shutil
3589+import tempfile
3590+
3591+from nova import flags
3592+from nova import objectstore
3593+from nova import test
3594+from nova.auth import users
3595+
3596+
3597+FLAGS = flags.FLAGS
3598+
3599+oss_tempdir = tempfile.mkdtemp(prefix='test_oss-')
3600+
3601+
3602+# delete tempdirs from previous runs (we don't delete after test to allow
3603+# checking the contents after running tests)
3604+# TODO: This fails on the test box with a permission denied error
3605+# Also, doing these things in a global tempdir means that different runs of
3606+# the test suite on the same box could clobber each other.
3607+#for path in glob.glob(os.path.abspath(os.path.join(oss_tempdir, '../test_oss-*'))):
3608+# if path != oss_tempdir:
3609+# shutil.rmtree(path)
3610+
3611+
3612+# create bucket/images path
3613+os.makedirs(os.path.join(oss_tempdir, 'images'))
3614+os.makedirs(os.path.join(oss_tempdir, 'buckets'))
3615+
3616+class ObjectStoreTestCase(test.BaseTestCase):
3617+ def setUp(self):
3618+ super(ObjectStoreTestCase, self).setUp()
3619+ self.flags(fake_users=True,
3620+ buckets_path=os.path.join(oss_tempdir, 'buckets'),
3621+ images_path=os.path.join(oss_tempdir, 'images'),
3622+ ca_path=os.path.join(os.path.dirname(__file__), 'CA'))
3623+ logging.getLogger().setLevel(logging.DEBUG)
3624+
3625+ self.um = users.UserManager.instance()
3626+ try:
3627+ self.um.create_user('user1')
3628+ except: pass
3629+ try:
3630+ self.um.create_user('user2')
3631+ except: pass
3632+ try:
3633+ self.um.create_user('admin_user', admin=True)
3634+ except: pass
3635+ try:
3636+ self.um.create_project('proj1', 'user1', 'a proj', ['user1'])
3637+ except: pass
3638+ try:
3639+ self.um.create_project('proj2', 'user2', 'a proj', ['user2'])
3640+ except: pass
3641+ class Context(object): pass
3642+ self.context = Context()
3643+
3644+ def tearDown(self):
3645+ self.um.delete_project('proj1')
3646+ self.um.delete_project('proj2')
3647+ self.um.delete_user('user1')
3648+ self.um.delete_user('user2')
3649+ self.um.delete_user('admin_user')
3650+ super(ObjectStoreTestCase, self).tearDown()
3651+
3652+ def test_buckets(self):
3653+ self.context.user = self.um.get_user('user1')
3654+ self.context.project = self.um.get_project('proj1')
3655+ objectstore.bucket.Bucket.create('new_bucket', self.context)
3656+ bucket = objectstore.bucket.Bucket('new_bucket')
3657+
3658+ # creator is authorized to use bucket
3659+ self.assert_(bucket.is_authorized(self.context))
3660+
3661+ # another user is not authorized
3662+ self.context.user = self.um.get_user('user2')
3663+ self.context.project = self.um.get_project('proj2')
3664+ self.assert_(bucket.is_authorized(self.context) == False)
3665+
3666+ # admin is authorized to use bucket
3667+ self.context.user = self.um.get_user('admin_user')
3668+ self.context.project = None
3669+ self.assert_(bucket.is_authorized(self.context))
3670+
3671+ # new buckets are empty
3672+ self.assert_(bucket.list_keys()['Contents'] == [])
3673+
3674+ # storing keys works
3675+ bucket['foo'] = "bar"
3676+
3677+ self.assert_(len(bucket.list_keys()['Contents']) == 1)
3678+
3679+ self.assert_(bucket['foo'].read() == 'bar')
3680+
3681+ # md5 of key works
3682+ self.assert_(bucket['foo'].md5 == hashlib.md5('bar').hexdigest())
3683+
3684+ # deleting non-empty bucket throws exception
3685+ exception = False
3686+ try:
3687+ bucket.delete()
3688+ except:
3689+ exception = True
3690+
3691+ self.assert_(exception)
3692+
3693+ # deleting key
3694+ del bucket['foo']
3695+
3696+ # deleting empty button
3697+ bucket.delete()
3698+
3699+ # accessing deleted bucket throws exception
3700+ exception = False
3701+ try:
3702+ objectstore.bucket.Bucket('new_bucket')
3703+ except:
3704+ exception = True
3705+
3706+ self.assert_(exception)
3707+
3708+ def test_images(self):
3709+ self.context.user = self.um.get_user('user1')
3710+ self.context.project = self.um.get_project('proj1')
3711+
3712+ # create a bucket for our bundle
3713+ objectstore.bucket.Bucket.create('image_bucket', self.context)
3714+ bucket = objectstore.bucket.Bucket('image_bucket')
3715+
3716+ # upload an image manifest/parts
3717+ bundle_path = os.path.join(os.path.dirname(__file__), 'bundle')
3718+ for path in glob.glob(bundle_path + '/*'):
3719+ bucket[os.path.basename(path)] = open(path, 'rb').read()
3720+
3721+ # register an image
3722+ objectstore.image.Image.register_aws_image('i-testing', 'image_bucket/1mb.manifest.xml', self.context)
3723+
3724+ # verify image
3725+ my_img = objectstore.image.Image('i-testing')
3726+ result_image_file = os.path.join(my_img.path, 'image')
3727+ self.assertEqual(os.stat(result_image_file).st_size, 1048576)
3728+
3729+ sha = hashlib.sha1(open(result_image_file).read()).hexdigest()
3730+ self.assertEqual(sha, '3b71f43ff30f4b15b5cd85dd9e95ebc7e84eb5a3')
3731+
3732+ # verify image permissions
3733+ self.context.user = self.um.get_user('user2')
3734+ self.context.project = self.um.get_project('proj2')
3735+ self.assert_(my_img.is_authorized(self.context) == False)
3736+
3737+# class ApiObjectStoreTestCase(test.BaseTestCase):
3738+# def setUp(self):
3739+# super(ApiObjectStoreTestCase, self).setUp()
3740+# FLAGS.fake_users = True
3741+# FLAGS.buckets_path = os.path.join(tempdir, 'buckets')
3742+# FLAGS.images_path = os.path.join(tempdir, 'images')
3743+# FLAGS.ca_path = os.path.join(os.path.dirname(__file__), 'CA')
3744+#
3745+# self.users = users.UserManager.instance()
3746+# self.app = handler.Application(self.users)
3747+#
3748+# self.host = '127.0.0.1'
3749+#
3750+# self.conn = boto.s3.connection.S3Connection(
3751+# aws_access_key_id=user.access,
3752+# aws_secret_access_key=user.secret,
3753+# is_secure=False,
3754+# calling_format=boto.s3.connection.OrdinaryCallingFormat(),
3755+# port=FLAGS.s3_port,
3756+# host=FLAGS.s3_host)
3757+#
3758+# self.mox.StubOutWithMock(self.ec2, 'new_http_connection')
3759+#
3760+# def tearDown(self):
3761+# FLAGS.Reset()
3762+# super(ApiObjectStoreTestCase, self).tearDown()
3763+#
3764+# def test_describe_instances(self):
3765+# self.expect_http()
3766+# self.mox.ReplayAll()
3767+#
3768+# self.assertEqual(self.ec2.get_all_instances(), [])
3769
3770=== added file 'tests/process_unittest.py'
3771--- tests/process_unittest.py 1970-01-01 00:00:00 +0000
3772+++ tests/process_unittest.py 2010-07-23 03:41:08 +0000
3773@@ -0,0 +1,122 @@
3774+# vim: tabstop=4 shiftwidth=4 softtabstop=4
3775+
3776+# Copyright 2010 United States Government as represented by the
3777+# Administrator of the National Aeronautics and Space Administration.
3778+# All Rights Reserved.
3779+#
3780+# Licensed under the Apache License, Version 2.0 (the "License"); you may
3781+# not use this file except in compliance with the License. You may obtain
3782+# a copy of the License at
3783+#
3784+# http://www.apache.org/licenses/LICENSE-2.0
3785+#
3786+# Unless required by applicable law or agreed to in writing, software
3787+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
3788+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
3789+# License for the specific language governing permissions and limitations
3790+# under the License.
3791+
3792+import logging
3793+from twisted.internet import defer
3794+from twisted.internet import reactor
3795+from xml.etree import ElementTree
3796+
3797+from nova import exception
3798+from nova import flags
3799+from nova import process
3800+from nova import test
3801+from nova import utils
3802+
3803+FLAGS = flags.FLAGS
3804+
3805+
3806+class ProcessTestCase(test.TrialTestCase):
3807+ def setUp(self):
3808+ logging.getLogger().setLevel(logging.DEBUG)
3809+ super(ProcessTestCase, self).setUp()
3810+
3811+ def test_execute_stdout(self):
3812+ pool = process.ProcessPool(2)
3813+ d = pool.simple_execute('echo test')
3814+ def _check(rv):
3815+ self.assertEqual(rv[0], 'test\n')
3816+ self.assertEqual(rv[1], '')
3817+
3818+ d.addCallback(_check)
3819+ d.addErrback(self.fail)
3820+ return d
3821+
3822+ def test_execute_stderr(self):
3823+ pool = process.ProcessPool(2)
3824+ d = pool.simple_execute('cat BAD_FILE', error_ok=1)
3825+ def _check(rv):
3826+ self.assertEqual(rv[0], '')
3827+ self.assert_('No such file' in rv[1])
3828+
3829+ d.addCallback(_check)
3830+ d.addErrback(self.fail)
3831+ return d
3832+
3833+ def test_execute_unexpected_stderr(self):
3834+ pool = process.ProcessPool(2)
3835+ d = pool.simple_execute('cat BAD_FILE')
3836+ d.addCallback(lambda x: self.fail('should have raised an error'))
3837+ d.addErrback(lambda failure: failure.trap(IOError))
3838+ return d
3839+
3840+ def test_max_processes(self):
3841+ pool = process.ProcessPool(2)
3842+ d1 = pool.simple_execute('sleep 0.01')
3843+ d2 = pool.simple_execute('sleep 0.01')
3844+ d3 = pool.simple_execute('sleep 0.005')
3845+ d4 = pool.simple_execute('sleep 0.005')
3846+
3847+ called = []
3848+ def _called(rv, name):
3849+ called.append(name)
3850+
3851+ d1.addCallback(_called, 'd1')
3852+ d2.addCallback(_called, 'd2')
3853+ d3.addCallback(_called, 'd3')
3854+ d4.addCallback(_called, 'd4')
3855+
3856+ # Make sure that d3 and d4 had to wait on the other two and were called
3857+ # in order
3858+ # NOTE(termie): there may be a race condition in this test if for some
3859+ # reason one of the sleeps takes longer to complete
3860+ # than it should
3861+ d4.addCallback(lambda x: self.assertEqual(called[2], 'd3'))
3862+ d4.addCallback(lambda x: self.assertEqual(called[3], 'd4'))
3863+ d4.addErrback(self.fail)
3864+ return d4
3865+
3866+ def test_kill_long_process(self):
3867+ pool = process.ProcessPool(2)
3868+
3869+ d1 = pool.simple_execute('sleep 1')
3870+ d2 = pool.simple_execute('sleep 0.005')
3871+
3872+ timeout = reactor.callLater(0.1, self.fail, 'should have been killed')
3873+
3874+ # kill d1 and wait on it to end then cancel the timeout
3875+ d2.addCallback(lambda _: d1.process.signalProcess('KILL'))
3876+ d2.addCallback(lambda _: d1)
3877+ d2.addBoth(lambda _: timeout.active() and timeout.cancel())
3878+ d2.addErrback(self.fail)
3879+ return d2
3880+
3881+ def test_process_exit_is_contained(self):
3882+ pool = process.ProcessPool(2)
3883+
3884+ d1 = pool.simple_execute('sleep 1')
3885+ d1.addCallback(lambda x: self.fail('should have errbacked'))
3886+ d1.addErrback(lambda fail: fail.trap(IOError))
3887+ reactor.callLater(0.05, d1.process.signalProcess, 'KILL')
3888+
3889+ return d1
3890+
3891+ def test_shared_pool_is_singleton(self):
3892+ pool1 = process.SharedPool()
3893+ pool2 = process.SharedPool()
3894+ self.assert_(id(pool1) == id(pool2))
3895+
3896
3897=== added file 'tests/real_flags.py'
3898--- tests/real_flags.py 1970-01-01 00:00:00 +0000
3899+++ tests/real_flags.py 2010-07-23 03:41:08 +0000
3900@@ -0,0 +1,28 @@
3901+# vim: tabstop=4 shiftwidth=4 softtabstop=4
3902+
3903+# Copyright 2010 United States Government as represented by the
3904+# Administrator of the National Aeronautics and Space Administration.
3905+# All Rights Reserved.
3906+#
3907+# Licensed under the Apache License, Version 2.0 (the "License"); you may
3908+# not use this file except in compliance with the License. You may obtain
3909+# a copy of the License at
3910+#
3911+# http://www.apache.org/licenses/LICENSE-2.0
3912+#
3913+# Unless required by applicable law or agreed to in writing, software
3914+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
3915+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
3916+# License for the specific language governing permissions and limitations
3917+# under the License.
3918+
3919+from nova import flags
3920+
3921+FLAGS = flags.FLAGS
3922+
3923+FLAGS.fake_libvirt = False
3924+FLAGS.fake_storage = False
3925+FLAGS.fake_rabbit = False
3926+FLAGS.fake_network = False
3927+FLAGS.fake_users = False
3928+FLAGS.verbose = False
3929
3930=== added file 'tests/storage_unittest.py'
3931--- tests/storage_unittest.py 1970-01-01 00:00:00 +0000
3932+++ tests/storage_unittest.py 2010-07-23 03:41:08 +0000
3933@@ -0,0 +1,115 @@
3934+# vim: tabstop=4 shiftwidth=4 softtabstop=4
3935+
3936+# Copyright 2010 United States Government as represented by the
3937+# Administrator of the National Aeronautics and Space Administration.
3938+# All Rights Reserved.
3939+#
3940+# Licensed under the Apache License, Version 2.0 (the "License"); you may
3941+# not use this file except in compliance with the License. You may obtain
3942+# a copy of the License at
3943+#
3944+# http://www.apache.org/licenses/LICENSE-2.0
3945+#
3946+# Unless required by applicable law or agreed to in writing, software
3947+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
3948+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
3949+# License for the specific language governing permissions and limitations
3950+# under the License.
3951+
3952+import logging
3953+
3954+from nova import exception
3955+from nova import flags
3956+from nova import test
3957+from nova.compute import node
3958+from nova.volume import storage
3959+
3960+
3961+FLAGS = flags.FLAGS
3962+
3963+
3964+class StorageTestCase(test.TrialTestCase):
3965+ def setUp(self):
3966+ logging.getLogger().setLevel(logging.DEBUG)
3967+ super(StorageTestCase, self).setUp()
3968+ self.mynode = node.Node()
3969+ self.mystorage = None
3970+ self.flags(fake_libvirt=True,
3971+ fake_storage=True)
3972+ self.mystorage = storage.BlockStore()
3973+
3974+ def test_run_create_volume(self):
3975+ vol_size = '0'
3976+ user_id = 'fake'
3977+ project_id = 'fake'
3978+ volume_id = self.mystorage.create_volume(vol_size, user_id, project_id)
3979+ # TODO(termie): get_volume returns differently than create_volume
3980+ self.assertEqual(volume_id,
3981+ storage.get_volume(volume_id)['volume_id'])
3982+
3983+ rv = self.mystorage.delete_volume(volume_id)
3984+ self.assertRaises(exception.Error,
3985+ storage.get_volume,
3986+ volume_id)
3987+
3988+ def test_too_big_volume(self):
3989+ vol_size = '1001'
3990+ user_id = 'fake'
3991+ project_id = 'fake'
3992+ self.assertRaises(TypeError,
3993+ self.mystorage.create_volume,
3994+ vol_size, user_id, project_id)
3995+
3996+ def test_too_many_volumes(self):
3997+ vol_size = '1'
3998+ user_id = 'fake'
3999+ project_id = 'fake'
4000+ num_shelves = FLAGS.last_shelf_id - FLAGS.first_shelf_id + 1
4001+ total_slots = FLAGS.slots_per_shelf * num_shelves
4002+ vols = []
4003+ for i in xrange(total_slots):
4004+ vid = self.mystorage.create_volume(vol_size, user_id, project_id)
4005+ vols.append(vid)
4006+ self.assertRaises(storage.NoMoreVolumes,
4007+ self.mystorage.create_volume,
4008+ vol_size, user_id, project_id)
4009+ for id in vols:
4010+ self.mystorage.delete_volume(id)
4011+
4012+ def test_run_attach_detach_volume(self):
4013+ # Create one volume and one node to test with
4014+ instance_id = "storage-test"
4015+ vol_size = "5"
4016+ user_id = "fake"
4017+ project_id = 'fake'
4018+ mountpoint = "/dev/sdf"
4019+ volume_id = self.mystorage.create_volume(vol_size, user_id, project_id)
4020+
4021+ volume_obj = storage.get_volume(volume_id)
4022+ volume_obj.start_attach(instance_id, mountpoint)
4023+ rv = yield self.mynode.attach_volume(volume_id,
4024+ instance_id,
4025+ mountpoint)
4026+ self.assertEqual(volume_obj['status'], "in-use")
4027+ self.assertEqual(volume_obj['attachStatus'], "attached")
4028+ self.assertEqual(volume_obj['instance_id'], instance_id)
4029+ self.assertEqual(volume_obj['mountpoint'], mountpoint)
4030+
4031+ self.assertRaises(exception.Error,
4032+ self.mystorage.delete_volume,
4033+ volume_id)
4034+
4035+ rv = yield self.mystorage.detach_volume(volume_id)
4036+ volume_obj = storage.get_volume(volume_id)
4037+ self.assertEqual(volume_obj['status'], "available")
4038+
4039+ rv = self.mystorage.delete_volume(volume_id)
4040+ self.assertRaises(exception.Error,
4041+ storage.get_volume,
4042+ volume_id)
4043+
4044+ def test_multi_node(self):
4045+ # TODO(termie): Figure out how to test with two nodes,
4046+ # each of them having a different FLAG for storage_node
4047+ # This will allow us to test cross-node interactions
4048+ pass
4049
4050=== added file 'tests/users_unittest.py'
4051--- tests/users_unittest.py 1970-01-01 00:00:00 +0000
4052+++ tests/users_unittest.py 2010-07-23 03:41:08 +0000
4053@@ -0,0 +1,207 @@
4054+# vim: tabstop=4 shiftwidth=4 softtabstop=4
4055+
4056+# Copyright 2010 United States Government as represented by the
4057+# Administrator of the National Aeronautics and Space Administration.
4058+# All Rights Reserved.
4059+#
4060+# Licensed under the Apache License, Version 2.0 (the "License"); you may
4061+# not use this file except in compliance with the License. You may obtain
4062+# a copy of the License at
4063+#
4064+# http://www.apache.org/licenses/LICENSE-2.0
4065+#
4066+# Unless required by applicable law or agreed to in writing, software
4067+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
4068+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
4069+# License for the specific language governing permissions and limitations
4070+# under the License.
4071+
4072+import logging
4073+from M2Crypto import BIO
4074+from M2Crypto import RSA
4075+from M2Crypto import X509
4076+import unittest
4077+
4078+from nova import crypto
4079+from nova import flags
4080+from nova import test
4081+from nova.auth import users
4082+from nova.endpoint import cloud
4083+
4084+FLAGS = flags.FLAGS
4085+
4086+
4087+class UserTestCase(test.BaseTestCase):
4088+ flush_db = False
4089+ def setUp(self):
4090+ super(UserTestCase, self).setUp()
4091+ self.flags(fake_libvirt=True,
4092+ fake_storage=True)
4093+ self.users = users.UserManager.instance()
4094+
4095+ def test_001_can_create_users(self):
4096+ self.users.create_user('test1', 'access', 'secret')
4097+ self.users.create_user('test2')
4098+
4099+ def test_002_can_get_user(self):
4100+ user = self.users.get_user('test1')
4101+
4102+ def test_003_can_retreive_properties(self):
4103+ user = self.users.get_user('test1')
4104+ self.assertEqual('test1', user.id)
4105+ self.assertEqual('access', user.access)
4106+ self.assertEqual('secret', user.secret)
4107+
4108+ def test_004_signature_is_valid(self):
4109+ #self.assertTrue(self.users.authenticate( **boto.generate_url ... ? ? ? ))
4110+ pass
4111+ #raise NotImplementedError
4112+
4113+ def test_005_can_get_credentials(self):
4114+ return
4115+ credentials = self.users.get_user('test1').get_credentials()
4116+ self.assertEqual(credentials,
4117+ 'export EC2_ACCESS_KEY="access"\n' +
4118+ 'export EC2_SECRET_KEY="secret"\n' +
4119+ 'export EC2_URL="http://127.0.0.1:8773/services/Cloud"\n' +
4120+ 'export S3_URL="http://127.0.0.1:3333/"\n' +
4121+ 'export EC2_USER_ID="test1"\n')
4122+
4123+ def test_006_test_key_storage(self):
4124+ user = self.users.get_user('test1')
4125+ user.create_key_pair('public', 'key', 'fingerprint')
4126+ key = user.get_key_pair('public')
4127+ self.assertEqual('key', key.public_key)
4128+ self.assertEqual('fingerprint', key.fingerprint)
4129+
4130+ def test_007_test_key_generation(self):
4131+ user = self.users.get_user('test1')
4132+ private_key, fingerprint = user.generate_key_pair('public2')
4133+ key = RSA.load_key_string(private_key, callback=lambda: None)
4134+ bio = BIO.MemoryBuffer()
4135+ public_key = user.get_key_pair('public2').public_key
4136+ key.save_pub_key_bio(bio)
4137+ converted = crypto.ssl_pub_to_ssh_pub(bio.read())
4138+ # assert key fields are equal
4139+ self.assertEqual(public_key.split(" ")[1].strip(),
4140+ converted.split(" ")[1].strip())
4141+
4142+ def test_008_can_list_key_pairs(self):
4143+ keys = self.users.get_user('test1').get_key_pairs()
4144+ self.assertTrue(filter(lambda k: k.name == 'public', keys))
4145+ self.assertTrue(filter(lambda k: k.name == 'public2', keys))
4146+
4147+ def test_009_can_delete_key_pair(self):
4148+ self.users.get_user('test1').delete_key_pair('public')
4149+ keys = self.users.get_user('test1').get_key_pairs()
4150+ self.assertFalse(filter(lambda k: k.name == 'public', keys))
4151+
4152+ def test_010_can_list_users(self):
4153+ users = self.users.get_users()
4154+ logging.warn(users)
4155+ self.assertTrue(filter(lambda u: u.id == 'test1', users))
4156+
4157+ def test_101_can_add_user_role(self):
4158+ self.assertFalse(self.users.has_role('test1', 'itsec'))
4159+ self.users.add_role('test1', 'itsec')
4160+ self.assertTrue(self.users.has_role('test1', 'itsec'))
4161+
4162+ def test_199_can_remove_user_role(self):
4163+ self.assertTrue(self.users.has_role('test1', 'itsec'))
4164+ self.users.remove_role('test1', 'itsec')
4165+ self.assertFalse(self.users.has_role('test1', 'itsec'))
4166+
4167+ def test_201_can_create_project(self):
4168+ project = self.users.create_project('testproj', 'test1', 'A test project', ['test1'])
4169+ self.assertTrue(filter(lambda p: p.name == 'testproj', self.users.get_projects()))
4170+ self.assertEqual(project.name, 'testproj')
4171+ self.assertEqual(project.description, 'A test project')
4172+ self.assertEqual(project.project_manager_id, 'test1')
4173+ self.assertTrue(project.has_member('test1'))
4174+
4175+ def test_202_user1_is_project_member(self):
4176+ self.assertTrue(self.users.get_user('test1').is_project_member('testproj'))
4177+
4178+ def test_203_user2_is_not_project_member(self):
4179+ self.assertFalse(self.users.get_user('test2').is_project_member('testproj'))
4180+
4181+ def test_204_user1_is_project_manager(self):
4182+ self.assertTrue(self.users.get_user('test1').is_project_manager('testproj'))
4183+
4184+ def test_205_user2_is_not_project_manager(self):
4185+ self.assertFalse(self.users.get_user('test2').is_project_manager('testproj'))
4186+
4187+ def test_206_can_add_user_to_project(self):
4188+ self.users.add_to_project('test2', 'testproj')
4189+ self.assertTrue(self.users.get_project('testproj').has_member('test2'))
4190+
4191+ def test_208_can_remove_user_from_project(self):
4192+ self.users.remove_from_project('test2', 'testproj')
4193+ self.assertFalse(self.users.get_project('testproj').has_member('test2'))
4194+
4195+ def test_209_can_generate_x509(self):
4196+ # MUST HAVE RUN CLOUD SETUP BY NOW
4197+ self.cloud = cloud.CloudController()
4198+ self.cloud.setup()
4199+ private_key, signed_cert_string = self.users.get_project('testproj').generate_x509_cert('test1')
4200+ logging.debug(signed_cert_string)
4201+
4202+ # Need to verify that it's signed by the right intermediate CA
4203+ full_chain = crypto.fetch_ca(project_id='testproj', chain=True)
4204+ int_cert = crypto.fetch_ca(project_id='testproj', chain=False)
4205+ cloud_cert = crypto.fetch_ca()
4206+ logging.debug("CA chain:\n\n =====\n%s\n\n=====" % full_chain)
4207+ signed_cert = X509.load_cert_string(signed_cert_string)
4208+ chain_cert = X509.load_cert_string(full_chain)
4209+ int_cert = X509.load_cert_string(int_cert)
4210+ cloud_cert = X509.load_cert_string(cloud_cert)
4211+ self.assertTrue(signed_cert.verify(chain_cert.get_pubkey()))
4212+ self.assertTrue(signed_cert.verify(int_cert.get_pubkey()))
4213+
4214+ if not FLAGS.use_intermediate_ca:
4215+ self.assertTrue(signed_cert.verify(cloud_cert.get_pubkey()))
4216+ else:
4217+ self.assertFalse(signed_cert.verify(cloud_cert.get_pubkey()))
4218+
4219+ def test_210_can_add_project_role(self):
4220+ project = self.users.get_project('testproj')
4221+ self.assertFalse(project.has_role('test1', 'sysadmin'))
4222+ self.users.add_role('test1', 'sysadmin')
4223+ self.assertFalse(project.has_role('test1', 'sysadmin'))
4224+ project.add_role('test1', 'sysadmin')
4225+ self.assertTrue(project.has_role('test1', 'sysadmin'))
4226+
4227+ def test_211_can_remove_project_role(self):
4228+ project = self.users.get_project('testproj')
4229+ self.assertTrue(project.has_role('test1', 'sysadmin'))
4230+ project.remove_role('test1', 'sysadmin')
4231+ self.assertFalse(project.has_role('test1', 'sysadmin'))
4232+ self.users.remove_role('test1', 'sysadmin')
4233+ self.assertFalse(project.has_role('test1', 'sysadmin'))
4234+
4235+ def test_212_vpn_ip_and_port_looks_valid(self):
4236+ project = self.users.get_project('testproj')
4237+ self.assert_(project.vpn_ip)
4238+ self.assert_(project.vpn_port >= FLAGS.vpn_start_port)
4239+ self.assert_(project.vpn_port <= FLAGS.vpn_end_port)
4240+
4241+ def test_213_too_many_vpns(self):
4242+ for i in xrange(users.Vpn.num_ports_for_ip(FLAGS.vpn_ip)):
4243+ users.Vpn.create("vpnuser%s" % i)
4244+ self.assertRaises(users.NoMorePorts, users.Vpn.create, "boom")
4245+
4246+ def test_299_can_delete_project(self):
4247+ self.users.delete_project('testproj')
4248+ self.assertFalse(filter(lambda p: p.name == 'testproj', self.users.get_projects()))
4249+
4250+ def test_999_can_delete_users(self):
4251+ self.users.delete_user('test1')
4252+ users = self.users.get_users()
4253+ self.assertFalse(filter(lambda u: u.id == 'test1', users))
4254+ self.users.delete_user('test2')
4255+ self.assertEqual(self.users.get_user('test2'), None)
4256+
4257+
4258+if __name__ == "__main__":
4259+ # TODO: Implement use_fake as an option
4260+ unittest.main()
4261
4262=== added file 'tests/validator_unittest.py'
4263--- tests/validator_unittest.py 1970-01-01 00:00:00 +0000
4264+++ tests/validator_unittest.py 2010-07-23 03:41:08 +0000
4265@@ -0,0 +1,41 @@
4266+# vim: tabstop=4 shiftwidth=4 softtabstop=4
4267+
4268+# Copyright 2010 United States Government as represented by the
4269+# Administrator of the National Aeronautics and Space Administration.
4270+# All Rights Reserved.
4271+#
4272+# Licensed under the Apache License, Version 2.0 (the "License"); you may
4273+# not use this file except in compliance with the License. You may obtain
4274+# a copy of the License at
4275+#
4276+# http://www.apache.org/licenses/LICENSE-2.0
4277+#
4278+# Unless required by applicable law or agreed to in writing, software
4279+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
4280+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
4281+# License for the specific language governing permissions and limitations
4282+# under the License.
4283+
4284+import logging
4285+import unittest
4286+
4287+from nova import flags
4288+from nova import test
4289+from nova import validate
4290+
4291+
4292+class ValidationTestCase(test.TrialTestCase):
4293+ def setUp(self):
4294+ super(ValidationTestCase, self).setUp()
4295+
4296+ def tearDown(self):
4297+ super(ValidationTestCase, self).tearDown()
4298+
4299+ def test_type_validation(self):
4300+ self.assertTrue(type_case("foo", 5, 1))
4301+ self.assertRaises(TypeError, type_case, "bar", "5", 1)
4302+ self.assertRaises(TypeError, type_case, None, 5, 1)
4303+
4304+@validate.typetest(instanceid=str, size=int, number_of_instances=int)
4305+def type_case(instanceid, size, number_of_instances):
4306+ return True