Merge lp:~santhoshkumar/network-service/quantum_testing_framework into lp:~registry/network-service/quantum

Proposed by Santhosh Kumar Muniraj
Status: Work in progress
Proposed branch: lp:~santhoshkumar/network-service/quantum_testing_framework
Merge into: lp:~registry/network-service/quantum
Diff against target: 3337 lines (+1124/-741)
37 files modified
bin/quantum (+2/-2)
etc/quantum.conf.test (+1/-1)
quantum/api/__init__.py (+3/-2)
quantum/api/faults.py (+2/-3)
quantum/api/networks.py (+20/-17)
quantum/api/ports.py (+34/-36)
quantum/api/views/__init__.py (+1/-1)
quantum/api/views/networks.py (+3/-3)
quantum/api/views/ports.py (+1/-1)
quantum/cli.py (+45/-26)
quantum/common/config.py (+4/-7)
quantum/common/exceptions.py (+7/-4)
quantum/common/flags.py (+1/-2)
quantum/common/utils.py (+2/-0)
quantum/common/wsgi.py (+11/-11)
quantum/db/api.py (+16/-2)
quantum/db/models.py (+7/-3)
quantum/manager.py (+8/-4)
quantum/plugins/SamplePlugin.py (+72/-100)
quantum/plugins/__init__.py (+1/-1)
quantum/plugins/openvswitch/agent/ovs_quantum_agent.py (+38/-28)
quantum/plugins/openvswitch/ovs_db.py (+5/-1)
quantum/plugins/openvswitch/ovs_models.py (+2/-1)
quantum/plugins/openvswitch/ovs_quantum_plugin.py (+24/-5)
quantum/service.py (+3/-1)
run_tests.py (+293/-0)
run_tests.sh (+83/-0)
smoketests/miniclient.py (+0/-98)
smoketests/tests.py (+0/-133)
test_scripts/miniclient.py (+0/-98)
test_scripts/tests.py (+0/-150)
tests/functional/miniclient.py (+99/-0)
tests/functional/test_service.py (+136/-0)
tests/unit/__init__.py (+32/-0)
tools/install_venv.py (+137/-0)
tools/pip-requires (+10/-0)
tools/with_venv.sh (+21/-0)
To merge this branch: bzr merge lp:~santhoshkumar/network-service/quantum_testing_framework
Reviewer Review Type Date Requested Status
Salvatore Orlando (community) Needs Information
Santhosh Kumar Muniraj (community) Needs Resubmitting
Review via email: mp+63842@code.launchpad.net

This proposal has been superseded by a proposal from 2011-06-09.

Description of the change

1> Added unit testing framework from glance. All the unit tests can be added to tests/unit directory.
    In order to run the unit tests, we have a script which runs the unit tests along with pep8 validations.
      FROM_PROJECT_DIRECTORY>./run_tests.sh unit
    To run the tests in a single Testclass,
      FROM_PROJECT_DIRECTORY>./run_tests.sh unit.<test_file_name>:<Testclassname>
    To run a single test in a test class,
      FROM_PROJECT_DIRECTORY>./run_tests.sh unit.<test_file_name>:<Testclassname>:<test_name>

2> Moved smoke tests to tests/functional as per Brad Hall's suggestion. We can add all the functional test to tests/functional folder. To run only functional tests -
    FROM_PROJECT_DIRECTORY> ./run_tests.sh functional

3> Fixed all the Pep8 violations. Mostly around extra space, lines, line greater than 80 characters etc.

4> Changed 'req' parameter to 'request' in all the controller actions, so as to be consistent with other Nova projects.

To post a comment you must log in.
Revision history for this message
Salvatore Orlando (salvatore-orlando) wrote :

Hi Santosh,

thanks for fixing pep8 errors. I know most of them aren't yours :-)

I think there might be still be some vestigial code left over from the split with the API extension branch, as in quantum/common/config.py line 36 there is the following import statement:

from quantum.common import extensions

This causes the quantum binary to fail with an ImportError

It seems you have also replaced load_paste_app in config.py. The method in your branch looks for a configuration file and then tries to load it, whereas the method in the current trunk accepts the path of the configuration file. This of course causes the executable to not work anymore. In the current trunk, the configuration file is located in QuantumAPIService.create and then the app loaded by WsgiService.start which executes _run_wsgi.

I'm completely fine with both approaches, we just need to make sure the code is coherent.

Finally, I noticed you renamed 'req' parameter to 'request'. Altough I agree this is a better naming convention, it unfortunately breaks the wsgi server. This can be sorted by updating line 343 in wsgi.py:

from arg_dict['req'] = req to arg_dict['request'] = req

Please let me know if you have any question or comments.
I will complete the review once these point have been addressed.

Thanks for your contribution,
Salvatore

review: Needs Fixing
19. By Santhosh Kumar Muniraj

Santhosh/Deepak | Fixed the import issue and config.load_paste_app issue

Revision history for this message
Santhosh Kumar Muniraj (santhoshkumar) wrote :

Hi Salvatore,

Thanks for the feedback. We fixed the ImportError and the usage of load_paste_app from config.py.

We had modified the load_paste_app as it has an advantage of loading the .conf file from a default set of locations similar to other Openstack projects. In addition to that, load_paste_app will also return the configuration variables defined in the .conf file which can be further used for other configurations.

In wsgi.py, the 'arg_dict' is already using 'request' key to pass the request variable to controller actions. After all these change we verified that app can be started and a simple request to get networks works fine.

We couldn't get the smoke tests passing both on the trunk and our branch. May be we are missing some setup on our side. All the functional tests were failing with the following error:

<code> TypeError: [NetworkInUse() is not JSON serializable]. </code>

We are not sure if these errors are due to our changes.

review: Needs Resubmitting
Revision history for this message
Salvatore Orlando (salvatore-orlando) wrote :

> Hi Salvatore,
>
> Thanks for the feedback. We fixed the ImportError and the usage of
> load_paste_app from config.py.
>
> We had modified the load_paste_app as it has an advantage of loading the .conf
> file from a default set of locations similar to other Openstack projects. In
> addition to that, load_paste_app will also return the configuration variables
> defined in the .conf file which can be further used for other configurations.
>
> In wsgi.py, the 'arg_dict' is already using 'request' key to pass the request
> variable to controller actions. After all these change we verified that app
> can be started and a simple request to get networks works fine.
>
> We couldn't get the smoke tests passing both on the trunk and our branch. May
> be we are missing some setup on our side. All the functional tests were
> failing with the following error:
>
> <code> TypeError: [NetworkInUse() is not JSON serializable]. </code>
>
> We are not sure if these errors are due to our changes.

Hi Santosh,
I'm already aware of the TypeError. It seems that response for Faults cannot be generated when JSON is the content type. I will push a fix for this issue with a separate bug.

For load_paste_app, also the previous code was loading the .conf file from a default set of locations... anyway, as long as the app successfully starts, it is fine!

I will do a complete review in a few hours.

Cheers,
Salvatore

Revision history for this message
Salvatore Orlando (salvatore-orlando) wrote :

Hi Santosh!

It seems the testing infrastructure is pretty much vanilla code taken from glance/nova, so it is fine for me.

As far as the functional tests are concerned, your failure is not related to the bug I mentioned earlier. The tests fail as simplejson.load fails to de-serialize the response body. I noticed that wsgi.Serializer().deserialize(response, content_type) works better, even though XML deserialization does not work properly.

Anyway, once the deserialization issue is fixed I got the following error:

TypeError: NetworkInUse() is not JSON serializable

And this is the TypeError I was talking about earlier on.

However, since the issues are not related to the code you are contributing, but concern code which is already in trunk... I think merging pep8 fixes and the testing infrastructure is the right thing to do.

I will cast my "Approve" vote as soon as Dan/Rick approve the branch.

Cheers,
Salvatore

> > Hi Salvatore,
> >
> > Thanks for the feedback. We fixed the ImportError and the usage of
> > load_paste_app from config.py.
> >
> > We had modified the load_paste_app as it has an advantage of loading the
> .conf
> > file from a default set of locations similar to other Openstack projects. In
> > addition to that, load_paste_app will also return the configuration
> variables
> > defined in the .conf file which can be further used for other
> configurations.
> >
> > In wsgi.py, the 'arg_dict' is already using 'request' key to pass the
> request
> > variable to controller actions. After all these change we verified that app
> > can be started and a simple request to get networks works fine.
> >
> > We couldn't get the smoke tests passing both on the trunk and our branch.
> May
> > be we are missing some setup on our side. All the functional tests were
> > failing with the following error:
> >
> > <code> TypeError: [NetworkInUse() is not JSON serializable]. </code>
> >
> > We are not sure if these errors are due to our changes.
>
> Hi Santosh,
> I'm already aware of the TypeError. It seems that response for Faults cannot
> be generated when JSON is the content type. I will push a fix for this issue
> with a separate bug.
>
> For load_paste_app, also the previous code was loading the .conf file from a
> default set of locations... anyway, as long as the app successfully starts, it
> is fine!
>
> I will do a complete review in a few hours.
>
> Cheers,
> Salvatore

review: Needs Information

Unmerged revisions

19. By Santhosh Kumar Muniraj

Santhosh/Deepak | Fixed the import issue and config.load_paste_app issue

18. By Santhosh Kumar Muniraj

Santhosh/Vinkesh | Fixed all the pep8 violations. Modified the 'req' to 'request' across all the services and wsgi so that it's consistent with other projects

17. By Santhosh Kumar Muniraj

Santhosh/Vinkesh | Added the testing framework. Moved the smoketest to tests/functional

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'bin/quantum'
2--- bin/quantum 2011-05-27 16:52:06 +0000
3+++ bin/quantum 2011-06-09 05:01:27 +0000
4@@ -33,9 +33,10 @@
5
6 gettext.install('quantum', unicode=1)
7
8-from quantum import service
9+from quantum import service
10 from quantum.common import config
11
12+
13 def create_options(parser):
14 """
15 Sets up the CLI and config-file options that may be
16@@ -58,4 +59,3 @@
17 service.wait()
18 except RuntimeError, e:
19 sys.exit("ERROR: %s" % e)
20-
21
22=== modified file 'etc/quantum.conf.test'
23--- etc/quantum.conf.test 2011-05-11 21:29:35 +0000
24+++ etc/quantum.conf.test 2011-06-09 05:01:27 +0000
25@@ -12,4 +12,4 @@
26 bind_host = 0.0.0.0
27
28 # Port the bind the API server to
29-bind_port = 9696
30\ No newline at end of file
31+bind_port = 9696
32
33=== modified file 'quantum/api/__init__.py'
34--- quantum/api/__init__.py 2011-06-07 06:07:05 +0000
35+++ quantum/api/__init__.py 2011-06-09 05:01:27 +0000
36@@ -54,8 +54,9 @@
37 mapper.resource('port', 'ports',
38 controller=ports.Controller(),
39 parent_resource=dict(member_name='network',
40- collection_name=\
41- uri_prefix + 'networks'))
42+ collection_name=uri_prefix +\
43+ 'networks'))
44+
45 mapper.connect("get_resource",
46 uri_prefix + 'networks/{network_id}/' \
47 'ports/{id}/attachment{.format}',
48
49=== modified file 'quantum/api/faults.py'
50--- quantum/api/faults.py 2011-05-31 17:15:00 +0000
51+++ quantum/api/faults.py 2011-06-09 05:01:27 +0000
52@@ -36,8 +36,7 @@
53 432: "portInUse",
54 440: "alreadyAttached",
55 470: "serviceUnavailable",
56- 471: "pluginFault"
57- }
58+ 471: "pluginFault"}
59
60 def __init__(self, exception):
61 """Create a Fault for the given webob.exc.exception."""
62@@ -52,7 +51,7 @@
63 fault_data = {
64 fault_name: {
65 'code': code,
66- 'message': self.wrapped_exc.explanation,
67+ 'message': self.wrapped_exc.explanation,
68 'detail': self.wrapped_exc.detail}}
69 # 'code' is an attribute on the fault tag itself
70 metadata = {'application/xml': {'attributes': {fault_name: 'code'}}}
71
72=== modified file 'quantum/api/networks.py'
73--- quantum/api/networks.py 2011-05-31 17:15:00 +0000
74+++ quantum/api/networks.py 2011-06-09 05:01:27 +0000
75@@ -44,63 +44,66 @@
76 self._resource_name = 'network'
77 super(Controller, self).__init__()
78
79- def index(self, req, tenant_id):
80+ def index(self, request, tenant_id):
81 """ Returns a list of network ids """
82 #TODO: this should be for a given tenant!!!
83- return self._items(req, tenant_id, is_detail=False)
84+ return self._items(request, tenant_id, is_detail=False)
85
86- def _items(self, req, tenant_id, is_detail):
87+ def _items(self, request, tenant_id, is_detail):
88 """ Returns a list of networks. """
89 networks = self.network_manager.get_all_networks(tenant_id)
90- builder = networks_view.get_view_builder(req)
91+ builder = networks_view.get_view_builder(request)
92 result = [builder.build(network, is_detail)['network']
93 for network in networks]
94 return dict(networks=result)
95
96- def show(self, req, tenant_id, id):
97+ def show(self, request, tenant_id, id):
98 """ Returns network details for the given network id """
99 try:
100 network = self.network_manager.get_network_details(
101 tenant_id, id)
102- builder = networks_view.get_view_builder(req)
103+ builder = networks_view.get_view_builder(request)
104 #build response with details
105 result = builder.build(network, True)
106 return dict(networks=result)
107 except exception.NetworkNotFound as e:
108 return faults.Fault(faults.NetworkNotFound(e))
109
110- def create(self, req, tenant_id):
111+ def create(self, request, tenant_id):
112 """ Creates a new network for a given tenant """
113 #look for network name in request
114 try:
115- req_params = \
116- self._parse_request_params(req, self._network_ops_param_list)
117+ request_params = \
118+ self._parse_request_params(request,
119+ self._network_ops_param_list)
120 except exc.HTTPError as e:
121 return faults.Fault(e)
122 network = self.network_manager.\
123- create_network(tenant_id,req_params['network-name'])
124- builder = networks_view.get_view_builder(req)
125+ create_network(tenant_id,
126+ request_params['network-name'])
127+ builder = networks_view.get_view_builder(request)
128 result = builder.build(network)
129 return dict(networks=result)
130
131- def update(self, req, tenant_id, id):
132+ def update(self, request, tenant_id, id):
133 """ Updates the name for the network with the given id """
134 try:
135- req_params = \
136- self._parse_request_params(req, self._network_ops_param_list)
137+ request_params = \
138+ self._parse_request_params(request,
139+ self._network_ops_param_list)
140 except exc.HTTPError as e:
141 return faults.Fault(e)
142 try:
143 network = self.network_manager.rename_network(tenant_id,
144- id, req_params['network-name'])
145+ id, request_params['network-name'])
146
147- builder = networks_view.get_view_builder(req)
148+ builder = networks_view.get_view_builder(request)
149 result = builder.build(network, True)
150 return dict(networks=result)
151 except exception.NetworkNotFound as e:
152 return faults.Fault(faults.NetworkNotFound(e))
153
154- def delete(self, req, tenant_id, id):
155+ def delete(self, request, tenant_id, id):
156 """ Destroys the network with the given id """
157 try:
158 self.network_manager.delete_network(tenant_id, id)
159
160=== modified file 'quantum/api/ports.py'
161--- quantum/api/ports.py 2011-06-03 17:26:36 +0000
162+++ quantum/api/ports.py 2011-06-09 05:01:27 +0000
163@@ -24,51 +24,49 @@
164
165 LOG = logging.getLogger('quantum.api.ports')
166
167+
168 class Controller(common.QuantumController):
169 """ Port API controller for Quantum API """
170
171 _port_ops_param_list = [{
172 'param-name': 'port-state',
173 'default-value': 'DOWN',
174- 'required': False},]
175+ 'required': False}, ]
176
177 _attachment_ops_param_list = [{
178 'param-name': 'attachment-id',
179- 'required': True},]
180+ 'required': True}, ]
181
182 _serialization_metadata = {
183 "application/xml": {
184 "attributes": {
185- "port": ["id","state"],
186- },
187- },
188- }
189+ "port": ["id", "state"], }, }, }
190
191 def __init__(self, plugin_conf_file=None):
192 self._resource_name = 'port'
193 super(Controller, self).__init__()
194
195- def index(self, req, tenant_id, network_id):
196+ def index(self, request, tenant_id, network_id):
197 """ Returns a list of port ids for a given network """
198- return self._items(req, tenant_id, network_id, is_detail=False)
199+ return self._items(request, tenant_id, network_id, is_detail=False)
200
201- def _items(self, req, tenant_id, network_id, is_detail):
202+ def _items(self, request, tenant_id, network_id, is_detail):
203 """ Returns a list of networks. """
204- try :
205+ try:
206 ports = self.network_manager.get_all_ports(tenant_id, network_id)
207- builder = ports_view.get_view_builder(req)
208+ builder = ports_view.get_view_builder(request)
209 result = [builder.build(port, is_detail)['port']
210 for port in ports]
211 return dict(ports=result)
212 except exception.NetworkNotFound as e:
213 return faults.Fault(faults.NetworkNotFound(e))
214
215- def show(self, req, tenant_id, network_id, id):
216+ def show(self, request, tenant_id, network_id, id):
217 """ Returns port details for given port and network """
218 try:
219 port = self.network_manager.get_port_details(
220 tenant_id, network_id, id)
221- builder = ports_view.get_view_builder(req)
222+ builder = ports_view.get_view_builder(request)
223 #build response with details
224 result = builder.build(port, True)
225 return dict(ports=result)
226@@ -77,19 +75,19 @@
227 except exception.PortNotFound as e:
228 return faults.Fault(faults.PortNotFound(e))
229
230- def create(self, req, tenant_id, network_id):
231+ def create(self, request, tenant_id, network_id):
232 """ Creates a new port for a given network """
233 #look for port state in request
234 try:
235- req_params = \
236- self._parse_request_params(req, self._port_ops_param_list)
237+ request_params = \
238+ self._parse_request_params(request, self._port_ops_param_list)
239 except exc.HTTPError as e:
240 return faults.Fault(e)
241 try:
242 port = self.network_manager.create_port(tenant_id,
243- network_id,
244- req_params['port-state'])
245- builder = ports_view.get_view_builder(req)
246+ network_id,
247+ request_params['port-state'])
248+ builder = ports_view.get_view_builder(request)
249 result = builder.build(port)
250 return dict(ports=result)
251 except exception.NetworkNotFound as e:
252@@ -97,18 +95,18 @@
253 except exception.StateInvalid as e:
254 return faults.Fault(faults.RequestedStateInvalid(e))
255
256- def update(self, req, tenant_id, network_id, id):
257+ def update(self, request, tenant_id, network_id, id):
258 """ Updates the state of a port for a given network """
259 #look for port state in request
260 try:
261- req_params = \
262- self._parse_request_params(req, self._port_ops_param_list)
263+ request_params = \
264+ self._parse_request_params(request, self._port_ops_param_list)
265 except exc.HTTPError as e:
266 return faults.Fault(e)
267 try:
268- port = self.network_manager.update_port(tenant_id,network_id, id,
269- req_params['port-state'])
270- builder = ports_view.get_view_builder(req)
271+ port = self.network_manager.update_port(tenant_id, network_id, id,
272+ request_params['port-state'])
273+ builder = ports_view.get_view_builder(request)
274 result = builder.build(port, True)
275 return dict(ports=result)
276 except exception.NetworkNotFound as e:
277@@ -118,7 +116,7 @@
278 except exception.StateInvalid as e:
279 return faults.Fault(faults.RequestedStateInvalid(e))
280
281- def delete(self, req, tenant_id, network_id, id):
282+ def delete(self, request, tenant_id, network_id, id):
283 """ Destroys the port with the given id """
284 #look for port state in request
285 try:
286@@ -132,7 +130,7 @@
287 except exception.PortInUse as e:
288 return faults.Fault(faults.PortInUse(e))
289
290- def get_resource(self,req,tenant_id, network_id, id):
291+ def get_resource(self, request, tenant_id, network_id, id):
292 try:
293 result = self.network_manager.get_interface_details(
294 tenant_id, network_id, id)
295@@ -143,19 +141,19 @@
296 return faults.Fault(faults.PortNotFound(e))
297
298 #TODO - Complete implementation of these APIs
299- def attach_resource(self,req,tenant_id, network_id, id):
300- content_type = req.best_match_content_type()
301- print "Content type:%s" %content_type
302+ def attach_resource(self, request, tenant_id, network_id, id):
303+ content_type = request.best_match_content_type()
304+ print "Content type:%s" % content_type
305 try:
306- req_params = \
307- self._parse_request_params(req,
308+ request_params = \
309+ self._parse_request_params(request,
310 self._attachment_ops_param_list)
311 except exc.HTTPError as e:
312 return faults.Fault(e)
313 try:
314 self.network_manager.plug_interface(tenant_id,
315- network_id,id,
316- req_params['attachment-id'])
317+ network_id, id,
318+ request_params['attachment-id'])
319 return exc.HTTPAccepted()
320 except exception.NetworkNotFound as e:
321 return faults.Fault(faults.NetworkNotFound(e))
322@@ -167,10 +165,10 @@
323 return faults.Fault(faults.AlreadyAttached(e))
324
325 #TODO - Complete implementation of these APIs
326- def detach_resource(self,req,tenant_id, network_id, id):
327+ def detach_resource(self, request, tenant_id, network_id, id):
328 try:
329 self.network_manager.unplug_interface(tenant_id,
330- network_id,id)
331+ network_id, id)
332 return exc.HTTPAccepted()
333 except exception.NetworkNotFound as e:
334 return faults.Fault(faults.NetworkNotFound(e))
335
336=== modified file 'quantum/api/views/__init__.py'
337--- quantum/api/views/__init__.py 2011-05-23 20:51:00 +0000
338+++ quantum/api/views/__init__.py 2011-06-09 05:01:27 +0000
339@@ -13,4 +13,4 @@
340 # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
341 # License for the specific language governing permissions and limitations
342 # under the License.
343-# @author: Somik Behera, Nicira Networks, Inc.
344\ No newline at end of file
345+# @author: Somik Behera, Nicira Networks, Inc.
346
347=== modified file 'quantum/api/views/networks.py'
348--- quantum/api/views/networks.py 2011-05-28 20:52:09 +0000
349+++ quantum/api/views/networks.py 2011-06-09 05:01:27 +0000
350@@ -33,17 +33,17 @@
351
352 def build(self, network_data, is_detail=False):
353 """Generic method used to generate a network entity."""
354- print "NETWORK-DATA:%s" %network_data
355+ print "NETWORK-DATA:%s" % network_data
356 if is_detail:
357 network = self._build_detail(network_data)
358 else:
359 network = self._build_simple(network_data)
360 return network
361-
362+
363 def _build_simple(self, network_data):
364 """Return a simple model of a server."""
365 return dict(network=dict(id=network_data['net-id']))
366-
367+
368 def _build_detail(self, network_data):
369 """Return a simple model of a server."""
370 return dict(network=dict(id=network_data['net-id'],
371
372=== modified file 'quantum/api/views/ports.py'
373--- quantum/api/views/ports.py 2011-06-03 05:30:37 +0000
374+++ quantum/api/views/ports.py 2011-06-09 05:01:27 +0000
375@@ -31,7 +31,7 @@
376
377 def build(self, port_data, is_detail=False):
378 """Generic method used to generate a port entity."""
379- print "PORT-DATA:%s" %port_data
380+ print "PORT-DATA:%s" % port_data
381 if is_detail:
382 port = self._build_detail(port_data)
383 else:
384
385=== modified file 'quantum/cli.py'
386--- quantum/cli.py 2011-06-07 23:56:53 +0000
387+++ quantum/cli.py 2011-06-09 05:01:27 +0000
388@@ -31,25 +31,29 @@
389 FORMAT = "json"
390 CONTENT_TYPE = "application/" + FORMAT
391
392+
393 ### --- Miniclient (taking from the test directory)
394 ### TODO(bgh): move this to a library within quantum
395 class MiniClient(object):
396 """A base client class - derived from Glance.BaseClient"""
397 action_prefix = '/v0.1/tenants/{tenant_id}'
398+
399 def __init__(self, host, port, use_ssl):
400 self.host = host
401 self.port = port
402 self.use_ssl = use_ssl
403 self.connection = None
404+
405 def get_connection_type(self):
406 if self.use_ssl:
407 return httplib.HTTPSConnection
408 else:
409 return httplib.HTTPConnection
410+
411 def do_request(self, tenant, method, action, body=None,
412 headers=None, params=None):
413 action = MiniClient.action_prefix + action
414- action = action.replace('{tenant_id}',tenant)
415+ action = action.replace('{tenant_id}', tenant)
416 if type(params) is dict:
417 action += '?' + urllib.urlencode(params)
418 try:
419@@ -67,6 +71,7 @@
420 raise Exception("Server returned error: %s" % res.read())
421 except (socket.error, IOError), e:
422 raise Exception("Unable to connect to server. Got error: %s" % e)
423+
424 def get_status_code(self, response):
425 if hasattr(response, 'status_int'):
426 return response.status_int
427@@ -76,6 +81,7 @@
428
429 ### -- Core CLI functions
430
431+
432 def list_nets(manager, *args):
433 tenant_id = args[0]
434 networks = manager.get_all_networks(tenant_id)
435@@ -85,6 +91,7 @@
436 name = net["net-name"]
437 print "\tNetwork ID:%s \n\tNetwork Name:%s \n" % (id, name)
438
439+
440 def api_list_nets(client, *args):
441 tenant_id = args[0]
442 res = client.do_request(tenant_id, 'GET', "/networks." + FORMAT)
443@@ -98,11 +105,13 @@
444 # name = n["net-name"]
445 # LOG.info("\tNetwork ID:%s \n\tNetwork Name:%s \n" % (id, name))
446
447+
448 def create_net(manager, *args):
449 tid, name = args
450 new_net_id = manager.create_network(tid, name)
451 print "Created a new Virtual Network with ID:%s\n" % new_net_id
452
453+
454 def api_create_net(client, *args):
455 tid, name = args
456 data = {'network': {'network-name': '%s' % name}}
457@@ -119,11 +128,13 @@
458 return
459 print "Created a new Virtual Network with ID:%s\n" % nid
460
461+
462 def delete_net(manager, *args):
463 tid, nid = args
464 manager.delete_network(tid, nid)
465 print "Deleted Virtual Network with ID:%s" % nid
466
467+
468 def api_delete_net(client, *args):
469 tid, nid = args
470 res = client.do_request(tid, 'DELETE', "/networks/" + nid + "." + FORMAT)
471@@ -135,6 +146,7 @@
472 else:
473 print "Deleted Virtual Network with ID:%s" % nid
474
475+
476 def detail_net(manager, *args):
477 tid, nid = args
478 iface_list = manager.get_network_details(tid, nid)
479@@ -142,6 +154,7 @@
480 for iface in iface_list:
481 print "\tRemote interface:%s" % iface
482
483+
484 def api_detail_net(client, *args):
485 tid, nid = args
486 res = client.do_request(tid, 'GET',
487@@ -163,11 +176,13 @@
488 remote_iface = rd["attachment"]
489 print "\tRemote interface:%s" % remote_iface
490
491+
492 def rename_net(manager, *args):
493 tid, nid, name = args
494 manager.rename_network(tid, nid, name)
495 print "Renamed Virtual Network with ID:%s" % nid
496
497+
498 def api_rename_net(client, *args):
499 tid, nid, name = args
500 data = {'network': {'network-name': '%s' % name}}
501@@ -178,6 +193,7 @@
502 LOG.debug(resdict)
503 print "Renamed Virtual Network with ID:%s" % nid
504
505+
506 def list_ports(manager, *args):
507 tid, nid = args
508 ports = manager.get_all_ports(tid, nid)
509@@ -185,6 +201,7 @@
510 for port in ports:
511 print "\tVirtual Port:%s" % port["port-id"]
512
513+
514 def api_list_ports(client, *args):
515 tid, nid = args
516 res = client.do_request(tid, 'GET',
517@@ -199,12 +216,14 @@
518 for port in rd["ports"]:
519 print "\tVirtual Port:%s" % port["id"]
520
521+
522 def create_port(manager, *args):
523 tid, nid = args
524 new_port = manager.create_port(tid, nid)
525 print "Created Virtual Port:%s " \
526 "on Virtual Network:%s" % (new_port, nid)
527
528+
529 def api_create_port(client, *args):
530 tid, nid = args
531 res = client.do_request(tid, 'POST',
532@@ -218,11 +237,13 @@
533 print "Created Virtual Port:%s " \
534 "on Virtual Network:%s" % (new_port, nid)
535
536+
537 def delete_port(manager, *args):
538 tid, nid, pid = args
539 LOG.info("Deleted Virtual Port:%s " \
540 "on Virtual Network:%s" % (pid, nid))
541
542+
543 def api_delete_port(client, *args):
544 tid, nid, pid = args
545 res = client.do_request(tid, 'DELETE',
546@@ -234,12 +255,14 @@
547 LOG.info("Deleted Virtual Port:%s " \
548 "on Virtual Network:%s" % (pid, nid))
549
550+
551 def detail_port(manager, *args):
552 tid, nid, pid = args
553 port_detail = manager.get_port_details(tid, nid, pid)
554 print "Virtual Port:%s on Virtual Network:%s " \
555 "contains remote interface:%s" % (pid, nid, port_detail)
556
557+
558 def api_detail_port(client, *args):
559 tid, nid, pid = args
560 res = client.do_request(tid, 'GET',
561@@ -256,12 +279,14 @@
562 print "Virtual Port:%s on Virtual Network:%s " \
563 "contains remote interface:%s" % (pid, nid, attachment)
564
565+
566 def plug_iface(manager, *args):
567 tid, nid, pid, vid = args
568 manager.plug_interface(tid, nid, pid, vid)
569 print "Plugged remote interface:%s " \
570 "into Virtual Network:%s" % (vid, nid)
571
572+
573 def api_plug_iface(client, *args):
574 tid, nid, pid, vid = args
575 data = {'port': {'attachment-id': '%s' % vid}}
576@@ -276,12 +301,14 @@
577 return
578 print "Plugged interface \"%s\" to port:%s on network:%s" % (vid, pid, nid)
579
580-def unplug_iface(manager, *args):
581+
582+def unplug_iface(manager, *args):
583 tid, nid, pid = args
584 manager.unplug_interface(tid, nid, pid)
585 print "UnPlugged remote interface " \
586 "from Virtual Port:%s Virtual Network:%s" % (pid, nid)
587
588+
589 def api_unplug_iface(client, *args):
590 tid, nid, pid = args
591 data = {'port': {'attachment-id': ''}}
592@@ -296,63 +323,53 @@
593 return
594 print "Unplugged interface from port:%s on network:%s" % (pid, nid)
595
596+
597 commands = {
598 "list_nets": {
599 "func": list_nets,
600 "api_func": api_list_nets,
601- "args": ["tenant-id"]
602- },
603+ "args": ["tenant-id"]},
604 "create_net": {
605 "func": create_net,
606 "api_func": api_create_net,
607- "args": ["tenant-id", "net-name"]
608- },
609+ "args": ["tenant-id", "net-name"]},
610 "delete_net": {
611 "func": delete_net,
612 "api_func": api_delete_net,
613- "args": ["tenant-id", "net-id"]
614- },
615+ "args": ["tenant-id", "net-id"]},
616 "detail_net": {
617 "func": detail_net,
618 "api_func": api_detail_net,
619- "args": ["tenant-id", "net-id"]
620- },
621+ "args": ["tenant-id", "net-id"]},
622 "rename_net": {
623 "func": rename_net,
624 "api_func": api_rename_net,
625- "args": ["tenant-id", "net-id", "new-name"]
626- },
627+ "args": ["tenant-id", "net-id", "new-name"]},
628 "list_ports": {
629 "func": list_ports,
630 "api_func": api_list_ports,
631- "args": ["tenant-id", "net-id"]
632- },
633+ "args": ["tenant-id", "net-id"]},
634 "create_port": {
635 "func": create_port,
636 "api_func": api_create_port,
637- "args": ["tenant-id", "net-id"]
638- },
639+ "args": ["tenant-id", "net-id"]},
640 "delete_port": {
641 "func": delete_port,
642 "api_func": api_delete_port,
643- "args": ["tenant-id", "net-id", "port-id"]
644- },
645+ "args": ["tenant-id", "net-id", "port-id"]},
646 "detail_port": {
647 "func": detail_port,
648 "api_func": api_detail_port,
649- "args": ["tenant-id", "net-id", "port-id"]
650- },
651+ "args": ["tenant-id", "net-id", "port-id"]},
652 "plug_iface": {
653 "func": plug_iface,
654 "api_func": api_plug_iface,
655- "args": ["tenant-id", "net-id", "port-id", "iface-id"]
656- },
657+ "args": ["tenant-id", "net-id", "port-id", "iface-id"]},
658 "unplug_iface": {
659 "func": unplug_iface,
660 "api_func": api_unplug_iface,
661- "args": ["tenant-id", "net-id", "port-id"]
662- },
663- }
664+ "args": ["tenant-id", "net-id", "port-id"]}, }
665+
666
667 def help():
668 print "\nCommands:"
669@@ -360,6 +377,7 @@
670 print " %s %s" % (k,
671 " ".join(["<%s>" % y for y in commands[k]["args"]]))
672
673+
674 def build_args(cmd, cmdargs, arglist):
675 args = []
676 orig_arglist = arglist[:]
677@@ -381,6 +399,7 @@
678 return None
679 return args
680
681+
682 if __name__ == "__main__":
683 usagestr = "Usage: %prog [OPTIONS] <command> [args]"
684 parser = OptionParser(usage=usagestr)
685@@ -420,7 +439,7 @@
686 LOG.debug("Executing command \"%s\" with args: %s" % (cmd, args))
687 if not options.load_plugin:
688 client = MiniClient(options.host, options.port, options.ssl)
689- if not commands[cmd].has_key("api_func"):
690+ if "api_func" not in commands[cmd]:
691 LOG.error("API version of \"%s\" is not yet implemented" % cmd)
692 sys.exit(1)
693 commands[cmd]["api_func"](client, *args)
694
695=== modified file 'quantum/common/config.py'
696--- quantum/common/config.py 2011-05-24 16:45:16 +0000
697+++ quantum/common/config.py 2011-06-09 05:01:27 +0000
698@@ -209,7 +209,7 @@
699 fix_path(os.path.join('~', '.quantum')),
700 fix_path('~'),
701 os.path.join(FLAGS.state_path, 'etc'),
702- os.path.join(FLAGS.state_path, 'etc','quantum'),
703+ os.path.join(FLAGS.state_path, 'etc', 'quantum'),
704 '/etc/quantum/',
705 '/etc']
706 for cfg_dir in config_file_dirs:
707@@ -244,12 +244,10 @@
708 problem loading the configuration file.
709 """
710 conf_file = find_config_file(options, args)
711- print "Conf_file:%s" %conf_file
712 if not conf_file:
713 raise RuntimeError("Unable to locate any configuration file. "
714 "Cannot load application %s" % app_name)
715 try:
716- print "App_name:%s" %app_name
717 conf = deploy.appconfig("config:%s" % conf_file, name=app_name)
718 return conf_file, conf
719 except Exception, e:
720@@ -257,7 +255,7 @@
721 % (conf_file, e))
722
723
724-def load_paste_app(conf_file, app_name):
725+def load_paste_app(app_name, options, args):
726 """
727 Builds and returns a WSGI app from a paste config file.
728
729@@ -278,16 +276,15 @@
730 :raises RuntimeError when config file cannot be located or application
731 cannot be loaded from config file
732 """
733- #conf_file, conf = load_paste_config(app_name, options, args)
734+ conf_file, conf = load_paste_config(app_name, options, args)
735
736 try:
737- conf_file = os.path.abspath(conf_file)
738 app = deploy.loadapp("config:%s" % conf_file, name=app_name)
739 except (LookupError, ImportError), e:
740 raise RuntimeError("Unable to load %(app_name)s from "
741 "configuration file %(conf_file)s."
742 "\nGot: %(e)r" % locals())
743- return app
744+ return conf, app
745
746
747 def get_option(options, option, **kwargs):
748
749=== modified file 'quantum/common/exceptions.py'
750--- quantum/common/exceptions.py 2011-05-30 00:08:46 +0000
751+++ quantum/common/exceptions.py 2011-06-09 05:01:27 +0000
752@@ -25,7 +25,7 @@
753
754 class QuantumException(Exception):
755 """Base Quantum Exception
756-
757+
758 Taken from nova.exception.NovaException
759 To correctly use this class, inherit from it and define
760 a 'message' property. That message will get printf'd
761@@ -45,6 +45,7 @@
762 def __str__(self):
763 return self._error_string
764
765+
766 class ProcessExecutionError(IOError):
767 def __init__(self, stdout=None, stderr=None, exit_code=None, cmd=None,
768 description=None):
769@@ -84,11 +85,11 @@
770 class PortNotFound(NotFound):
771 message = _("Port %(port_id)s could not be found " \
772 "on network %(net_id)s")
773-
774+
775
776 class StateInvalid(QuantumException):
777 message = _("Unsupported port state: %(port_state)s")
778-
779+
780
781 class NetworkInUse(QuantumException):
782 message = _("Unable to complete operation on network %(net_id)s. " \
783@@ -100,11 +101,13 @@
784 "for network %(net_id)s. The attachment '%(att_id)s" \
785 "is plugged into the logical port.")
786
787+
788 class AlreadyAttached(QuantumException):
789 message = _("Unable to plug the attachment %(att_id)s into port " \
790 "%(port_id)s for network %(net_id)s. The attachment is " \
791 "already plugged into port %(att_port_id)s")
792-
793+
794+
795 class Duplicate(Error):
796 pass
797
798
799=== modified file 'quantum/common/flags.py'
800--- quantum/common/flags.py 2011-05-23 20:51:00 +0000
801+++ quantum/common/flags.py 2011-06-09 05:01:27 +0000
802@@ -23,7 +23,7 @@
803 """
804
805 import getopt
806-import os
807+import os
808 import string
809 import sys
810
811@@ -249,4 +249,3 @@
812
813 DEFINE_string('state_path', os.path.join(os.path.dirname(__file__), '../../'),
814 "Top-level directory for maintaining quantum's state")
815-
816
817=== modified file 'quantum/common/utils.py'
818--- quantum/common/utils.py 2011-06-05 00:45:36 +0000
819+++ quantum/common/utils.py 2011-06-09 05:01:27 +0000
820@@ -37,6 +37,7 @@
821 TIME_FORMAT = "%Y-%m-%dT%H:%M:%SZ"
822 FLAGS = flags.FLAGS
823
824+
825 def int_from_bool_as_string(subject):
826 """
827 Interpret a string as a boolean and return either 1 or 0.
828@@ -188,6 +189,7 @@
829 def parse_isotime(timestr):
830 return datetime.datetime.strptime(timestr, TIME_FORMAT)
831
832+
833 def getPluginFromConfig(file="config.ini"):
834 Config = ConfigParser.ConfigParser()
835 Config.read(file)
836
837=== modified file 'quantum/common/wsgi.py'
838--- quantum/common/wsgi.py 2011-05-27 16:52:06 +0000
839+++ quantum/common/wsgi.py 2011-06-09 05:01:27 +0000
840@@ -40,6 +40,7 @@
841
842 LOG = logging.getLogger('quantum.common.wsgi')
843
844+
845 class WritableLogger(object):
846 """A thin wrapper that responds to `write` and logs."""
847
848@@ -126,7 +127,7 @@
849
850 """
851 parts = self.path.rsplit('.', 1)
852- LOG.debug("Request parts:%s",parts)
853+ LOG.debug("Request parts:%s", parts)
854 if len(parts) > 1:
855 format = parts[1]
856 if format in ['json', 'xml']:
857@@ -134,7 +135,7 @@
858
859 ctypes = ['application/json', 'application/xml']
860 bm = self.accept.best_match(ctypes)
861- LOG.debug("BM:%s",bm)
862+ LOG.debug("BM:%s", bm)
863 return bm or 'application/json'
864
865 def get_content_type(self):
866@@ -336,21 +337,21 @@
867 arg_dict = req.environ['wsgiorg.routing_args'][1]
868 action = arg_dict['action']
869 method = getattr(self, action)
870- LOG.debug("ARG_DICT:%s",arg_dict)
871- LOG.debug("Action:%s",action)
872- LOG.debug("Method:%s",method)
873+ LOG.debug("ARG_DICT:%s", arg_dict)
874+ LOG.debug("Action:%s", action)
875+ LOG.debug("Method:%s", method)
876 LOG.debug("%s %s" % (req.method, req.url))
877 del arg_dict['controller']
878 del arg_dict['action']
879 if 'format' in arg_dict:
880 del arg_dict['format']
881- arg_dict['req'] = req
882+ arg_dict['request'] = req
883 result = method(**arg_dict)
884
885 if type(result) is dict:
886 content_type = req.best_match_content_type()
887- LOG.debug("Content type:%s",content_type)
888- LOG.debug("Result:%s",result)
889+ LOG.debug("Content type:%s", content_type)
890+ LOG.debug("Result:%s", result)
891 default_xmlns = self.get_default_xmlns(req)
892 body = self._serialize(result, content_type, default_xmlns)
893
894@@ -497,7 +498,7 @@
895 xmlns = metadata.get('xmlns', None)
896 if xmlns:
897 result.setAttribute('xmlns', xmlns)
898- LOG.debug("DATA:%s",data)
899+ LOG.debug("DATA:%s", data)
900 if type(data) is list:
901 LOG.debug("TYPE IS LIST")
902 collections = metadata.get('list_collections', {})
903@@ -538,8 +539,7 @@
904 result.appendChild(node)
905 else:
906 # Type is atom
907- LOG.debug("TYPE IS ATOM:%s",data)
908+ LOG.debug("TYPE IS ATOM:%s", data)
909 node = doc.createTextNode(str(data))
910 result.appendChild(node)
911 return result
912-
913
914=== modified file 'quantum/db/api.py'
915--- quantum/db/api.py 2011-06-04 20:17:32 +0000
916+++ quantum/db/api.py 2011-06-09 05:01:27 +0000
917@@ -25,6 +25,7 @@
918 _MAKER = None
919 BASE = models.BASE
920
921+
922 def configure_db(options):
923 """
924 Establish the database, create an engine if needed, and
925@@ -40,6 +41,7 @@
926 pool_recycle=3600)
927 register_models()
928
929+
930 def get_session(autocommit=True, expire_on_commit=False):
931 """Helper method to grab session"""
932 global _MAKER, _ENGINE
933@@ -50,18 +52,21 @@
934 expire_on_commit=expire_on_commit)
935 return _MAKER()
936
937+
938 def register_models():
939 """Register Models and create properties"""
940 global _ENGINE
941 assert _ENGINE
942 BASE.metadata.create_all(_ENGINE)
943
944+
945 def unregister_models():
946 """Unregister Models, useful clearing out data before testing"""
947 global _ENGINE
948 assert _ENGINE
949 BASE.metadata.drop_all(_ENGINE)
950
951+
952 def network_create(tenant_id, name):
953 session = get_session()
954 net = None
955@@ -77,12 +82,14 @@
956 session.flush()
957 return net
958
959+
960 def network_list(tenant_id):
961 session = get_session()
962 return session.query(models.Network).\
963 filter_by(tenant_id=tenant_id).\
964 all()
965
966+
967 def network_get(net_id):
968 session = get_session()
969 try:
970@@ -92,6 +99,7 @@
971 except exc.NoResultFound:
972 raise Exception("No net found with id = %s" % net_id)
973
974+
975 def network_rename(net_id, tenant_id, new_name):
976 session = get_session()
977 try:
978@@ -106,6 +114,7 @@
979 return net
980 raise Exception("A network with name \"%s\" already exists" % new_name)
981
982+
983 def network_destroy(net_id):
984 session = get_session()
985 try:
986@@ -118,6 +127,7 @@
987 except exc.NoResultFound:
988 raise Exception("No network found with id = %s" % net_id)
989
990+
991 def port_create(net_id):
992 session = get_session()
993 with session.begin():
994@@ -126,12 +136,14 @@
995 session.flush()
996 return port
997
998+
999 def port_list(net_id):
1000 session = get_session()
1001 return session.query(models.Port).\
1002 filter_by(network_id=net_id).\
1003 all()
1004
1005+
1006 def port_get(port_id):
1007 session = get_session()
1008 try:
1009@@ -141,6 +153,7 @@
1010 except exc.NoResultFound:
1011 raise Exception("No port found with id = %s " % port_id)
1012
1013+
1014 def port_set_attachment(port_id, new_interface_id):
1015 session = get_session()
1016 ports = None
1017@@ -157,7 +170,9 @@
1018 session.flush()
1019 return port
1020 else:
1021- raise Exception("Port with attachment \"%s\" already exists" % (new_interface_id))
1022+ raise Exception("Port with attachment \"%s\" already exists"
1023+ % (new_interface_id))
1024+
1025
1026 def port_destroy(port_id):
1027 session = get_session()
1028@@ -170,4 +185,3 @@
1029 return port
1030 except exc.NoResultFound:
1031 raise Exception("No port found with id = %s " % port_id)
1032-
1033
1034=== modified file 'quantum/db/models.py'
1035--- quantum/db/models.py 2011-06-04 03:55:26 +0000
1036+++ quantum/db/models.py 2011-06-09 05:01:27 +0000
1037@@ -25,12 +25,14 @@
1038
1039 BASE = declarative_base()
1040
1041+
1042 class Port(BASE):
1043 """Represents a port on a quantum network"""
1044 __tablename__ = 'ports'
1045
1046 uuid = Column(String(255), primary_key=True)
1047- network_id = Column(String(255), ForeignKey("networks.uuid"), nullable=False)
1048+ network_id = Column(String(255), ForeignKey("networks.uuid"),
1049+ nullable=False)
1050 interface_id = Column(String(255))
1051
1052 def __init__(self, network_id):
1053@@ -38,7 +40,9 @@
1054 self.network_id = network_id
1055
1056 def __repr__(self):
1057- return "<Port(%s,%s,%s)>" % (self.uuid, self.network_id, self.interface_id)
1058+ return "<Port(%s,%s,%s)>" % (self.uuid, self.network_id,
1059+ self.interface_id)
1060+
1061
1062 class Network(BASE):
1063 """Represents a quantum network"""
1064@@ -56,4 +60,4 @@
1065
1066 def __repr__(self):
1067 return "<Network(%s,%s,%s)>" % \
1068- (self.uuid,self.name,self.tenant_id)
1069+ (self.uuid, self.name, self.tenant_id)
1070
1071=== modified file 'quantum/manager.py'
1072--- quantum/manager.py 2011-06-07 06:09:53 +0000
1073+++ quantum/manager.py 2011-06-09 05:01:27 +0000
1074@@ -18,12 +18,14 @@
1075
1076
1077 """
1078-Quantum's Manager class is responsible for parsing a config file and instantiating the correct
1079-plugin that concretely implement quantum_plugin_base class
1080+Quantum's Manager class is responsible for parsing a config file and
1081+instantiating the correct plugin that concretely implement quantum_plugin_base
1082+class.
1083
1084 The caller should make sure that QuantumManager is a singleton.
1085 """
1086 import gettext
1087+import os
1088 gettext.install('quantum', unicode=1)
1089
1090 import os
1091@@ -33,16 +35,19 @@
1092
1093 CONFIG_FILE = "plugins.ini"
1094
1095+
1096 def find_config(basepath):
1097 for root, dirs, files in os.walk(basepath):
1098 if CONFIG_FILE in files:
1099 return os.path.join(root, CONFIG_FILE)
1100 return None
1101
1102+
1103 class QuantumManager(object):
1104 def __init__(self, config=None):
1105 if config == None:
1106- self.configuration_file = find_config(os.path.abspath(os.path.dirname(__file__)))
1107+ self.configuration_file = find_config(
1108+ os.path.abspath(os.path.dirname(__file__)))
1109 else:
1110 self.configuration_file = config
1111 plugin_location = utils.getPluginFromConfig(self.configuration_file)
1112@@ -58,4 +63,3 @@
1113
1114 def get_manager(self):
1115 return self.plugin
1116-
1117
1118=== modified file 'quantum/plugins/SamplePlugin.py'
1119--- quantum/plugins/SamplePlugin.py 2011-06-07 05:48:57 +0000
1120+++ quantum/plugins/SamplePlugin.py 2011-06-09 05:01:27 +0000
1121@@ -17,33 +17,32 @@
1122
1123 from quantum.common import exceptions as exc
1124
1125+
1126 class QuantumEchoPlugin(object):
1127
1128 """
1129 QuantumEchoPlugin is a demo plugin that doesn't
1130 do anything but demonstrated the concept of a
1131 concrete Quantum Plugin. Any call to this plugin
1132- will result in just a "print" to std. out with
1133+ will result in just a "print" to std. out with
1134 the name of the method that was called.
1135 """
1136-
1137+
1138 def get_all_networks(self, tenant_id):
1139 """
1140 Returns a dictionary containing all
1141 <network_uuid, network_name> for
1142- the specified tenant.
1143+ the specified tenant.
1144 """
1145 print("get_all_networks() called\n")
1146-
1147-
1148+
1149 def create_network(self, tenant_id, net_name):
1150 """
1151 Creates a new Virtual Network, and assigns it
1152 a symbolic name.
1153 """
1154 print("create_network() called\n")
1155-
1156-
1157+
1158 def delete_network(self, tenant_id, net_id):
1159 """
1160 Deletes the network with the specified network identifier
1161@@ -51,38 +50,33 @@
1162 """
1163 print("delete_network() called\n")
1164
1165-
1166 def get_network_details(self, tenant_id, net_id):
1167 """
1168 Deletes the Virtual Network belonging to a the
1169 spec
1170 """
1171 print("get_network_details() called\n")
1172-
1173-
1174+
1175 def rename_network(self, tenant_id, net_id, new_name):
1176 """
1177 Updates the symbolic name belonging to a particular
1178 Virtual Network.
1179 """
1180 print("rename_network() called\n")
1181-
1182-
1183+
1184 def get_all_ports(self, tenant_id, net_id):
1185 """
1186 Retrieves all port identifiers belonging to the
1187 specified Virtual Network.
1188 """
1189 print("get_all_ports() called\n")
1190-
1191-
1192+
1193 def create_port(self, tenant_id, net_id):
1194 """
1195 Creates a port on the specified Virtual Network.
1196 """
1197 print("create_port() called\n")
1198-
1199-
1200+
1201 def delete_port(self, tenant_id, net_id, port_id):
1202 """
1203 Deletes a port on a specified Virtual Network,
1204@@ -97,24 +91,21 @@
1205 Updates the state of a port on the specified Virtual Network.
1206 """
1207 print("update_port() called\n")
1208-
1209-
1210+
1211 def get_port_details(self, tenant_id, net_id, port_id):
1212 """
1213 This method allows the user to retrieve a remote interface
1214 that is attached to this particular port.
1215 """
1216 print("get_port_details() called\n")
1217-
1218-
1219+
1220 def plug_interface(self, tenant_id, net_id, port_id, remote_interface_id):
1221 """
1222 Attaches a remote interface to the specified port on the
1223 specified Virtual Network.
1224 """
1225 print("plug_interface() called\n")
1226-
1227-
1228+
1229 def unplug_interface(self, tenant_id, net_id, port_id):
1230 """
1231 Detaches a remote interface from the specified port on the
1232@@ -130,18 +121,17 @@
1233 hard-coded data structures to aid in quantum
1234 client/cli development
1235 """
1236-
1237+
1238 def get_all_networks(self, tenant_id):
1239 """
1240 Returns a dictionary containing all
1241 <network_uuid, network_name> for
1242- the specified tenant.
1243+ the specified tenant.
1244 """
1245- nets = {"001": "lNet1", "002": "lNet2" , "003": "lNet3"}
1246+ nets = {"001": "lNet1", "002": "lNet2", "003": "lNet3"}
1247 print("get_all_networks() called\n")
1248 return nets
1249-
1250-
1251+
1252 def create_network(self, tenant_id, net_name):
1253 """
1254 Creates a new Virtual Network, and assigns it
1255@@ -150,8 +140,7 @@
1256 print("create_network() called\n")
1257 # return network_id of the created network
1258 return 101
1259-
1260-
1261+
1262 def delete_network(self, tenant_id, net_id):
1263 """
1264 Deletes the network with the specified network identifier
1265@@ -159,7 +148,6 @@
1266 """
1267 print("delete_network() called\n")
1268
1269-
1270 def get_network_details(self, tenant_id, net_id):
1271 """
1272 retrieved a list of all the remote vifs that
1273@@ -168,16 +156,14 @@
1274 print("get_network_details() called\n")
1275 vifs_on_net = ["/tenant1/networks/net_id/portid/vif2.0"]
1276 return vifs_on_net
1277-
1278-
1279+
1280 def rename_network(self, tenant_id, net_id, new_name):
1281 """
1282 Updates the symbolic name belonging to a particular
1283 Virtual Network.
1284 """
1285 print("rename_network() called\n")
1286-
1287-
1288+
1289 def get_all_ports(self, tenant_id, net_id):
1290 """
1291 Retrieves all port identifiers belonging to the
1292@@ -186,8 +172,7 @@
1293 print("get_all_ports() called\n")
1294 port_ids_on_net = ["2", "3", "4"]
1295 return port_ids_on_net
1296-
1297-
1298+
1299 def create_port(self, tenant_id, net_id):
1300 """
1301 Creates a port on the specified Virtual Network.
1302@@ -201,8 +186,7 @@
1303 Updates the state of a port on the specified Virtual Network.
1304 """
1305 print("update_port() called\n")
1306-
1307-
1308+
1309 def delete_port(self, tenant_id, net_id, port_id):
1310 """
1311 Deletes a port on a specified Virtual Network,
1312@@ -211,8 +195,7 @@
1313 is deleted.
1314 """
1315 print("delete_port() called\n")
1316-
1317-
1318+
1319 def get_port_details(self, tenant_id, net_id, port_id):
1320 """
1321 This method allows the user to retrieve a remote interface
1322@@ -221,24 +204,22 @@
1323 print("get_port_details() called\n")
1324 #returns the remote interface UUID
1325 return "/tenant1/networks/net_id/portid/vif2.1"
1326-
1327-
1328+
1329 def plug_interface(self, tenant_id, net_id, port_id, remote_interface_id):
1330 """
1331 Attaches a remote interface to the specified port on the
1332 specified Virtual Network.
1333 """
1334 print("plug_interface() called\n")
1335-
1336-
1337+
1338 def unplug_interface(self, tenant_id, net_id, port_id):
1339 """
1340 Detaches a remote interface from the specified port on the
1341 specified Virtual Network.
1342 """
1343 print("unplug_interface() called\n")
1344-
1345-
1346+
1347+
1348 class FakePlugin(object):
1349 """
1350 FakePlugin is a demo plugin that provides
1351@@ -248,72 +229,66 @@
1352
1353 #static data for networks and ports
1354 _port_dict_1 = {
1355- 1 : {'port-id': 1,
1356+ 1: {'port-id': 1,
1357 'port-state': 'DOWN',
1358 'attachment': None},
1359- 2 : {'port-id': 2,
1360- 'port-state':'UP',
1361- 'attachment': None}
1362- }
1363+ 2: {'port-id': 2,
1364+ 'port-state': 'UP',
1365+ 'attachment': None}}
1366 _port_dict_2 = {
1367- 1 : {'port-id': 1,
1368+ 1: {'port-id': 1,
1369 'port-state': 'UP',
1370 'attachment': 'SomeFormOfVIFID'},
1371- 2 : {'port-id': 2,
1372- 'port-state':'DOWN',
1373- 'attachment': None}
1374- }
1375- _networks={'001':
1376+ 2: {'port-id': 2,
1377+ 'port-state': 'DOWN',
1378+ 'attachment': None}}
1379+ _networks = {'001':
1380 {
1381- 'net-id':'001',
1382- 'net-name':'pippotest',
1383- 'net-ports': _port_dict_1
1384- },
1385+ 'net-id': '001',
1386+ 'net-name': 'pippotest',
1387+ 'net-ports': _port_dict_1},
1388 '002':
1389 {
1390- 'net-id':'002',
1391- 'net-name':'cicciotest',
1392- 'net-ports': _port_dict_2
1393- }}
1394-
1395-
1396+ 'net-id': '002',
1397+ 'net-name': 'cicciotest',
1398+ 'net-ports': _port_dict_2}}
1399+
1400 def __init__(self):
1401- FakePlugin._net_counter=len(FakePlugin._networks)
1402-
1403+ FakePlugin._net_counter = len(FakePlugin._networks)
1404+
1405 def _get_network(self, tenant_id, network_id):
1406 network = FakePlugin._networks.get(network_id)
1407 if not network:
1408 raise exc.NetworkNotFound(net_id=network_id)
1409 return network
1410
1411-
1412 def _get_port(self, tenant_id, network_id, port_id):
1413 net = self._get_network(tenant_id, network_id)
1414 port = net['net-ports'].get(int(port_id))
1415 if not port:
1416 raise exc.PortNotFound(net_id=network_id, port_id=port_id)
1417 return port
1418-
1419+
1420 def _validate_port_state(self, port_state):
1421- if port_state.upper() not in ('UP','DOWN'):
1422+ if port_state.upper() not in ('UP', 'DOWN'):
1423 raise exc.StateInvalid(port_state=port_state)
1424 return True
1425-
1426+
1427 def _validate_attachment(self, tenant_id, network_id, port_id,
1428 remote_interface_id):
1429 network = self._get_network(tenant_id, network_id)
1430 for port in network['net-ports'].values():
1431 if port['attachment'] == remote_interface_id:
1432- raise exc.AlreadyAttached(net_id = network_id,
1433- port_id = port_id,
1434- att_id = port['attachment'],
1435- att_port_id = port['port-id'])
1436-
1437+ raise exc.AlreadyAttached(net_id=network_id,
1438+ port_id=port_id,
1439+ att_id=port['attachment'],
1440+ att_port_id=port['port-id'])
1441+
1442 def get_all_networks(self, tenant_id):
1443 """
1444 Returns a dictionary containing all
1445 <network_uuid, network_name> for
1446- the specified tenant.
1447+ the specified tenant.
1448 """
1449 print("get_all_networks() called\n")
1450 return FakePlugin._networks.values()
1451@@ -333,16 +308,16 @@
1452 """
1453 print("create_network() called\n")
1454 FakePlugin._net_counter += 1
1455- new_net_id=("0" * (3 - len(str(FakePlugin._net_counter)))) + \
1456+ new_net_id = ("0" * (3 - len(str(FakePlugin._net_counter)))) + \
1457 str(FakePlugin._net_counter)
1458 print new_net_id
1459- new_net_dict={'net-id':new_net_id,
1460- 'net-name':net_name,
1461+ new_net_dict = {'net-id': new_net_id,
1462+ 'net-name': net_name,
1463 'net-ports': {}}
1464- FakePlugin._networks[new_net_id]=new_net_dict
1465+ FakePlugin._networks[new_net_id] = new_net_dict
1466 # return network_id of the created network
1467 return new_net_dict
1468-
1469+
1470 def delete_network(self, tenant_id, net_id):
1471 """
1472 Deletes the network with the specified network identifier
1473@@ -360,7 +335,7 @@
1474 return net
1475 # Network not found
1476 raise exc.NetworkNotFound(net_id=net_id)
1477-
1478+
1479 def rename_network(self, tenant_id, net_id, new_name):
1480 """
1481 Updates the symbolic name belonging to a particular
1482@@ -368,7 +343,7 @@
1483 """
1484 print("rename_network() called\n")
1485 net = self._get_network(tenant_id, net_id)
1486- net['net-name']=new_name
1487+ net['net-name'] = new_name
1488 return net
1489
1490 def get_all_ports(self, tenant_id, net_id):
1491@@ -388,7 +363,7 @@
1492 """
1493 print("get_port_details() called\n")
1494 return self._get_port(tenant_id, net_id, port_id)
1495-
1496+
1497 def create_port(self, tenant_id, net_id, port_state=None):
1498 """
1499 Creates a port on the specified Virtual Network.
1500@@ -396,15 +371,15 @@
1501 print("create_port() called\n")
1502 net = self._get_network(tenant_id, net_id)
1503 # check port state
1504- # TODO(salvatore-orlando): Validate port state in API?
1505+ # TODO(salvatore-orlando): Validate port state in API?
1506 self._validate_port_state(port_state)
1507 ports = net['net-ports']
1508- new_port_id = max(ports.keys())+1
1509- new_port_dict = {'port-id':new_port_id,
1510+ new_port_id = max(ports.keys()) + 1
1511+ new_port_dict = {'port-id': new_port_id,
1512 'port-state': port_state,
1513 'attachment': None}
1514 ports[new_port_id] = new_port_dict
1515- return new_port_dict
1516+ return new_port_dict
1517
1518 def update_port(self, tenant_id, net_id, port_id, port_state):
1519 """
1520@@ -414,8 +389,8 @@
1521 port = self._get_port(tenant_id, net_id, port_id)
1522 self._validate_port_state(port_state)
1523 port['port-state'] = port_state
1524- return port
1525-
1526+ return port
1527+
1528 def delete_port(self, tenant_id, net_id, port_id):
1529 """
1530 Deletes a port on a specified Virtual Network,
1531@@ -427,14 +402,13 @@
1532 net = self._get_network(tenant_id, net_id)
1533 port = self._get_port(tenant_id, net_id, port_id)
1534 if port['attachment']:
1535- raise exc.PortInUse(net_id=net_id,port_id=port_id,
1536+ raise exc.PortInUse(net_id=net_id, port_id=port_id,
1537 att_id=port['attachment'])
1538 try:
1539 net['net-ports'].pop(int(port_id))
1540- except KeyError:
1541+ except KeyError:
1542 raise exc.PortNotFound(net_id=net_id, port_id=port_id)
1543
1544-
1545 def plug_interface(self, tenant_id, net_id, port_id, remote_interface_id):
1546 """
1547 Attaches a remote interface to the specified port on the
1548@@ -446,10 +420,10 @@
1549 remote_interface_id)
1550 port = self._get_port(tenant_id, net_id, port_id)
1551 if port['attachment']:
1552- raise exc.PortInUse(net_id=net_id,port_id=port_id,
1553+ raise exc.PortInUse(net_id=net_id, port_id=port_id,
1554 att_id=port['attachment'])
1555 port['attachment'] = remote_interface_id
1556-
1557+
1558 def unplug_interface(self, tenant_id, net_id, port_id):
1559 """
1560 Detaches a remote interface from the specified port on the
1561@@ -460,5 +434,3 @@
1562 # TODO(salvatore-orlando):
1563 # Should unplug on port without attachment raise an Error?
1564 port['attachment'] = None
1565-
1566-
1567\ No newline at end of file
1568
1569=== modified file 'quantum/plugins/__init__.py'
1570--- quantum/plugins/__init__.py 2011-05-13 21:23:37 +0000
1571+++ quantum/plugins/__init__.py 2011-06-09 05:01:27 +0000
1572@@ -13,4 +13,4 @@
1573 # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
1574 # License for the specific language governing permissions and limitations
1575 # under the License.
1576-# @author: Somik Behera, Nicira Networks, Inc.
1577\ No newline at end of file
1578+# @author: Somik Behera, Nicira Networks, Inc.
1579
1580=== modified file 'quantum/plugins/openvswitch/agent/ovs_quantum_agent.py'
1581--- quantum/plugins/openvswitch/agent/ovs_quantum_agent.py 2011-06-04 03:59:49 +0000
1582+++ quantum/plugins/openvswitch/agent/ovs_quantum_agent.py 2011-06-09 05:01:27 +0000
1583@@ -28,6 +28,7 @@
1584 from optparse import OptionParser
1585 from subprocess import *
1586
1587+
1588 # A class to represent a VIF (i.e., a port that has 'iface-id' and 'vif-mac'
1589 # attributes set).
1590 class VifPort:
1591@@ -37,11 +38,13 @@
1592 self.vif_id = vif_id
1593 self.vif_mac = vif_mac
1594 self.switch = switch
1595+
1596 def __str__(self):
1597 return "iface-id=" + self.vif_id + ", vif_mac=" + \
1598 self.vif_mac + ", port_name=" + self.port_name + \
1599 ", ofport=" + self.ofport + ", bridge name = " + self.switch.br_name
1600
1601+
1602 class OVSBridge:
1603 def __init__(self, br_name):
1604 self.br_name = br_name
1605@@ -51,27 +54,27 @@
1606 return Popen(args, stdout=PIPE).communicate()[0]
1607
1608 def run_vsctl(self, args):
1609- full_args = ["ovs-vsctl" ] + args
1610+ full_args = ["ovs-vsctl"] + args
1611 return self.run_cmd(full_args)
1612
1613 def reset_bridge(self):
1614- self.run_vsctl([ "--" , "--if-exists", "del-br", self.br_name])
1615+ self.run_vsctl(["--", "--if-exists", "del-br", self.br_name])
1616 self.run_vsctl(["add-br", self.br_name])
1617
1618 def delete_port(self, port_name):
1619- self.run_vsctl([ "--" , "--if-exists", "del-port", self.br_name,
1620+ self.run_vsctl(["--", "--if-exists", "del-port", self.br_name,
1621 port_name])
1622
1623 def set_db_attribute(self, table_name, record, column, value):
1624- args = [ "set", table_name, record, "%s=%s" % (column,value) ]
1625+ args = ["set", table_name, record, "%s=%s" % (column, value)]
1626 self.run_vsctl(args)
1627
1628- def clear_db_attribute(self, table_name,record, column):
1629- args = [ "clear", table_name, record, column ]
1630+ def clear_db_attribute(self, table_name, record, column):
1631+ args = ["clear", table_name, record, column]
1632 self.run_vsctl(args)
1633
1634 def run_ofctl(self, cmd, args):
1635- full_args = ["ovs-ofctl", cmd, self.br_name ] + args
1636+ full_args = ["ovs-ofctl", cmd, self.br_name] + args
1637 return self.run_cmd(full_args)
1638
1639 def remove_all_flows(self):
1640@@ -80,7 +83,7 @@
1641 def get_port_ofport(self, port_name):
1642 return self.db_get_val("Interface", port_name, "ofport")
1643
1644- def add_flow(self,**dict):
1645+ def add_flow(self, **dict):
1646 if "actions" not in dict:
1647 raise Exception("must specify one or more actions")
1648 if "priority" not in dict:
1649@@ -90,9 +93,9 @@
1650 if "match" in dict:
1651 flow_str += "," + dict["match"]
1652 flow_str += ",actions=%s" % (dict["actions"])
1653- self.run_ofctl("add-flow", [ flow_str ] )
1654+ self.run_ofctl("add-flow", [flow_str])
1655
1656- def delete_flows(self,**dict):
1657+ def delete_flows(self, **dict):
1658 all_args = []
1659 if "priority" in dict:
1660 all_args.append("priority=%s" % dict["priority"])
1661@@ -101,14 +104,14 @@
1662 if "actions" in dict:
1663 all_args.append("actions=%s" % (dict["actions"]))
1664 flow_str = ",".join(all_args)
1665- self.run_ofctl("del-flows", [ flow_str ] )
1666+ self.run_ofctl("del-flows", [flow_str])
1667
1668 def db_get_map(self, table, record, column):
1669- str = self.run_vsctl([ "get" , table, record, column ]).rstrip("\n\r")
1670+ str = self.run_vsctl(["get", table, record, column]).rstrip("\n\r")
1671 return self.db_str_to_map(str)
1672
1673 def db_get_val(self, table, record, column):
1674- return self.run_vsctl([ "get" , table, record, column ]).rstrip("\n\r")
1675+ return self.run_vsctl(["get", table, record, column]).rstrip("\n\r")
1676
1677 def db_str_to_map(self, full_str):
1678 list = full_str.strip("{}").split(", ")
1679@@ -121,7 +124,7 @@
1680 return ret
1681
1682 def get_port_name_list(self):
1683- res = self.run_vsctl([ "list-ports", self.br_name])
1684+ res = self.run_vsctl(["list-ports", self.br_name])
1685 return res.split("\n")[0:-1]
1686
1687 def get_port_stats(self, port_name):
1688@@ -132,47 +135,53 @@
1689 edge_ports = []
1690 port_names = self.get_port_name_list()
1691 for name in port_names:
1692- external_ids = self.db_get_map("Interface",name,"external_ids")
1693+ external_ids = self.db_get_map("Interface", name, "external_ids")
1694 if "iface-id" in external_ids and "attached-mac" in external_ids:
1695- ofport = self.db_get_val("Interface",name,"ofport")
1696+ ofport = self.db_get_val("Interface", name, "ofport")
1697 p = VifPort(name, ofport, external_ids["iface-id"],
1698 external_ids["attached-mac"], self)
1699 edge_ports.append(p)
1700 else:
1701 # iface-id might not be set. See if we can figure it out and
1702 # set it here.
1703- external_ids = self.db_get_map("Interface",name,"external_ids")
1704+ external_ids = self.db_get_map("Interface", name,
1705+ "external_ids")
1706 if "attached-mac" not in external_ids:
1707 continue
1708 vif_uuid = external_ids.get("xs-vif-uuid", "")
1709 if len(vif_uuid) == 0:
1710 continue
1711 LOG.debug("iface-id not set, got vif-uuid: %s" % vif_uuid)
1712- res = os.popen("xe vif-param-get param-name=other-config uuid=%s | grep nicira-iface-id | awk '{print $2}'" % vif_uuid).readline()
1713+ res = os.popen("xe vif-param-get param-name=other-config "
1714+ "uuid=%s | grep nicira-iface-id | "
1715+ "awk '{print $2}'"
1716+ % vif_uuid).readline()
1717 res = res.strip()
1718 if len(res) == 0:
1719 continue
1720 external_ids["iface-id"] = res
1721- LOG.info("Setting interface \"%s\" iface-id to \"%s\"" % (name, res))
1722+ LOG.info("Setting interface \"%s\" iface-id to \"%s\""
1723+ % (name, res))
1724 self.set_db_attribute("Interface", name,
1725- "external-ids:iface-id", res)
1726- ofport = self.db_get_val("Interface",name,"ofport")
1727+ "external-ids:iface-id", res)
1728+ ofport = self.db_get_val("Interface", name, "ofport")
1729 p = VifPort(name, ofport, external_ids["iface-id"],
1730- external_ids["attached-mac"], self)
1731+ external_ids["attached-mac"], self)
1732 edge_ports.append(p)
1733 return edge_ports
1734
1735+
1736 class OVSNaaSPlugin:
1737 def __init__(self, integ_br):
1738 self.setup_integration_br(integ_br)
1739
1740 def port_bound(self, port, vlan_id):
1741- self.int_br.set_db_attribute("Port", port.port_name,"tag",
1742+ self.int_br.set_db_attribute("Port", port.port_name, "tag",
1743 str(vlan_id))
1744
1745 def port_unbound(self, port, still_exists):
1746 if still_exists:
1747- self.int_br.clear_db_attribute("Port", port.port_name,"tag")
1748+ self.int_br.clear_db_attribute("Port", port.port_name, "tag")
1749
1750 def setup_integration_br(self, integ_br):
1751 self.int_br = OVSBridge(integ_br)
1752@@ -182,7 +191,8 @@
1753 # switch all other traffic using L2 learning
1754 self.int_br.add_flow(priority=1, actions="normal")
1755 # FIXME send broadcast everywhere, regardless of tenant
1756- #int_br.add_flow(priority=3, match="dl_dst=ff:ff:ff:ff:ff:ff", actions="normal")
1757+ #int_br.add_flow(priority=3, match="dl_dst=ff:ff:ff:ff:ff:ff",
1758+ # actions="normal")
1759
1760 def daemon_loop(self, conn):
1761 self.local_vlan_map = {}
1762@@ -216,9 +226,9 @@
1763 else:
1764 # no binding, put him on the 'dead vlan'
1765 self.int_br.set_db_attribute("Port", p.port_name, "tag",
1766- "4095")
1767- old_b = old_local_bindings.get(p.vif_id,None)
1768- new_b = new_local_bindings.get(p.vif_id,None)
1769+ "4095")
1770+ old_b = old_local_bindings.get(p.vif_id, None)
1771+ new_b = new_local_bindings.get(p.vif_id, None)
1772 if old_b != new_b:
1773 if old_b is not None:
1774 LOG.info("Removing binding to net-id = %s for %s"
1775
1776=== modified file 'quantum/plugins/openvswitch/ovs_db.py'
1777--- quantum/plugins/openvswitch/ovs_db.py 2011-06-04 03:59:49 +0000
1778+++ quantum/plugins/openvswitch/ovs_db.py 2011-06-09 05:01:27 +0000
1779@@ -24,6 +24,7 @@
1780 import quantum.db.models as models
1781 import ovs_models
1782
1783+
1784 def get_vlans():
1785 session = db.get_session()
1786 try:
1787@@ -33,9 +34,10 @@
1788 return []
1789 res = []
1790 for x in bindings:
1791- res.append((x.vlan_id, x.network_id))
1792+ res.append((x.vlan_id, x.network_id))
1793 return res
1794
1795+
1796 def add_vlan_binding(vlanid, netid):
1797 session = db.get_session()
1798 binding = ovs_models.VlanBinding(vlanid, netid)
1799@@ -43,6 +45,7 @@
1800 session.flush()
1801 return binding.vlan_id
1802
1803+
1804 def remove_vlan_binding(netid):
1805 session = db.get_session()
1806 try:
1807@@ -54,6 +57,7 @@
1808 pass
1809 session.flush()
1810
1811+
1812 def update_network_binding(netid, ifaceid):
1813 session = db.get_session()
1814 # Add to or delete from the bindings table
1815
1816=== modified file 'quantum/plugins/openvswitch/ovs_models.py'
1817--- quantum/plugins/openvswitch/ovs_models.py 2011-06-04 03:59:49 +0000
1818+++ quantum/plugins/openvswitch/ovs_models.py 2011-06-09 05:01:27 +0000
1819@@ -23,9 +23,9 @@
1820 from sqlalchemy import Column, Integer, String, ForeignKey
1821 from sqlalchemy.ext.declarative import declarative_base
1822 from sqlalchemy.orm import relation
1823-
1824 from quantum.db.models import BASE
1825
1826+
1827 class NetworkBinding(BASE):
1828 """Represents a binding of network_id, vif_id"""
1829 __tablename__ = 'network_bindings'
1830@@ -42,6 +42,7 @@
1831 return "<NetworkBinding(%s,%s)>" % \
1832 (self.network_id, self.vif_id)
1833
1834+
1835 class VlanBinding(BASE):
1836 """Represents a binding of network_id, vlan_id"""
1837 __tablename__ = 'vlan_bindings'
1838
1839=== modified file 'quantum/plugins/openvswitch/ovs_quantum_plugin.py'
1840--- quantum/plugins/openvswitch/ovs_quantum_plugin.py 2011-06-05 01:46:44 +0000
1841+++ quantum/plugins/openvswitch/ovs_quantum_plugin.py 2011-06-09 05:01:27 +0000
1842@@ -29,24 +29,29 @@
1843 import quantum.db.api as db
1844 import ovs_db
1845
1846-CONF_FILE="ovs_quantum_plugin.ini"
1847+CONF_FILE = "ovs_quantum_plugin.ini"
1848
1849 LOG.basicConfig(level=LOG.WARN)
1850 LOG.getLogger("ovs_quantum_plugin")
1851
1852+
1853 def find_config(basepath):
1854 for root, dirs, files in os.walk(basepath):
1855 if CONF_FILE in files:
1856 return os.path.join(root, CONF_FILE)
1857 return None
1858
1859+
1860 class VlanMap(object):
1861 vlans = {}
1862+
1863 def __init__(self):
1864 for x in xrange(2, 4094):
1865 self.vlans[x] = None
1866+
1867 def set(self, vlan_id, network_id):
1868 self.vlans[vlan_id] = network_id
1869+
1870 def acquire(self, network_id):
1871 for x in xrange(2, 4094):
1872 if self.vlans[x] == None:
1873@@ -54,8 +59,10 @@
1874 # LOG.debug("VlanMap::acquire %s -> %s" % (x, network_id))
1875 return x
1876 raise Exception("No free vlans..")
1877+
1878 def get(self, vlan_id):
1879 return self.vlans[vlan_id]
1880+
1881 def release(self, network_id):
1882 for x in self.vlans.keys():
1883 if self.vlans[x] == network_id:
1884@@ -64,14 +71,17 @@
1885 return
1886 LOG.error("No vlan found with network \"%s\"" % network_id)
1887
1888+
1889 class OVSQuantumPlugin(QuantumPluginBase):
1890+
1891 def __init__(self, configfile=None):
1892 config = ConfigParser.ConfigParser()
1893 if configfile == None:
1894 if os.path.exists(CONF_FILE):
1895 configfile = CONF_FILE
1896 else:
1897- configfile = find_config(os.path.abspath(os.path.dirname(__file__)))
1898+ configfile = find_config(os.path.abspath(
1899+ os.path.dirname(__file__)))
1900 if configfile == None:
1901 raise Exception("Configuration file \"%s\" doesn't exist" %
1902 (configfile))
1903@@ -93,7 +103,8 @@
1904 vlans = ovs_db.get_vlans()
1905 for x in vlans:
1906 vlan_id, network_id = x
1907- # LOG.debug("Adding already populated vlan %s -> %s" % (vlan_id, network_id))
1908+ # LOG.debug("Adding already populated vlan %s -> %s"
1909+ # % (vlan_id, network_id))
1910 self.vmap.set(vlan_id, network_id)
1911
1912 def get_all_networks(self, tenant_id):
1913@@ -109,8 +120,8 @@
1914 def create_network(self, tenant_id, net_name):
1915 d = {}
1916 try:
1917- res = db.network_create(tenant_id, net_name)
1918- LOG.debug("Created newtork: %s" % res)
1919+ res = db.network_create(tenant_id, net_name)
1920+ LOG.debug("Created newtork: %s" % res)
1921 except Exception, e:
1922 LOG.error("Error: %s" % str(e))
1923 return d
1924@@ -199,21 +210,28 @@
1925 res = db.port_get(port_id)
1926 return res.interface_id
1927
1928+
1929 class VlanMapTest(unittest.TestCase):
1930+
1931 def setUp(self):
1932 self.vmap = VlanMap()
1933+
1934 def tearDown(self):
1935 pass
1936+
1937 def testAddVlan(self):
1938 vlan_id = self.vmap.acquire("foobar")
1939 self.assertTrue(vlan_id == 2)
1940+
1941 def testReleaseVlan(self):
1942 vlan_id = self.vmap.acquire("foobar")
1943 self.vmap.release("foobar")
1944 self.assertTrue(self.vmap.get(vlan_id) == None)
1945
1946+
1947 # TODO(bgh): Make the tests use a sqlite database instead of mysql
1948 class OVSPluginTest(unittest.TestCase):
1949+
1950 def setUp(self):
1951 self.quantum = OVSQuantumPlugin()
1952 self.tenant_id = "testtenant"
1953@@ -312,6 +330,7 @@
1954 self.quantum.delete_port(self.tenant_id, id, p["port-id"])
1955 self.quantum.delete_network(self.tenant_id, id)
1956
1957+
1958 if __name__ == "__main__":
1959 usagestr = "Usage: %prog [OPTIONS] <command> [args]"
1960 parser = OptionParser(usage=usagestr)
1961
1962=== modified file 'quantum/service.py'
1963--- quantum/service.py 2011-06-07 05:48:57 +0000
1964+++ quantum/service.py 2011-06-09 05:01:27 +0000
1965@@ -102,7 +102,9 @@
1966
1967 def _run_wsgi(app_name, paste_conf, paste_config_file):
1968 LOG.info(_('Using paste.deploy config at: %s'), paste_config_file)
1969- app = config.load_paste_app(paste_config_file, app_name)
1970+ conf, app = config.load_paste_app(app_name,
1971+ {'config_file': paste_config_file},
1972+ None)
1973 if not app:
1974 LOG.error(_('No known API applications configured in %s.'),
1975 paste_config_file)
1976
1977=== added file 'run_tests.py'
1978--- run_tests.py 1970-01-01 00:00:00 +0000
1979+++ run_tests.py 2011-06-09 05:01:27 +0000
1980@@ -0,0 +1,293 @@
1981+#!/usr/bin/env python
1982+# vim: tabstop=4 shiftwidth=4 softtabstop=4
1983+
1984+# Copyright 2010 OpenStack, LLC
1985+# All Rights Reserved.
1986+#
1987+# Licensed under the Apache License, Version 2.0 (the "License");
1988+# you may not use this file except in compliance with the License.
1989+# You may obtain a copy of the License at
1990+#
1991+# http://www.apache.org/licenses/LICENSE-2.0
1992+#
1993+# Unless required by applicable law or agreed to in writing, software
1994+# distributed under the License is distributed on an "AS IS" BASIS,
1995+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
1996+# See the License for the specific language governing permissions and
1997+# limitations under the License.
1998+
1999+# Colorizer Code is borrowed from Twisted:
2000+# Copyright (c) 2001-2010 Twisted Matrix Laboratories.
2001+#
2002+# Permission is hereby granted, free of charge, to any person obtaining
2003+# a copy of this software and associated documentation files (the
2004+# "Software"), to deal in the Software without restriction, including
2005+# without limitation the rights to use, copy, modify, merge, publish,
2006+# distribute, sublicense, and/or sell copies of the Software, and to
2007+# permit persons to whom the Software is furnished to do so, subject to
2008+# the following conditions:
2009+#
2010+# The above copyright notice and this permission notice shall be
2011+# included in all copies or substantial portions of the Software.
2012+#
2013+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
2014+# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
2015+# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
2016+# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
2017+# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
2018+# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
2019+# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
2020+
2021+"""Unittest runner for quantum
2022+
2023+To run all test::
2024+ python run_tests.py
2025+
2026+To run all unit tests::
2027+ python run_tests.py unit
2028+
2029+To run all functional tests::
2030+ python run_tests.py functional
2031+
2032+To run a single unit test::
2033+ python run_tests.py unit.test_stores:TestSwiftBackend.test_get
2034+
2035+To run a single functional test::
2036+ python run_tests.py functional.test_service:TestController.test_create
2037+
2038+To run a single unit test module::
2039+ python run_tests.py unit.test_stores
2040+
2041+To run a single functional test module::
2042+ python run_tests.py functional.test_stores
2043+"""
2044+
2045+import gettext
2046+import os
2047+import unittest
2048+import sys
2049+
2050+from nose import config
2051+from nose import result
2052+from nose import core
2053+
2054+
2055+class _AnsiColorizer(object):
2056+ """
2057+ A colorizer is an object that loosely wraps around a stream, allowing
2058+ callers to write text to the stream in a particular color.
2059+
2060+ Colorizer classes must implement C{supported()} and C{write(text, color)}.
2061+ """
2062+ _colors = dict(black=30, red=31, green=32, yellow=33,
2063+ blue=34, magenta=35, cyan=36, white=37)
2064+
2065+ def __init__(self, stream):
2066+ self.stream = stream
2067+
2068+ def supported(cls, stream=sys.stdout):
2069+ """
2070+ A class method that returns True if the current platform supports
2071+ coloring terminal output using this method. Returns False otherwise.
2072+ """
2073+ if not stream.isatty():
2074+ return False # auto color only on TTYs
2075+ try:
2076+ import curses
2077+ except ImportError:
2078+ return False
2079+ else:
2080+ try:
2081+ try:
2082+ return curses.tigetnum("colors") > 2
2083+ except curses.error:
2084+ curses.setupterm()
2085+ return curses.tigetnum("colors") > 2
2086+ except:
2087+ raise
2088+ # guess false in case of error
2089+ return False
2090+ supported = classmethod(supported)
2091+
2092+ def write(self, text, color):
2093+ """
2094+ Write the given text to the stream in the given color.
2095+
2096+ @param text: Text to be written to the stream.
2097+
2098+ @param color: A string label for a color. e.g. 'red', 'white'.
2099+ """
2100+ color = self._colors[color]
2101+ self.stream.write('\x1b[%s;1m%s\x1b[0m' % (color, text))
2102+
2103+
2104+class _Win32Colorizer(object):
2105+ """
2106+ See _AnsiColorizer docstring.
2107+ """
2108+ def __init__(self, stream):
2109+ from win32console import GetStdHandle, STD_OUT_HANDLE, \
2110+ FOREGROUND_RED, FOREGROUND_BLUE, FOREGROUND_GREEN, \
2111+ FOREGROUND_INTENSITY
2112+ red, green, blue, bold = (FOREGROUND_RED, FOREGROUND_GREEN,
2113+ FOREGROUND_BLUE, FOREGROUND_INTENSITY)
2114+ self.stream = stream
2115+ self.screenBuffer = GetStdHandle(STD_OUT_HANDLE)
2116+ self._colors = {
2117+ 'normal': red | green | blue,
2118+ 'red': red | bold,
2119+ 'green': green | bold,
2120+ 'blue': blue | bold,
2121+ 'yellow': red | green | bold,
2122+ 'magenta': red | blue | bold,
2123+ 'cyan': green | blue | bold,
2124+ 'white': red | green | blue | bold}
2125+
2126+ def supported(cls, stream=sys.stdout):
2127+ try:
2128+ import win32console
2129+ screenBuffer = win32console.GetStdHandle(
2130+ win32console.STD_OUT_HANDLE)
2131+ except ImportError:
2132+ return False
2133+ import pywintypes
2134+ try:
2135+ screenBuffer.SetConsoleTextAttribute(
2136+ win32console.FOREGROUND_RED |
2137+ win32console.FOREGROUND_GREEN |
2138+ win32console.FOREGROUND_BLUE)
2139+ except pywintypes.error:
2140+ return False
2141+ else:
2142+ return True
2143+ supported = classmethod(supported)
2144+
2145+ def write(self, text, color):
2146+ color = self._colors[color]
2147+ self.screenBuffer.SetConsoleTextAttribute(color)
2148+ self.stream.write(text)
2149+ self.screenBuffer.SetConsoleTextAttribute(self._colors['normal'])
2150+
2151+
2152+class _NullColorizer(object):
2153+ """
2154+ See _AnsiColorizer docstring.
2155+ """
2156+ def __init__(self, stream):
2157+ self.stream = stream
2158+
2159+ def supported(cls, stream=sys.stdout):
2160+ return True
2161+ supported = classmethod(supported)
2162+
2163+ def write(self, text, color):
2164+ self.stream.write(text)
2165+
2166+
2167+class QuantumTestResult(result.TextTestResult):
2168+ def __init__(self, *args, **kw):
2169+ result.TextTestResult.__init__(self, *args, **kw)
2170+ self._last_case = None
2171+ self.colorizer = None
2172+ # NOTE(vish, tfukushima): reset stdout for the terminal check
2173+ stdout = sys.__stdout__
2174+ for colorizer in [_Win32Colorizer, _AnsiColorizer, _NullColorizer]:
2175+ if colorizer.supported():
2176+ self.colorizer = colorizer(self.stream)
2177+ break
2178+ sys.stdout = stdout
2179+
2180+ def getDescription(self, test):
2181+ return str(test)
2182+
2183+ # NOTE(vish, tfukushima): copied from unittest with edit to add color
2184+ def addSuccess(self, test):
2185+ unittest.TestResult.addSuccess(self, test)
2186+ if self.showAll:
2187+ self.colorizer.write("OK", 'green')
2188+ self.stream.writeln()
2189+ elif self.dots:
2190+ self.stream.write('.')
2191+ self.stream.flush()
2192+
2193+ # NOTE(vish, tfukushima): copied from unittest with edit to add color
2194+ def addFailure(self, test, err):
2195+ unittest.TestResult.addFailure(self, test, err)
2196+ if self.showAll:
2197+ self.colorizer.write("FAIL", 'red')
2198+ self.stream.writeln()
2199+ elif self.dots:
2200+ self.stream.write('F')
2201+ self.stream.flush()
2202+
2203+ # NOTE(vish, tfukushima): copied from unittest with edit to add color
2204+ def addError(self, test, err):
2205+ """Overrides normal addError to add support for errorClasses.
2206+ If the exception is a registered class, the error will be added
2207+ to the list for that class, not errors.
2208+ """
2209+ stream = getattr(self, 'stream', None)
2210+ ec, ev, tb = err
2211+ try:
2212+ exc_info = self._exc_info_to_string(err, test)
2213+ except TypeError:
2214+ # This is for compatibility with Python 2.3.
2215+ exc_info = self._exc_info_to_string(err)
2216+ for cls, (storage, label, isfail) in self.errorClasses.items():
2217+ if result.isclass(ec) and issubclass(ec, cls):
2218+ if isfail:
2219+ test.passwd = False
2220+ storage.append((test, exc_info))
2221+ # Might get patched into a streamless result
2222+ if stream is not None:
2223+ if self.showAll:
2224+ message = [label]
2225+ detail = result._exception_details(err[1])
2226+ if detail:
2227+ message.append(detail)
2228+ stream.writeln(": ".join(message))
2229+ elif self.dots:
2230+ stream.write(label[:1])
2231+ return
2232+ self.errors.append((test, exc_info))
2233+ test.passed = False
2234+ if stream is not None:
2235+ if self.showAll:
2236+ self.colorizer.write("ERROR", 'red')
2237+ self.stream.writeln()
2238+ elif self.dots:
2239+ stream.write('E')
2240+
2241+ def startTest(self, test):
2242+ unittest.TestResult.startTest(self, test)
2243+ current_case = test.test.__class__.__name__
2244+
2245+ if self.showAll:
2246+ if current_case != self._last_case:
2247+ self.stream.writeln(current_case)
2248+ self._last_case = current_case
2249+
2250+ self.stream.write(
2251+ ' %s' % str(test.test._testMethodName).ljust(60))
2252+ self.stream.flush()
2253+
2254+
2255+class QuantumTestRunner(core.TextTestRunner):
2256+ def _makeResult(self):
2257+ return QuantumTestResult(self.stream,
2258+ self.descriptions,
2259+ self.verbosity,
2260+ self.config)
2261+
2262+
2263+if __name__ == '__main__':
2264+ working_dir = os.path.abspath("tests")
2265+ c = config.Config(stream=sys.stdout,
2266+ env=os.environ,
2267+ verbosity=3,
2268+ workingDir=working_dir)
2269+
2270+ runner = QuantumTestRunner(stream=c.stream,
2271+ verbosity=c.verbosity,
2272+ config=c)
2273+ sys.exit(not core.run(config=c, testRunner=runner))
2274
2275=== added file 'run_tests.sh'
2276--- run_tests.sh 1970-01-01 00:00:00 +0000
2277+++ run_tests.sh 2011-06-09 05:01:27 +0000
2278@@ -0,0 +1,83 @@
2279+#!/bin/bash
2280+
2281+function usage {
2282+ echo "Usage: $0 [OPTION]..."
2283+ echo "Run Melange's test suite(s)"
2284+ echo ""
2285+ echo " -V, --virtual-env Always use virtualenv. Install automatically if not present"
2286+ echo " -N, --no-virtual-env Don't use virtualenv. Run tests in local environment"
2287+ echo " -f, --force Force a clean re-build of the virtual environment. Useful when dependencies have been added."
2288+ echo " -h, --help Print this usage message"
2289+ echo ""
2290+ echo "Note: with no options specified, the script will try to run the tests in a virtual environment,"
2291+ echo " If no virtualenv is found, the script will ask if you would like to create one. If you "
2292+ echo " prefer to run tests NOT in a virtual environment, simply pass the -N option."
2293+ exit
2294+}
2295+
2296+function process_option {
2297+ case "$1" in
2298+ -h|--help) usage;;
2299+ -V|--virtual-env) let always_venv=1; let never_venv=0;;
2300+ -N|--no-virtual-env) let always_venv=0; let never_venv=1;;
2301+ -f|--force) let force=1;;
2302+ *) noseargs="$noseargs $1"
2303+ esac
2304+}
2305+
2306+venv=.quantum-venv
2307+with_venv=tools/with_venv.sh
2308+always_venv=0
2309+never_venv=0
2310+force=0
2311+noseargs=
2312+wrapper=""
2313+
2314+for arg in "$@"; do
2315+ process_option $arg
2316+done
2317+
2318+function run_tests {
2319+ # Just run the test suites in current environment
2320+ ${wrapper} rm -f tests.sqlite
2321+ ${wrapper} $NOSETESTS 2> run_tests.err.log
2322+}
2323+
2324+NOSETESTS="python run_tests.py $noseargs"
2325+
2326+if [ $never_venv -eq 0 ]
2327+then
2328+ # Remove the virtual environment if --force used
2329+ if [ $force -eq 1 ]; then
2330+ echo "Cleaning virtualenv..."
2331+ rm -rf ${venv}
2332+ fi
2333+ if [ -e ${venv} ]; then
2334+ wrapper="${with_venv}"
2335+ else
2336+ if [ $always_venv -eq 1 ]; then
2337+ # Automatically install the virtualenv
2338+ python tools/install_venv.py
2339+ wrapper="${with_venv}"
2340+ else
2341+ echo -e "No virtual environment found...create one? (Y/n) \c"
2342+ read use_ve
2343+ if [ "x$use_ve" = "xY" -o "x$use_ve" = "x" -o "x$use_ve" = "xy" ]; then
2344+ # Install the virtualenv and run the test suite in it
2345+ python tools/install_venv.py
2346+ wrapper=${with_venv}
2347+ fi
2348+ fi
2349+ fi
2350+fi
2351+
2352+# FIXME(sirp): bzr version-info is not currently pep-8. This was fixed with
2353+# lp701898 [1], however, until that version of bzr becomes standard, I'm just
2354+# excluding the vcsversion.py file
2355+#
2356+# [1] https://bugs.launchpad.net/bzr/+bug/701898
2357+#
2358+PEP8_EXCLUDE=vcsversion.py
2359+PEP8_OPTIONS="--exclude=$PEP8_EXCLUDE --repeat --show-source"
2360+PEP8_INCLUDE="bin/* quantum tests tools run_tests.py"
2361+run_tests && pep8 $PEP8_OPTIONS $PEP8_INCLUDE || exit 1
2362
2363=== removed directory 'smoketests'
2364=== removed file 'smoketests/__init__.py'
2365=== removed file 'smoketests/miniclient.py'
2366--- smoketests/miniclient.py 2011-06-01 18:00:15 +0000
2367+++ smoketests/miniclient.py 1970-01-01 00:00:00 +0000
2368@@ -1,98 +0,0 @@
2369-# vim: tabstop=4 shiftwidth=4 softtabstop=4
2370-
2371-# Copyright 2011 Citrix Systems
2372-# All Rights Reserved.
2373-#
2374-# Licensed under the Apache License, Version 2.0 (the "License"); you may
2375-# not use this file except in compliance with the License. You may obtain
2376-# a copy of the License at
2377-#
2378-# http://www.apache.org/licenses/LICENSE-2.0
2379-#
2380-# Unless required by applicable law or agreed to in writing, software
2381-# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
2382-# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
2383-# License for the specific language governing permissions and limitations
2384-# under the License.
2385-
2386-import httplib
2387-import socket
2388-import urllib
2389-
2390-class MiniClient(object):
2391-
2392- """A base client class - derived from Glance.BaseClient"""
2393-
2394- action_prefix = '/v0.1/tenants/{tenant_id}'
2395-
2396- def __init__(self, host, port, use_ssl):
2397- """
2398- Creates a new client to some service.
2399-
2400- :param host: The host where service resides
2401- :param port: The port where service resides
2402- :param use_ssl: Should we use HTTPS?
2403- """
2404- self.host = host
2405- self.port = port
2406- self.use_ssl = use_ssl
2407- self.connection = None
2408-
2409- def get_connection_type(self):
2410- """
2411- Returns the proper connection type
2412- """
2413- if self.use_ssl:
2414- return httplib.HTTPSConnection
2415- else:
2416- return httplib.HTTPConnection
2417-
2418- def do_request(self, tenant, method, action, body=None,
2419- headers=None, params=None):
2420- """
2421- Connects to the server and issues a request.
2422- Returns the result data, or raises an appropriate exception if
2423- HTTP status code is not 2xx
2424-
2425- :param method: HTTP method ("GET", "POST", "PUT", etc...)
2426- :param body: string of data to send, or None (default)
2427- :param headers: mapping of key/value pairs to add as headers
2428- :param params: dictionary of key/value pairs to add to append
2429- to action
2430-
2431- """
2432- action = MiniClient.action_prefix + action
2433- action = action.replace('{tenant_id}',tenant)
2434- if type(params) is dict:
2435- action += '?' + urllib.urlencode(params)
2436-
2437- try:
2438- connection_type = self.get_connection_type()
2439- headers = headers or {}
2440-
2441- # Open connection and send request
2442- c = connection_type(self.host, self.port)
2443- c.request(method, action, body, headers)
2444- res = c.getresponse()
2445- status_code = self.get_status_code(res)
2446- if status_code in (httplib.OK,
2447- httplib.CREATED,
2448- httplib.ACCEPTED,
2449- httplib.NO_CONTENT):
2450- return res
2451- else:
2452- raise Exception("Server returned error: %s" % res.read())
2453-
2454- except (socket.error, IOError), e:
2455- raise Exception("Unable to connect to "
2456- "server. Got error: %s" % e)
2457-
2458- def get_status_code(self, response):
2459- """
2460- Returns the integer status code from the response, which
2461- can be either a Webob.Response (used in testing) or httplib.Response
2462- """
2463- if hasattr(response, 'status_int'):
2464- return response.status_int
2465- else:
2466- return response.status
2467\ No newline at end of file
2468
2469=== removed file 'smoketests/tests.py'
2470--- smoketests/tests.py 2011-06-01 18:00:15 +0000
2471+++ smoketests/tests.py 1970-01-01 00:00:00 +0000
2472@@ -1,133 +0,0 @@
2473-# vim: tabstop=4 shiftwidth=4 softtabstop=4
2474-
2475-# Copyright 2011 Citrix Systems
2476-# Copyright 2011 Nicira Networks
2477-# All Rights Reserved.
2478-#
2479-# Licensed under the Apache License, Version 2.0 (the "License"); you may
2480-# not use this file except in compliance with the License. You may obtain
2481-# a copy of the License at
2482-#
2483-# http://www.apache.org/licenses/LICENSE-2.0
2484-#
2485-# Unless required by applicable law or agreed to in writing, software
2486-# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
2487-# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
2488-# License for the specific language governing permissions and limitations
2489-# under the License.
2490-
2491-import gettext
2492-import simplejson
2493-import sys
2494-import unittest
2495-
2496-gettext.install('quantum', unicode=1)
2497-
2498-from miniclient import MiniClient
2499-from quantum.common.wsgi import Serializer
2500-
2501-HOST = '127.0.0.1'
2502-PORT = 9696
2503-USE_SSL = False
2504-
2505-TENANT_ID = 'totore'
2506-FORMAT = "json"
2507-
2508-test_network1_data = \
2509- {'network': {'network-name': 'test1' }}
2510-test_network2_data = \
2511- {'network': {'network-name': 'test2' }}
2512-
2513-def print_response(res):
2514- content = res.read()
2515- print "Status: %s" %res.status
2516- print "Content: %s" %content
2517- return content
2518-
2519-class QuantumTest(unittest.TestCase):
2520- def setUp(self):
2521- self.client = MiniClient(HOST, PORT, USE_SSL)
2522-
2523- def create_network(self, data):
2524- content_type = "application/" + FORMAT
2525- body = Serializer().serialize(data, content_type)
2526- res = self.client.do_request(TENANT_ID, 'POST', "/networks." + FORMAT,
2527- body=body)
2528- self.assertEqual(res.status, 200, "bad response: %s" % res.read())
2529-
2530- def test_listNetworks(self):
2531- self.create_network(test_network1_data)
2532- self.create_network(test_network2_data)
2533- res = self.client.do_request(TENANT_ID,'GET', "/networks." + FORMAT)
2534- self.assertEqual(res.status, 200, "bad response: %s" % res.read())
2535-
2536- def test_createNetwork(self):
2537- self.create_network(test_network1_data)
2538-
2539- def test_createPort(self):
2540- self.create_network(test_network1_data)
2541- res = self.client.do_request(TENANT_ID, 'GET', "/networks." + FORMAT)
2542- resdict = simplejson.loads(res.read())
2543- for n in resdict["networks"]:
2544- net_id = n["id"]
2545-
2546- # Step 1 - List Ports for network (should not find any)
2547- res = self.client.do_request(TENANT_ID, 'GET',
2548- "/networks/%s/ports.%s" % (net_id, FORMAT))
2549- self.assertEqual(res.status, 200, "Bad response: %s" % res.read())
2550- output = res.read()
2551- self.assertTrue(len(output) == 0,
2552- "Found unexpected ports: %s" % output)
2553-
2554- # Step 2 - Create Port for network
2555- res = self.client.do_request(TENANT_ID, 'POST',
2556- "/networks/%s/ports.%s" % (net_id, FORMAT))
2557- self.assertEqual(res.status, 200, "Bad response: %s" % output)
2558-
2559- # Step 3 - List Ports for network (again); should find one
2560- res = self.client.do_request(TENANT_ID, 'GET',
2561- "/networks/%s/ports.%s" % (net_id, FORMAT))
2562- output = res.read()
2563- self.assertEqual(res.status, 200, "Bad response: %s" % output)
2564- resdict = simplejson.loads(output)
2565- ids = []
2566- for p in resdict["ports"]:
2567- ids.append(p["id"])
2568- self.assertTrue(len(ids) == 1,
2569- "Didn't find expected # of ports (1): %s" % ids)
2570-
2571- def test_renameNetwork(self):
2572- self.create_network(test_network1_data)
2573- res = self.client.do_request(TENANT_ID, 'GET', "/networks." + FORMAT)
2574- resdict = simplejson.loads(res.read())
2575- net_id = resdict["networks"][0]["id"]
2576-
2577- data = test_network1_data.copy()
2578- data['network']['network-name'] = 'test_renamed'
2579- content_type = "application/" + FORMAT
2580- body = Serializer().serialize(data, content_type)
2581- res = self.client.do_request(TENANT_ID, 'PUT',
2582- "/networks/%s.%s" % (net_id, FORMAT), body=body)
2583- resdict = simplejson.loads(res.read())
2584- self.assertTrue(resdict["networks"]["network"]["id"] == net_id,
2585- "Network_rename: renamed network has a different uuid")
2586- self.assertTrue(resdict["networks"]["network"]["name"] == "test_renamed",
2587- "Network rename didn't take effect")
2588-
2589- def delete_networks(self):
2590- # Remove all the networks created on the tenant
2591- res = self.client.do_request(TENANT_ID, 'GET', "/networks." + FORMAT)
2592- resdict = simplejson.loads(res.read())
2593- for n in resdict["networks"]:
2594- net_id = n["id"]
2595- res = self.client.do_request(TENANT_ID, 'DELETE',
2596- "/networks/" + net_id + "." + FORMAT)
2597- self.assertEqual(res.status, 202)
2598-
2599- def tearDown(self):
2600- self.delete_networks()
2601-
2602-# Standard boilerplate to call the main() function.
2603-if __name__ == '__main__':
2604- suite = unittest.TestLoader().loadTestsFromTestCase(QuantumTest)
2605- unittest.TextTestRunner(verbosity=2).run(suite)
2606
2607=== removed directory 'test_scripts'
2608=== removed file 'test_scripts/__init__.py'
2609=== removed file 'test_scripts/miniclient.py'
2610--- test_scripts/miniclient.py 2011-05-30 00:08:46 +0000
2611+++ test_scripts/miniclient.py 1970-01-01 00:00:00 +0000
2612@@ -1,98 +0,0 @@
2613-# vim: tabstop=4 shiftwidth=4 softtabstop=4
2614-
2615-# Copyright 2011 Citrix Systems
2616-# All Rights Reserved.
2617-#
2618-# Licensed under the Apache License, Version 2.0 (the "License"); you may
2619-# not use this file except in compliance with the License. You may obtain
2620-# a copy of the License at
2621-#
2622-# http://www.apache.org/licenses/LICENSE-2.0
2623-#
2624-# Unless required by applicable law or agreed to in writing, software
2625-# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
2626-# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
2627-# License for the specific language governing permissions and limitations
2628-# under the License.
2629-
2630-import httplib
2631-import socket
2632-import urllib
2633-
2634-class MiniClient(object):
2635-
2636- """A base client class - derived from Glance.BaseClient"""
2637-
2638- action_prefix = '/v0.1/tenants/{tenant_id}'
2639-
2640- def __init__(self, host, port, use_ssl):
2641- """
2642- Creates a new client to some service.
2643-
2644- :param host: The host where service resides
2645- :param port: The port where service resides
2646- :param use_ssl: Should we use HTTPS?
2647- """
2648- self.host = host
2649- self.port = port
2650- self.use_ssl = use_ssl
2651- self.connection = None
2652-
2653- def get_connection_type(self):
2654- """
2655- Returns the proper connection type
2656- """
2657- if self.use_ssl:
2658- return httplib.HTTPSConnection
2659- else:
2660- return httplib.HTTPConnection
2661-
2662- def do_request(self, tenant, method, action, body=None,
2663- headers=None, params=None):
2664- """
2665- Connects to the server and issues a request.
2666- Returns the result data, or raises an appropriate exception if
2667- HTTP status code is not 2xx
2668-
2669- :param method: HTTP method ("GET", "POST", "PUT", etc...)
2670- :param body: string of data to send, or None (default)
2671- :param headers: mapping of key/value pairs to add as headers
2672- :param params: dictionary of key/value pairs to add to append
2673- to action
2674-
2675- """
2676- action = MiniClient.action_prefix + action
2677- action = action.replace('{tenant_id}',tenant)
2678- if type(params) is dict:
2679- action += '?' + urllib.urlencode(params)
2680-
2681- try:
2682- connection_type = self.get_connection_type()
2683- headers = headers or {}
2684-
2685- # Open connection and send request
2686- c = connection_type(self.host, self.port)
2687- c.request(method, action, body, headers)
2688- res = c.getresponse()
2689- status_code = self.get_status_code(res)
2690- if status_code in (httplib.OK,
2691- httplib.CREATED,
2692- httplib.ACCEPTED,
2693- httplib.NO_CONTENT):
2694- return res
2695- else:
2696- raise Exception("Server returned error: %s" % res.read())
2697-
2698- except (socket.error, IOError), e:
2699- raise Exception("Unable to connect to "
2700- "server. Got error: %s" % e)
2701-
2702- def get_status_code(self, response):
2703- """
2704- Returns the integer status code from the response, which
2705- can be either a Webob.Response (used in testing) or httplib.Response
2706- """
2707- if hasattr(response, 'status_int'):
2708- return response.status_int
2709- else:
2710- return response.status
2711\ No newline at end of file
2712
2713=== removed file 'test_scripts/tests.py'
2714--- test_scripts/tests.py 2011-05-30 00:08:46 +0000
2715+++ test_scripts/tests.py 1970-01-01 00:00:00 +0000
2716@@ -1,150 +0,0 @@
2717-# vim: tabstop=4 shiftwidth=4 softtabstop=4
2718-
2719-# Copyright 2011 Citrix Systems
2720-# All Rights Reserved.
2721-#
2722-# Licensed under the Apache License, Version 2.0 (the "License"); you may
2723-# not use this file except in compliance with the License. You may obtain
2724-# a copy of the License at
2725-#
2726-# http://www.apache.org/licenses/LICENSE-2.0
2727-#
2728-# Unless required by applicable law or agreed to in writing, software
2729-# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
2730-# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
2731-# License for the specific language governing permissions and limitations
2732-# under the License.
2733-
2734-import gettext
2735-
2736-gettext.install('quantum', unicode=1)
2737-
2738-from miniclient import MiniClient
2739-from quantum.common.wsgi import Serializer
2740-
2741-HOST = '127.0.0.1'
2742-PORT = 9696
2743-USE_SSL = False
2744-TENANT_ID = 'totore'
2745-
2746-test_network_data = \
2747- {'network': {'network-name': 'test' }}
2748-
2749-def print_response(res):
2750- content = res.read()
2751- print "Status: %s" %res.status
2752- print "Content: %s" %content
2753- return content
2754-
2755-def test_list_networks_and_ports(format = 'xml'):
2756- client = MiniClient(HOST, PORT, USE_SSL)
2757- print "TEST LIST NETWORKS AND PORTS -- FORMAT:%s" %format
2758- print "----------------------------"
2759- print "--> Step 1 - List All Networks"
2760- res = client.do_request(TENANT_ID,'GET', "/networks." + format)
2761- print_response(res)
2762- print "--> Step 2 - Details for Network 001"
2763- res = client.do_request(TENANT_ID,'GET', "/networks/001." + format)
2764- print_response(res)
2765- print "--> Step 3 - Ports for Network 001"
2766- res = client.do_request(TENANT_ID,'GET', "/networks/001/ports." + format)
2767- print_response(res)
2768- print "--> Step 4 - Details for Port 1"
2769- res = client.do_request(TENANT_ID,'GET', "/networks/001/ports/1." + format)
2770- print_response(res)
2771- print "COMPLETED"
2772- print "----------------------------"
2773-
2774-def test_create_network(format = 'xml'):
2775- client = MiniClient(HOST, PORT, USE_SSL)
2776- print "TEST CREATE NETWORK -- FORMAT:%s" %format
2777- print "----------------------------"
2778- print "--> Step 1 - Create Network"
2779- content_type = "application/" + format
2780- body = Serializer().serialize(test_network_data, content_type)
2781- res = client.do_request(TENANT_ID,'POST', "/networks." + format, body=body)
2782- print_response(res)
2783- print "--> Step 2 - List All Networks"
2784- res = client.do_request(TENANT_ID,'GET', "/networks." + format)
2785- print_response(res)
2786- print "COMPLETED"
2787- print "----------------------------"
2788-
2789-def test_rename_network(format = 'xml'):
2790- client = MiniClient(HOST, PORT, USE_SSL)
2791- content_type = "application/" + format
2792- print "TEST RENAME NETWORK -- FORMAT:%s" %format
2793- print "----------------------------"
2794- print "--> Step 1 - Retrieve network"
2795- res = client.do_request(TENANT_ID,'GET', "/networks/001." + format)
2796- print_response(res)
2797- print "--> Step 2 - Rename network to 'test_renamed'"
2798- test_network_data['network']['network-name'] = 'test_renamed'
2799- body = Serializer().serialize(test_network_data, content_type)
2800- res = client.do_request(TENANT_ID,'PUT', "/networks/001." + format, body=body)
2801- print_response(res)
2802- print "--> Step 2 - Retrieve network (again)"
2803- res = client.do_request(TENANT_ID,'GET', "/networks/001." + format)
2804- print_response(res)
2805- print "COMPLETED"
2806- print "----------------------------"
2807-
2808-def test_delete_network(format = 'xml'):
2809- client = MiniClient(HOST, PORT, USE_SSL)
2810- content_type = "application/" + format
2811- print "TEST DELETE NETWORK -- FORMAT:%s" %format
2812- print "----------------------------"
2813- print "--> Step 1 - List All Networks"
2814- res = client.do_request(TENANT_ID,'GET', "/networks." + format)
2815- content = print_response(res)
2816- network_data = Serializer().deserialize(content, content_type)
2817- print network_data
2818- net_id = network_data['networks'][0]['id']
2819- print "--> Step 2 - Delete network %s" %net_id
2820- res = client.do_request(TENANT_ID,'DELETE',
2821- "/networks/" + net_id + "." + format)
2822- print_response(res)
2823- print "--> Step 3 - List All Networks (Again)"
2824- res = client.do_request(TENANT_ID,'GET', "/networks." + format)
2825- print_response(res)
2826- print "COMPLETED"
2827- print "----------------------------"
2828-
2829-
2830-def test_create_port(format = 'xml'):
2831- client = MiniClient(HOST, PORT, USE_SSL)
2832- print "TEST CREATE PORT -- FORMAT:%s" %format
2833- print "----------------------------"
2834- print "--> Step 1 - List Ports for network 001"
2835- res = client.do_request(TENANT_ID,'GET', "/networks/001/ports." + format)
2836- print_response(res)
2837- print "--> Step 2 - Create Port for network 001"
2838- res = client.do_request(TENANT_ID,'POST', "/networks/001/ports." + format)
2839- print_response(res)
2840- print "--> Step 3 - List Ports for network 001 (again)"
2841- res = client.do_request(TENANT_ID,'GET', "/networks/001/ports." + format)
2842- print_response(res)
2843- print "COMPLETED"
2844- print "----------------------------"
2845-
2846-
2847-def main():
2848- test_list_networks_and_ports('xml')
2849- test_list_networks_and_ports('json')
2850- test_create_network('xml')
2851- test_create_network('json')
2852- test_rename_network('xml')
2853- test_rename_network('json')
2854- # NOTE: XML deserializer does not work properly
2855- # disabling XML test - this is NOT a server-side issue
2856- #test_delete_network('xml')
2857- test_delete_network('json')
2858- test_create_port('xml')
2859- test_create_port('json')
2860-
2861- pass
2862-
2863-
2864-# Standard boilerplate to call the main() function.
2865-if __name__ == '__main__':
2866- main()
2867\ No newline at end of file
2868
2869=== added directory 'tests'
2870=== added file 'tests/__init__.py'
2871=== added directory 'tests/functional'
2872=== added file 'tests/functional/__init__.py'
2873=== added file 'tests/functional/miniclient.py'
2874--- tests/functional/miniclient.py 1970-01-01 00:00:00 +0000
2875+++ tests/functional/miniclient.py 2011-06-09 05:01:27 +0000
2876@@ -0,0 +1,99 @@
2877+# vim: tabstop=4 shiftwidth=4 softtabstop=4
2878+
2879+# Copyright 2011 Citrix Systems
2880+# All Rights Reserved.
2881+#
2882+# Licensed under the Apache License, Version 2.0 (the "License"); you may
2883+# not use this file except in compliance with the License. You may obtain
2884+# a copy of the License at
2885+#
2886+# http://www.apache.org/licenses/LICENSE-2.0
2887+#
2888+# Unless required by applicable law or agreed to in writing, software
2889+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
2890+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
2891+# License for the specific language governing permissions and limitations
2892+# under the License.
2893+
2894+import httplib
2895+import socket
2896+import urllib
2897+
2898+
2899+class MiniClient(object):
2900+
2901+ """A base client class - derived from Glance.BaseClient"""
2902+
2903+ action_prefix = '/v0.1/tenants/{tenant_id}'
2904+
2905+ def __init__(self, host, port, use_ssl):
2906+ """
2907+ Creates a new client to some service.
2908+
2909+ :param host: The host where service resides
2910+ :param port: The port where service resides
2911+ :param use_ssl: Should we use HTTPS?
2912+ """
2913+ self.host = host
2914+ self.port = port
2915+ self.use_ssl = use_ssl
2916+ self.connection = None
2917+
2918+ def get_connection_type(self):
2919+ """
2920+ Returns the proper connection type
2921+ """
2922+ if self.use_ssl:
2923+ return httplib.HTTPSConnection
2924+ else:
2925+ return httplib.HTTPConnection
2926+
2927+ def do_request(self, tenant, method, action, body=None,
2928+ headers=None, params=None):
2929+ """
2930+ Connects to the server and issues a request.
2931+ Returns the result data, or raises an appropriate exception if
2932+ HTTP status code is not 2xx
2933+
2934+ :param method: HTTP method ("GET", "POST", "PUT", etc...)
2935+ :param body: string of data to send, or None (default)
2936+ :param headers: mapping of key/value pairs to add as headers
2937+ :param params: dictionary of key/value pairs to add to append
2938+ to action
2939+
2940+ """
2941+ action = MiniClient.action_prefix + action
2942+ action = action.replace('{tenant_id}', tenant)
2943+ if type(params) is dict:
2944+ action += '?' + urllib.urlencode(params)
2945+
2946+ try:
2947+ connection_type = self.get_connection_type()
2948+ headers = headers or {}
2949+
2950+ # Open connection and send request
2951+ c = connection_type(self.host, self.port)
2952+ c.request(method, action, body, headers)
2953+ res = c.getresponse()
2954+ status_code = self.get_status_code(res)
2955+ if status_code in (httplib.OK,
2956+ httplib.CREATED,
2957+ httplib.ACCEPTED,
2958+ httplib.NO_CONTENT):
2959+ return res
2960+ else:
2961+ raise Exception("Server returned error: %s" % res.read())
2962+
2963+ except (socket.error, IOError), e:
2964+ raise Exception("Unable to connect to "
2965+ "server. Got error: %s" % e)
2966+
2967+ def get_status_code(self, response):
2968+ """
2969+ Returns the integer status code from the response, which
2970+ can be either a Webob.Response (used in testing) or httplib.Response
2971+ """
2972+ if hasattr(response, 'status_int'):
2973+ return response.status_int
2974+ else:
2975+ return response.status
2976
2977=== added file 'tests/functional/test_service.py'
2978--- tests/functional/test_service.py 1970-01-01 00:00:00 +0000
2979+++ tests/functional/test_service.py 2011-06-09 05:01:27 +0000
2980@@ -0,0 +1,136 @@
2981+# vim: tabstop=4 shiftwidth=4 softtabstop=4
2982+
2983+# Copyright 2011 Citrix Systems
2984+# Copyright 2011 Nicira Networks
2985+# All Rights Reserved.
2986+#
2987+# Licensed under the Apache License, Version 2.0 (the "License"); you may
2988+# not use this file except in compliance with the License. You may obtain
2989+# a copy of the License at
2990+#
2991+# http://www.apache.org/licenses/LICENSE-2.0
2992+#
2993+# Unless required by applicable law or agreed to in writing, software
2994+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
2995+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
2996+# License for the specific language governing permissions and limitations
2997+# under the License.
2998+
2999+import gettext
3000+import simplejson
3001+import sys
3002+import unittest
3003+
3004+gettext.install('quantum', unicode=1)
3005+
3006+from miniclient import MiniClient
3007+from quantum.common.wsgi import Serializer
3008+
3009+HOST = '127.0.0.1'
3010+PORT = 9696
3011+USE_SSL = False
3012+
3013+TENANT_ID = 'totore'
3014+FORMAT = "json"
3015+
3016+test_network1_data = \
3017+ {'network': {'network-name': 'test1'}}
3018+test_network2_data = \
3019+ {'network': {'network-name': 'test2'}}
3020+
3021+
3022+def print_response(res):
3023+ content = res.read()
3024+ print "Status: %s" % res.status
3025+ print "Content: %s" % content
3026+ return content
3027+
3028+
3029+class QuantumTest(unittest.TestCase):
3030+ def setUp(self):
3031+ self.client = MiniClient(HOST, PORT, USE_SSL)
3032+
3033+ def create_network(self, data):
3034+ content_type = "application/" + FORMAT
3035+ body = Serializer().serialize(data, content_type)
3036+ res = self.client.do_request(TENANT_ID, 'POST', "/networks." + FORMAT,
3037+ body=body)
3038+ self.assertEqual(res.status, 200, "bad response: %s" % res.read())
3039+
3040+ def test_listNetworks(self):
3041+ self.create_network(test_network1_data)
3042+ self.create_network(test_network2_data)
3043+ res = self.client.do_request(TENANT_ID, 'GET', "/networks." + FORMAT)
3044+ self.assertEqual(res.status, 200, "bad response: %s" % res.read())
3045+
3046+ def test_createNetwork(self):
3047+ self.create_network(test_network1_data)
3048+
3049+ def test_createPort(self):
3050+ self.create_network(test_network1_data)
3051+ res = self.client.do_request(TENANT_ID, 'GET', "/networks." + FORMAT)
3052+ resdict = simplejson.loads(res.read())
3053+ for n in resdict["networks"]:
3054+ net_id = n["id"]
3055+
3056+ # Step 1 - List Ports for network (should not find any)
3057+ res = self.client.do_request(TENANT_ID, 'GET',
3058+ "/networks/%s/ports.%s" % (net_id, FORMAT))
3059+ self.assertEqual(res.status, 200, "Bad response: %s" % res.read())
3060+ output = res.read()
3061+ self.assertTrue(len(output) == 0,
3062+ "Found unexpected ports: %s" % output)
3063+
3064+ # Step 2 - Create Port for network
3065+ res = self.client.do_request(TENANT_ID, 'POST',
3066+ "/networks/%s/ports.%s" % (net_id, FORMAT))
3067+ self.assertEqual(res.status, 200, "Bad response: %s" % output)
3068+
3069+ # Step 3 - List Ports for network (again); should find one
3070+ res = self.client.do_request(TENANT_ID, 'GET',
3071+ "/networks/%s/ports.%s" % (net_id, FORMAT))
3072+ output = res.read()
3073+ self.assertEqual(res.status, 200, "Bad response: %s" % output)
3074+ resdict = simplejson.loads(output)
3075+ ids = []
3076+ for p in resdict["ports"]:
3077+ ids.append(p["id"])
3078+ self.assertTrue(len(ids) == 1,
3079+ "Didn't find expected # of ports (1): %s" % ids)
3080+
3081+ def test_renameNetwork(self):
3082+ self.create_network(test_network1_data)
3083+ res = self.client.do_request(TENANT_ID, 'GET', "/networks." + FORMAT)
3084+ resdict = simplejson.loads(res.read())
3085+ net_id = resdict["networks"][0]["id"]
3086+
3087+ data = test_network1_data.copy()
3088+ data['network']['network-name'] = 'test_renamed'
3089+ content_type = "application/" + FORMAT
3090+ body = Serializer().serialize(data, content_type)
3091+ res = self.client.do_request(TENANT_ID, 'PUT',
3092+ "/networks/%s.%s" % (net_id, FORMAT), body=body)
3093+ resdict = simplejson.loads(res.read())
3094+ self.assertTrue(resdict["networks"]["network"]["id"] == net_id,
3095+ "Network_rename: renamed network has a different uuid")
3096+ self.assertTrue(
3097+ resdict["networks"]["network"]["name"] == "test_renamed",
3098+ "Network rename didn't take effect")
3099+
3100+ def delete_networks(self):
3101+ # Remove all the networks created on the tenant
3102+ res = self.client.do_request(TENANT_ID, 'GET', "/networks." + FORMAT)
3103+ resdict = simplejson.loads(res.read())
3104+ for n in resdict["networks"]:
3105+ net_id = n["id"]
3106+ res = self.client.do_request(TENANT_ID, 'DELETE',
3107+ "/networks/" + net_id + "." + FORMAT)
3108+ self.assertEqual(res.status, 202)
3109+
3110+ def tearDown(self):
3111+ self.delete_networks()
3112+
3113+# Standard boilerplate to call the main() function.
3114+if __name__ == '__main__':
3115+ suite = unittest.TestLoader().loadTestsFromTestCase(QuantumTest)
3116+ unittest.TextTestRunner(verbosity=2).run(suite)
3117
3118=== added directory 'tests/unit'
3119=== added file 'tests/unit/__init__.py'
3120--- tests/unit/__init__.py 1970-01-01 00:00:00 +0000
3121+++ tests/unit/__init__.py 2011-06-09 05:01:27 +0000
3122@@ -0,0 +1,32 @@
3123+# vim: tabstop=4 shiftwidth=4 softtabstop=4
3124+
3125+# Copyright 2011 OpenStack LLC.
3126+# All Rights Reserved.
3127+#
3128+# Licensed under the Apache License, Version 2.0 (the "License"); you may
3129+# not use this file except in compliance with the License. You may obtain
3130+# a copy of the License at
3131+#
3132+# http://www.apache.org/licenses/LICENSE-2.0
3133+#
3134+# Unless required by applicable law or agreed to in writing, software
3135+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
3136+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
3137+# License for the specific language governing permissions and limitations
3138+# under the License.
3139+
3140+# See http://code.google.com/p/python-nose/issues/detail?id=373
3141+# The code below enables nosetests to work with i18n _() blocks
3142+import __builtin__
3143+import unittest
3144+setattr(__builtin__, '_', lambda x: x)
3145+
3146+
3147+class BaseTest(unittest.TestCase):
3148+
3149+ def setUp(self):
3150+ pass
3151+
3152+
3153+def setUp():
3154+ pass
3155
3156=== added file 'tools/install_venv.py'
3157--- tools/install_venv.py 1970-01-01 00:00:00 +0000
3158+++ tools/install_venv.py 2011-06-09 05:01:27 +0000
3159@@ -0,0 +1,137 @@
3160+# vim: tabstop=4 shiftwidth=4 softtabstop=4
3161+
3162+# Copyright 2010 United States Government as represented by the
3163+# Administrator of the National Aeronautics and Space Administration.
3164+# All Rights Reserved.
3165+#
3166+# Copyright 2010 OpenStack LLC.
3167+#
3168+# Licensed under the Apache License, Version 2.0 (the "License"); you may
3169+# not use this file except in compliance with the License. You may obtain
3170+# a copy of the License at
3171+#
3172+# http://www.apache.org/licenses/LICENSE-2.0
3173+#
3174+# Unless required by applicable law or agreed to in writing, software
3175+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
3176+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
3177+# License for the specific language governing permissions and limitations
3178+# under the License.
3179+
3180+"""
3181+Installation script for Quantum's development virtualenv
3182+"""
3183+
3184+import os
3185+import subprocess
3186+import sys
3187+
3188+
3189+ROOT = os.path.dirname(os.path.dirname(os.path.realpath(__file__)))
3190+VENV = os.path.join(ROOT, '.quantum-venv')
3191+PIP_REQUIRES = os.path.join(ROOT, 'tools', 'pip-requires')
3192+
3193+
3194+def die(message, *args):
3195+ print >> sys.stderr, message % args
3196+ sys.exit(1)
3197+
3198+
3199+def run_command(cmd, redirect_output=True, check_exit_code=True):
3200+ """
3201+ Runs a command in an out-of-process shell, returning the
3202+ output of that command. Working directory is ROOT.
3203+ """
3204+ if redirect_output:
3205+ stdout = subprocess.PIPE
3206+ else:
3207+ stdout = None
3208+
3209+ proc = subprocess.Popen(cmd, cwd=ROOT, stdout=stdout)
3210+ output = proc.communicate()[0]
3211+ if check_exit_code and proc.returncode != 0:
3212+ die('Command "%s" failed.\n%s', ' '.join(cmd), output)
3213+ return output
3214+
3215+
3216+HAS_EASY_INSTALL = bool(run_command(['which', 'easy_install'],
3217+ check_exit_code=False).strip())
3218+HAS_VIRTUALENV = bool(run_command(['which', 'virtualenv'],
3219+ check_exit_code=False).strip())
3220+
3221+
3222+def check_dependencies():
3223+ """Make sure virtualenv is in the path."""
3224+
3225+ if not HAS_VIRTUALENV:
3226+ print 'not found.'
3227+ # Try installing it via easy_install...
3228+ if HAS_EASY_INSTALL:
3229+ print 'Installing virtualenv via easy_install...',
3230+ if not run_command(['which', 'easy_install']):
3231+ die('ERROR: virtualenv not found.\n\n'
3232+ 'Quantum requires virtualenv, please install'
3233+ ' it using your favorite package management tool')
3234+ print 'done.'
3235+ print 'done.'
3236+
3237+
3238+def create_virtualenv(venv=VENV):
3239+ """Creates the virtual environment and installs PIP only into the
3240+ virtual environment
3241+ """
3242+ print 'Creating venv...',
3243+ run_command(['virtualenv', '-q', '--no-site-packages', VENV])
3244+ print 'done.'
3245+ print 'Installing pip in virtualenv...',
3246+ if not run_command(['tools/with_venv.sh', 'easy_install', 'pip']).strip():
3247+ die("Failed to install pip.")
3248+ print 'done.'
3249+
3250+
3251+def install_dependencies(venv=VENV):
3252+ print 'Installing dependencies with pip (this can take a while)...'
3253+
3254+ # Install greenlet by hand - just listing it in the requires file does not
3255+ # get it in stalled in the right order
3256+ venv_tool = 'tools/with_venv.sh'
3257+ run_command([venv_tool, 'pip', 'install', '-E', venv, '-r', PIP_REQUIRES],
3258+ redirect_output=False)
3259+
3260+ # Tell the virtual env how to "import quantum"
3261+ pthfile = os.path.join(venv, "lib", "python2.6", "site-packages",
3262+ "quantum.pth")
3263+ f = open(pthfile, 'w')
3264+ f.write("%s\n" % ROOT)
3265+
3266+
3267+def print_help():
3268+ help = """
3269+ Quantum development environment setup is complete.
3270+
3271+ Quantum development uses virtualenv to track and manage Python dependencies
3272+ while in development and testing.
3273+
3274+ To activate the Quantum virtualenv for the extent of your current shell
3275+ session you can run:
3276+
3277+ $ source .quantum-venv/bin/activate
3278+
3279+ Or, if you prefer, you can run commands in the virtualenv on a case by case
3280+ basis by running:
3281+
3282+ $ tools/with_venv.sh <your command>
3283+
3284+ Also, make test will automatically use the virtualenv.
3285+ """
3286+ print help
3287+
3288+
3289+def main(argv):
3290+ check_dependencies()
3291+ create_virtualenv()
3292+ install_dependencies()
3293+ print_help()
3294+
3295+if __name__ == '__main__':
3296+ main(sys.argv)
3297
3298=== added file 'tools/pip-requires'
3299--- tools/pip-requires 1970-01-01 00:00:00 +0000
3300+++ tools/pip-requires 2011-06-09 05:01:27 +0000
3301@@ -0,0 +1,10 @@
3302+eventlet>=0.9.12
3303+nose
3304+Paste
3305+PasteDeploy
3306+pep8==0.5.0
3307+python-gflags
3308+routes
3309+simplejson
3310+webob
3311+webtest
3312
3313=== added file 'tools/with_venv.sh'
3314--- tools/with_venv.sh 1970-01-01 00:00:00 +0000
3315+++ tools/with_venv.sh 2011-06-09 05:01:27 +0000
3316@@ -0,0 +1,21 @@
3317+#!/bin/bash
3318+# vim: tabstop=4 shiftwidth=4 softtabstop=4
3319+
3320+# Copyright 2011 OpenStack LLC.
3321+# All Rights Reserved.
3322+#
3323+# Licensed under the Apache License, Version 2.0 (the "License"); you may
3324+# not use this file except in compliance with the License. You may obtain
3325+# a copy of the License at
3326+#
3327+# http://www.apache.org/licenses/LICENSE-2.0
3328+#
3329+# Unless required by applicable law or agreed to in writing, software
3330+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
3331+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
3332+# License for the specific language governing permissions and limitations
3333+# under the License.
3334+
3335+TOOLS=`dirname $0`
3336+VENV=$TOOLS/../.quantum-venv
3337+source $VENV/bin/activate && $@

Subscribers

People subscribed via source and target branches