Merge lp:~zedshaw/nova/generic-msg-queue-layer into lp:~hudson-openstack/nova/trunk

Proposed by Zed A. Shaw
Status: Merged
Approved by: Vish Ishaya
Approved revision: 1333
Merged at revision: 1346
Proposed branch: lp:~zedshaw/nova/generic-msg-queue-layer
Merge into: lp:~hudson-openstack/nova/trunk
Diff against target: 689 lines (+198/-305)
12 files modified
Authors (+1/-0)
nova/rpc/__init__.py (+66/-0)
nova/rpc/amqp.py (+1/-22)
nova/rpc/common.py (+23/-0)
nova/service.py (+12/-16)
nova/test.py (+0/-16)
nova/tests/test_adminapi.py (+1/-1)
nova/tests/test_cloud.py (+5/-27)
nova/tests/test_rpc.py (+12/-49)
nova/tests/test_rpc_amqp.py (+68/-0)
nova/tests/test_service.py (+0/-170)
nova/tests/test_test.py (+9/-4)
To merge this branch: bzr merge lp:~zedshaw/nova/generic-msg-queue-layer
Reviewer Review Type Date Requested Status
Trey Morris (community) Approve
Jason Kölker (community) Approve
Vish Ishaya (community) Needs Information
Lorin Hochstein (community) Abstain
Review via email: mp+69712@code.launchpad.net

Description of the change

This change creates a minimalist API abstraction for the nova/rpc.py code so that it's possible to use other queue mechanisms besides Rabbit and/or AMQP, and even use other drivers for AMQP rather than Rabbit. The change is intended to give the least amount of interference with the rest of the code, fixes several bugs in the tests, and works with the current branch. I also have a small demo driver+server for using 0MQ which I'll submit after this patch is merged.

To post a comment you must log in.
Revision history for this message
Brian Waldon (bcwaldon) wrote :

Looks like you may have requested the wrong Vish to review this. You probably want https://launchpad.net/~vishvananda

Revision history for this message
Lorin Hochstein (lorinh) wrote :

It looks like there are some artifacts in the source files left over from resolving merge conflicts:

nova/tests/test_adminapi.py

266: +<<<<<<< TREE
274: +=======
281: +>>>>>>> MERGE-SOURCE

review: Needs Fixing
Revision history for this message
Vish Ishaya (vishvananda) wrote :

Looks pretty good. Something weird happened in merge of test_cloud. I think you accidentally deleted a test or two. Any possible replacement for the deleted tests in test_service, or were they just too dependent on amqp to be useful?

review: Needs Fixing
Revision history for this message
Zed A. Shaw (zedshaw) wrote :

On Fri, Jul 29, 2011 at 12:36:34AM -0000, Lorin Hochstein wrote:
> Review: Needs Fixing
> It looks like there are some artifacts in the source files left over from resolving merge conflicts:

No, that's not in my source, it's from the bzr merge resolution. Since
it takes people days to review things there ends up being conflicts by
the time it's ready. I'll try it again.

--
Zed A. Shaw
http://zedshaw.com/

Revision history for this message
Zed A. Shaw (zedshaw) wrote :

On Fri, Jul 29, 2011 at 01:58:50AM -0000, Vish Ishaya wrote:
> Review: Needs Fixing
> Looks pretty good. Something weird happened in merge of test_cloud. I think you accidentally deleted a test or two. Any possible replacement for the deleted tests in test_service, or were they just too dependent on amqp to be useful?

We found the tests actually didn't test anything in service, they just
tested some things in amqp, but then used mock objects which meant they
didn't test anything.

--
Zed A. Shaw
http://zedshaw.com/

Revision history for this message
Lorin Hochstein (lorinh) wrote :

> On Fri, Jul 29, 2011 at 12:36:34AM -0000, Lorin Hochstein wrote:
> > Review: Needs Fixing
> > It looks like there are some artifacts in the source files left over from
> resolving merge conflicts:
>
> No, that's not in my source, it's from the bzr merge resolution. Since
> it takes people days to review things there ends up being conflicts by
> the time it's ready. I'll try it again.
>

Whoops, sorry about that.

review: Abstain
Revision history for this message
Zed A. Shaw (zedshaw) wrote :

On Fri, Jul 29, 2011 at 03:16:28AM -0000, Lorin Hochstein wrote:
> Whoops, sorry about that.

No problem, all the tools do a bad job of showing what's really going
on. I'm doing the merge and retest now, should have it committed soon.

--
Zed A. Shaw
http://zedshaw.com/

Revision history for this message
Vish Ishaya (vishvananda) wrote :

nice zed!

Looking forward to the alternative implementation. Just one minor question. Why use load_module instead of using the existing import_object? It looks like it does the same thing (with the exception that import_object will also let you use a class instead of a module for your backend)

review: Needs Information
Revision history for this message
Zed A. Shaw (zedshaw) wrote :

On Fri, Jul 29, 2011 at 06:43:24AM -0000, Vish Ishaya wrote:
> Review: Needs Information nice zed!
>
> Looking forward to the alternative implementation. Just one minor
> question. Why use load_module instead of using the existing
> import_object? It looks like it does the same thing (with the
> exception that import_object will also let you use a class instead of
> a module for your backend)

I needed just a module loader, not an object-in-a-module loader. The
way the nova.rpc code is used only at the module level in most of the
code (rpc.call, rpc.cast, etc.) meant I could refactor the code to use
an OOP style without changing tons of code everywhere. It could be
changed to be generic (since python sort of doesn't care), and use an
object or module, but right now that'd be too much change for one patch.

--
Zed A. Shaw
http://zedshaw.com/

Revision history for this message
Vish Ishaya (vishvananda) wrote :

> It could be
> changed to be generic (since python sort of doesn't care), and use an
> object or module, but right now that'd be too much change for one patch.

that is exactly what utils.import_object does. It attempts to import it as a module or object, if that fails, it attempts to import it as a callable and call it.

network/manager.py uses a module:
97:flags.DEFINE_string('network_driver', 'nova.network.linux_net'
314: self.driver = utils.import_object(network_driver)

compute/manager.py uses a method:
65:flags.DEFINE_string('compute_driver', 'nova.virt.connection.get_connection'
134: utils.import_object(compute_driver),

volume/manager.py uses a class:
59:flags.DEFINE_string('volume_driver', 'nova.volume.driver.ISCSIDriver',
71: self.driver = utils.import_object(volume_driver)

Gotta love python! I think you can just replace your load_module with import_object with no issues.

Revision history for this message
Zed A. Shaw (zedshaw) wrote :

On Fri, Jul 29, 2011 at 07:07:25AM -0000, Vish Ishaya wrote:
> > It could be
> > changed to be generic (since python sort of doesn't care), and use an
> > object or module, but right now that'd be too much change for one patch.
>
> that is exactly what utils.import_object does. It attempts to import it as a module or object, if that fails, it attempts to import it as a callable and call it.

So then it's not really import_object, it's more "import stuff
magically"? :-)

--
Zed A. Shaw
http://zedshaw.com/

Revision history for this message
Vish Ishaya (vishvananda) wrote :

> So then it's not really import_object, it's more "import stuff
> magically"? :-)

I never claimed it had a good name. :p

Revision history for this message
Jason Kölker (jason-koelker) wrote :

Any reason not to rename rpc_backends to rpc and move rpc.py into __init__.py or rpc/api.py and then import it in __init__.py to keep the layout the same as the rest of the codebase?

Other than that, is bueno. Tests pass and I was able to boot an instance. I'm glad you squared this away, its been annoying me that it wasn't pluggable.

review: Needs Information
Revision history for this message
Zed A. Shaw (zedshaw) wrote :

On Fri, Jul 29, 2011 at 03:27:51PM -0000, Jason Kölker wrote:
> Review: Needs Information
> Any reason not to rename rpc_backends to rpc and move rpc.py into __init__.py or rpc/api.py and then import it in __init__.py to keep the layout the same as the rest of the codebase?

I believe I tried that but the imports didn't work right without the
abstration code being in an explicit nova/rpc.py module.

--
Zed A. Shaw
http://zedshaw.com/

Revision history for this message
Zed A. Shaw (zedshaw) wrote :

On Fri, Jul 29, 2011 at 03:01:51PM -0000, Vish Ishaya wrote:
>
> > So then it's not really import_object, it's more "import stuff
> > magically"? :-)
>
> I never claimed it had a good name. :p

Ok, this is fixed, it now just uses the nova.util.import_object.

FYI, the tests are broken if you have to make a new virtualenv.

--
Zed A. Shaw
http://zedshaw.com/

Revision history for this message
Zed A. Shaw (zedshaw) wrote :

On Fri, Jul 29, 2011 at 03:27:51PM -0000, Jason Kölker wrote:
> Review: Needs Information
> Any reason not to rename rpc_backends to rpc and move rpc.py into __init__.py or rpc/api.py and then import it in __init__.py to keep the layout the same as the rest of the codebase?

I just fixed the latest thing Vish brought up, and then looking at how
I'd have to change the code to fit this request I'm thinking it makes it
more complicated.

I'm going to ask that, if the reviewers want this request, that they
vote on it so that I'm not chasing my tail implementing something that's
not really necessary.

After this patch is in, it'll be easier to refactor.

--
Zed A. Shaw
http://zedshaw.com/

Revision history for this message
Trey Morris (tr3buchet) wrote :

"...keep the layout the same as the rest of the codebase?"
+1

Revision history for this message
Zed A. Shaw (zedshaw) wrote :

On Fri, Jul 29, 2011 at 06:07:26PM -0000, Trey Morris wrote:
> "...keep the layout the same as the rest of the codebase?"
> +1

If that's the case, point me to another part of the codebase I should
model this after. Specific files that do this same design of "loadable
modules are inside a directory with __init__.py as the abstraction."

--
Zed A. Shaw
http://zedshaw.com/

Revision history for this message
Lorin Hochstein (lorinh) wrote :

> On Fri, Jul 29, 2011 at 03:01:51PM -0000, Vish Ishaya wrote:
> FYI, the tests are broken if you have to make a new virtualenv.
>

Logged this as https://bugs.launchpad.net/nova/+bug/818210 (assuming it's the same problem you're encountering).

Revision history for this message
Jason Kölker (jason-koelker) wrote :

> On Fri, Jul 29, 2011 at 06:07:26PM -0000, Trey Morris wrote:
> > "...keep the layout the same as the rest of the codebase?"
> > +1
>
> If that's the case, point me to another part of the codebase I should
> model this after. Specific files that do this same design of "loadable
> modules are inside a directory with __init__.py as the abstraction."

The ipv6 module is the simplest, but the db uses the same pattern as well. Its a trivial change:

$ bzr diff
=== renamed directory 'nova/rpc_backends' => 'nova/rpc'
=== modified file 'nova/rpc/__init__.py'
--- nova/rpc_backends/__init__.py 2011-07-26 23:29:50 +0000
+++ nova/rpc/__init__.py 2011-07-29 18:58:38 +0000
@@ -0,0 +1,2 @@
+from nova.rpc.api import *
+from nova.rpc.common import RemoteError, LOG

=== modified file 'nova/rpc/amqp.py'
--- nova/rpc_backends/amqp.py 2011-07-26 23:29:50 +0000
+++ nova/rpc/amqp.py 2011-07-29 18:57:42 +0000
@@ -44,7 +44,7 @@
 from nova import flags
 from nova import log as logging
 from nova import utils
-from nova.rpc_backends.common import RemoteError, LOG
+from nova.rpc.common import RemoteError, LOG

 FLAGS = flags.FLAGS

=== renamed file 'nova/rpc.py' => 'nova/rpc/api.py'
--- nova/rpc.py 2011-07-29 17:33:58 +0000
+++ nova/rpc/api.py 2011-07-29 18:58:11 +0000
@@ -18,12 +18,11 @@

 from nova.utils import import_object
-from nova.rpc_backends.common import RemoteError, LOG
 from nova import flags

 FLAGS = flags.FLAGS
 flags.DEFINE_string('rpc_backend',
- 'nova.rpc_backends.amqp',
+ 'nova.rpc.amqp',
                     "The messaging module to use, defaults to AMQP.")

 RPCIMPL = import_object(FLAGS.rpc_backend)

=== modified file 'nova/tests/test_rpc_amqp.py'
--- nova/tests/test_rpc_amqp.py 2011-07-26 23:29:50 +0000
+++ nova/tests/test_rpc_amqp.py 2011-07-29 19:02:57 +0000
@@ -2,7 +2,7 @@
 from nova import flags
 from nova import log as logging
 from nova import rpc
-from nova.rpc_backends import amqp
+from nova.rpc import amqp
 from nova import test

Revision history for this message
Jason Kölker (jason-koelker) wrote :

> > On Fri, Jul 29, 2011 at 06:07:26PM -0000, Trey Morris wrote:
> > > "...keep the layout the same as the rest of the codebase?"
> > > +1
> >
> > If that's the case, point me to another part of the codebase I should
> > model this after. Specific files that do this same design of "loadable
> > modules are inside a directory with __init__.py as the abstraction."
>
> The ipv6 module is the simplest, but the db uses the same pattern as well. Its
> a trivial change:
>

Ah, I see you already did that, excellent!

review: Approve
Revision history for this message
Zed A. Shaw (zedshaw) wrote :

On Fri, Jul 29, 2011 at 06:16:02PM -0000, Zed A. Shaw wrote:
> On Fri, Jul 29, 2011 at 06:07:26PM -0000, Trey Morris wrote:
> > "...keep the layout the same as the rest of the codebase?"
> > +1

Alright, it's now matching the "rest of the codebase", based on the
description since I can't find other modules doing this with a similar
layout.

It's also remerged, and new request to review put in.

--
Zed A. Shaw
http://zedshaw.com/

Revision history for this message
Trey Morris (tr3buchet) wrote :

koelker: haha way to pay attention!

zed: i see you just added yourself to Authors. welcome to the party!

review: Approve
Revision history for this message
Zed A. Shaw (zedshaw) wrote :

On Fri, Jul 29, 2011 at 07:22:35PM -0000, Jason Kölker wrote:
> > The ipv6 module is the simplest, but the db uses the same pattern as well. Its
> > a trivial change:
> >
>
> Ah, I see you already did that, excellent!

Yep, did it already. FYI, it's not trivial to move these modules around
because people are trying to edit them *and* bzr tends to have problems
with moved files. If there's much more moving required I'll ask that it
be done on subsequent changes to the code rather than wait around for
perfection on this patch.

--
Zed A. Shaw
http://zedshaw.com/

Revision history for this message
Zed A. Shaw (zedshaw) wrote :

On Fri, Jul 29, 2011 at 07:25:58PM -0000, Trey Morris wrote:
> Review: Approve
> koelker: haha way to pay attention!
>
> zed: i see you just added yourself to Authors. welcome to the party!

Thanks, although it was mostly to satisfy a unit test.

--
Zed A. Shaw
http://zedshaw.com/

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
=== modified file 'Authors'
--- Authors 2011-07-26 00:49:36 +0000
+++ Authors 2011-07-29 19:17:40 +0000
@@ -105,3 +105,4 @@
105Youcef Laribi <Youcef.Laribi@eu.citrix.com>105Youcef Laribi <Youcef.Laribi@eu.citrix.com>
106Yuriy Taraday <yorik.sar@gmail.com>106Yuriy Taraday <yorik.sar@gmail.com>
107Zhixue Wu <Zhixue.Wu@citrix.com>107Zhixue Wu <Zhixue.Wu@citrix.com>
108Zed Shaw <zedshaw@zedshaw.com>
108109
=== added directory 'nova/rpc'
=== added file 'nova/rpc/__init__.py'
--- nova/rpc/__init__.py 1970-01-01 00:00:00 +0000
+++ nova/rpc/__init__.py 2011-07-29 19:17:40 +0000
@@ -0,0 +1,66 @@
1# vim: tabstop=4 shiftwidth=4 softtabstop=4
2
3# Copyright 2010 United States Government as represented by the
4# Administrator of the National Aeronautics and Space Administration.
5# All Rights Reserved.
6#
7# Licensed under the Apache License, Version 2.0 (the "License"); you may
8# not use this file except in compliance with the License. You may obtain
9# a copy of the License at
10#
11# http://www.apache.org/licenses/LICENSE-2.0
12#
13# Unless required by applicable law or agreed to in writing, software
14# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
15# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
16# License for the specific language governing permissions and limitations
17# under the License.
18
19
20from nova.utils import import_object
21from nova.rpc.common import RemoteError, LOG
22from nova import flags
23
24FLAGS = flags.FLAGS
25flags.DEFINE_string('rpc_backend',
26 'nova.rpc.amqp',
27 "The messaging module to use, defaults to AMQP.")
28
29RPCIMPL = import_object(FLAGS.rpc_backend)
30
31
32def create_connection(new=True):
33 return RPCIMPL.Connection.instance(new=True)
34
35
36def create_consumer(conn, topic, proxy, fanout=False):
37 if fanout:
38 return RPCIMPL.FanoutAdapterConsumer(
39 connection=conn,
40 topic=topic,
41 proxy=proxy)
42 else:
43 return RPCIMPL.TopicAdapterConsumer(
44 connection=conn,
45 topic=topic,
46 proxy=proxy)
47
48
49def create_consumer_set(conn, consumers):
50 return RPCIMPL.ConsumerSet(connection=conn, consumer_list=consumers)
51
52
53def call(context, topic, msg):
54 return RPCIMPL.call(context, topic, msg)
55
56
57def cast(context, topic, msg):
58 return RPCIMPL.cast(context, topic, msg)
59
60
61def fanout_cast(context, topic, msg):
62 return RPCIMPL.fanout_cast(context, topic, msg)
63
64
65def multicall(context, topic, msg):
66 return RPCIMPL.multicall(context, topic, msg)
067
=== renamed file 'nova/rpc.py' => 'nova/rpc/amqp.py'
--- nova/rpc.py 2011-07-01 14:53:20 +0000
+++ nova/rpc/amqp.py 2011-07-29 19:17:40 +0000
@@ -44,9 +44,7 @@
44from nova import flags44from nova import flags
45from nova import log as logging45from nova import log as logging
46from nova import utils46from nova import utils
4747from nova.rpc.common import RemoteError, LOG
48
49LOG = logging.getLogger('nova.rpc')
5048
5149
52FLAGS = flags.FLAGS50FLAGS = flags.FLAGS
@@ -418,25 +416,6 @@
418 publisher.close()416 publisher.close()
419417
420418
421class RemoteError(exception.Error):
422 """Signifies that a remote class has raised an exception.
423
424 Containes a string representation of the type of the original exception,
425 the value of the original exception, and the traceback. These are
426 sent to the parent as a joined string so printing the exception
427 contains all of the relevent info.
428
429 """
430
431 def __init__(self, exc_type, value, traceback):
432 self.exc_type = exc_type
433 self.value = value
434 self.traceback = traceback
435 super(RemoteError, self).__init__('%s %s\n%s' % (exc_type,
436 value,
437 traceback))
438
439
440def _unpack_context(msg):419def _unpack_context(msg):
441 """Unpack context from msg."""420 """Unpack context from msg."""
442 context_dict = {}421 context_dict = {}
443422
=== added file 'nova/rpc/common.py'
--- nova/rpc/common.py 1970-01-01 00:00:00 +0000
+++ nova/rpc/common.py 2011-07-29 19:17:40 +0000
@@ -0,0 +1,23 @@
1from nova import exception
2from nova import log as logging
3
4LOG = logging.getLogger('nova.rpc')
5
6
7class RemoteError(exception.Error):
8 """Signifies that a remote class has raised an exception.
9
10 Containes a string representation of the type of the original exception,
11 the value of the original exception, and the traceback. These are
12 sent to the parent as a joined string so printing the exception
13 contains all of the relevent info.
14
15 """
16
17 def __init__(self, exc_type, value, traceback):
18 self.exc_type = exc_type
19 self.value = value
20 self.traceback = traceback
21 super(RemoteError, self).__init__('%s %s\n%s' % (exc_type,
22 value,
23 traceback))
024
=== modified file 'nova/service.py'
--- nova/service.py 2011-06-24 01:31:00 +0000
+++ nova/service.py 2011-07-29 19:17:40 +0000
@@ -149,26 +149,22 @@
149 if 'nova-compute' == self.binary:149 if 'nova-compute' == self.binary:
150 self.manager.update_available_resource(ctxt)150 self.manager.update_available_resource(ctxt)
151151
152 self.conn = rpc.Connection.instance(new=True)152 self.conn = rpc.create_connection(new=True)
153 logging.debug("Creating Consumer connection for Service %s" %153 logging.debug("Creating Consumer connection for Service %s" %
154 self.topic)154 self.topic)
155155
156 # Share this same connection for these Consumers156 # Share this same connection for these Consumers
157 consumer_all = rpc.TopicAdapterConsumer(157 consumer_all = rpc.create_consumer(self.conn, self.topic, self,
158 connection=self.conn,158 fanout=False)
159 topic=self.topic,159
160 proxy=self)160 node_topic = '%s.%s' % (self.topic, self.host)
161 consumer_node = rpc.TopicAdapterConsumer(161 consumer_node = rpc.create_consumer(self.conn, node_topic, self,
162 connection=self.conn,162 fanout=False)
163 topic='%s.%s' % (self.topic, self.host),163
164 proxy=self)164 fanout = rpc.create_consumer(self.conn, self.topic, self, fanout=True)
165 fanout = rpc.FanoutAdapterConsumer(165
166 connection=self.conn,166 consumers = [consumer_all, consumer_node, fanout]
167 topic=self.topic,167 consumer_set = rpc.create_consumer_set(self.conn, consumers)
168 proxy=self)
169 consumer_set = rpc.ConsumerSet(
170 connection=self.conn,
171 consumer_list=[consumer_all, consumer_node, fanout])
172168
173 # Wait forever, processing these consumers169 # Wait forever, processing these consumers
174 def _wait():170 def _wait():
175171
=== modified file 'nova/test.py'
--- nova/test.py 2011-07-08 09:35:01 +0000
+++ nova/test.py 2011-07-29 19:17:40 +0000
@@ -99,9 +99,7 @@
99 self.flag_overrides = {}99 self.flag_overrides = {}
100 self.injected = []100 self.injected = []
101 self._services = []101 self._services = []
102 self._monkey_patch_attach()
103 self._original_flags = FLAGS.FlagValuesDict()102 self._original_flags = FLAGS.FlagValuesDict()
104 rpc.ConnectionPool = rpc.Pool(max_size=FLAGS.rpc_conn_pool_size)
105103
106 def tearDown(self):104 def tearDown(self):
107 """Runs after each test method to tear down test environment."""105 """Runs after each test method to tear down test environment."""
@@ -126,9 +124,6 @@
126 # Reset any overriden flags124 # Reset any overriden flags
127 self.reset_flags()125 self.reset_flags()
128126
129 # Reset our monkey-patches
130 rpc.Consumer.attach_to_eventlet = self.original_attach
131
132 # Stop any timers127 # Stop any timers
133 for x in self.injected:128 for x in self.injected:
134 try:129 try:
@@ -172,17 +167,6 @@
172 self._services.append(svc)167 self._services.append(svc)
173 return svc168 return svc
174169
175 def _monkey_patch_attach(self):
176 self.original_attach = rpc.Consumer.attach_to_eventlet
177
178 def _wrapped(inner_self):
179 rv = self.original_attach(inner_self)
180 self.injected.append(rv)
181 return rv
182
183 _wrapped.func_name = self.original_attach.func_name
184 rpc.Consumer.attach_to_eventlet = _wrapped
185
186 # Useful assertions170 # Useful assertions
187 def assertDictMatch(self, d1, d2, approx_equal=False, tolerance=0.001):171 def assertDictMatch(self, d1, d2, approx_equal=False, tolerance=0.001):
188 """Assert two dicts are equivalent.172 """Assert two dicts are equivalent.
189173
=== modified file 'nova/tests/test_adminapi.py'
--- nova/tests/test_adminapi.py 2011-06-27 21:48:03 +0000
+++ nova/tests/test_adminapi.py 2011-07-29 19:17:40 +0000
@@ -39,7 +39,7 @@
39 super(AdminApiTestCase, self).setUp()39 super(AdminApiTestCase, self).setUp()
40 self.flags(connection_type='fake')40 self.flags(connection_type='fake')
4141
42 self.conn = rpc.Connection.instance()42 self.conn = rpc.create_connection()
4343
44 # set up our cloud44 # set up our cloud
45 self.api = admin.AdminController()45 self.api = admin.AdminController()
4646
=== modified file 'nova/tests/test_cloud.py'
--- nova/tests/test_cloud.py 2011-07-27 21:39:27 +0000
+++ nova/tests/test_cloud.py 2011-07-29 19:17:40 +0000
@@ -50,7 +50,7 @@
50 self.flags(connection_type='fake',50 self.flags(connection_type='fake',
51 stub_network=True)51 stub_network=True)
5252
53 self.conn = rpc.Connection.instance()53 self.conn = rpc.create_connection()
5454
55 # set up our cloud55 # set up our cloud
56 self.cloud = cloud.CloudController()56 self.cloud = cloud.CloudController()
@@ -326,23 +326,16 @@
326 revoke = self.cloud.revoke_security_group_ingress326 revoke = self.cloud.revoke_security_group_ingress
327 self.assertTrue(revoke(self.context, group_name=sec['name'], **kwargs))327 self.assertTrue(revoke(self.context, group_name=sec['name'], **kwargs))
328328
329 def test_revoke_security_group_ingress_by_id(self):329 def test_authorize_revoke_security_group_ingress_by_id(self):
330 kwargs = {'project_id': self.context.project_id, 'name': 'test'}330 sec = db.security_group_create(self.context,
331 sec = db.security_group_create(self.context, kwargs)331 {'project_id': self.context.project_id,
332 'name': 'test'})
332 authz = self.cloud.authorize_security_group_ingress333 authz = self.cloud.authorize_security_group_ingress
333 kwargs = {'to_port': '999', 'from_port': '999', 'ip_protocol': 'tcp'}334 kwargs = {'to_port': '999', 'from_port': '999', 'ip_protocol': 'tcp'}
334 authz(self.context, group_id=sec['id'], **kwargs)335 authz(self.context, group_id=sec['id'], **kwargs)
335 revoke = self.cloud.revoke_security_group_ingress336 revoke = self.cloud.revoke_security_group_ingress
336 self.assertTrue(revoke(self.context, group_id=sec['id'], **kwargs))337 self.assertTrue(revoke(self.context, group_id=sec['id'], **kwargs))
337338
338 def test_authorize_security_group_ingress_by_id(self):
339 sec = db.security_group_create(self.context,
340 {'project_id': self.context.project_id,
341 'name': 'test'})
342 authz = self.cloud.authorize_security_group_ingress
343 kwargs = {'to_port': '999', 'from_port': '999', 'ip_protocol': 'tcp'}
344 self.assertTrue(authz(self.context, group_id=sec['id'], **kwargs))
345
346 def test_authorize_security_group_ingress_missing_protocol_params(self):339 def test_authorize_security_group_ingress_missing_protocol_params(self):
347 sec = db.security_group_create(self.context,340 sec = db.security_group_create(self.context,
348 {'project_id': self.context.project_id,341 {'project_id': self.context.project_id,
@@ -961,21 +954,6 @@
961 self._wait_for_running(ec2_instance_id)954 self._wait_for_running(ec2_instance_id)
962 return ec2_instance_id955 return ec2_instance_id
963956
964 def test_rescue_unrescue_instance(self):
965 instance_id = self._run_instance(
966 image_id='ami-1',
967 instance_type=FLAGS.default_instance_type,
968 max_count=1)
969 self.cloud.rescue_instance(context=self.context,
970 instance_id=instance_id)
971 # NOTE(vish): This currently does no validation, it simply makes sure
972 # that the code path doesn't throw an exception.
973 self.cloud.unrescue_instance(context=self.context,
974 instance_id=instance_id)
975 # TODO(soren): We need this until we can stop polling in the rpc code
976 # for unit tests.
977 self.cloud.terminate_instances(self.context, [instance_id])
978
979 def test_console_output(self):957 def test_console_output(self):
980 instance_id = self._run_instance(958 instance_id = self._run_instance(
981 image_id='ami-1',959 image_id='ami-1',
982960
=== modified file 'nova/tests/test_rpc.py'
--- nova/tests/test_rpc.py 2011-05-26 22:08:53 +0000
+++ nova/tests/test_rpc.py 2011-07-29 19:17:40 +0000
@@ -33,11 +33,12 @@
33class RpcTestCase(test.TestCase):33class RpcTestCase(test.TestCase):
34 def setUp(self):34 def setUp(self):
35 super(RpcTestCase, self).setUp()35 super(RpcTestCase, self).setUp()
36 self.conn = rpc.Connection.instance(True)36 self.conn = rpc.create_connection(True)
37 self.receiver = TestReceiver()37 self.receiver = TestReceiver()
38 self.consumer = rpc.TopicAdapterConsumer(connection=self.conn,38 self.consumer = rpc.create_consumer(self.conn,
39 topic='test',39 'test',
40 proxy=self.receiver)40 self.receiver,
41 False)
41 self.consumer.attach_to_eventlet()42 self.consumer.attach_to_eventlet()
42 self.context = context.get_admin_context()43 self.context = context.get_admin_context()
4344
@@ -129,6 +130,8 @@
129 """Calls echo in the passed queue"""130 """Calls echo in the passed queue"""
130 LOG.debug(_("Nested received %(queue)s, %(value)s")131 LOG.debug(_("Nested received %(queue)s, %(value)s")
131 % locals())132 % locals())
133 # TODO: so, it will replay the context and use the same REQID?
134 # that's bizarre.
132 ret = rpc.call(context,135 ret = rpc.call(context,
133 queue,136 queue,
134 {"method": "echo",137 {"method": "echo",
@@ -137,10 +140,11 @@
137 return value140 return value
138141
139 nested = Nested()142 nested = Nested()
140 conn = rpc.Connection.instance(True)143 conn = rpc.create_connection(True)
141 consumer = rpc.TopicAdapterConsumer(connection=conn,144 consumer = rpc.create_consumer(conn,
142 topic='nested',145 'nested',
143 proxy=nested)146 nested,
147 False)
144 consumer.attach_to_eventlet()148 consumer.attach_to_eventlet()
145 value = 42149 value = 42
146 result = rpc.call(self.context,150 result = rpc.call(self.context,
@@ -149,47 +153,6 @@
149 "value": value}})153 "value": value}})
150 self.assertEqual(value, result)154 self.assertEqual(value, result)
151155
152 def test_connectionpool_single(self):
153 """Test that ConnectionPool recycles a single connection."""
154 conn1 = rpc.ConnectionPool.get()
155 rpc.ConnectionPool.put(conn1)
156 conn2 = rpc.ConnectionPool.get()
157 rpc.ConnectionPool.put(conn2)
158 self.assertEqual(conn1, conn2)
159
160 def test_connectionpool_double(self):
161 """Test that ConnectionPool returns and reuses separate connections.
162
163 When called consecutively we should get separate connections and upon
164 returning them those connections should be reused for future calls
165 before generating a new connection.
166
167 """
168 conn1 = rpc.ConnectionPool.get()
169 conn2 = rpc.ConnectionPool.get()
170
171 self.assertNotEqual(conn1, conn2)
172 rpc.ConnectionPool.put(conn1)
173 rpc.ConnectionPool.put(conn2)
174
175 conn3 = rpc.ConnectionPool.get()
176 conn4 = rpc.ConnectionPool.get()
177 self.assertEqual(conn1, conn3)
178 self.assertEqual(conn2, conn4)
179
180 def test_connectionpool_limit(self):
181 """Test connection pool limit and connection uniqueness."""
182 max_size = FLAGS.rpc_conn_pool_size
183 conns = []
184
185 for i in xrange(max_size):
186 conns.append(rpc.ConnectionPool.get())
187
188 self.assertFalse(rpc.ConnectionPool.free_items)
189 self.assertEqual(rpc.ConnectionPool.current_size,
190 rpc.ConnectionPool.max_size)
191 self.assertEqual(len(set(conns)), max_size)
192
193156
194class TestReceiver(object):157class TestReceiver(object):
195 """Simple Proxy class so the consumer has methods to call.158 """Simple Proxy class so the consumer has methods to call.
196159
=== added file 'nova/tests/test_rpc_amqp.py'
--- nova/tests/test_rpc_amqp.py 1970-01-01 00:00:00 +0000
+++ nova/tests/test_rpc_amqp.py 2011-07-29 19:17:40 +0000
@@ -0,0 +1,68 @@
1from nova import context
2from nova import flags
3from nova import log as logging
4from nova import rpc
5from nova.rpc import amqp
6from nova import test
7
8
9FLAGS = flags.FLAGS
10LOG = logging.getLogger('nova.tests.rpc')
11
12
13class RpcAMQPTestCase(test.TestCase):
14 def setUp(self):
15 super(RpcAMQPTestCase, self).setUp()
16 self.conn = rpc.create_connection(True)
17 self.receiver = TestReceiver()
18 self.consumer = rpc.create_consumer(self.conn,
19 'test',
20 self.receiver,
21 False)
22 self.consumer.attach_to_eventlet()
23 self.context = context.get_admin_context()
24
25 def test_connectionpool_single(self):
26 """Test that ConnectionPool recycles a single connection."""
27 conn1 = amqp.ConnectionPool.get()
28 amqp.ConnectionPool.put(conn1)
29 conn2 = amqp.ConnectionPool.get()
30 amqp.ConnectionPool.put(conn2)
31 self.assertEqual(conn1, conn2)
32
33
34class TestReceiver(object):
35 """Simple Proxy class so the consumer has methods to call.
36
37 Uses static methods because we aren't actually storing any state.
38
39 """
40
41 @staticmethod
42 def echo(context, value):
43 """Simply returns whatever value is sent in."""
44 LOG.debug(_("Received %s"), value)
45 return value
46
47 @staticmethod
48 def context(context, value):
49 """Returns dictionary version of context."""
50 LOG.debug(_("Received %s"), context)
51 return context.to_dict()
52
53 @staticmethod
54 def echo_three_times(context, value):
55 context.reply(value)
56 context.reply(value + 1)
57 context.reply(value + 2)
58
59 @staticmethod
60 def echo_three_times_yield(context, value):
61 yield value
62 yield value + 1
63 yield value + 2
64
65 @staticmethod
66 def fail(context, value):
67 """Raises an exception with the value sent in."""
68 raise Exception(value)
069
=== modified file 'nova/tests/test_service.py'
--- nova/tests/test_service.py 2011-06-28 14:39:04 +0000
+++ nova/tests/test_service.py 2011-07-29 19:17:40 +0000
@@ -109,103 +109,8 @@
109 # the looping calls are created in StartService.109 # the looping calls are created in StartService.
110 app = service.Service.create(host=host, binary=binary, topic=topic)110 app = service.Service.create(host=host, binary=binary, topic=topic)
111111
112 self.mox.StubOutWithMock(service.rpc.Connection, 'instance')
113 service.rpc.Connection.instance(new=mox.IgnoreArg())
114
115 self.mox.StubOutWithMock(rpc,
116 'TopicAdapterConsumer',
117 use_mock_anything=True)
118 self.mox.StubOutWithMock(rpc,
119 'FanoutAdapterConsumer',
120 use_mock_anything=True)
121
122 self.mox.StubOutWithMock(rpc,
123 'ConsumerSet',
124 use_mock_anything=True)
125
126 rpc.TopicAdapterConsumer(connection=mox.IgnoreArg(),
127 topic=topic,
128 proxy=mox.IsA(service.Service)).AndReturn(
129 rpc.TopicAdapterConsumer)
130
131 rpc.TopicAdapterConsumer(connection=mox.IgnoreArg(),
132 topic='%s.%s' % (topic, host),
133 proxy=mox.IsA(service.Service)).AndReturn(
134 rpc.TopicAdapterConsumer)
135
136 rpc.FanoutAdapterConsumer(connection=mox.IgnoreArg(),
137 topic=topic,
138 proxy=mox.IsA(service.Service)).AndReturn(
139 rpc.FanoutAdapterConsumer)
140
141 def wait_func(self, limit=None):
142 return None
143
144 mock_cset = self.mox.CreateMock(rpc.ConsumerSet,
145 {'wait': wait_func})
146 rpc.ConsumerSet(connection=mox.IgnoreArg(),
147 consumer_list=mox.IsA(list)).AndReturn(mock_cset)
148 wait_func(mox.IgnoreArg())
149
150 service_create = {'host': host,
151 'binary': binary,
152 'topic': topic,
153 'report_count': 0,
154 'availability_zone': 'nova'}
155 service_ref = {'host': host,
156 'binary': binary,
157 'report_count': 0,
158 'id': 1}
159
160 service.db.service_get_by_args(mox.IgnoreArg(),
161 host,
162 binary).AndRaise(exception.NotFound())
163 service.db.service_create(mox.IgnoreArg(),
164 service_create).AndReturn(service_ref)
165 self.mox.ReplayAll()
166
167 app.start()
168 app.stop()
169 self.assert_(app)112 self.assert_(app)
170113
171 # We're testing sort of weird behavior in how report_state decides
172 # whether it is disconnected, it looks for a variable on itself called
173 # 'model_disconnected' and report_state doesn't really do much so this
174 # these are mostly just for coverage
175 def test_report_state_no_service(self):
176 host = 'foo'
177 binary = 'bar'
178 topic = 'test'
179 service_create = {'host': host,
180 'binary': binary,
181 'topic': topic,
182 'report_count': 0,
183 'availability_zone': 'nova'}
184 service_ref = {'host': host,
185 'binary': binary,
186 'topic': topic,
187 'report_count': 0,
188 'availability_zone': 'nova',
189 'id': 1}
190
191 service.db.service_get_by_args(mox.IgnoreArg(),
192 host,
193 binary).AndRaise(exception.NotFound())
194 service.db.service_create(mox.IgnoreArg(),
195 service_create).AndReturn(service_ref)
196 service.db.service_get(mox.IgnoreArg(),
197 service_ref['id']).AndReturn(service_ref)
198 service.db.service_update(mox.IgnoreArg(), service_ref['id'],
199 mox.ContainsKeyValue('report_count', 1))
200
201 self.mox.ReplayAll()
202 serv = service.Service(host,
203 binary,
204 topic,
205 'nova.tests.test_service.FakeManager')
206 serv.start()
207 serv.report_state()
208
209 def test_report_state_newly_disconnected(self):114 def test_report_state_newly_disconnected(self):
210 host = 'foo'115 host = 'foo'
211 binary = 'bar'116 binary = 'bar'
@@ -276,81 +181,6 @@
276181
277 self.assert_(not serv.model_disconnected)182 self.assert_(not serv.model_disconnected)
278183
279 def test_compute_can_update_available_resource(self):
280 """Confirm compute updates their record of compute-service table."""
281 host = 'foo'
282 binary = 'nova-compute'
283 topic = 'compute'
284
285 # Any mocks are not working without UnsetStubs() here.
286 self.mox.UnsetStubs()
287 ctxt = context.get_admin_context()
288 service_ref = db.service_create(ctxt, {'host': host,
289 'binary': binary,
290 'topic': topic})
291 serv = service.Service(host,
292 binary,
293 topic,
294 'nova.compute.manager.ComputeManager')
295
296 # This testcase want to test calling update_available_resource.
297 # No need to call periodic call, then below variable must be set 0.
298 serv.report_interval = 0
299 serv.periodic_interval = 0
300
301 # Creating mocks
302 self.mox.StubOutWithMock(service.rpc.Connection, 'instance')
303 service.rpc.Connection.instance(new=mox.IgnoreArg())
304
305 self.mox.StubOutWithMock(rpc,
306 'TopicAdapterConsumer',
307 use_mock_anything=True)
308 self.mox.StubOutWithMock(rpc,
309 'FanoutAdapterConsumer',
310 use_mock_anything=True)
311
312 self.mox.StubOutWithMock(rpc,
313 'ConsumerSet',
314 use_mock_anything=True)
315
316 rpc.TopicAdapterConsumer(connection=mox.IgnoreArg(),
317 topic=topic,
318 proxy=mox.IsA(service.Service)).AndReturn(
319 rpc.TopicAdapterConsumer)
320
321 rpc.TopicAdapterConsumer(connection=mox.IgnoreArg(),
322 topic='%s.%s' % (topic, host),
323 proxy=mox.IsA(service.Service)).AndReturn(
324 rpc.TopicAdapterConsumer)
325
326 rpc.FanoutAdapterConsumer(connection=mox.IgnoreArg(),
327 topic=topic,
328 proxy=mox.IsA(service.Service)).AndReturn(
329 rpc.FanoutAdapterConsumer)
330
331 def wait_func(self, limit=None):
332 return None
333
334 mock_cset = self.mox.CreateMock(rpc.ConsumerSet,
335 {'wait': wait_func})
336 rpc.ConsumerSet(connection=mox.IgnoreArg(),
337 consumer_list=mox.IsA(list)).AndReturn(mock_cset)
338 wait_func(mox.IgnoreArg())
339
340 self.mox.StubOutWithMock(serv.manager.driver,
341 'update_available_resource')
342 serv.manager.driver.update_available_resource(mox.IgnoreArg(), host)
343
344 # Just doing start()-stop(), not confirm new db record is created,
345 # because update_available_resource() works only in
346 # libvirt environment. This testcase confirms
347 # update_available_resource() is called. Otherwise, mox complains.
348 self.mox.ReplayAll()
349 serv.start()
350 serv.stop()
351
352 db.service_destroy(ctxt, service_ref['id'])
353
354184
355class TestWSGIService(test.TestCase):185class TestWSGIService(test.TestCase):
356186
357187
=== modified file 'nova/tests/test_test.py'
--- nova/tests/test_test.py 2011-02-24 01:58:32 +0000
+++ nova/tests/test_test.py 2011-07-29 19:17:40 +0000
@@ -33,8 +33,13 @@
33 self.start_service('compute')33 self.start_service('compute')
3434
35 def test_rpc_consumer_isolation(self):35 def test_rpc_consumer_isolation(self):
36 connection = rpc.Connection.instance(new=True)36 class NeverCalled(object):
37 consumer = rpc.TopicAdapterConsumer(connection, topic='compute')37
38 consumer.register_callback(38 def __getattribute__(*args):
39 lambda x, y: self.fail('I should never be called'))39 assert False, "I should never get called."
40
41 connection = rpc.create_connection(new=True)
42 proxy = NeverCalled()
43 consumer = rpc.create_consumer(connection, 'compute',
44 proxy, fanout=False)
40 consumer.attach_to_eventlet()45 consumer.attach_to_eventlet()