Merge lp:~santhoshkumar/network-service/quantum_testing_framework into lp:network-service
- quantum_testing_framework
- Merge into diablo
Status: | Merged |
---|---|
Approved by: | Somik Behera |
Approved revision: | 19 |
Merged at revision: | 19 |
Proposed branch: | lp:~santhoshkumar/network-service/quantum_testing_framework |
Merge into: | lp:network-service |
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 |
Related bugs: |
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Salvatore Orlando | Approve | ||
Somik Behera | Approve | ||
Santhosh Kumar Muniraj | Pending | ||
Review via email: mp+64034@code.launchpad.net |
This proposal supersedes a proposal from 2011-06-08.
Commit message
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_
To run the tests in a single Testclass,
FROM_
To run a single test in a test class,
FROM_
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_
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.
Salvatore Orlando (salvatore-orlando) wrote : Posted in a previous version of this proposal | # |
Santhosh Kumar Muniraj (santhoshkumar) wrote : Posted in a previous version of this proposal | # |
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.
Salvatore Orlando (salvatore-orlando) wrote : Posted in a previous version of this proposal | # |
> 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
Salvatore Orlando (salvatore-orlando) wrote : Posted in a previous version of this proposal | # |
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
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
Somik Behera (somikbehera) wrote : | # |
Unit Test framework and pep8 changes look innocuous and otherwise good.
I attempted to resubmit this merge proposal for merge into lp:quantum but launchpad kept failing with some internal error.
This branch should be merged into lp:quantum to get our pep8 house in order.
Thanks,
Somik
Salvatore Orlando (salvatore-orlando) wrote : | # |
Let.s get this branch merged!
Preview Diff
1 | === modified file 'bin/quantum' |
2 | --- bin/quantum 2011-05-27 16:52:06 +0000 |
3 | +++ bin/quantum 2011-06-09 15:29: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 15:29: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 15:29: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 15:29: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 15:29: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 15:29: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 15:29: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 15:29: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 15:29: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 15:29: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 15:29: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 15:29: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 15:29: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 15:29: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 15:29: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 15:29: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 15:29: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 15:29: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 15:29: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 15:29: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 15:29: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 15:29: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 15:29: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 15:29: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 15:29: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 15:29: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 15:29: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 15:29: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 15:29: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 15:29: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 15:29: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 15:29: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 15:29: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 && $@ |
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 QuantumAPIServi ce.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