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

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

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

Description of the change

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

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

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

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

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

Hi Santosh,

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

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

from quantum.common import extensions

This causes the quantum binary to fail with an ImportError

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

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

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

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

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

Thanks for your contribution,
Salvatore

review: Needs Fixing
19. By Santhosh Kumar Muniraj

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

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

Hi Salvatore,

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

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

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

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

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

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

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

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

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

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

I will do a complete review in a few hours.

Cheers,
Salvatore

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

Hi Santosh!

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

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

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

TypeError: NetworkInUse() is not JSON serializable

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

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

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

Cheers,
Salvatore

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

review: Needs Information

Unmerged revisions

19. By Santhosh Kumar Muniraj

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

18. By Santhosh Kumar Muniraj

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

17. By Santhosh Kumar Muniraj

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

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
=== modified file 'bin/quantum'
--- bin/quantum 2011-05-27 16:52:06 +0000
+++ bin/quantum 2011-06-09 05:01:27 +0000
@@ -33,9 +33,10 @@
3333
34gettext.install('quantum', unicode=1)34gettext.install('quantum', unicode=1)
3535
36from quantum import service 36from quantum import service
37from quantum.common import config37from quantum.common import config
3838
39
39def create_options(parser):40def create_options(parser):
40 """41 """
41 Sets up the CLI and config-file options that may be42 Sets up the CLI and config-file options that may be
@@ -58,4 +59,3 @@
58 service.wait()59 service.wait()
59 except RuntimeError, e:60 except RuntimeError, e:
60 sys.exit("ERROR: %s" % e)61 sys.exit("ERROR: %s" % e)
61
6262
=== modified file 'etc/quantum.conf.test'
--- etc/quantum.conf.test 2011-05-11 21:29:35 +0000
+++ etc/quantum.conf.test 2011-06-09 05:01:27 +0000
@@ -12,4 +12,4 @@
12bind_host = 0.0.0.012bind_host = 0.0.0.0
1313
14# Port the bind the API server to14# Port the bind the API server to
15bind_port = 9696
16\ No newline at end of file15\ No newline at end of file
16bind_port = 9696
1717
=== modified file 'quantum/api/__init__.py'
--- quantum/api/__init__.py 2011-06-07 06:07:05 +0000
+++ quantum/api/__init__.py 2011-06-09 05:01:27 +0000
@@ -54,8 +54,9 @@
54 mapper.resource('port', 'ports',54 mapper.resource('port', 'ports',
55 controller=ports.Controller(),55 controller=ports.Controller(),
56 parent_resource=dict(member_name='network',56 parent_resource=dict(member_name='network',
57 collection_name=\57 collection_name=uri_prefix +\
58 uri_prefix + 'networks'))58 'networks'))
59
59 mapper.connect("get_resource",60 mapper.connect("get_resource",
60 uri_prefix + 'networks/{network_id}/' \61 uri_prefix + 'networks/{network_id}/' \
61 'ports/{id}/attachment{.format}',62 'ports/{id}/attachment{.format}',
6263
=== modified file 'quantum/api/faults.py'
--- quantum/api/faults.py 2011-05-31 17:15:00 +0000
+++ quantum/api/faults.py 2011-06-09 05:01:27 +0000
@@ -36,8 +36,7 @@
36 432: "portInUse",36 432: "portInUse",
37 440: "alreadyAttached",37 440: "alreadyAttached",
38 470: "serviceUnavailable",38 470: "serviceUnavailable",
39 471: "pluginFault"39 471: "pluginFault"}
40 }
4140
42 def __init__(self, exception):41 def __init__(self, exception):
43 """Create a Fault for the given webob.exc.exception."""42 """Create a Fault for the given webob.exc.exception."""
@@ -52,7 +51,7 @@
52 fault_data = {51 fault_data = {
53 fault_name: {52 fault_name: {
54 'code': code,53 'code': code,
55 'message': self.wrapped_exc.explanation, 54 'message': self.wrapped_exc.explanation,
56 'detail': self.wrapped_exc.detail}}55 'detail': self.wrapped_exc.detail}}
57 # 'code' is an attribute on the fault tag itself56 # 'code' is an attribute on the fault tag itself
58 metadata = {'application/xml': {'attributes': {fault_name: 'code'}}}57 metadata = {'application/xml': {'attributes': {fault_name: 'code'}}}
5958
=== modified file 'quantum/api/networks.py'
--- quantum/api/networks.py 2011-05-31 17:15:00 +0000
+++ quantum/api/networks.py 2011-06-09 05:01:27 +0000
@@ -44,63 +44,66 @@
44 self._resource_name = 'network'44 self._resource_name = 'network'
45 super(Controller, self).__init__()45 super(Controller, self).__init__()
4646
47 def index(self, req, tenant_id):47 def index(self, request, tenant_id):
48 """ Returns a list of network ids """48 """ Returns a list of network ids """
49 #TODO: this should be for a given tenant!!!49 #TODO: this should be for a given tenant!!!
50 return self._items(req, tenant_id, is_detail=False)50 return self._items(request, tenant_id, is_detail=False)
5151
52 def _items(self, req, tenant_id, is_detail):52 def _items(self, request, tenant_id, is_detail):
53 """ Returns a list of networks. """53 """ Returns a list of networks. """
54 networks = self.network_manager.get_all_networks(tenant_id)54 networks = self.network_manager.get_all_networks(tenant_id)
55 builder = networks_view.get_view_builder(req)55 builder = networks_view.get_view_builder(request)
56 result = [builder.build(network, is_detail)['network']56 result = [builder.build(network, is_detail)['network']
57 for network in networks]57 for network in networks]
58 return dict(networks=result)58 return dict(networks=result)
5959
60 def show(self, req, tenant_id, id):60 def show(self, request, tenant_id, id):
61 """ Returns network details for the given network id """61 """ Returns network details for the given network id """
62 try:62 try:
63 network = self.network_manager.get_network_details(63 network = self.network_manager.get_network_details(
64 tenant_id, id)64 tenant_id, id)
65 builder = networks_view.get_view_builder(req)65 builder = networks_view.get_view_builder(request)
66 #build response with details66 #build response with details
67 result = builder.build(network, True)67 result = builder.build(network, True)
68 return dict(networks=result)68 return dict(networks=result)
69 except exception.NetworkNotFound as e:69 except exception.NetworkNotFound as e:
70 return faults.Fault(faults.NetworkNotFound(e))70 return faults.Fault(faults.NetworkNotFound(e))
7171
72 def create(self, req, tenant_id):72 def create(self, request, tenant_id):
73 """ Creates a new network for a given tenant """73 """ Creates a new network for a given tenant """
74 #look for network name in request74 #look for network name in request
75 try:75 try:
76 req_params = \76 request_params = \
77 self._parse_request_params(req, self._network_ops_param_list)77 self._parse_request_params(request,
78 self._network_ops_param_list)
78 except exc.HTTPError as e:79 except exc.HTTPError as e:
79 return faults.Fault(e)80 return faults.Fault(e)
80 network = self.network_manager.\81 network = self.network_manager.\
81 create_network(tenant_id,req_params['network-name'])82 create_network(tenant_id,
82 builder = networks_view.get_view_builder(req)83 request_params['network-name'])
84 builder = networks_view.get_view_builder(request)
83 result = builder.build(network)85 result = builder.build(network)
84 return dict(networks=result)86 return dict(networks=result)
8587
86 def update(self, req, tenant_id, id):88 def update(self, request, tenant_id, id):
87 """ Updates the name for the network with the given id """89 """ Updates the name for the network with the given id """
88 try:90 try:
89 req_params = \91 request_params = \
90 self._parse_request_params(req, self._network_ops_param_list)92 self._parse_request_params(request,
93 self._network_ops_param_list)
91 except exc.HTTPError as e:94 except exc.HTTPError as e:
92 return faults.Fault(e)95 return faults.Fault(e)
93 try:96 try:
94 network = self.network_manager.rename_network(tenant_id,97 network = self.network_manager.rename_network(tenant_id,
95 id, req_params['network-name'])98 id, request_params['network-name'])
9699
97 builder = networks_view.get_view_builder(req)100 builder = networks_view.get_view_builder(request)
98 result = builder.build(network, True)101 result = builder.build(network, True)
99 return dict(networks=result)102 return dict(networks=result)
100 except exception.NetworkNotFound as e:103 except exception.NetworkNotFound as e:
101 return faults.Fault(faults.NetworkNotFound(e))104 return faults.Fault(faults.NetworkNotFound(e))
102105
103 def delete(self, req, tenant_id, id):106 def delete(self, request, tenant_id, id):
104 """ Destroys the network with the given id """107 """ Destroys the network with the given id """
105 try:108 try:
106 self.network_manager.delete_network(tenant_id, id)109 self.network_manager.delete_network(tenant_id, id)
107110
=== modified file 'quantum/api/ports.py'
--- quantum/api/ports.py 2011-06-03 17:26:36 +0000
+++ quantum/api/ports.py 2011-06-09 05:01:27 +0000
@@ -24,51 +24,49 @@
2424
25LOG = logging.getLogger('quantum.api.ports')25LOG = logging.getLogger('quantum.api.ports')
2626
27
27class Controller(common.QuantumController):28class Controller(common.QuantumController):
28 """ Port API controller for Quantum API """29 """ Port API controller for Quantum API """
2930
30 _port_ops_param_list = [{31 _port_ops_param_list = [{
31 'param-name': 'port-state',32 'param-name': 'port-state',
32 'default-value': 'DOWN',33 'default-value': 'DOWN',
33 'required': False},]34 'required': False}, ]
3435
35 _attachment_ops_param_list = [{36 _attachment_ops_param_list = [{
36 'param-name': 'attachment-id',37 'param-name': 'attachment-id',
37 'required': True},]38 'required': True}, ]
3839
39 _serialization_metadata = {40 _serialization_metadata = {
40 "application/xml": {41 "application/xml": {
41 "attributes": {42 "attributes": {
42 "port": ["id","state"],43 "port": ["id", "state"], }, }, }
43 },
44 },
45 }
4644
47 def __init__(self, plugin_conf_file=None):45 def __init__(self, plugin_conf_file=None):
48 self._resource_name = 'port'46 self._resource_name = 'port'
49 super(Controller, self).__init__()47 super(Controller, self).__init__()
5048
51 def index(self, req, tenant_id, network_id):49 def index(self, request, tenant_id, network_id):
52 """ Returns a list of port ids for a given network """50 """ Returns a list of port ids for a given network """
53 return self._items(req, tenant_id, network_id, is_detail=False)51 return self._items(request, tenant_id, network_id, is_detail=False)
5452
55 def _items(self, req, tenant_id, network_id, is_detail):53 def _items(self, request, tenant_id, network_id, is_detail):
56 """ Returns a list of networks. """54 """ Returns a list of networks. """
57 try :55 try:
58 ports = self.network_manager.get_all_ports(tenant_id, network_id)56 ports = self.network_manager.get_all_ports(tenant_id, network_id)
59 builder = ports_view.get_view_builder(req)57 builder = ports_view.get_view_builder(request)
60 result = [builder.build(port, is_detail)['port']58 result = [builder.build(port, is_detail)['port']
61 for port in ports]59 for port in ports]
62 return dict(ports=result)60 return dict(ports=result)
63 except exception.NetworkNotFound as e:61 except exception.NetworkNotFound as e:
64 return faults.Fault(faults.NetworkNotFound(e))62 return faults.Fault(faults.NetworkNotFound(e))
6563
66 def show(self, req, tenant_id, network_id, id):64 def show(self, request, tenant_id, network_id, id):
67 """ Returns port details for given port and network """65 """ Returns port details for given port and network """
68 try:66 try:
69 port = self.network_manager.get_port_details(67 port = self.network_manager.get_port_details(
70 tenant_id, network_id, id)68 tenant_id, network_id, id)
71 builder = ports_view.get_view_builder(req)69 builder = ports_view.get_view_builder(request)
72 #build response with details70 #build response with details
73 result = builder.build(port, True)71 result = builder.build(port, True)
74 return dict(ports=result)72 return dict(ports=result)
@@ -77,19 +75,19 @@
77 except exception.PortNotFound as e:75 except exception.PortNotFound as e:
78 return faults.Fault(faults.PortNotFound(e))76 return faults.Fault(faults.PortNotFound(e))
7977
80 def create(self, req, tenant_id, network_id):78 def create(self, request, tenant_id, network_id):
81 """ Creates a new port for a given network """79 """ Creates a new port for a given network """
82 #look for port state in request80 #look for port state in request
83 try:81 try:
84 req_params = \82 request_params = \
85 self._parse_request_params(req, self._port_ops_param_list)83 self._parse_request_params(request, self._port_ops_param_list)
86 except exc.HTTPError as e:84 except exc.HTTPError as e:
87 return faults.Fault(e)85 return faults.Fault(e)
88 try:86 try:
89 port = self.network_manager.create_port(tenant_id,87 port = self.network_manager.create_port(tenant_id,
90 network_id,88 network_id,
91 req_params['port-state'])89 request_params['port-state'])
92 builder = ports_view.get_view_builder(req)90 builder = ports_view.get_view_builder(request)
93 result = builder.build(port)91 result = builder.build(port)
94 return dict(ports=result)92 return dict(ports=result)
95 except exception.NetworkNotFound as e:93 except exception.NetworkNotFound as e:
@@ -97,18 +95,18 @@
97 except exception.StateInvalid as e:95 except exception.StateInvalid as e:
98 return faults.Fault(faults.RequestedStateInvalid(e))96 return faults.Fault(faults.RequestedStateInvalid(e))
9997
100 def update(self, req, tenant_id, network_id, id):98 def update(self, request, tenant_id, network_id, id):
101 """ Updates the state of a port for a given network """99 """ Updates the state of a port for a given network """
102 #look for port state in request100 #look for port state in request
103 try:101 try:
104 req_params = \102 request_params = \
105 self._parse_request_params(req, self._port_ops_param_list)103 self._parse_request_params(request, self._port_ops_param_list)
106 except exc.HTTPError as e:104 except exc.HTTPError as e:
107 return faults.Fault(e)105 return faults.Fault(e)
108 try:106 try:
109 port = self.network_manager.update_port(tenant_id,network_id, id,107 port = self.network_manager.update_port(tenant_id, network_id, id,
110 req_params['port-state'])108 request_params['port-state'])
111 builder = ports_view.get_view_builder(req)109 builder = ports_view.get_view_builder(request)
112 result = builder.build(port, True)110 result = builder.build(port, True)
113 return dict(ports=result)111 return dict(ports=result)
114 except exception.NetworkNotFound as e:112 except exception.NetworkNotFound as e:
@@ -118,7 +116,7 @@
118 except exception.StateInvalid as e:116 except exception.StateInvalid as e:
119 return faults.Fault(faults.RequestedStateInvalid(e))117 return faults.Fault(faults.RequestedStateInvalid(e))
120118
121 def delete(self, req, tenant_id, network_id, id):119 def delete(self, request, tenant_id, network_id, id):
122 """ Destroys the port with the given id """120 """ Destroys the port with the given id """
123 #look for port state in request121 #look for port state in request
124 try:122 try:
@@ -132,7 +130,7 @@
132 except exception.PortInUse as e:130 except exception.PortInUse as e:
133 return faults.Fault(faults.PortInUse(e))131 return faults.Fault(faults.PortInUse(e))
134132
135 def get_resource(self,req,tenant_id, network_id, id):133 def get_resource(self, request, tenant_id, network_id, id):
136 try:134 try:
137 result = self.network_manager.get_interface_details(135 result = self.network_manager.get_interface_details(
138 tenant_id, network_id, id)136 tenant_id, network_id, id)
@@ -143,19 +141,19 @@
143 return faults.Fault(faults.PortNotFound(e))141 return faults.Fault(faults.PortNotFound(e))
144142
145 #TODO - Complete implementation of these APIs143 #TODO - Complete implementation of these APIs
146 def attach_resource(self,req,tenant_id, network_id, id):144 def attach_resource(self, request, tenant_id, network_id, id):
147 content_type = req.best_match_content_type()145 content_type = request.best_match_content_type()
148 print "Content type:%s" %content_type146 print "Content type:%s" % content_type
149 try:147 try:
150 req_params = \148 request_params = \
151 self._parse_request_params(req,149 self._parse_request_params(request,
152 self._attachment_ops_param_list)150 self._attachment_ops_param_list)
153 except exc.HTTPError as e:151 except exc.HTTPError as e:
154 return faults.Fault(e)152 return faults.Fault(e)
155 try:153 try:
156 self.network_manager.plug_interface(tenant_id,154 self.network_manager.plug_interface(tenant_id,
157 network_id,id,155 network_id, id,
158 req_params['attachment-id'])156 request_params['attachment-id'])
159 return exc.HTTPAccepted()157 return exc.HTTPAccepted()
160 except exception.NetworkNotFound as e:158 except exception.NetworkNotFound as e:
161 return faults.Fault(faults.NetworkNotFound(e))159 return faults.Fault(faults.NetworkNotFound(e))
@@ -167,10 +165,10 @@
167 return faults.Fault(faults.AlreadyAttached(e))165 return faults.Fault(faults.AlreadyAttached(e))
168166
169 #TODO - Complete implementation of these APIs167 #TODO - Complete implementation of these APIs
170 def detach_resource(self,req,tenant_id, network_id, id):168 def detach_resource(self, request, tenant_id, network_id, id):
171 try:169 try:
172 self.network_manager.unplug_interface(tenant_id,170 self.network_manager.unplug_interface(tenant_id,
173 network_id,id)171 network_id, id)
174 return exc.HTTPAccepted()172 return exc.HTTPAccepted()
175 except exception.NetworkNotFound as e:173 except exception.NetworkNotFound as e:
176 return faults.Fault(faults.NetworkNotFound(e))174 return faults.Fault(faults.NetworkNotFound(e))
177175
=== modified file 'quantum/api/views/__init__.py'
--- quantum/api/views/__init__.py 2011-05-23 20:51:00 +0000
+++ quantum/api/views/__init__.py 2011-06-09 05:01:27 +0000
@@ -13,4 +13,4 @@
13# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the13# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
14# License for the specific language governing permissions and limitations14# License for the specific language governing permissions and limitations
15# under the License.15# under the License.
16# @author: Somik Behera, Nicira Networks, Inc.
17\ No newline at end of file16\ No newline at end of file
17# @author: Somik Behera, Nicira Networks, Inc.
1818
=== modified file 'quantum/api/views/networks.py'
--- quantum/api/views/networks.py 2011-05-28 20:52:09 +0000
+++ quantum/api/views/networks.py 2011-06-09 05:01:27 +0000
@@ -33,17 +33,17 @@
3333
34 def build(self, network_data, is_detail=False):34 def build(self, network_data, is_detail=False):
35 """Generic method used to generate a network entity."""35 """Generic method used to generate a network entity."""
36 print "NETWORK-DATA:%s" %network_data36 print "NETWORK-DATA:%s" % network_data
37 if is_detail:37 if is_detail:
38 network = self._build_detail(network_data)38 network = self._build_detail(network_data)
39 else:39 else:
40 network = self._build_simple(network_data)40 network = self._build_simple(network_data)
41 return network41 return network
42 42
43 def _build_simple(self, network_data):43 def _build_simple(self, network_data):
44 """Return a simple model of a server."""44 """Return a simple model of a server."""
45 return dict(network=dict(id=network_data['net-id']))45 return dict(network=dict(id=network_data['net-id']))
46 46
47 def _build_detail(self, network_data):47 def _build_detail(self, network_data):
48 """Return a simple model of a server."""48 """Return a simple model of a server."""
49 return dict(network=dict(id=network_data['net-id'],49 return dict(network=dict(id=network_data['net-id'],
5050
=== modified file 'quantum/api/views/ports.py'
--- quantum/api/views/ports.py 2011-06-03 05:30:37 +0000
+++ quantum/api/views/ports.py 2011-06-09 05:01:27 +0000
@@ -31,7 +31,7 @@
3131
32 def build(self, port_data, is_detail=False):32 def build(self, port_data, is_detail=False):
33 """Generic method used to generate a port entity."""33 """Generic method used to generate a port entity."""
34 print "PORT-DATA:%s" %port_data34 print "PORT-DATA:%s" % port_data
35 if is_detail:35 if is_detail:
36 port = self._build_detail(port_data)36 port = self._build_detail(port_data)
37 else:37 else:
3838
=== modified file 'quantum/cli.py'
--- quantum/cli.py 2011-06-07 23:56:53 +0000
+++ quantum/cli.py 2011-06-09 05:01:27 +0000
@@ -31,25 +31,29 @@
31FORMAT = "json"31FORMAT = "json"
32CONTENT_TYPE = "application/" + FORMAT32CONTENT_TYPE = "application/" + FORMAT
3333
34
34### --- Miniclient (taking from the test directory)35### --- Miniclient (taking from the test directory)
35### TODO(bgh): move this to a library within quantum36### TODO(bgh): move this to a library within quantum
36class MiniClient(object):37class MiniClient(object):
37 """A base client class - derived from Glance.BaseClient"""38 """A base client class - derived from Glance.BaseClient"""
38 action_prefix = '/v0.1/tenants/{tenant_id}'39 action_prefix = '/v0.1/tenants/{tenant_id}'
40
39 def __init__(self, host, port, use_ssl):41 def __init__(self, host, port, use_ssl):
40 self.host = host42 self.host = host
41 self.port = port43 self.port = port
42 self.use_ssl = use_ssl44 self.use_ssl = use_ssl
43 self.connection = None45 self.connection = None
46
44 def get_connection_type(self):47 def get_connection_type(self):
45 if self.use_ssl:48 if self.use_ssl:
46 return httplib.HTTPSConnection49 return httplib.HTTPSConnection
47 else:50 else:
48 return httplib.HTTPConnection51 return httplib.HTTPConnection
52
49 def do_request(self, tenant, method, action, body=None,53 def do_request(self, tenant, method, action, body=None,
50 headers=None, params=None):54 headers=None, params=None):
51 action = MiniClient.action_prefix + action55 action = MiniClient.action_prefix + action
52 action = action.replace('{tenant_id}',tenant)56 action = action.replace('{tenant_id}', tenant)
53 if type(params) is dict:57 if type(params) is dict:
54 action += '?' + urllib.urlencode(params)58 action += '?' + urllib.urlencode(params)
55 try:59 try:
@@ -67,6 +71,7 @@
67 raise Exception("Server returned error: %s" % res.read())71 raise Exception("Server returned error: %s" % res.read())
68 except (socket.error, IOError), e:72 except (socket.error, IOError), e:
69 raise Exception("Unable to connect to server. Got error: %s" % e)73 raise Exception("Unable to connect to server. Got error: %s" % e)
74
70 def get_status_code(self, response):75 def get_status_code(self, response):
71 if hasattr(response, 'status_int'):76 if hasattr(response, 'status_int'):
72 return response.status_int77 return response.status_int
@@ -76,6 +81,7 @@
7681
77### -- Core CLI functions82### -- Core CLI functions
7883
84
79def list_nets(manager, *args):85def list_nets(manager, *args):
80 tenant_id = args[0]86 tenant_id = args[0]
81 networks = manager.get_all_networks(tenant_id)87 networks = manager.get_all_networks(tenant_id)
@@ -85,6 +91,7 @@
85 name = net["net-name"]91 name = net["net-name"]
86 print "\tNetwork ID:%s \n\tNetwork Name:%s \n" % (id, name)92 print "\tNetwork ID:%s \n\tNetwork Name:%s \n" % (id, name)
8793
94
88def api_list_nets(client, *args):95def api_list_nets(client, *args):
89 tenant_id = args[0]96 tenant_id = args[0]
90 res = client.do_request(tenant_id, 'GET', "/networks." + FORMAT)97 res = client.do_request(tenant_id, 'GET', "/networks." + FORMAT)
@@ -98,11 +105,13 @@
98 # name = n["net-name"]105 # name = n["net-name"]
99 # LOG.info("\tNetwork ID:%s \n\tNetwork Name:%s \n" % (id, name))106 # LOG.info("\tNetwork ID:%s \n\tNetwork Name:%s \n" % (id, name))
100107
108
101def create_net(manager, *args):109def create_net(manager, *args):
102 tid, name = args110 tid, name = args
103 new_net_id = manager.create_network(tid, name)111 new_net_id = manager.create_network(tid, name)
104 print "Created a new Virtual Network with ID:%s\n" % new_net_id112 print "Created a new Virtual Network with ID:%s\n" % new_net_id
105113
114
106def api_create_net(client, *args):115def api_create_net(client, *args):
107 tid, name = args116 tid, name = args
108 data = {'network': {'network-name': '%s' % name}}117 data = {'network': {'network-name': '%s' % name}}
@@ -119,11 +128,13 @@
119 return128 return
120 print "Created a new Virtual Network with ID:%s\n" % nid129 print "Created a new Virtual Network with ID:%s\n" % nid
121130
131
122def delete_net(manager, *args):132def delete_net(manager, *args):
123 tid, nid = args133 tid, nid = args
124 manager.delete_network(tid, nid)134 manager.delete_network(tid, nid)
125 print "Deleted Virtual Network with ID:%s" % nid135 print "Deleted Virtual Network with ID:%s" % nid
126136
137
127def api_delete_net(client, *args):138def api_delete_net(client, *args):
128 tid, nid = args139 tid, nid = args
129 res = client.do_request(tid, 'DELETE', "/networks/" + nid + "." + FORMAT)140 res = client.do_request(tid, 'DELETE', "/networks/" + nid + "." + FORMAT)
@@ -135,6 +146,7 @@
135 else:146 else:
136 print "Deleted Virtual Network with ID:%s" % nid147 print "Deleted Virtual Network with ID:%s" % nid
137148
149
138def detail_net(manager, *args):150def detail_net(manager, *args):
139 tid, nid = args151 tid, nid = args
140 iface_list = manager.get_network_details(tid, nid)152 iface_list = manager.get_network_details(tid, nid)
@@ -142,6 +154,7 @@
142 for iface in iface_list:154 for iface in iface_list:
143 print "\tRemote interface:%s" % iface155 print "\tRemote interface:%s" % iface
144156
157
145def api_detail_net(client, *args):158def api_detail_net(client, *args):
146 tid, nid = args159 tid, nid = args
147 res = client.do_request(tid, 'GET',160 res = client.do_request(tid, 'GET',
@@ -163,11 +176,13 @@
163 remote_iface = rd["attachment"]176 remote_iface = rd["attachment"]
164 print "\tRemote interface:%s" % remote_iface177 print "\tRemote interface:%s" % remote_iface
165178
179
166def rename_net(manager, *args):180def rename_net(manager, *args):
167 tid, nid, name = args181 tid, nid, name = args
168 manager.rename_network(tid, nid, name)182 manager.rename_network(tid, nid, name)
169 print "Renamed Virtual Network with ID:%s" % nid183 print "Renamed Virtual Network with ID:%s" % nid
170184
185
171def api_rename_net(client, *args):186def api_rename_net(client, *args):
172 tid, nid, name = args187 tid, nid, name = args
173 data = {'network': {'network-name': '%s' % name}}188 data = {'network': {'network-name': '%s' % name}}
@@ -178,6 +193,7 @@
178 LOG.debug(resdict)193 LOG.debug(resdict)
179 print "Renamed Virtual Network with ID:%s" % nid194 print "Renamed Virtual Network with ID:%s" % nid
180195
196
181def list_ports(manager, *args):197def list_ports(manager, *args):
182 tid, nid = args198 tid, nid = args
183 ports = manager.get_all_ports(tid, nid)199 ports = manager.get_all_ports(tid, nid)
@@ -185,6 +201,7 @@
185 for port in ports:201 for port in ports:
186 print "\tVirtual Port:%s" % port["port-id"]202 print "\tVirtual Port:%s" % port["port-id"]
187203
204
188def api_list_ports(client, *args):205def api_list_ports(client, *args):
189 tid, nid = args206 tid, nid = args
190 res = client.do_request(tid, 'GET',207 res = client.do_request(tid, 'GET',
@@ -199,12 +216,14 @@
199 for port in rd["ports"]:216 for port in rd["ports"]:
200 print "\tVirtual Port:%s" % port["id"]217 print "\tVirtual Port:%s" % port["id"]
201218
219
202def create_port(manager, *args):220def create_port(manager, *args):
203 tid, nid = args221 tid, nid = args
204 new_port = manager.create_port(tid, nid)222 new_port = manager.create_port(tid, nid)
205 print "Created Virtual Port:%s " \223 print "Created Virtual Port:%s " \
206 "on Virtual Network:%s" % (new_port, nid)224 "on Virtual Network:%s" % (new_port, nid)
207225
226
208def api_create_port(client, *args):227def api_create_port(client, *args):
209 tid, nid = args228 tid, nid = args
210 res = client.do_request(tid, 'POST',229 res = client.do_request(tid, 'POST',
@@ -218,11 +237,13 @@
218 print "Created Virtual Port:%s " \237 print "Created Virtual Port:%s " \
219 "on Virtual Network:%s" % (new_port, nid)238 "on Virtual Network:%s" % (new_port, nid)
220239
240
221def delete_port(manager, *args):241def delete_port(manager, *args):
222 tid, nid, pid = args242 tid, nid, pid = args
223 LOG.info("Deleted Virtual Port:%s " \243 LOG.info("Deleted Virtual Port:%s " \
224 "on Virtual Network:%s" % (pid, nid))244 "on Virtual Network:%s" % (pid, nid))
225245
246
226def api_delete_port(client, *args):247def api_delete_port(client, *args):
227 tid, nid, pid = args248 tid, nid, pid = args
228 res = client.do_request(tid, 'DELETE',249 res = client.do_request(tid, 'DELETE',
@@ -234,12 +255,14 @@
234 LOG.info("Deleted Virtual Port:%s " \255 LOG.info("Deleted Virtual Port:%s " \
235 "on Virtual Network:%s" % (pid, nid))256 "on Virtual Network:%s" % (pid, nid))
236257
258
237def detail_port(manager, *args):259def detail_port(manager, *args):
238 tid, nid, pid = args260 tid, nid, pid = args
239 port_detail = manager.get_port_details(tid, nid, pid)261 port_detail = manager.get_port_details(tid, nid, pid)
240 print "Virtual Port:%s on Virtual Network:%s " \262 print "Virtual Port:%s on Virtual Network:%s " \
241 "contains remote interface:%s" % (pid, nid, port_detail)263 "contains remote interface:%s" % (pid, nid, port_detail)
242264
265
243def api_detail_port(client, *args):266def api_detail_port(client, *args):
244 tid, nid, pid = args267 tid, nid, pid = args
245 res = client.do_request(tid, 'GET',268 res = client.do_request(tid, 'GET',
@@ -256,12 +279,14 @@
256 print "Virtual Port:%s on Virtual Network:%s " \279 print "Virtual Port:%s on Virtual Network:%s " \
257 "contains remote interface:%s" % (pid, nid, attachment)280 "contains remote interface:%s" % (pid, nid, attachment)
258281
282
259def plug_iface(manager, *args):283def plug_iface(manager, *args):
260 tid, nid, pid, vid = args284 tid, nid, pid, vid = args
261 manager.plug_interface(tid, nid, pid, vid)285 manager.plug_interface(tid, nid, pid, vid)
262 print "Plugged remote interface:%s " \286 print "Plugged remote interface:%s " \
263 "into Virtual Network:%s" % (vid, nid)287 "into Virtual Network:%s" % (vid, nid)
264288
289
265def api_plug_iface(client, *args):290def api_plug_iface(client, *args):
266 tid, nid, pid, vid = args291 tid, nid, pid, vid = args
267 data = {'port': {'attachment-id': '%s' % vid}}292 data = {'port': {'attachment-id': '%s' % vid}}
@@ -276,12 +301,14 @@
276 return301 return
277 print "Plugged interface \"%s\" to port:%s on network:%s" % (vid, pid, nid)302 print "Plugged interface \"%s\" to port:%s on network:%s" % (vid, pid, nid)
278303
279def unplug_iface(manager, *args):304
305def unplug_iface(manager, *args):
280 tid, nid, pid = args306 tid, nid, pid = args
281 manager.unplug_interface(tid, nid, pid)307 manager.unplug_interface(tid, nid, pid)
282 print "UnPlugged remote interface " \308 print "UnPlugged remote interface " \
283 "from Virtual Port:%s Virtual Network:%s" % (pid, nid)309 "from Virtual Port:%s Virtual Network:%s" % (pid, nid)
284310
311
285def api_unplug_iface(client, *args):312def api_unplug_iface(client, *args):
286 tid, nid, pid = args313 tid, nid, pid = args
287 data = {'port': {'attachment-id': ''}}314 data = {'port': {'attachment-id': ''}}
@@ -296,63 +323,53 @@
296 return323 return
297 print "Unplugged interface from port:%s on network:%s" % (pid, nid)324 print "Unplugged interface from port:%s on network:%s" % (pid, nid)
298325
326
299commands = {327commands = {
300 "list_nets": {328 "list_nets": {
301 "func": list_nets,329 "func": list_nets,
302 "api_func": api_list_nets,330 "api_func": api_list_nets,
303 "args": ["tenant-id"]331 "args": ["tenant-id"]},
304 },
305 "create_net": {332 "create_net": {
306 "func": create_net,333 "func": create_net,
307 "api_func": api_create_net,334 "api_func": api_create_net,
308 "args": ["tenant-id", "net-name"]335 "args": ["tenant-id", "net-name"]},
309 },
310 "delete_net": {336 "delete_net": {
311 "func": delete_net,337 "func": delete_net,
312 "api_func": api_delete_net,338 "api_func": api_delete_net,
313 "args": ["tenant-id", "net-id"]339 "args": ["tenant-id", "net-id"]},
314 },
315 "detail_net": {340 "detail_net": {
316 "func": detail_net,341 "func": detail_net,
317 "api_func": api_detail_net,342 "api_func": api_detail_net,
318 "args": ["tenant-id", "net-id"]343 "args": ["tenant-id", "net-id"]},
319 },
320 "rename_net": {344 "rename_net": {
321 "func": rename_net,345 "func": rename_net,
322 "api_func": api_rename_net,346 "api_func": api_rename_net,
323 "args": ["tenant-id", "net-id", "new-name"]347 "args": ["tenant-id", "net-id", "new-name"]},
324 },
325 "list_ports": {348 "list_ports": {
326 "func": list_ports,349 "func": list_ports,
327 "api_func": api_list_ports,350 "api_func": api_list_ports,
328 "args": ["tenant-id", "net-id"]351 "args": ["tenant-id", "net-id"]},
329 },
330 "create_port": {352 "create_port": {
331 "func": create_port,353 "func": create_port,
332 "api_func": api_create_port,354 "api_func": api_create_port,
333 "args": ["tenant-id", "net-id"]355 "args": ["tenant-id", "net-id"]},
334 },
335 "delete_port": {356 "delete_port": {
336 "func": delete_port,357 "func": delete_port,
337 "api_func": api_delete_port,358 "api_func": api_delete_port,
338 "args": ["tenant-id", "net-id", "port-id"]359 "args": ["tenant-id", "net-id", "port-id"]},
339 },
340 "detail_port": {360 "detail_port": {
341 "func": detail_port,361 "func": detail_port,
342 "api_func": api_detail_port,362 "api_func": api_detail_port,
343 "args": ["tenant-id", "net-id", "port-id"]363 "args": ["tenant-id", "net-id", "port-id"]},
344 },
345 "plug_iface": {364 "plug_iface": {
346 "func": plug_iface,365 "func": plug_iface,
347 "api_func": api_plug_iface,366 "api_func": api_plug_iface,
348 "args": ["tenant-id", "net-id", "port-id", "iface-id"]367 "args": ["tenant-id", "net-id", "port-id", "iface-id"]},
349 },
350 "unplug_iface": {368 "unplug_iface": {
351 "func": unplug_iface,369 "func": unplug_iface,
352 "api_func": api_unplug_iface,370 "api_func": api_unplug_iface,
353 "args": ["tenant-id", "net-id", "port-id"]371 "args": ["tenant-id", "net-id", "port-id"]}, }
354 },372
355 }
356373
357def help():374def help():
358 print "\nCommands:"375 print "\nCommands:"
@@ -360,6 +377,7 @@
360 print " %s %s" % (k,377 print " %s %s" % (k,
361 " ".join(["<%s>" % y for y in commands[k]["args"]]))378 " ".join(["<%s>" % y for y in commands[k]["args"]]))
362379
380
363def build_args(cmd, cmdargs, arglist):381def build_args(cmd, cmdargs, arglist):
364 args = []382 args = []
365 orig_arglist = arglist[:]383 orig_arglist = arglist[:]
@@ -381,6 +399,7 @@
381 return None399 return None
382 return args400 return args
383401
402
384if __name__ == "__main__":403if __name__ == "__main__":
385 usagestr = "Usage: %prog [OPTIONS] <command> [args]"404 usagestr = "Usage: %prog [OPTIONS] <command> [args]"
386 parser = OptionParser(usage=usagestr)405 parser = OptionParser(usage=usagestr)
@@ -420,7 +439,7 @@
420 LOG.debug("Executing command \"%s\" with args: %s" % (cmd, args))439 LOG.debug("Executing command \"%s\" with args: %s" % (cmd, args))
421 if not options.load_plugin:440 if not options.load_plugin:
422 client = MiniClient(options.host, options.port, options.ssl)441 client = MiniClient(options.host, options.port, options.ssl)
423 if not commands[cmd].has_key("api_func"):442 if "api_func" not in commands[cmd]:
424 LOG.error("API version of \"%s\" is not yet implemented" % cmd)443 LOG.error("API version of \"%s\" is not yet implemented" % cmd)
425 sys.exit(1)444 sys.exit(1)
426 commands[cmd]["api_func"](client, *args)445 commands[cmd]["api_func"](client, *args)
427446
=== modified file 'quantum/common/config.py'
--- quantum/common/config.py 2011-05-24 16:45:16 +0000
+++ quantum/common/config.py 2011-06-09 05:01:27 +0000
@@ -209,7 +209,7 @@
209 fix_path(os.path.join('~', '.quantum')),209 fix_path(os.path.join('~', '.quantum')),
210 fix_path('~'),210 fix_path('~'),
211 os.path.join(FLAGS.state_path, 'etc'),211 os.path.join(FLAGS.state_path, 'etc'),
212 os.path.join(FLAGS.state_path, 'etc','quantum'),212 os.path.join(FLAGS.state_path, 'etc', 'quantum'),
213 '/etc/quantum/',213 '/etc/quantum/',
214 '/etc']214 '/etc']
215 for cfg_dir in config_file_dirs:215 for cfg_dir in config_file_dirs:
@@ -244,12 +244,10 @@
244 problem loading the configuration file.244 problem loading the configuration file.
245 """245 """
246 conf_file = find_config_file(options, args)246 conf_file = find_config_file(options, args)
247 print "Conf_file:%s" %conf_file
248 if not conf_file:247 if not conf_file:
249 raise RuntimeError("Unable to locate any configuration file. "248 raise RuntimeError("Unable to locate any configuration file. "
250 "Cannot load application %s" % app_name)249 "Cannot load application %s" % app_name)
251 try:250 try:
252 print "App_name:%s" %app_name
253 conf = deploy.appconfig("config:%s" % conf_file, name=app_name)251 conf = deploy.appconfig("config:%s" % conf_file, name=app_name)
254 return conf_file, conf252 return conf_file, conf
255 except Exception, e:253 except Exception, e:
@@ -257,7 +255,7 @@
257 % (conf_file, e))255 % (conf_file, e))
258256
259257
260def load_paste_app(conf_file, app_name):258def load_paste_app(app_name, options, args):
261 """259 """
262 Builds and returns a WSGI app from a paste config file.260 Builds and returns a WSGI app from a paste config file.
263261
@@ -278,16 +276,15 @@
278 :raises RuntimeError when config file cannot be located or application276 :raises RuntimeError when config file cannot be located or application
279 cannot be loaded from config file277 cannot be loaded from config file
280 """278 """
281 #conf_file, conf = load_paste_config(app_name, options, args)279 conf_file, conf = load_paste_config(app_name, options, args)
282280
283 try:281 try:
284 conf_file = os.path.abspath(conf_file)
285 app = deploy.loadapp("config:%s" % conf_file, name=app_name)282 app = deploy.loadapp("config:%s" % conf_file, name=app_name)
286 except (LookupError, ImportError), e:283 except (LookupError, ImportError), e:
287 raise RuntimeError("Unable to load %(app_name)s from "284 raise RuntimeError("Unable to load %(app_name)s from "
288 "configuration file %(conf_file)s."285 "configuration file %(conf_file)s."
289 "\nGot: %(e)r" % locals())286 "\nGot: %(e)r" % locals())
290 return app287 return conf, app
291288
292289
293def get_option(options, option, **kwargs):290def get_option(options, option, **kwargs):
294291
=== modified file 'quantum/common/exceptions.py'
--- quantum/common/exceptions.py 2011-05-30 00:08:46 +0000
+++ quantum/common/exceptions.py 2011-06-09 05:01:27 +0000
@@ -25,7 +25,7 @@
2525
26class QuantumException(Exception):26class QuantumException(Exception):
27 """Base Quantum Exception27 """Base Quantum Exception
28 28
29 Taken from nova.exception.NovaException29 Taken from nova.exception.NovaException
30 To correctly use this class, inherit from it and define30 To correctly use this class, inherit from it and define
31 a 'message' property. That message will get printf'd31 a 'message' property. That message will get printf'd
@@ -45,6 +45,7 @@
45 def __str__(self):45 def __str__(self):
46 return self._error_string46 return self._error_string
4747
48
48class ProcessExecutionError(IOError):49class ProcessExecutionError(IOError):
49 def __init__(self, stdout=None, stderr=None, exit_code=None, cmd=None,50 def __init__(self, stdout=None, stderr=None, exit_code=None, cmd=None,
50 description=None):51 description=None):
@@ -84,11 +85,11 @@
84class PortNotFound(NotFound):85class PortNotFound(NotFound):
85 message = _("Port %(port_id)s could not be found " \86 message = _("Port %(port_id)s could not be found " \
86 "on network %(net_id)s")87 "on network %(net_id)s")
87 88
8889
89class StateInvalid(QuantumException):90class StateInvalid(QuantumException):
90 message = _("Unsupported port state: %(port_state)s")91 message = _("Unsupported port state: %(port_state)s")
91 92
9293
93class NetworkInUse(QuantumException):94class NetworkInUse(QuantumException):
94 message = _("Unable to complete operation on network %(net_id)s. " \95 message = _("Unable to complete operation on network %(net_id)s. " \
@@ -100,11 +101,13 @@
100 "for network %(net_id)s. The attachment '%(att_id)s" \101 "for network %(net_id)s. The attachment '%(att_id)s" \
101 "is plugged into the logical port.")102 "is plugged into the logical port.")
102103
104
103class AlreadyAttached(QuantumException):105class AlreadyAttached(QuantumException):
104 message = _("Unable to plug the attachment %(att_id)s into port " \106 message = _("Unable to plug the attachment %(att_id)s into port " \
105 "%(port_id)s for network %(net_id)s. The attachment is " \107 "%(port_id)s for network %(net_id)s. The attachment is " \
106 "already plugged into port %(att_port_id)s")108 "already plugged into port %(att_port_id)s")
107 109
110
108class Duplicate(Error):111class Duplicate(Error):
109 pass112 pass
110113
111114
=== modified file 'quantum/common/flags.py'
--- quantum/common/flags.py 2011-05-23 20:51:00 +0000
+++ quantum/common/flags.py 2011-06-09 05:01:27 +0000
@@ -23,7 +23,7 @@
23"""23"""
2424
25import getopt25import getopt
26import os 26import os
27import string27import string
28import sys28import sys
2929
@@ -249,4 +249,3 @@
249249
250DEFINE_string('state_path', os.path.join(os.path.dirname(__file__), '../../'),250DEFINE_string('state_path', os.path.join(os.path.dirname(__file__), '../../'),
251 "Top-level directory for maintaining quantum's state")251 "Top-level directory for maintaining quantum's state")
252
253252
=== modified file 'quantum/common/utils.py'
--- quantum/common/utils.py 2011-06-05 00:45:36 +0000
+++ quantum/common/utils.py 2011-06-09 05:01:27 +0000
@@ -37,6 +37,7 @@
37TIME_FORMAT = "%Y-%m-%dT%H:%M:%SZ"37TIME_FORMAT = "%Y-%m-%dT%H:%M:%SZ"
38FLAGS = flags.FLAGS38FLAGS = flags.FLAGS
3939
40
40def int_from_bool_as_string(subject):41def int_from_bool_as_string(subject):
41 """42 """
42 Interpret a string as a boolean and return either 1 or 0.43 Interpret a string as a boolean and return either 1 or 0.
@@ -188,6 +189,7 @@
188def parse_isotime(timestr):189def parse_isotime(timestr):
189 return datetime.datetime.strptime(timestr, TIME_FORMAT)190 return datetime.datetime.strptime(timestr, TIME_FORMAT)
190191
192
191def getPluginFromConfig(file="config.ini"):193def getPluginFromConfig(file="config.ini"):
192 Config = ConfigParser.ConfigParser()194 Config = ConfigParser.ConfigParser()
193 Config.read(file)195 Config.read(file)
194196
=== modified file 'quantum/common/wsgi.py'
--- quantum/common/wsgi.py 2011-05-27 16:52:06 +0000
+++ quantum/common/wsgi.py 2011-06-09 05:01:27 +0000
@@ -40,6 +40,7 @@
4040
41LOG = logging.getLogger('quantum.common.wsgi')41LOG = logging.getLogger('quantum.common.wsgi')
4242
43
43class WritableLogger(object):44class WritableLogger(object):
44 """A thin wrapper that responds to `write` and logs."""45 """A thin wrapper that responds to `write` and logs."""
4546
@@ -126,7 +127,7 @@
126127
127 """128 """
128 parts = self.path.rsplit('.', 1)129 parts = self.path.rsplit('.', 1)
129 LOG.debug("Request parts:%s",parts)130 LOG.debug("Request parts:%s", parts)
130 if len(parts) > 1:131 if len(parts) > 1:
131 format = parts[1]132 format = parts[1]
132 if format in ['json', 'xml']:133 if format in ['json', 'xml']:
@@ -134,7 +135,7 @@
134135
135 ctypes = ['application/json', 'application/xml']136 ctypes = ['application/json', 'application/xml']
136 bm = self.accept.best_match(ctypes)137 bm = self.accept.best_match(ctypes)
137 LOG.debug("BM:%s",bm)138 LOG.debug("BM:%s", bm)
138 return bm or 'application/json'139 return bm or 'application/json'
139140
140 def get_content_type(self):141 def get_content_type(self):
@@ -336,21 +337,21 @@
336 arg_dict = req.environ['wsgiorg.routing_args'][1]337 arg_dict = req.environ['wsgiorg.routing_args'][1]
337 action = arg_dict['action']338 action = arg_dict['action']
338 method = getattr(self, action)339 method = getattr(self, action)
339 LOG.debug("ARG_DICT:%s",arg_dict)340 LOG.debug("ARG_DICT:%s", arg_dict)
340 LOG.debug("Action:%s",action)341 LOG.debug("Action:%s", action)
341 LOG.debug("Method:%s",method)342 LOG.debug("Method:%s", method)
342 LOG.debug("%s %s" % (req.method, req.url))343 LOG.debug("%s %s" % (req.method, req.url))
343 del arg_dict['controller']344 del arg_dict['controller']
344 del arg_dict['action']345 del arg_dict['action']
345 if 'format' in arg_dict:346 if 'format' in arg_dict:
346 del arg_dict['format']347 del arg_dict['format']
347 arg_dict['req'] = req348 arg_dict['request'] = req
348 result = method(**arg_dict)349 result = method(**arg_dict)
349350
350 if type(result) is dict:351 if type(result) is dict:
351 content_type = req.best_match_content_type()352 content_type = req.best_match_content_type()
352 LOG.debug("Content type:%s",content_type)353 LOG.debug("Content type:%s", content_type)
353 LOG.debug("Result:%s",result)354 LOG.debug("Result:%s", result)
354 default_xmlns = self.get_default_xmlns(req)355 default_xmlns = self.get_default_xmlns(req)
355 body = self._serialize(result, content_type, default_xmlns)356 body = self._serialize(result, content_type, default_xmlns)
356357
@@ -497,7 +498,7 @@
497 xmlns = metadata.get('xmlns', None)498 xmlns = metadata.get('xmlns', None)
498 if xmlns:499 if xmlns:
499 result.setAttribute('xmlns', xmlns)500 result.setAttribute('xmlns', xmlns)
500 LOG.debug("DATA:%s",data)501 LOG.debug("DATA:%s", data)
501 if type(data) is list:502 if type(data) is list:
502 LOG.debug("TYPE IS LIST")503 LOG.debug("TYPE IS LIST")
503 collections = metadata.get('list_collections', {})504 collections = metadata.get('list_collections', {})
@@ -538,8 +539,7 @@
538 result.appendChild(node)539 result.appendChild(node)
539 else:540 else:
540 # Type is atom541 # Type is atom
541 LOG.debug("TYPE IS ATOM:%s",data)542 LOG.debug("TYPE IS ATOM:%s", data)
542 node = doc.createTextNode(str(data))543 node = doc.createTextNode(str(data))
543 result.appendChild(node)544 result.appendChild(node)
544 return result545 return result
545
546546
=== modified file 'quantum/db/api.py'
--- quantum/db/api.py 2011-06-04 20:17:32 +0000
+++ quantum/db/api.py 2011-06-09 05:01:27 +0000
@@ -25,6 +25,7 @@
25_MAKER = None25_MAKER = None
26BASE = models.BASE26BASE = models.BASE
2727
28
28def configure_db(options):29def configure_db(options):
29 """30 """
30 Establish the database, create an engine if needed, and31 Establish the database, create an engine if needed, and
@@ -40,6 +41,7 @@
40 pool_recycle=3600)41 pool_recycle=3600)
41 register_models()42 register_models()
4243
44
43def get_session(autocommit=True, expire_on_commit=False):45def get_session(autocommit=True, expire_on_commit=False):
44 """Helper method to grab session"""46 """Helper method to grab session"""
45 global _MAKER, _ENGINE47 global _MAKER, _ENGINE
@@ -50,18 +52,21 @@
50 expire_on_commit=expire_on_commit)52 expire_on_commit=expire_on_commit)
51 return _MAKER()53 return _MAKER()
5254
55
53def register_models():56def register_models():
54 """Register Models and create properties"""57 """Register Models and create properties"""
55 global _ENGINE58 global _ENGINE
56 assert _ENGINE59 assert _ENGINE
57 BASE.metadata.create_all(_ENGINE)60 BASE.metadata.create_all(_ENGINE)
5861
62
59def unregister_models():63def unregister_models():
60 """Unregister Models, useful clearing out data before testing"""64 """Unregister Models, useful clearing out data before testing"""
61 global _ENGINE65 global _ENGINE
62 assert _ENGINE66 assert _ENGINE
63 BASE.metadata.drop_all(_ENGINE)67 BASE.metadata.drop_all(_ENGINE)
6468
69
65def network_create(tenant_id, name):70def network_create(tenant_id, name):
66 session = get_session()71 session = get_session()
67 net = None72 net = None
@@ -77,12 +82,14 @@
77 session.flush()82 session.flush()
78 return net83 return net
7984
85
80def network_list(tenant_id):86def network_list(tenant_id):
81 session = get_session()87 session = get_session()
82 return session.query(models.Network).\88 return session.query(models.Network).\
83 filter_by(tenant_id=tenant_id).\89 filter_by(tenant_id=tenant_id).\
84 all()90 all()
8591
92
86def network_get(net_id):93def network_get(net_id):
87 session = get_session()94 session = get_session()
88 try:95 try:
@@ -92,6 +99,7 @@
92 except exc.NoResultFound:99 except exc.NoResultFound:
93 raise Exception("No net found with id = %s" % net_id)100 raise Exception("No net found with id = %s" % net_id)
94101
102
95def network_rename(net_id, tenant_id, new_name):103def network_rename(net_id, tenant_id, new_name):
96 session = get_session()104 session = get_session()
97 try:105 try:
@@ -106,6 +114,7 @@
106 return net114 return net
107 raise Exception("A network with name \"%s\" already exists" % new_name)115 raise Exception("A network with name \"%s\" already exists" % new_name)
108116
117
109def network_destroy(net_id):118def network_destroy(net_id):
110 session = get_session()119 session = get_session()
111 try:120 try:
@@ -118,6 +127,7 @@
118 except exc.NoResultFound:127 except exc.NoResultFound:
119 raise Exception("No network found with id = %s" % net_id)128 raise Exception("No network found with id = %s" % net_id)
120129
130
121def port_create(net_id):131def port_create(net_id):
122 session = get_session()132 session = get_session()
123 with session.begin():133 with session.begin():
@@ -126,12 +136,14 @@
126 session.flush()136 session.flush()
127 return port137 return port
128138
139
129def port_list(net_id):140def port_list(net_id):
130 session = get_session()141 session = get_session()
131 return session.query(models.Port).\142 return session.query(models.Port).\
132 filter_by(network_id=net_id).\143 filter_by(network_id=net_id).\
133 all()144 all()
134145
146
135def port_get(port_id):147def port_get(port_id):
136 session = get_session()148 session = get_session()
137 try:149 try:
@@ -141,6 +153,7 @@
141 except exc.NoResultFound:153 except exc.NoResultFound:
142 raise Exception("No port found with id = %s " % port_id)154 raise Exception("No port found with id = %s " % port_id)
143155
156
144def port_set_attachment(port_id, new_interface_id):157def port_set_attachment(port_id, new_interface_id):
145 session = get_session()158 session = get_session()
146 ports = None159 ports = None
@@ -157,7 +170,9 @@
157 session.flush()170 session.flush()
158 return port171 return port
159 else:172 else:
160 raise Exception("Port with attachment \"%s\" already exists" % (new_interface_id))173 raise Exception("Port with attachment \"%s\" already exists"
174 % (new_interface_id))
175
161176
162def port_destroy(port_id):177def port_destroy(port_id):
163 session = get_session()178 session = get_session()
@@ -170,4 +185,3 @@
170 return port185 return port
171 except exc.NoResultFound:186 except exc.NoResultFound:
172 raise Exception("No port found with id = %s " % port_id)187 raise Exception("No port found with id = %s " % port_id)
173
174188
=== modified file 'quantum/db/models.py'
--- quantum/db/models.py 2011-06-04 03:55:26 +0000
+++ quantum/db/models.py 2011-06-09 05:01:27 +0000
@@ -25,12 +25,14 @@
2525
26BASE = declarative_base()26BASE = declarative_base()
2727
28
28class Port(BASE):29class Port(BASE):
29 """Represents a port on a quantum network"""30 """Represents a port on a quantum network"""
30 __tablename__ = 'ports'31 __tablename__ = 'ports'
3132
32 uuid = Column(String(255), primary_key=True)33 uuid = Column(String(255), primary_key=True)
33 network_id = Column(String(255), ForeignKey("networks.uuid"), nullable=False)34 network_id = Column(String(255), ForeignKey("networks.uuid"),
35 nullable=False)
34 interface_id = Column(String(255))36 interface_id = Column(String(255))
3537
36 def __init__(self, network_id):38 def __init__(self, network_id):
@@ -38,7 +40,9 @@
38 self.network_id = network_id40 self.network_id = network_id
3941
40 def __repr__(self):42 def __repr__(self):
41 return "<Port(%s,%s,%s)>" % (self.uuid, self.network_id, self.interface_id)43 return "<Port(%s,%s,%s)>" % (self.uuid, self.network_id,
44 self.interface_id)
45
4246
43class Network(BASE):47class Network(BASE):
44 """Represents a quantum network"""48 """Represents a quantum network"""
@@ -56,4 +60,4 @@
5660
57 def __repr__(self):61 def __repr__(self):
58 return "<Network(%s,%s,%s)>" % \62 return "<Network(%s,%s,%s)>" % \
59 (self.uuid,self.name,self.tenant_id)63 (self.uuid, self.name, self.tenant_id)
6064
=== modified file 'quantum/manager.py'
--- quantum/manager.py 2011-06-07 06:09:53 +0000
+++ quantum/manager.py 2011-06-09 05:01:27 +0000
@@ -18,12 +18,14 @@
1818
1919
20"""20"""
21Quantum's Manager class is responsible for parsing a config file and instantiating the correct21Quantum's Manager class is responsible for parsing a config file and
22plugin that concretely implement quantum_plugin_base class22instantiating the correct plugin that concretely implement quantum_plugin_base
23class.
2324
24The caller should make sure that QuantumManager is a singleton.25The caller should make sure that QuantumManager is a singleton.
25"""26"""
26import gettext27import gettext
28import os
27gettext.install('quantum', unicode=1)29gettext.install('quantum', unicode=1)
2830
29import os31import os
@@ -33,16 +35,19 @@
3335
34CONFIG_FILE = "plugins.ini"36CONFIG_FILE = "plugins.ini"
3537
38
36def find_config(basepath):39def find_config(basepath):
37 for root, dirs, files in os.walk(basepath):40 for root, dirs, files in os.walk(basepath):
38 if CONFIG_FILE in files:41 if CONFIG_FILE in files:
39 return os.path.join(root, CONFIG_FILE)42 return os.path.join(root, CONFIG_FILE)
40 return None43 return None
4144
45
42class QuantumManager(object):46class QuantumManager(object):
43 def __init__(self, config=None):47 def __init__(self, config=None):
44 if config == None:48 if config == None:
45 self.configuration_file = find_config(os.path.abspath(os.path.dirname(__file__)))49 self.configuration_file = find_config(
50 os.path.abspath(os.path.dirname(__file__)))
46 else:51 else:
47 self.configuration_file = config52 self.configuration_file = config
48 plugin_location = utils.getPluginFromConfig(self.configuration_file)53 plugin_location = utils.getPluginFromConfig(self.configuration_file)
@@ -58,4 +63,3 @@
5863
59 def get_manager(self):64 def get_manager(self):
60 return self.plugin65 return self.plugin
61
6266
=== modified file 'quantum/plugins/SamplePlugin.py'
--- quantum/plugins/SamplePlugin.py 2011-06-07 05:48:57 +0000
+++ quantum/plugins/SamplePlugin.py 2011-06-09 05:01:27 +0000
@@ -17,33 +17,32 @@
1717
18from quantum.common import exceptions as exc18from quantum.common import exceptions as exc
1919
20
20class QuantumEchoPlugin(object):21class QuantumEchoPlugin(object):
2122
22 """23 """
23 QuantumEchoPlugin is a demo plugin that doesn't24 QuantumEchoPlugin is a demo plugin that doesn't
24 do anything but demonstrated the concept of a25 do anything but demonstrated the concept of a
25 concrete Quantum Plugin. Any call to this plugin26 concrete Quantum Plugin. Any call to this plugin
26 will result in just a "print" to std. out with 27 will result in just a "print" to std. out with
27 the name of the method that was called.28 the name of the method that was called.
28 """29 """
29 30
30 def get_all_networks(self, tenant_id):31 def get_all_networks(self, tenant_id):
31 """32 """
32 Returns a dictionary containing all33 Returns a dictionary containing all
33 <network_uuid, network_name> for34 <network_uuid, network_name> for
34 the specified tenant. 35 the specified tenant.
35 """36 """
36 print("get_all_networks() called\n")37 print("get_all_networks() called\n")
37 38
38
39 def create_network(self, tenant_id, net_name):39 def create_network(self, tenant_id, net_name):
40 """40 """
41 Creates a new Virtual Network, and assigns it41 Creates a new Virtual Network, and assigns it
42 a symbolic name.42 a symbolic name.
43 """43 """
44 print("create_network() called\n")44 print("create_network() called\n")
45 45
46
47 def delete_network(self, tenant_id, net_id):46 def delete_network(self, tenant_id, net_id):
48 """47 """
49 Deletes the network with the specified network identifier48 Deletes the network with the specified network identifier
@@ -51,38 +50,33 @@
51 """50 """
52 print("delete_network() called\n")51 print("delete_network() called\n")
5352
54
55 def get_network_details(self, tenant_id, net_id):53 def get_network_details(self, tenant_id, net_id):
56 """54 """
57 Deletes the Virtual Network belonging to a the55 Deletes the Virtual Network belonging to a the
58 spec56 spec
59 """57 """
60 print("get_network_details() called\n")58 print("get_network_details() called\n")
61 59
62
63 def rename_network(self, tenant_id, net_id, new_name):60 def rename_network(self, tenant_id, net_id, new_name):
64 """61 """
65 Updates the symbolic name belonging to a particular62 Updates the symbolic name belonging to a particular
66 Virtual Network.63 Virtual Network.
67 """64 """
68 print("rename_network() called\n")65 print("rename_network() called\n")
69 66
70
71 def get_all_ports(self, tenant_id, net_id):67 def get_all_ports(self, tenant_id, net_id):
72 """68 """
73 Retrieves all port identifiers belonging to the69 Retrieves all port identifiers belonging to the
74 specified Virtual Network.70 specified Virtual Network.
75 """71 """
76 print("get_all_ports() called\n")72 print("get_all_ports() called\n")
77 73
78
79 def create_port(self, tenant_id, net_id):74 def create_port(self, tenant_id, net_id):
80 """75 """
81 Creates a port on the specified Virtual Network.76 Creates a port on the specified Virtual Network.
82 """77 """
83 print("create_port() called\n")78 print("create_port() called\n")
84 79
85
86 def delete_port(self, tenant_id, net_id, port_id):80 def delete_port(self, tenant_id, net_id, port_id):
87 """81 """
88 Deletes a port on a specified Virtual Network,82 Deletes a port on a specified Virtual Network,
@@ -97,24 +91,21 @@
97 Updates the state of a port on the specified Virtual Network.91 Updates the state of a port on the specified Virtual Network.
98 """92 """
99 print("update_port() called\n")93 print("update_port() called\n")
100 94
101
102 def get_port_details(self, tenant_id, net_id, port_id):95 def get_port_details(self, tenant_id, net_id, port_id):
103 """96 """
104 This method allows the user to retrieve a remote interface97 This method allows the user to retrieve a remote interface
105 that is attached to this particular port.98 that is attached to this particular port.
106 """99 """
107 print("get_port_details() called\n")100 print("get_port_details() called\n")
108 101
109
110 def plug_interface(self, tenant_id, net_id, port_id, remote_interface_id):102 def plug_interface(self, tenant_id, net_id, port_id, remote_interface_id):
111 """103 """
112 Attaches a remote interface to the specified port on the104 Attaches a remote interface to the specified port on the
113 specified Virtual Network.105 specified Virtual Network.
114 """106 """
115 print("plug_interface() called\n")107 print("plug_interface() called\n")
116 108
117
118 def unplug_interface(self, tenant_id, net_id, port_id):109 def unplug_interface(self, tenant_id, net_id, port_id):
119 """110 """
120 Detaches a remote interface from the specified port on the111 Detaches a remote interface from the specified port on the
@@ -130,18 +121,17 @@
130 hard-coded data structures to aid in quantum121 hard-coded data structures to aid in quantum
131 client/cli development122 client/cli development
132 """123 """
133 124
134 def get_all_networks(self, tenant_id):125 def get_all_networks(self, tenant_id):
135 """126 """
136 Returns a dictionary containing all127 Returns a dictionary containing all
137 <network_uuid, network_name> for128 <network_uuid, network_name> for
138 the specified tenant. 129 the specified tenant.
139 """130 """
140 nets = {"001": "lNet1", "002": "lNet2" , "003": "lNet3"}131 nets = {"001": "lNet1", "002": "lNet2", "003": "lNet3"}
141 print("get_all_networks() called\n")132 print("get_all_networks() called\n")
142 return nets133 return nets
143 134
144
145 def create_network(self, tenant_id, net_name):135 def create_network(self, tenant_id, net_name):
146 """136 """
147 Creates a new Virtual Network, and assigns it137 Creates a new Virtual Network, and assigns it
@@ -150,8 +140,7 @@
150 print("create_network() called\n")140 print("create_network() called\n")
151 # return network_id of the created network141 # return network_id of the created network
152 return 101142 return 101
153 143
154
155 def delete_network(self, tenant_id, net_id):144 def delete_network(self, tenant_id, net_id):
156 """145 """
157 Deletes the network with the specified network identifier146 Deletes the network with the specified network identifier
@@ -159,7 +148,6 @@
159 """148 """
160 print("delete_network() called\n")149 print("delete_network() called\n")
161150
162
163 def get_network_details(self, tenant_id, net_id):151 def get_network_details(self, tenant_id, net_id):
164 """152 """
165 retrieved a list of all the remote vifs that153 retrieved a list of all the remote vifs that
@@ -168,16 +156,14 @@
168 print("get_network_details() called\n")156 print("get_network_details() called\n")
169 vifs_on_net = ["/tenant1/networks/net_id/portid/vif2.0"]157 vifs_on_net = ["/tenant1/networks/net_id/portid/vif2.0"]
170 return vifs_on_net158 return vifs_on_net
171 159
172
173 def rename_network(self, tenant_id, net_id, new_name):160 def rename_network(self, tenant_id, net_id, new_name):
174 """161 """
175 Updates the symbolic name belonging to a particular162 Updates the symbolic name belonging to a particular
176 Virtual Network.163 Virtual Network.
177 """164 """
178 print("rename_network() called\n")165 print("rename_network() called\n")
179 166
180
181 def get_all_ports(self, tenant_id, net_id):167 def get_all_ports(self, tenant_id, net_id):
182 """168 """
183 Retrieves all port identifiers belonging to the169 Retrieves all port identifiers belonging to the
@@ -186,8 +172,7 @@
186 print("get_all_ports() called\n")172 print("get_all_ports() called\n")
187 port_ids_on_net = ["2", "3", "4"]173 port_ids_on_net = ["2", "3", "4"]
188 return port_ids_on_net174 return port_ids_on_net
189 175
190
191 def create_port(self, tenant_id, net_id):176 def create_port(self, tenant_id, net_id):
192 """177 """
193 Creates a port on the specified Virtual Network.178 Creates a port on the specified Virtual Network.
@@ -201,8 +186,7 @@
201 Updates the state of a port on the specified Virtual Network.186 Updates the state of a port on the specified Virtual Network.
202 """187 """
203 print("update_port() called\n")188 print("update_port() called\n")
204 189
205
206 def delete_port(self, tenant_id, net_id, port_id):190 def delete_port(self, tenant_id, net_id, port_id):
207 """191 """
208 Deletes a port on a specified Virtual Network,192 Deletes a port on a specified Virtual Network,
@@ -211,8 +195,7 @@
211 is deleted.195 is deleted.
212 """196 """
213 print("delete_port() called\n")197 print("delete_port() called\n")
214 198
215
216 def get_port_details(self, tenant_id, net_id, port_id):199 def get_port_details(self, tenant_id, net_id, port_id):
217 """200 """
218 This method allows the user to retrieve a remote interface201 This method allows the user to retrieve a remote interface
@@ -221,24 +204,22 @@
221 print("get_port_details() called\n")204 print("get_port_details() called\n")
222 #returns the remote interface UUID205 #returns the remote interface UUID
223 return "/tenant1/networks/net_id/portid/vif2.1"206 return "/tenant1/networks/net_id/portid/vif2.1"
224 207
225
226 def plug_interface(self, tenant_id, net_id, port_id, remote_interface_id):208 def plug_interface(self, tenant_id, net_id, port_id, remote_interface_id):
227 """209 """
228 Attaches a remote interface to the specified port on the210 Attaches a remote interface to the specified port on the
229 specified Virtual Network.211 specified Virtual Network.
230 """212 """
231 print("plug_interface() called\n")213 print("plug_interface() called\n")
232 214
233
234 def unplug_interface(self, tenant_id, net_id, port_id):215 def unplug_interface(self, tenant_id, net_id, port_id):
235 """216 """
236 Detaches a remote interface from the specified port on the217 Detaches a remote interface from the specified port on the
237 specified Virtual Network.218 specified Virtual Network.
238 """219 """
239 print("unplug_interface() called\n")220 print("unplug_interface() called\n")
240 221
241 222
242class FakePlugin(object):223class FakePlugin(object):
243 """224 """
244 FakePlugin is a demo plugin that provides225 FakePlugin is a demo plugin that provides
@@ -248,72 +229,66 @@
248229
249 #static data for networks and ports230 #static data for networks and ports
250 _port_dict_1 = {231 _port_dict_1 = {
251 1 : {'port-id': 1, 232 1: {'port-id': 1,
252 'port-state': 'DOWN',233 'port-state': 'DOWN',
253 'attachment': None},234 'attachment': None},
254 2 : {'port-id': 2, 235 2: {'port-id': 2,
255 'port-state':'UP',236 'port-state': 'UP',
256 'attachment': None}237 'attachment': None}}
257 }
258 _port_dict_2 = {238 _port_dict_2 = {
259 1 : {'port-id': 1, 239 1: {'port-id': 1,
260 'port-state': 'UP',240 'port-state': 'UP',
261 'attachment': 'SomeFormOfVIFID'},241 'attachment': 'SomeFormOfVIFID'},
262 2 : {'port-id': 2, 242 2: {'port-id': 2,
263 'port-state':'DOWN',243 'port-state': 'DOWN',
264 'attachment': None}244 'attachment': None}}
265 } 245 _networks = {'001':
266 _networks={'001':
267 {246 {
268 'net-id':'001',247 'net-id': '001',
269 'net-name':'pippotest',248 'net-name': 'pippotest',
270 'net-ports': _port_dict_1249 'net-ports': _port_dict_1},
271 },
272 '002':250 '002':
273 {251 {
274 'net-id':'002',252 'net-id': '002',
275 'net-name':'cicciotest',253 'net-name': 'cicciotest',
276 'net-ports': _port_dict_2 254 'net-ports': _port_dict_2}}
277 }}255
278
279
280 def __init__(self):256 def __init__(self):
281 FakePlugin._net_counter=len(FakePlugin._networks)257 FakePlugin._net_counter = len(FakePlugin._networks)
282 258
283 def _get_network(self, tenant_id, network_id):259 def _get_network(self, tenant_id, network_id):
284 network = FakePlugin._networks.get(network_id)260 network = FakePlugin._networks.get(network_id)
285 if not network:261 if not network:
286 raise exc.NetworkNotFound(net_id=network_id)262 raise exc.NetworkNotFound(net_id=network_id)
287 return network263 return network
288264
289
290 def _get_port(self, tenant_id, network_id, port_id):265 def _get_port(self, tenant_id, network_id, port_id):
291 net = self._get_network(tenant_id, network_id)266 net = self._get_network(tenant_id, network_id)
292 port = net['net-ports'].get(int(port_id))267 port = net['net-ports'].get(int(port_id))
293 if not port:268 if not port:
294 raise exc.PortNotFound(net_id=network_id, port_id=port_id)269 raise exc.PortNotFound(net_id=network_id, port_id=port_id)
295 return port270 return port
296 271
297 def _validate_port_state(self, port_state):272 def _validate_port_state(self, port_state):
298 if port_state.upper() not in ('UP','DOWN'):273 if port_state.upper() not in ('UP', 'DOWN'):
299 raise exc.StateInvalid(port_state=port_state)274 raise exc.StateInvalid(port_state=port_state)
300 return True275 return True
301 276
302 def _validate_attachment(self, tenant_id, network_id, port_id,277 def _validate_attachment(self, tenant_id, network_id, port_id,
303 remote_interface_id):278 remote_interface_id):
304 network = self._get_network(tenant_id, network_id)279 network = self._get_network(tenant_id, network_id)
305 for port in network['net-ports'].values():280 for port in network['net-ports'].values():
306 if port['attachment'] == remote_interface_id:281 if port['attachment'] == remote_interface_id:
307 raise exc.AlreadyAttached(net_id = network_id,282 raise exc.AlreadyAttached(net_id=network_id,
308 port_id = port_id,283 port_id=port_id,
309 att_id = port['attachment'],284 att_id=port['attachment'],
310 att_port_id = port['port-id'])285 att_port_id=port['port-id'])
311 286
312 def get_all_networks(self, tenant_id):287 def get_all_networks(self, tenant_id):
313 """288 """
314 Returns a dictionary containing all289 Returns a dictionary containing all
315 <network_uuid, network_name> for290 <network_uuid, network_name> for
316 the specified tenant. 291 the specified tenant.
317 """292 """
318 print("get_all_networks() called\n")293 print("get_all_networks() called\n")
319 return FakePlugin._networks.values()294 return FakePlugin._networks.values()
@@ -333,16 +308,16 @@
333 """308 """
334 print("create_network() called\n")309 print("create_network() called\n")
335 FakePlugin._net_counter += 1310 FakePlugin._net_counter += 1
336 new_net_id=("0" * (3 - len(str(FakePlugin._net_counter)))) + \311 new_net_id = ("0" * (3 - len(str(FakePlugin._net_counter)))) + \
337 str(FakePlugin._net_counter)312 str(FakePlugin._net_counter)
338 print new_net_id313 print new_net_id
339 new_net_dict={'net-id':new_net_id,314 new_net_dict = {'net-id': new_net_id,
340 'net-name':net_name,315 'net-name': net_name,
341 'net-ports': {}}316 'net-ports': {}}
342 FakePlugin._networks[new_net_id]=new_net_dict317 FakePlugin._networks[new_net_id] = new_net_dict
343 # return network_id of the created network318 # return network_id of the created network
344 return new_net_dict319 return new_net_dict
345 320
346 def delete_network(self, tenant_id, net_id):321 def delete_network(self, tenant_id, net_id):
347 """322 """
348 Deletes the network with the specified network identifier323 Deletes the network with the specified network identifier
@@ -360,7 +335,7 @@
360 return net335 return net
361 # Network not found336 # Network not found
362 raise exc.NetworkNotFound(net_id=net_id)337 raise exc.NetworkNotFound(net_id=net_id)
363 338
364 def rename_network(self, tenant_id, net_id, new_name):339 def rename_network(self, tenant_id, net_id, new_name):
365 """340 """
366 Updates the symbolic name belonging to a particular341 Updates the symbolic name belonging to a particular
@@ -368,7 +343,7 @@
368 """343 """
369 print("rename_network() called\n")344 print("rename_network() called\n")
370 net = self._get_network(tenant_id, net_id)345 net = self._get_network(tenant_id, net_id)
371 net['net-name']=new_name 346 net['net-name'] = new_name
372 return net347 return net
373348
374 def get_all_ports(self, tenant_id, net_id):349 def get_all_ports(self, tenant_id, net_id):
@@ -388,7 +363,7 @@
388 """363 """
389 print("get_port_details() called\n")364 print("get_port_details() called\n")
390 return self._get_port(tenant_id, net_id, port_id)365 return self._get_port(tenant_id, net_id, port_id)
391 366
392 def create_port(self, tenant_id, net_id, port_state=None):367 def create_port(self, tenant_id, net_id, port_state=None):
393 """368 """
394 Creates a port on the specified Virtual Network.369 Creates a port on the specified Virtual Network.
@@ -396,15 +371,15 @@
396 print("create_port() called\n")371 print("create_port() called\n")
397 net = self._get_network(tenant_id, net_id)372 net = self._get_network(tenant_id, net_id)
398 # check port state373 # check port state
399 # TODO(salvatore-orlando): Validate port state in API? 374 # TODO(salvatore-orlando): Validate port state in API?
400 self._validate_port_state(port_state)375 self._validate_port_state(port_state)
401 ports = net['net-ports']376 ports = net['net-ports']
402 new_port_id = max(ports.keys())+1377 new_port_id = max(ports.keys()) + 1
403 new_port_dict = {'port-id':new_port_id,378 new_port_dict = {'port-id': new_port_id,
404 'port-state': port_state,379 'port-state': port_state,
405 'attachment': None}380 'attachment': None}
406 ports[new_port_id] = new_port_dict381 ports[new_port_id] = new_port_dict
407 return new_port_dict 382 return new_port_dict
408383
409 def update_port(self, tenant_id, net_id, port_id, port_state):384 def update_port(self, tenant_id, net_id, port_id, port_state):
410 """385 """
@@ -414,8 +389,8 @@
414 port = self._get_port(tenant_id, net_id, port_id)389 port = self._get_port(tenant_id, net_id, port_id)
415 self._validate_port_state(port_state)390 self._validate_port_state(port_state)
416 port['port-state'] = port_state391 port['port-state'] = port_state
417 return port 392 return port
418 393
419 def delete_port(self, tenant_id, net_id, port_id):394 def delete_port(self, tenant_id, net_id, port_id):
420 """395 """
421 Deletes a port on a specified Virtual Network,396 Deletes a port on a specified Virtual Network,
@@ -427,14 +402,13 @@
427 net = self._get_network(tenant_id, net_id)402 net = self._get_network(tenant_id, net_id)
428 port = self._get_port(tenant_id, net_id, port_id)403 port = self._get_port(tenant_id, net_id, port_id)
429 if port['attachment']:404 if port['attachment']:
430 raise exc.PortInUse(net_id=net_id,port_id=port_id,405 raise exc.PortInUse(net_id=net_id, port_id=port_id,
431 att_id=port['attachment'])406 att_id=port['attachment'])
432 try:407 try:
433 net['net-ports'].pop(int(port_id))408 net['net-ports'].pop(int(port_id))
434 except KeyError: 409 except KeyError:
435 raise exc.PortNotFound(net_id=net_id, port_id=port_id)410 raise exc.PortNotFound(net_id=net_id, port_id=port_id)
436411
437
438 def plug_interface(self, tenant_id, net_id, port_id, remote_interface_id):412 def plug_interface(self, tenant_id, net_id, port_id, remote_interface_id):
439 """413 """
440 Attaches a remote interface to the specified port on the414 Attaches a remote interface to the specified port on the
@@ -446,10 +420,10 @@
446 remote_interface_id)420 remote_interface_id)
447 port = self._get_port(tenant_id, net_id, port_id)421 port = self._get_port(tenant_id, net_id, port_id)
448 if port['attachment']:422 if port['attachment']:
449 raise exc.PortInUse(net_id=net_id,port_id=port_id,423 raise exc.PortInUse(net_id=net_id, port_id=port_id,
450 att_id=port['attachment'])424 att_id=port['attachment'])
451 port['attachment'] = remote_interface_id425 port['attachment'] = remote_interface_id
452 426
453 def unplug_interface(self, tenant_id, net_id, port_id):427 def unplug_interface(self, tenant_id, net_id, port_id):
454 """428 """
455 Detaches a remote interface from the specified port on the429 Detaches a remote interface from the specified port on the
@@ -460,5 +434,3 @@
460 # TODO(salvatore-orlando):434 # TODO(salvatore-orlando):
461 # Should unplug on port without attachment raise an Error?435 # Should unplug on port without attachment raise an Error?
462 port['attachment'] = None436 port['attachment'] = None
463
464
465\ No newline at end of file437\ No newline at end of file
466438
=== modified file 'quantum/plugins/__init__.py'
--- quantum/plugins/__init__.py 2011-05-13 21:23:37 +0000
+++ quantum/plugins/__init__.py 2011-06-09 05:01:27 +0000
@@ -13,4 +13,4 @@
13# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the13# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
14# License for the specific language governing permissions and limitations14# License for the specific language governing permissions and limitations
15# under the License.15# under the License.
16# @author: Somik Behera, Nicira Networks, Inc.
17\ No newline at end of file16\ No newline at end of file
17# @author: Somik Behera, Nicira Networks, Inc.
1818
=== modified file 'quantum/plugins/openvswitch/agent/ovs_quantum_agent.py'
--- quantum/plugins/openvswitch/agent/ovs_quantum_agent.py 2011-06-04 03:59:49 +0000
+++ quantum/plugins/openvswitch/agent/ovs_quantum_agent.py 2011-06-09 05:01:27 +0000
@@ -28,6 +28,7 @@
28from optparse import OptionParser28from optparse import OptionParser
29from subprocess import *29from subprocess import *
3030
31
31# A class to represent a VIF (i.e., a port that has 'iface-id' and 'vif-mac'32# A class to represent a VIF (i.e., a port that has 'iface-id' and 'vif-mac'
32# attributes set).33# attributes set).
33class VifPort:34class VifPort:
@@ -37,11 +38,13 @@
37 self.vif_id = vif_id38 self.vif_id = vif_id
38 self.vif_mac = vif_mac39 self.vif_mac = vif_mac
39 self.switch = switch40 self.switch = switch
41
40 def __str__(self):42 def __str__(self):
41 return "iface-id=" + self.vif_id + ", vif_mac=" + \43 return "iface-id=" + self.vif_id + ", vif_mac=" + \
42 self.vif_mac + ", port_name=" + self.port_name + \44 self.vif_mac + ", port_name=" + self.port_name + \
43 ", ofport=" + self.ofport + ", bridge name = " + self.switch.br_name45 ", ofport=" + self.ofport + ", bridge name = " + self.switch.br_name
4446
47
45class OVSBridge:48class OVSBridge:
46 def __init__(self, br_name):49 def __init__(self, br_name):
47 self.br_name = br_name50 self.br_name = br_name
@@ -51,27 +54,27 @@
51 return Popen(args, stdout=PIPE).communicate()[0]54 return Popen(args, stdout=PIPE).communicate()[0]
5255
53 def run_vsctl(self, args):56 def run_vsctl(self, args):
54 full_args = ["ovs-vsctl" ] + args57 full_args = ["ovs-vsctl"] + args
55 return self.run_cmd(full_args)58 return self.run_cmd(full_args)
5659
57 def reset_bridge(self):60 def reset_bridge(self):
58 self.run_vsctl([ "--" , "--if-exists", "del-br", self.br_name])61 self.run_vsctl(["--", "--if-exists", "del-br", self.br_name])
59 self.run_vsctl(["add-br", self.br_name])62 self.run_vsctl(["add-br", self.br_name])
6063
61 def delete_port(self, port_name):64 def delete_port(self, port_name):
62 self.run_vsctl([ "--" , "--if-exists", "del-port", self.br_name,65 self.run_vsctl(["--", "--if-exists", "del-port", self.br_name,
63 port_name])66 port_name])
6467
65 def set_db_attribute(self, table_name, record, column, value):68 def set_db_attribute(self, table_name, record, column, value):
66 args = [ "set", table_name, record, "%s=%s" % (column,value) ]69 args = ["set", table_name, record, "%s=%s" % (column, value)]
67 self.run_vsctl(args)70 self.run_vsctl(args)
6871
69 def clear_db_attribute(self, table_name,record, column):72 def clear_db_attribute(self, table_name, record, column):
70 args = [ "clear", table_name, record, column ]73 args = ["clear", table_name, record, column]
71 self.run_vsctl(args)74 self.run_vsctl(args)
7275
73 def run_ofctl(self, cmd, args):76 def run_ofctl(self, cmd, args):
74 full_args = ["ovs-ofctl", cmd, self.br_name ] + args77 full_args = ["ovs-ofctl", cmd, self.br_name] + args
75 return self.run_cmd(full_args)78 return self.run_cmd(full_args)
7679
77 def remove_all_flows(self):80 def remove_all_flows(self):
@@ -80,7 +83,7 @@
80 def get_port_ofport(self, port_name):83 def get_port_ofport(self, port_name):
81 return self.db_get_val("Interface", port_name, "ofport")84 return self.db_get_val("Interface", port_name, "ofport")
8285
83 def add_flow(self,**dict):86 def add_flow(self, **dict):
84 if "actions" not in dict:87 if "actions" not in dict:
85 raise Exception("must specify one or more actions")88 raise Exception("must specify one or more actions")
86 if "priority" not in dict:89 if "priority" not in dict:
@@ -90,9 +93,9 @@
90 if "match" in dict:93 if "match" in dict:
91 flow_str += "," + dict["match"]94 flow_str += "," + dict["match"]
92 flow_str += ",actions=%s" % (dict["actions"])95 flow_str += ",actions=%s" % (dict["actions"])
93 self.run_ofctl("add-flow", [ flow_str ] )96 self.run_ofctl("add-flow", [flow_str])
9497
95 def delete_flows(self,**dict):98 def delete_flows(self, **dict):
96 all_args = []99 all_args = []
97 if "priority" in dict:100 if "priority" in dict:
98 all_args.append("priority=%s" % dict["priority"])101 all_args.append("priority=%s" % dict["priority"])
@@ -101,14 +104,14 @@
101 if "actions" in dict:104 if "actions" in dict:
102 all_args.append("actions=%s" % (dict["actions"]))105 all_args.append("actions=%s" % (dict["actions"]))
103 flow_str = ",".join(all_args)106 flow_str = ",".join(all_args)
104 self.run_ofctl("del-flows", [ flow_str ] )107 self.run_ofctl("del-flows", [flow_str])
105108
106 def db_get_map(self, table, record, column):109 def db_get_map(self, table, record, column):
107 str = self.run_vsctl([ "get" , table, record, column ]).rstrip("\n\r")110 str = self.run_vsctl(["get", table, record, column]).rstrip("\n\r")
108 return self.db_str_to_map(str)111 return self.db_str_to_map(str)
109112
110 def db_get_val(self, table, record, column):113 def db_get_val(self, table, record, column):
111 return self.run_vsctl([ "get" , table, record, column ]).rstrip("\n\r")114 return self.run_vsctl(["get", table, record, column]).rstrip("\n\r")
112115
113 def db_str_to_map(self, full_str):116 def db_str_to_map(self, full_str):
114 list = full_str.strip("{}").split(", ")117 list = full_str.strip("{}").split(", ")
@@ -121,7 +124,7 @@
121 return ret124 return ret
122125
123 def get_port_name_list(self):126 def get_port_name_list(self):
124 res = self.run_vsctl([ "list-ports", self.br_name])127 res = self.run_vsctl(["list-ports", self.br_name])
125 return res.split("\n")[0:-1]128 return res.split("\n")[0:-1]
126129
127 def get_port_stats(self, port_name):130 def get_port_stats(self, port_name):
@@ -132,47 +135,53 @@
132 edge_ports = []135 edge_ports = []
133 port_names = self.get_port_name_list()136 port_names = self.get_port_name_list()
134 for name in port_names:137 for name in port_names:
135 external_ids = self.db_get_map("Interface",name,"external_ids")138 external_ids = self.db_get_map("Interface", name, "external_ids")
136 if "iface-id" in external_ids and "attached-mac" in external_ids:139 if "iface-id" in external_ids and "attached-mac" in external_ids:
137 ofport = self.db_get_val("Interface",name,"ofport")140 ofport = self.db_get_val("Interface", name, "ofport")
138 p = VifPort(name, ofport, external_ids["iface-id"],141 p = VifPort(name, ofport, external_ids["iface-id"],
139 external_ids["attached-mac"], self)142 external_ids["attached-mac"], self)
140 edge_ports.append(p)143 edge_ports.append(p)
141 else:144 else:
142 # iface-id might not be set. See if we can figure it out and145 # iface-id might not be set. See if we can figure it out and
143 # set it here.146 # set it here.
144 external_ids = self.db_get_map("Interface",name,"external_ids")147 external_ids = self.db_get_map("Interface", name,
148 "external_ids")
145 if "attached-mac" not in external_ids:149 if "attached-mac" not in external_ids:
146 continue150 continue
147 vif_uuid = external_ids.get("xs-vif-uuid", "")151 vif_uuid = external_ids.get("xs-vif-uuid", "")
148 if len(vif_uuid) == 0:152 if len(vif_uuid) == 0:
149 continue153 continue
150 LOG.debug("iface-id not set, got vif-uuid: %s" % vif_uuid)154 LOG.debug("iface-id not set, got vif-uuid: %s" % vif_uuid)
151 res = os.popen("xe vif-param-get param-name=other-config uuid=%s | grep nicira-iface-id | awk '{print $2}'" % vif_uuid).readline()155 res = os.popen("xe vif-param-get param-name=other-config "
156 "uuid=%s | grep nicira-iface-id | "
157 "awk '{print $2}'"
158 % vif_uuid).readline()
152 res = res.strip()159 res = res.strip()
153 if len(res) == 0:160 if len(res) == 0:
154 continue161 continue
155 external_ids["iface-id"] = res162 external_ids["iface-id"] = res
156 LOG.info("Setting interface \"%s\" iface-id to \"%s\"" % (name, res))163 LOG.info("Setting interface \"%s\" iface-id to \"%s\""
164 % (name, res))
157 self.set_db_attribute("Interface", name,165 self.set_db_attribute("Interface", name,
158 "external-ids:iface-id", res)166 "external-ids:iface-id", res)
159 ofport = self.db_get_val("Interface",name,"ofport")167 ofport = self.db_get_val("Interface", name, "ofport")
160 p = VifPort(name, ofport, external_ids["iface-id"],168 p = VifPort(name, ofport, external_ids["iface-id"],
161 external_ids["attached-mac"], self)169 external_ids["attached-mac"], self)
162 edge_ports.append(p)170 edge_ports.append(p)
163 return edge_ports171 return edge_ports
164172
173
165class OVSNaaSPlugin:174class OVSNaaSPlugin:
166 def __init__(self, integ_br):175 def __init__(self, integ_br):
167 self.setup_integration_br(integ_br)176 self.setup_integration_br(integ_br)
168177
169 def port_bound(self, port, vlan_id):178 def port_bound(self, port, vlan_id):
170 self.int_br.set_db_attribute("Port", port.port_name,"tag",179 self.int_br.set_db_attribute("Port", port.port_name, "tag",
171 str(vlan_id))180 str(vlan_id))
172181
173 def port_unbound(self, port, still_exists):182 def port_unbound(self, port, still_exists):
174 if still_exists:183 if still_exists:
175 self.int_br.clear_db_attribute("Port", port.port_name,"tag")184 self.int_br.clear_db_attribute("Port", port.port_name, "tag")
176185
177 def setup_integration_br(self, integ_br):186 def setup_integration_br(self, integ_br):
178 self.int_br = OVSBridge(integ_br)187 self.int_br = OVSBridge(integ_br)
@@ -182,7 +191,8 @@
182 # switch all other traffic using L2 learning191 # switch all other traffic using L2 learning
183 self.int_br.add_flow(priority=1, actions="normal")192 self.int_br.add_flow(priority=1, actions="normal")
184 # FIXME send broadcast everywhere, regardless of tenant193 # FIXME send broadcast everywhere, regardless of tenant
185 #int_br.add_flow(priority=3, match="dl_dst=ff:ff:ff:ff:ff:ff", actions="normal")194 #int_br.add_flow(priority=3, match="dl_dst=ff:ff:ff:ff:ff:ff",
195 # actions="normal")
186196
187 def daemon_loop(self, conn):197 def daemon_loop(self, conn):
188 self.local_vlan_map = {}198 self.local_vlan_map = {}
@@ -216,9 +226,9 @@
216 else:226 else:
217 # no binding, put him on the 'dead vlan'227 # no binding, put him on the 'dead vlan'
218 self.int_br.set_db_attribute("Port", p.port_name, "tag",228 self.int_br.set_db_attribute("Port", p.port_name, "tag",
219 "4095")229 "4095")
220 old_b = old_local_bindings.get(p.vif_id,None)230 old_b = old_local_bindings.get(p.vif_id, None)
221 new_b = new_local_bindings.get(p.vif_id,None)231 new_b = new_local_bindings.get(p.vif_id, None)
222 if old_b != new_b:232 if old_b != new_b:
223 if old_b is not None:233 if old_b is not None:
224 LOG.info("Removing binding to net-id = %s for %s"234 LOG.info("Removing binding to net-id = %s for %s"
225235
=== modified file 'quantum/plugins/openvswitch/ovs_db.py'
--- quantum/plugins/openvswitch/ovs_db.py 2011-06-04 03:59:49 +0000
+++ quantum/plugins/openvswitch/ovs_db.py 2011-06-09 05:01:27 +0000
@@ -24,6 +24,7 @@
24import quantum.db.models as models24import quantum.db.models as models
25import ovs_models25import ovs_models
2626
27
27def get_vlans():28def get_vlans():
28 session = db.get_session()29 session = db.get_session()
29 try:30 try:
@@ -33,9 +34,10 @@
33 return []34 return []
34 res = []35 res = []
35 for x in bindings:36 for x in bindings:
36 res.append((x.vlan_id, x.network_id))37 res.append((x.vlan_id, x.network_id))
37 return res38 return res
3839
40
39def add_vlan_binding(vlanid, netid):41def add_vlan_binding(vlanid, netid):
40 session = db.get_session()42 session = db.get_session()
41 binding = ovs_models.VlanBinding(vlanid, netid)43 binding = ovs_models.VlanBinding(vlanid, netid)
@@ -43,6 +45,7 @@
43 session.flush()45 session.flush()
44 return binding.vlan_id46 return binding.vlan_id
4547
48
46def remove_vlan_binding(netid):49def remove_vlan_binding(netid):
47 session = db.get_session()50 session = db.get_session()
48 try:51 try:
@@ -54,6 +57,7 @@
54 pass57 pass
55 session.flush()58 session.flush()
5659
60
57def update_network_binding(netid, ifaceid):61def update_network_binding(netid, ifaceid):
58 session = db.get_session()62 session = db.get_session()
59 # Add to or delete from the bindings table63 # Add to or delete from the bindings table
6064
=== modified file 'quantum/plugins/openvswitch/ovs_models.py'
--- quantum/plugins/openvswitch/ovs_models.py 2011-06-04 03:59:49 +0000
+++ quantum/plugins/openvswitch/ovs_models.py 2011-06-09 05:01:27 +0000
@@ -23,9 +23,9 @@
23from sqlalchemy import Column, Integer, String, ForeignKey23from sqlalchemy import Column, Integer, String, ForeignKey
24from sqlalchemy.ext.declarative import declarative_base24from sqlalchemy.ext.declarative import declarative_base
25from sqlalchemy.orm import relation25from sqlalchemy.orm import relation
26
27from quantum.db.models import BASE26from quantum.db.models import BASE
2827
28
29class NetworkBinding(BASE):29class NetworkBinding(BASE):
30 """Represents a binding of network_id, vif_id"""30 """Represents a binding of network_id, vif_id"""
31 __tablename__ = 'network_bindings'31 __tablename__ = 'network_bindings'
@@ -42,6 +42,7 @@
42 return "<NetworkBinding(%s,%s)>" % \42 return "<NetworkBinding(%s,%s)>" % \
43 (self.network_id, self.vif_id)43 (self.network_id, self.vif_id)
4444
45
45class VlanBinding(BASE):46class VlanBinding(BASE):
46 """Represents a binding of network_id, vlan_id"""47 """Represents a binding of network_id, vlan_id"""
47 __tablename__ = 'vlan_bindings'48 __tablename__ = 'vlan_bindings'
4849
=== modified file 'quantum/plugins/openvswitch/ovs_quantum_plugin.py'
--- quantum/plugins/openvswitch/ovs_quantum_plugin.py 2011-06-05 01:46:44 +0000
+++ quantum/plugins/openvswitch/ovs_quantum_plugin.py 2011-06-09 05:01:27 +0000
@@ -29,24 +29,29 @@
29import quantum.db.api as db29import quantum.db.api as db
30import ovs_db30import ovs_db
3131
32CONF_FILE="ovs_quantum_plugin.ini"32CONF_FILE = "ovs_quantum_plugin.ini"
3333
34LOG.basicConfig(level=LOG.WARN)34LOG.basicConfig(level=LOG.WARN)
35LOG.getLogger("ovs_quantum_plugin")35LOG.getLogger("ovs_quantum_plugin")
3636
37
37def find_config(basepath):38def find_config(basepath):
38 for root, dirs, files in os.walk(basepath):39 for root, dirs, files in os.walk(basepath):
39 if CONF_FILE in files:40 if CONF_FILE in files:
40 return os.path.join(root, CONF_FILE)41 return os.path.join(root, CONF_FILE)
41 return None42 return None
4243
44
43class VlanMap(object):45class VlanMap(object):
44 vlans = {}46 vlans = {}
47
45 def __init__(self):48 def __init__(self):
46 for x in xrange(2, 4094):49 for x in xrange(2, 4094):
47 self.vlans[x] = None50 self.vlans[x] = None
51
48 def set(self, vlan_id, network_id):52 def set(self, vlan_id, network_id):
49 self.vlans[vlan_id] = network_id53 self.vlans[vlan_id] = network_id
54
50 def acquire(self, network_id):55 def acquire(self, network_id):
51 for x in xrange(2, 4094):56 for x in xrange(2, 4094):
52 if self.vlans[x] == None:57 if self.vlans[x] == None:
@@ -54,8 +59,10 @@
54 # LOG.debug("VlanMap::acquire %s -> %s" % (x, network_id))59 # LOG.debug("VlanMap::acquire %s -> %s" % (x, network_id))
55 return x60 return x
56 raise Exception("No free vlans..")61 raise Exception("No free vlans..")
62
57 def get(self, vlan_id):63 def get(self, vlan_id):
58 return self.vlans[vlan_id]64 return self.vlans[vlan_id]
65
59 def release(self, network_id):66 def release(self, network_id):
60 for x in self.vlans.keys():67 for x in self.vlans.keys():
61 if self.vlans[x] == network_id:68 if self.vlans[x] == network_id:
@@ -64,14 +71,17 @@
64 return71 return
65 LOG.error("No vlan found with network \"%s\"" % network_id)72 LOG.error("No vlan found with network \"%s\"" % network_id)
6673
74
67class OVSQuantumPlugin(QuantumPluginBase):75class OVSQuantumPlugin(QuantumPluginBase):
76
68 def __init__(self, configfile=None):77 def __init__(self, configfile=None):
69 config = ConfigParser.ConfigParser()78 config = ConfigParser.ConfigParser()
70 if configfile == None:79 if configfile == None:
71 if os.path.exists(CONF_FILE):80 if os.path.exists(CONF_FILE):
72 configfile = CONF_FILE81 configfile = CONF_FILE
73 else:82 else:
74 configfile = find_config(os.path.abspath(os.path.dirname(__file__)))83 configfile = find_config(os.path.abspath(
84 os.path.dirname(__file__)))
75 if configfile == None:85 if configfile == None:
76 raise Exception("Configuration file \"%s\" doesn't exist" %86 raise Exception("Configuration file \"%s\" doesn't exist" %
77 (configfile))87 (configfile))
@@ -93,7 +103,8 @@
93 vlans = ovs_db.get_vlans()103 vlans = ovs_db.get_vlans()
94 for x in vlans:104 for x in vlans:
95 vlan_id, network_id = x105 vlan_id, network_id = x
96 # LOG.debug("Adding already populated vlan %s -> %s" % (vlan_id, network_id))106 # LOG.debug("Adding already populated vlan %s -> %s"
107 # % (vlan_id, network_id))
97 self.vmap.set(vlan_id, network_id)108 self.vmap.set(vlan_id, network_id)
98109
99 def get_all_networks(self, tenant_id):110 def get_all_networks(self, tenant_id):
@@ -109,8 +120,8 @@
109 def create_network(self, tenant_id, net_name):120 def create_network(self, tenant_id, net_name):
110 d = {}121 d = {}
111 try:122 try:
112 res = db.network_create(tenant_id, net_name)123 res = db.network_create(tenant_id, net_name)
113 LOG.debug("Created newtork: %s" % res)124 LOG.debug("Created newtork: %s" % res)
114 except Exception, e:125 except Exception, e:
115 LOG.error("Error: %s" % str(e))126 LOG.error("Error: %s" % str(e))
116 return d127 return d
@@ -199,21 +210,28 @@
199 res = db.port_get(port_id)210 res = db.port_get(port_id)
200 return res.interface_id211 return res.interface_id
201212
213
202class VlanMapTest(unittest.TestCase):214class VlanMapTest(unittest.TestCase):
215
203 def setUp(self):216 def setUp(self):
204 self.vmap = VlanMap()217 self.vmap = VlanMap()
218
205 def tearDown(self):219 def tearDown(self):
206 pass220 pass
221
207 def testAddVlan(self):222 def testAddVlan(self):
208 vlan_id = self.vmap.acquire("foobar")223 vlan_id = self.vmap.acquire("foobar")
209 self.assertTrue(vlan_id == 2)224 self.assertTrue(vlan_id == 2)
225
210 def testReleaseVlan(self):226 def testReleaseVlan(self):
211 vlan_id = self.vmap.acquire("foobar")227 vlan_id = self.vmap.acquire("foobar")
212 self.vmap.release("foobar")228 self.vmap.release("foobar")
213 self.assertTrue(self.vmap.get(vlan_id) == None)229 self.assertTrue(self.vmap.get(vlan_id) == None)
214230
231
215# TODO(bgh): Make the tests use a sqlite database instead of mysql232# TODO(bgh): Make the tests use a sqlite database instead of mysql
216class OVSPluginTest(unittest.TestCase):233class OVSPluginTest(unittest.TestCase):
234
217 def setUp(self):235 def setUp(self):
218 self.quantum = OVSQuantumPlugin()236 self.quantum = OVSQuantumPlugin()
219 self.tenant_id = "testtenant"237 self.tenant_id = "testtenant"
@@ -312,6 +330,7 @@
312 self.quantum.delete_port(self.tenant_id, id, p["port-id"])330 self.quantum.delete_port(self.tenant_id, id, p["port-id"])
313 self.quantum.delete_network(self.tenant_id, id)331 self.quantum.delete_network(self.tenant_id, id)
314332
333
315if __name__ == "__main__":334if __name__ == "__main__":
316 usagestr = "Usage: %prog [OPTIONS] <command> [args]"335 usagestr = "Usage: %prog [OPTIONS] <command> [args]"
317 parser = OptionParser(usage=usagestr)336 parser = OptionParser(usage=usagestr)
318337
=== modified file 'quantum/service.py'
--- quantum/service.py 2011-06-07 05:48:57 +0000
+++ quantum/service.py 2011-06-09 05:01:27 +0000
@@ -102,7 +102,9 @@
102102
103def _run_wsgi(app_name, paste_conf, paste_config_file):103def _run_wsgi(app_name, paste_conf, paste_config_file):
104 LOG.info(_('Using paste.deploy config at: %s'), paste_config_file)104 LOG.info(_('Using paste.deploy config at: %s'), paste_config_file)
105 app = config.load_paste_app(paste_config_file, app_name)105 conf, app = config.load_paste_app(app_name,
106 {'config_file': paste_config_file},
107 None)
106 if not app:108 if not app:
107 LOG.error(_('No known API applications configured in %s.'),109 LOG.error(_('No known API applications configured in %s.'),
108 paste_config_file)110 paste_config_file)
109111
=== added file 'run_tests.py'
--- run_tests.py 1970-01-01 00:00:00 +0000
+++ run_tests.py 2011-06-09 05:01:27 +0000
@@ -0,0 +1,293 @@
1#!/usr/bin/env python
2# vim: tabstop=4 shiftwidth=4 softtabstop=4
3
4# Copyright 2010 OpenStack, LLC
5# All Rights Reserved.
6#
7# Licensed under the Apache License, Version 2.0 (the "License");
8# you may not use this file except in compliance with the License.
9# You may obtain a copy of the License at
10#
11# http://www.apache.org/licenses/LICENSE-2.0
12#
13# Unless required by applicable law or agreed to in writing, software
14# distributed under the License is distributed on an "AS IS" BASIS,
15# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16# See the License for the specific language governing permissions and
17# limitations under the License.
18
19# Colorizer Code is borrowed from Twisted:
20# Copyright (c) 2001-2010 Twisted Matrix Laboratories.
21#
22# Permission is hereby granted, free of charge, to any person obtaining
23# a copy of this software and associated documentation files (the
24# "Software"), to deal in the Software without restriction, including
25# without limitation the rights to use, copy, modify, merge, publish,
26# distribute, sublicense, and/or sell copies of the Software, and to
27# permit persons to whom the Software is furnished to do so, subject to
28# the following conditions:
29#
30# The above copyright notice and this permission notice shall be
31# included in all copies or substantial portions of the Software.
32#
33# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
34# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
35# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
36# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
37# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
38# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
39# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
40
41"""Unittest runner for quantum
42
43To run all test::
44 python run_tests.py
45
46To run all unit tests::
47 python run_tests.py unit
48
49To run all functional tests::
50 python run_tests.py functional
51
52To run a single unit test::
53 python run_tests.py unit.test_stores:TestSwiftBackend.test_get
54
55To run a single functional test::
56 python run_tests.py functional.test_service:TestController.test_create
57
58To run a single unit test module::
59 python run_tests.py unit.test_stores
60
61To run a single functional test module::
62 python run_tests.py functional.test_stores
63"""
64
65import gettext
66import os
67import unittest
68import sys
69
70from nose import config
71from nose import result
72from nose import core
73
74
75class _AnsiColorizer(object):
76 """
77 A colorizer is an object that loosely wraps around a stream, allowing
78 callers to write text to the stream in a particular color.
79
80 Colorizer classes must implement C{supported()} and C{write(text, color)}.
81 """
82 _colors = dict(black=30, red=31, green=32, yellow=33,
83 blue=34, magenta=35, cyan=36, white=37)
84
85 def __init__(self, stream):
86 self.stream = stream
87
88 def supported(cls, stream=sys.stdout):
89 """
90 A class method that returns True if the current platform supports
91 coloring terminal output using this method. Returns False otherwise.
92 """
93 if not stream.isatty():
94 return False # auto color only on TTYs
95 try:
96 import curses
97 except ImportError:
98 return False
99 else:
100 try:
101 try:
102 return curses.tigetnum("colors") > 2
103 except curses.error:
104 curses.setupterm()
105 return curses.tigetnum("colors") > 2
106 except:
107 raise
108 # guess false in case of error
109 return False
110 supported = classmethod(supported)
111
112 def write(self, text, color):
113 """
114 Write the given text to the stream in the given color.
115
116 @param text: Text to be written to the stream.
117
118 @param color: A string label for a color. e.g. 'red', 'white'.
119 """
120 color = self._colors[color]
121 self.stream.write('\x1b[%s;1m%s\x1b[0m' % (color, text))
122
123
124class _Win32Colorizer(object):
125 """
126 See _AnsiColorizer docstring.
127 """
128 def __init__(self, stream):
129 from win32console import GetStdHandle, STD_OUT_HANDLE, \
130 FOREGROUND_RED, FOREGROUND_BLUE, FOREGROUND_GREEN, \
131 FOREGROUND_INTENSITY
132 red, green, blue, bold = (FOREGROUND_RED, FOREGROUND_GREEN,
133 FOREGROUND_BLUE, FOREGROUND_INTENSITY)
134 self.stream = stream
135 self.screenBuffer = GetStdHandle(STD_OUT_HANDLE)
136 self._colors = {
137 'normal': red | green | blue,
138 'red': red | bold,
139 'green': green | bold,
140 'blue': blue | bold,
141 'yellow': red | green | bold,
142 'magenta': red | blue | bold,
143 'cyan': green | blue | bold,
144 'white': red | green | blue | bold}
145
146 def supported(cls, stream=sys.stdout):
147 try:
148 import win32console
149 screenBuffer = win32console.GetStdHandle(
150 win32console.STD_OUT_HANDLE)
151 except ImportError:
152 return False
153 import pywintypes
154 try:
155 screenBuffer.SetConsoleTextAttribute(
156 win32console.FOREGROUND_RED |
157 win32console.FOREGROUND_GREEN |
158 win32console.FOREGROUND_BLUE)
159 except pywintypes.error:
160 return False
161 else:
162 return True
163 supported = classmethod(supported)
164
165 def write(self, text, color):
166 color = self._colors[color]
167 self.screenBuffer.SetConsoleTextAttribute(color)
168 self.stream.write(text)
169 self.screenBuffer.SetConsoleTextAttribute(self._colors['normal'])
170
171
172class _NullColorizer(object):
173 """
174 See _AnsiColorizer docstring.
175 """
176 def __init__(self, stream):
177 self.stream = stream
178
179 def supported(cls, stream=sys.stdout):
180 return True
181 supported = classmethod(supported)
182
183 def write(self, text, color):
184 self.stream.write(text)
185
186
187class QuantumTestResult(result.TextTestResult):
188 def __init__(self, *args, **kw):
189 result.TextTestResult.__init__(self, *args, **kw)
190 self._last_case = None
191 self.colorizer = None
192 # NOTE(vish, tfukushima): reset stdout for the terminal check
193 stdout = sys.__stdout__
194 for colorizer in [_Win32Colorizer, _AnsiColorizer, _NullColorizer]:
195 if colorizer.supported():
196 self.colorizer = colorizer(self.stream)
197 break
198 sys.stdout = stdout
199
200 def getDescription(self, test):
201 return str(test)
202
203 # NOTE(vish, tfukushima): copied from unittest with edit to add color
204 def addSuccess(self, test):
205 unittest.TestResult.addSuccess(self, test)
206 if self.showAll:
207 self.colorizer.write("OK", 'green')
208 self.stream.writeln()
209 elif self.dots:
210 self.stream.write('.')
211 self.stream.flush()
212
213 # NOTE(vish, tfukushima): copied from unittest with edit to add color
214 def addFailure(self, test, err):
215 unittest.TestResult.addFailure(self, test, err)
216 if self.showAll:
217 self.colorizer.write("FAIL", 'red')
218 self.stream.writeln()
219 elif self.dots:
220 self.stream.write('F')
221 self.stream.flush()
222
223 # NOTE(vish, tfukushima): copied from unittest with edit to add color
224 def addError(self, test, err):
225 """Overrides normal addError to add support for errorClasses.
226 If the exception is a registered class, the error will be added
227 to the list for that class, not errors.
228 """
229 stream = getattr(self, 'stream', None)
230 ec, ev, tb = err
231 try:
232 exc_info = self._exc_info_to_string(err, test)
233 except TypeError:
234 # This is for compatibility with Python 2.3.
235 exc_info = self._exc_info_to_string(err)
236 for cls, (storage, label, isfail) in self.errorClasses.items():
237 if result.isclass(ec) and issubclass(ec, cls):
238 if isfail:
239 test.passwd = False
240 storage.append((test, exc_info))
241 # Might get patched into a streamless result
242 if stream is not None:
243 if self.showAll:
244 message = [label]
245 detail = result._exception_details(err[1])
246 if detail:
247 message.append(detail)
248 stream.writeln(": ".join(message))
249 elif self.dots:
250 stream.write(label[:1])
251 return
252 self.errors.append((test, exc_info))
253 test.passed = False
254 if stream is not None:
255 if self.showAll:
256 self.colorizer.write("ERROR", 'red')
257 self.stream.writeln()
258 elif self.dots:
259 stream.write('E')
260
261 def startTest(self, test):
262 unittest.TestResult.startTest(self, test)
263 current_case = test.test.__class__.__name__
264
265 if self.showAll:
266 if current_case != self._last_case:
267 self.stream.writeln(current_case)
268 self._last_case = current_case
269
270 self.stream.write(
271 ' %s' % str(test.test._testMethodName).ljust(60))
272 self.stream.flush()
273
274
275class QuantumTestRunner(core.TextTestRunner):
276 def _makeResult(self):
277 return QuantumTestResult(self.stream,
278 self.descriptions,
279 self.verbosity,
280 self.config)
281
282
283if __name__ == '__main__':
284 working_dir = os.path.abspath("tests")
285 c = config.Config(stream=sys.stdout,
286 env=os.environ,
287 verbosity=3,
288 workingDir=working_dir)
289
290 runner = QuantumTestRunner(stream=c.stream,
291 verbosity=c.verbosity,
292 config=c)
293 sys.exit(not core.run(config=c, testRunner=runner))
0294
=== added file 'run_tests.sh'
--- run_tests.sh 1970-01-01 00:00:00 +0000
+++ run_tests.sh 2011-06-09 05:01:27 +0000
@@ -0,0 +1,83 @@
1#!/bin/bash
2
3function usage {
4 echo "Usage: $0 [OPTION]..."
5 echo "Run Melange's test suite(s)"
6 echo ""
7 echo " -V, --virtual-env Always use virtualenv. Install automatically if not present"
8 echo " -N, --no-virtual-env Don't use virtualenv. Run tests in local environment"
9 echo " -f, --force Force a clean re-build of the virtual environment. Useful when dependencies have been added."
10 echo " -h, --help Print this usage message"
11 echo ""
12 echo "Note: with no options specified, the script will try to run the tests in a virtual environment,"
13 echo " If no virtualenv is found, the script will ask if you would like to create one. If you "
14 echo " prefer to run tests NOT in a virtual environment, simply pass the -N option."
15 exit
16}
17
18function process_option {
19 case "$1" in
20 -h|--help) usage;;
21 -V|--virtual-env) let always_venv=1; let never_venv=0;;
22 -N|--no-virtual-env) let always_venv=0; let never_venv=1;;
23 -f|--force) let force=1;;
24 *) noseargs="$noseargs $1"
25 esac
26}
27
28venv=.quantum-venv
29with_venv=tools/with_venv.sh
30always_venv=0
31never_venv=0
32force=0
33noseargs=
34wrapper=""
35
36for arg in "$@"; do
37 process_option $arg
38done
39
40function run_tests {
41 # Just run the test suites in current environment
42 ${wrapper} rm -f tests.sqlite
43 ${wrapper} $NOSETESTS 2> run_tests.err.log
44}
45
46NOSETESTS="python run_tests.py $noseargs"
47
48if [ $never_venv -eq 0 ]
49then
50 # Remove the virtual environment if --force used
51 if [ $force -eq 1 ]; then
52 echo "Cleaning virtualenv..."
53 rm -rf ${venv}
54 fi
55 if [ -e ${venv} ]; then
56 wrapper="${with_venv}"
57 else
58 if [ $always_venv -eq 1 ]; then
59 # Automatically install the virtualenv
60 python tools/install_venv.py
61 wrapper="${with_venv}"
62 else
63 echo -e "No virtual environment found...create one? (Y/n) \c"
64 read use_ve
65 if [ "x$use_ve" = "xY" -o "x$use_ve" = "x" -o "x$use_ve" = "xy" ]; then
66 # Install the virtualenv and run the test suite in it
67 python tools/install_venv.py
68 wrapper=${with_venv}
69 fi
70 fi
71 fi
72fi
73
74# FIXME(sirp): bzr version-info is not currently pep-8. This was fixed with
75# lp701898 [1], however, until that version of bzr becomes standard, I'm just
76# excluding the vcsversion.py file
77#
78# [1] https://bugs.launchpad.net/bzr/+bug/701898
79#
80PEP8_EXCLUDE=vcsversion.py
81PEP8_OPTIONS="--exclude=$PEP8_EXCLUDE --repeat --show-source"
82PEP8_INCLUDE="bin/* quantum tests tools run_tests.py"
83run_tests && pep8 $PEP8_OPTIONS $PEP8_INCLUDE || exit 1
084
=== removed directory 'smoketests'
=== removed file 'smoketests/__init__.py'
=== removed file 'smoketests/miniclient.py'
--- smoketests/miniclient.py 2011-06-01 18:00:15 +0000
+++ smoketests/miniclient.py 1970-01-01 00:00:00 +0000
@@ -1,98 +0,0 @@
1# vim: tabstop=4 shiftwidth=4 softtabstop=4
2
3# Copyright 2011 Citrix Systems
4# All Rights Reserved.
5#
6# Licensed under the Apache License, Version 2.0 (the "License"); you may
7# not use this file except in compliance with the License. You may obtain
8# a copy of the License at
9#
10# http://www.apache.org/licenses/LICENSE-2.0
11#
12# Unless required by applicable law or agreed to in writing, software
13# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
14# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
15# License for the specific language governing permissions and limitations
16# under the License.
17
18import httplib
19import socket
20import urllib
21
22class MiniClient(object):
23
24 """A base client class - derived from Glance.BaseClient"""
25
26 action_prefix = '/v0.1/tenants/{tenant_id}'
27
28 def __init__(self, host, port, use_ssl):
29 """
30 Creates a new client to some service.
31
32 :param host: The host where service resides
33 :param port: The port where service resides
34 :param use_ssl: Should we use HTTPS?
35 """
36 self.host = host
37 self.port = port
38 self.use_ssl = use_ssl
39 self.connection = None
40
41 def get_connection_type(self):
42 """
43 Returns the proper connection type
44 """
45 if self.use_ssl:
46 return httplib.HTTPSConnection
47 else:
48 return httplib.HTTPConnection
49
50 def do_request(self, tenant, method, action, body=None,
51 headers=None, params=None):
52 """
53 Connects to the server and issues a request.
54 Returns the result data, or raises an appropriate exception if
55 HTTP status code is not 2xx
56
57 :param method: HTTP method ("GET", "POST", "PUT", etc...)
58 :param body: string of data to send, or None (default)
59 :param headers: mapping of key/value pairs to add as headers
60 :param params: dictionary of key/value pairs to add to append
61 to action
62
63 """
64 action = MiniClient.action_prefix + action
65 action = action.replace('{tenant_id}',tenant)
66 if type(params) is dict:
67 action += '?' + urllib.urlencode(params)
68
69 try:
70 connection_type = self.get_connection_type()
71 headers = headers or {}
72
73 # Open connection and send request
74 c = connection_type(self.host, self.port)
75 c.request(method, action, body, headers)
76 res = c.getresponse()
77 status_code = self.get_status_code(res)
78 if status_code in (httplib.OK,
79 httplib.CREATED,
80 httplib.ACCEPTED,
81 httplib.NO_CONTENT):
82 return res
83 else:
84 raise Exception("Server returned error: %s" % res.read())
85
86 except (socket.error, IOError), e:
87 raise Exception("Unable to connect to "
88 "server. Got error: %s" % e)
89
90 def get_status_code(self, response):
91 """
92 Returns the integer status code from the response, which
93 can be either a Webob.Response (used in testing) or httplib.Response
94 """
95 if hasattr(response, 'status_int'):
96 return response.status_int
97 else:
98 return response.status
99\ No newline at end of file0\ No newline at end of file
1001
=== removed file 'smoketests/tests.py'
--- smoketests/tests.py 2011-06-01 18:00:15 +0000
+++ smoketests/tests.py 1970-01-01 00:00:00 +0000
@@ -1,133 +0,0 @@
1# vim: tabstop=4 shiftwidth=4 softtabstop=4
2
3# Copyright 2011 Citrix Systems
4# Copyright 2011 Nicira Networks
5# All Rights Reserved.
6#
7# Licensed under the Apache License, Version 2.0 (the "License"); you may
8# not use this file except in compliance with the License. You may obtain
9# a copy of the License at
10#
11# http://www.apache.org/licenses/LICENSE-2.0
12#
13# Unless required by applicable law or agreed to in writing, software
14# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
15# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
16# License for the specific language governing permissions and limitations
17# under the License.
18
19import gettext
20import simplejson
21import sys
22import unittest
23
24gettext.install('quantum', unicode=1)
25
26from miniclient import MiniClient
27from quantum.common.wsgi import Serializer
28
29HOST = '127.0.0.1'
30PORT = 9696
31USE_SSL = False
32
33TENANT_ID = 'totore'
34FORMAT = "json"
35
36test_network1_data = \
37 {'network': {'network-name': 'test1' }}
38test_network2_data = \
39 {'network': {'network-name': 'test2' }}
40
41def print_response(res):
42 content = res.read()
43 print "Status: %s" %res.status
44 print "Content: %s" %content
45 return content
46
47class QuantumTest(unittest.TestCase):
48 def setUp(self):
49 self.client = MiniClient(HOST, PORT, USE_SSL)
50
51 def create_network(self, data):
52 content_type = "application/" + FORMAT
53 body = Serializer().serialize(data, content_type)
54 res = self.client.do_request(TENANT_ID, 'POST', "/networks." + FORMAT,
55 body=body)
56 self.assertEqual(res.status, 200, "bad response: %s" % res.read())
57
58 def test_listNetworks(self):
59 self.create_network(test_network1_data)
60 self.create_network(test_network2_data)
61 res = self.client.do_request(TENANT_ID,'GET', "/networks." + FORMAT)
62 self.assertEqual(res.status, 200, "bad response: %s" % res.read())
63
64 def test_createNetwork(self):
65 self.create_network(test_network1_data)
66
67 def test_createPort(self):
68 self.create_network(test_network1_data)
69 res = self.client.do_request(TENANT_ID, 'GET', "/networks." + FORMAT)
70 resdict = simplejson.loads(res.read())
71 for n in resdict["networks"]:
72 net_id = n["id"]
73
74 # Step 1 - List Ports for network (should not find any)
75 res = self.client.do_request(TENANT_ID, 'GET',
76 "/networks/%s/ports.%s" % (net_id, FORMAT))
77 self.assertEqual(res.status, 200, "Bad response: %s" % res.read())
78 output = res.read()
79 self.assertTrue(len(output) == 0,
80 "Found unexpected ports: %s" % output)
81
82 # Step 2 - Create Port for network
83 res = self.client.do_request(TENANT_ID, 'POST',
84 "/networks/%s/ports.%s" % (net_id, FORMAT))
85 self.assertEqual(res.status, 200, "Bad response: %s" % output)
86
87 # Step 3 - List Ports for network (again); should find one
88 res = self.client.do_request(TENANT_ID, 'GET',
89 "/networks/%s/ports.%s" % (net_id, FORMAT))
90 output = res.read()
91 self.assertEqual(res.status, 200, "Bad response: %s" % output)
92 resdict = simplejson.loads(output)
93 ids = []
94 for p in resdict["ports"]:
95 ids.append(p["id"])
96 self.assertTrue(len(ids) == 1,
97 "Didn't find expected # of ports (1): %s" % ids)
98
99 def test_renameNetwork(self):
100 self.create_network(test_network1_data)
101 res = self.client.do_request(TENANT_ID, 'GET', "/networks." + FORMAT)
102 resdict = simplejson.loads(res.read())
103 net_id = resdict["networks"][0]["id"]
104
105 data = test_network1_data.copy()
106 data['network']['network-name'] = 'test_renamed'
107 content_type = "application/" + FORMAT
108 body = Serializer().serialize(data, content_type)
109 res = self.client.do_request(TENANT_ID, 'PUT',
110 "/networks/%s.%s" % (net_id, FORMAT), body=body)
111 resdict = simplejson.loads(res.read())
112 self.assertTrue(resdict["networks"]["network"]["id"] == net_id,
113 "Network_rename: renamed network has a different uuid")
114 self.assertTrue(resdict["networks"]["network"]["name"] == "test_renamed",
115 "Network rename didn't take effect")
116
117 def delete_networks(self):
118 # Remove all the networks created on the tenant
119 res = self.client.do_request(TENANT_ID, 'GET', "/networks." + FORMAT)
120 resdict = simplejson.loads(res.read())
121 for n in resdict["networks"]:
122 net_id = n["id"]
123 res = self.client.do_request(TENANT_ID, 'DELETE',
124 "/networks/" + net_id + "." + FORMAT)
125 self.assertEqual(res.status, 202)
126
127 def tearDown(self):
128 self.delete_networks()
129
130# Standard boilerplate to call the main() function.
131if __name__ == '__main__':
132 suite = unittest.TestLoader().loadTestsFromTestCase(QuantumTest)
133 unittest.TextTestRunner(verbosity=2).run(suite)
1340
=== removed directory 'test_scripts'
=== removed file 'test_scripts/__init__.py'
=== removed file 'test_scripts/miniclient.py'
--- test_scripts/miniclient.py 2011-05-30 00:08:46 +0000
+++ test_scripts/miniclient.py 1970-01-01 00:00:00 +0000
@@ -1,98 +0,0 @@
1# vim: tabstop=4 shiftwidth=4 softtabstop=4
2
3# Copyright 2011 Citrix Systems
4# All Rights Reserved.
5#
6# Licensed under the Apache License, Version 2.0 (the "License"); you may
7# not use this file except in compliance with the License. You may obtain
8# a copy of the License at
9#
10# http://www.apache.org/licenses/LICENSE-2.0
11#
12# Unless required by applicable law or agreed to in writing, software
13# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
14# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
15# License for the specific language governing permissions and limitations
16# under the License.
17
18import httplib
19import socket
20import urllib
21
22class MiniClient(object):
23
24 """A base client class - derived from Glance.BaseClient"""
25
26 action_prefix = '/v0.1/tenants/{tenant_id}'
27
28 def __init__(self, host, port, use_ssl):
29 """
30 Creates a new client to some service.
31
32 :param host: The host where service resides
33 :param port: The port where service resides
34 :param use_ssl: Should we use HTTPS?
35 """
36 self.host = host
37 self.port = port
38 self.use_ssl = use_ssl
39 self.connection = None
40
41 def get_connection_type(self):
42 """
43 Returns the proper connection type
44 """
45 if self.use_ssl:
46 return httplib.HTTPSConnection
47 else:
48 return httplib.HTTPConnection
49
50 def do_request(self, tenant, method, action, body=None,
51 headers=None, params=None):
52 """
53 Connects to the server and issues a request.
54 Returns the result data, or raises an appropriate exception if
55 HTTP status code is not 2xx
56
57 :param method: HTTP method ("GET", "POST", "PUT", etc...)
58 :param body: string of data to send, or None (default)
59 :param headers: mapping of key/value pairs to add as headers
60 :param params: dictionary of key/value pairs to add to append
61 to action
62
63 """
64 action = MiniClient.action_prefix + action
65 action = action.replace('{tenant_id}',tenant)
66 if type(params) is dict:
67 action += '?' + urllib.urlencode(params)
68
69 try:
70 connection_type = self.get_connection_type()
71 headers = headers or {}
72
73 # Open connection and send request
74 c = connection_type(self.host, self.port)
75 c.request(method, action, body, headers)
76 res = c.getresponse()
77 status_code = self.get_status_code(res)
78 if status_code in (httplib.OK,
79 httplib.CREATED,
80 httplib.ACCEPTED,
81 httplib.NO_CONTENT):
82 return res
83 else:
84 raise Exception("Server returned error: %s" % res.read())
85
86 except (socket.error, IOError), e:
87 raise Exception("Unable to connect to "
88 "server. Got error: %s" % e)
89
90 def get_status_code(self, response):
91 """
92 Returns the integer status code from the response, which
93 can be either a Webob.Response (used in testing) or httplib.Response
94 """
95 if hasattr(response, 'status_int'):
96 return response.status_int
97 else:
98 return response.status
99\ No newline at end of file0\ No newline at end of file
1001
=== removed file 'test_scripts/tests.py'
--- test_scripts/tests.py 2011-05-30 00:08:46 +0000
+++ test_scripts/tests.py 1970-01-01 00:00:00 +0000
@@ -1,150 +0,0 @@
1# vim: tabstop=4 shiftwidth=4 softtabstop=4
2
3# Copyright 2011 Citrix Systems
4# All Rights Reserved.
5#
6# Licensed under the Apache License, Version 2.0 (the "License"); you may
7# not use this file except in compliance with the License. You may obtain
8# a copy of the License at
9#
10# http://www.apache.org/licenses/LICENSE-2.0
11#
12# Unless required by applicable law or agreed to in writing, software
13# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
14# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
15# License for the specific language governing permissions and limitations
16# under the License.
17
18import gettext
19
20gettext.install('quantum', unicode=1)
21
22from miniclient import MiniClient
23from quantum.common.wsgi import Serializer
24
25HOST = '127.0.0.1'
26PORT = 9696
27USE_SSL = False
28TENANT_ID = 'totore'
29
30test_network_data = \
31 {'network': {'network-name': 'test' }}
32
33def print_response(res):
34 content = res.read()
35 print "Status: %s" %res.status
36 print "Content: %s" %content
37 return content
38
39def test_list_networks_and_ports(format = 'xml'):
40 client = MiniClient(HOST, PORT, USE_SSL)
41 print "TEST LIST NETWORKS AND PORTS -- FORMAT:%s" %format
42 print "----------------------------"
43 print "--> Step 1 - List All Networks"
44 res = client.do_request(TENANT_ID,'GET', "/networks." + format)
45 print_response(res)
46 print "--> Step 2 - Details for Network 001"
47 res = client.do_request(TENANT_ID,'GET', "/networks/001." + format)
48 print_response(res)
49 print "--> Step 3 - Ports for Network 001"
50 res = client.do_request(TENANT_ID,'GET', "/networks/001/ports." + format)
51 print_response(res)
52 print "--> Step 4 - Details for Port 1"
53 res = client.do_request(TENANT_ID,'GET', "/networks/001/ports/1." + format)
54 print_response(res)
55 print "COMPLETED"
56 print "----------------------------"
57
58def test_create_network(format = 'xml'):
59 client = MiniClient(HOST, PORT, USE_SSL)
60 print "TEST CREATE NETWORK -- FORMAT:%s" %format
61 print "----------------------------"
62 print "--> Step 1 - Create Network"
63 content_type = "application/" + format
64 body = Serializer().serialize(test_network_data, content_type)
65 res = client.do_request(TENANT_ID,'POST', "/networks." + format, body=body)
66 print_response(res)
67 print "--> Step 2 - List All Networks"
68 res = client.do_request(TENANT_ID,'GET', "/networks." + format)
69 print_response(res)
70 print "COMPLETED"
71 print "----------------------------"
72
73def test_rename_network(format = 'xml'):
74 client = MiniClient(HOST, PORT, USE_SSL)
75 content_type = "application/" + format
76 print "TEST RENAME NETWORK -- FORMAT:%s" %format
77 print "----------------------------"
78 print "--> Step 1 - Retrieve network"
79 res = client.do_request(TENANT_ID,'GET', "/networks/001." + format)
80 print_response(res)
81 print "--> Step 2 - Rename network to 'test_renamed'"
82 test_network_data['network']['network-name'] = 'test_renamed'
83 body = Serializer().serialize(test_network_data, content_type)
84 res = client.do_request(TENANT_ID,'PUT', "/networks/001." + format, body=body)
85 print_response(res)
86 print "--> Step 2 - Retrieve network (again)"
87 res = client.do_request(TENANT_ID,'GET', "/networks/001." + format)
88 print_response(res)
89 print "COMPLETED"
90 print "----------------------------"
91
92def test_delete_network(format = 'xml'):
93 client = MiniClient(HOST, PORT, USE_SSL)
94 content_type = "application/" + format
95 print "TEST DELETE NETWORK -- FORMAT:%s" %format
96 print "----------------------------"
97 print "--> Step 1 - List All Networks"
98 res = client.do_request(TENANT_ID,'GET', "/networks." + format)
99 content = print_response(res)
100 network_data = Serializer().deserialize(content, content_type)
101 print network_data
102 net_id = network_data['networks'][0]['id']
103 print "--> Step 2 - Delete network %s" %net_id
104 res = client.do_request(TENANT_ID,'DELETE',
105 "/networks/" + net_id + "." + format)
106 print_response(res)
107 print "--> Step 3 - List All Networks (Again)"
108 res = client.do_request(TENANT_ID,'GET', "/networks." + format)
109 print_response(res)
110 print "COMPLETED"
111 print "----------------------------"
112
113
114def test_create_port(format = 'xml'):
115 client = MiniClient(HOST, PORT, USE_SSL)
116 print "TEST CREATE PORT -- FORMAT:%s" %format
117 print "----------------------------"
118 print "--> Step 1 - List Ports for network 001"
119 res = client.do_request(TENANT_ID,'GET', "/networks/001/ports." + format)
120 print_response(res)
121 print "--> Step 2 - Create Port for network 001"
122 res = client.do_request(TENANT_ID,'POST', "/networks/001/ports." + format)
123 print_response(res)
124 print "--> Step 3 - List Ports for network 001 (again)"
125 res = client.do_request(TENANT_ID,'GET', "/networks/001/ports." + format)
126 print_response(res)
127 print "COMPLETED"
128 print "----------------------------"
129
130
131def main():
132 test_list_networks_and_ports('xml')
133 test_list_networks_and_ports('json')
134 test_create_network('xml')
135 test_create_network('json')
136 test_rename_network('xml')
137 test_rename_network('json')
138 # NOTE: XML deserializer does not work properly
139 # disabling XML test - this is NOT a server-side issue
140 #test_delete_network('xml')
141 test_delete_network('json')
142 test_create_port('xml')
143 test_create_port('json')
144
145 pass
146
147
148# Standard boilerplate to call the main() function.
149if __name__ == '__main__':
150 main()
151\ No newline at end of file0\ No newline at end of file
1521
=== added directory 'tests'
=== added file 'tests/__init__.py'
=== added directory 'tests/functional'
=== added file 'tests/functional/__init__.py'
=== added file 'tests/functional/miniclient.py'
--- tests/functional/miniclient.py 1970-01-01 00:00:00 +0000
+++ tests/functional/miniclient.py 2011-06-09 05:01:27 +0000
@@ -0,0 +1,99 @@
1# vim: tabstop=4 shiftwidth=4 softtabstop=4
2
3# Copyright 2011 Citrix Systems
4# All Rights Reserved.
5#
6# Licensed under the Apache License, Version 2.0 (the "License"); you may
7# not use this file except in compliance with the License. You may obtain
8# a copy of the License at
9#
10# http://www.apache.org/licenses/LICENSE-2.0
11#
12# Unless required by applicable law or agreed to in writing, software
13# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
14# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
15# License for the specific language governing permissions and limitations
16# under the License.
17
18import httplib
19import socket
20import urllib
21
22
23class MiniClient(object):
24
25 """A base client class - derived from Glance.BaseClient"""
26
27 action_prefix = '/v0.1/tenants/{tenant_id}'
28
29 def __init__(self, host, port, use_ssl):
30 """
31 Creates a new client to some service.
32
33 :param host: The host where service resides
34 :param port: The port where service resides
35 :param use_ssl: Should we use HTTPS?
36 """
37 self.host = host
38 self.port = port
39 self.use_ssl = use_ssl
40 self.connection = None
41
42 def get_connection_type(self):
43 """
44 Returns the proper connection type
45 """
46 if self.use_ssl:
47 return httplib.HTTPSConnection
48 else:
49 return httplib.HTTPConnection
50
51 def do_request(self, tenant, method, action, body=None,
52 headers=None, params=None):
53 """
54 Connects to the server and issues a request.
55 Returns the result data, or raises an appropriate exception if
56 HTTP status code is not 2xx
57
58 :param method: HTTP method ("GET", "POST", "PUT", etc...)
59 :param body: string of data to send, or None (default)
60 :param headers: mapping of key/value pairs to add as headers
61 :param params: dictionary of key/value pairs to add to append
62 to action
63
64 """
65 action = MiniClient.action_prefix + action
66 action = action.replace('{tenant_id}', tenant)
67 if type(params) is dict:
68 action += '?' + urllib.urlencode(params)
69
70 try:
71 connection_type = self.get_connection_type()
72 headers = headers or {}
73
74 # Open connection and send request
75 c = connection_type(self.host, self.port)
76 c.request(method, action, body, headers)
77 res = c.getresponse()
78 status_code = self.get_status_code(res)
79 if status_code in (httplib.OK,
80 httplib.CREATED,
81 httplib.ACCEPTED,
82 httplib.NO_CONTENT):
83 return res
84 else:
85 raise Exception("Server returned error: %s" % res.read())
86
87 except (socket.error, IOError), e:
88 raise Exception("Unable to connect to "
89 "server. Got error: %s" % e)
90
91 def get_status_code(self, response):
92 """
93 Returns the integer status code from the response, which
94 can be either a Webob.Response (used in testing) or httplib.Response
95 """
96 if hasattr(response, 'status_int'):
97 return response.status_int
98 else:
99 return response.status
0100
=== added file 'tests/functional/test_service.py'
--- tests/functional/test_service.py 1970-01-01 00:00:00 +0000
+++ tests/functional/test_service.py 2011-06-09 05:01:27 +0000
@@ -0,0 +1,136 @@
1# vim: tabstop=4 shiftwidth=4 softtabstop=4
2
3# Copyright 2011 Citrix Systems
4# Copyright 2011 Nicira Networks
5# All Rights Reserved.
6#
7# Licensed under the Apache License, Version 2.0 (the "License"); you may
8# not use this file except in compliance with the License. You may obtain
9# a copy of the License at
10#
11# http://www.apache.org/licenses/LICENSE-2.0
12#
13# Unless required by applicable law or agreed to in writing, software
14# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
15# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
16# License for the specific language governing permissions and limitations
17# under the License.
18
19import gettext
20import simplejson
21import sys
22import unittest
23
24gettext.install('quantum', unicode=1)
25
26from miniclient import MiniClient
27from quantum.common.wsgi import Serializer
28
29HOST = '127.0.0.1'
30PORT = 9696
31USE_SSL = False
32
33TENANT_ID = 'totore'
34FORMAT = "json"
35
36test_network1_data = \
37 {'network': {'network-name': 'test1'}}
38test_network2_data = \
39 {'network': {'network-name': 'test2'}}
40
41
42def print_response(res):
43 content = res.read()
44 print "Status: %s" % res.status
45 print "Content: %s" % content
46 return content
47
48
49class QuantumTest(unittest.TestCase):
50 def setUp(self):
51 self.client = MiniClient(HOST, PORT, USE_SSL)
52
53 def create_network(self, data):
54 content_type = "application/" + FORMAT
55 body = Serializer().serialize(data, content_type)
56 res = self.client.do_request(TENANT_ID, 'POST', "/networks." + FORMAT,
57 body=body)
58 self.assertEqual(res.status, 200, "bad response: %s" % res.read())
59
60 def test_listNetworks(self):
61 self.create_network(test_network1_data)
62 self.create_network(test_network2_data)
63 res = self.client.do_request(TENANT_ID, 'GET', "/networks." + FORMAT)
64 self.assertEqual(res.status, 200, "bad response: %s" % res.read())
65
66 def test_createNetwork(self):
67 self.create_network(test_network1_data)
68
69 def test_createPort(self):
70 self.create_network(test_network1_data)
71 res = self.client.do_request(TENANT_ID, 'GET', "/networks." + FORMAT)
72 resdict = simplejson.loads(res.read())
73 for n in resdict["networks"]:
74 net_id = n["id"]
75
76 # Step 1 - List Ports for network (should not find any)
77 res = self.client.do_request(TENANT_ID, 'GET',
78 "/networks/%s/ports.%s" % (net_id, FORMAT))
79 self.assertEqual(res.status, 200, "Bad response: %s" % res.read())
80 output = res.read()
81 self.assertTrue(len(output) == 0,
82 "Found unexpected ports: %s" % output)
83
84 # Step 2 - Create Port for network
85 res = self.client.do_request(TENANT_ID, 'POST',
86 "/networks/%s/ports.%s" % (net_id, FORMAT))
87 self.assertEqual(res.status, 200, "Bad response: %s" % output)
88
89 # Step 3 - List Ports for network (again); should find one
90 res = self.client.do_request(TENANT_ID, 'GET',
91 "/networks/%s/ports.%s" % (net_id, FORMAT))
92 output = res.read()
93 self.assertEqual(res.status, 200, "Bad response: %s" % output)
94 resdict = simplejson.loads(output)
95 ids = []
96 for p in resdict["ports"]:
97 ids.append(p["id"])
98 self.assertTrue(len(ids) == 1,
99 "Didn't find expected # of ports (1): %s" % ids)
100
101 def test_renameNetwork(self):
102 self.create_network(test_network1_data)
103 res = self.client.do_request(TENANT_ID, 'GET', "/networks." + FORMAT)
104 resdict = simplejson.loads(res.read())
105 net_id = resdict["networks"][0]["id"]
106
107 data = test_network1_data.copy()
108 data['network']['network-name'] = 'test_renamed'
109 content_type = "application/" + FORMAT
110 body = Serializer().serialize(data, content_type)
111 res = self.client.do_request(TENANT_ID, 'PUT',
112 "/networks/%s.%s" % (net_id, FORMAT), body=body)
113 resdict = simplejson.loads(res.read())
114 self.assertTrue(resdict["networks"]["network"]["id"] == net_id,
115 "Network_rename: renamed network has a different uuid")
116 self.assertTrue(
117 resdict["networks"]["network"]["name"] == "test_renamed",
118 "Network rename didn't take effect")
119
120 def delete_networks(self):
121 # Remove all the networks created on the tenant
122 res = self.client.do_request(TENANT_ID, 'GET', "/networks." + FORMAT)
123 resdict = simplejson.loads(res.read())
124 for n in resdict["networks"]:
125 net_id = n["id"]
126 res = self.client.do_request(TENANT_ID, 'DELETE',
127 "/networks/" + net_id + "." + FORMAT)
128 self.assertEqual(res.status, 202)
129
130 def tearDown(self):
131 self.delete_networks()
132
133# Standard boilerplate to call the main() function.
134if __name__ == '__main__':
135 suite = unittest.TestLoader().loadTestsFromTestCase(QuantumTest)
136 unittest.TextTestRunner(verbosity=2).run(suite)
0137
=== added directory 'tests/unit'
=== added file 'tests/unit/__init__.py'
--- tests/unit/__init__.py 1970-01-01 00:00:00 +0000
+++ tests/unit/__init__.py 2011-06-09 05:01:27 +0000
@@ -0,0 +1,32 @@
1# vim: tabstop=4 shiftwidth=4 softtabstop=4
2
3# Copyright 2011 OpenStack LLC.
4# All Rights Reserved.
5#
6# Licensed under the Apache License, Version 2.0 (the "License"); you may
7# not use this file except in compliance with the License. You may obtain
8# a copy of the License at
9#
10# http://www.apache.org/licenses/LICENSE-2.0
11#
12# Unless required by applicable law or agreed to in writing, software
13# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
14# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
15# License for the specific language governing permissions and limitations
16# under the License.
17
18# See http://code.google.com/p/python-nose/issues/detail?id=373
19# The code below enables nosetests to work with i18n _() blocks
20import __builtin__
21import unittest
22setattr(__builtin__, '_', lambda x: x)
23
24
25class BaseTest(unittest.TestCase):
26
27 def setUp(self):
28 pass
29
30
31def setUp():
32 pass
033
=== added file 'tools/install_venv.py'
--- tools/install_venv.py 1970-01-01 00:00:00 +0000
+++ tools/install_venv.py 2011-06-09 05:01:27 +0000
@@ -0,0 +1,137 @@
1# vim: tabstop=4 shiftwidth=4 softtabstop=4
2
3# Copyright 2010 United States Government as represented by the
4# Administrator of the National Aeronautics and Space Administration.
5# All Rights Reserved.
6#
7# Copyright 2010 OpenStack LLC.
8#
9# Licensed under the Apache License, Version 2.0 (the "License"); you may
10# not use this file except in compliance with the License. You may obtain
11# a copy of the License at
12#
13# http://www.apache.org/licenses/LICENSE-2.0
14#
15# Unless required by applicable law or agreed to in writing, software
16# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
17# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
18# License for the specific language governing permissions and limitations
19# under the License.
20
21"""
22Installation script for Quantum's development virtualenv
23"""
24
25import os
26import subprocess
27import sys
28
29
30ROOT = os.path.dirname(os.path.dirname(os.path.realpath(__file__)))
31VENV = os.path.join(ROOT, '.quantum-venv')
32PIP_REQUIRES = os.path.join(ROOT, 'tools', 'pip-requires')
33
34
35def die(message, *args):
36 print >> sys.stderr, message % args
37 sys.exit(1)
38
39
40def run_command(cmd, redirect_output=True, check_exit_code=True):
41 """
42 Runs a command in an out-of-process shell, returning the
43 output of that command. Working directory is ROOT.
44 """
45 if redirect_output:
46 stdout = subprocess.PIPE
47 else:
48 stdout = None
49
50 proc = subprocess.Popen(cmd, cwd=ROOT, stdout=stdout)
51 output = proc.communicate()[0]
52 if check_exit_code and proc.returncode != 0:
53 die('Command "%s" failed.\n%s', ' '.join(cmd), output)
54 return output
55
56
57HAS_EASY_INSTALL = bool(run_command(['which', 'easy_install'],
58 check_exit_code=False).strip())
59HAS_VIRTUALENV = bool(run_command(['which', 'virtualenv'],
60 check_exit_code=False).strip())
61
62
63def check_dependencies():
64 """Make sure virtualenv is in the path."""
65
66 if not HAS_VIRTUALENV:
67 print 'not found.'
68 # Try installing it via easy_install...
69 if HAS_EASY_INSTALL:
70 print 'Installing virtualenv via easy_install...',
71 if not run_command(['which', 'easy_install']):
72 die('ERROR: virtualenv not found.\n\n'
73 'Quantum requires virtualenv, please install'
74 ' it using your favorite package management tool')
75 print 'done.'
76 print 'done.'
77
78
79def create_virtualenv(venv=VENV):
80 """Creates the virtual environment and installs PIP only into the
81 virtual environment
82 """
83 print 'Creating venv...',
84 run_command(['virtualenv', '-q', '--no-site-packages', VENV])
85 print 'done.'
86 print 'Installing pip in virtualenv...',
87 if not run_command(['tools/with_venv.sh', 'easy_install', 'pip']).strip():
88 die("Failed to install pip.")
89 print 'done.'
90
91
92def install_dependencies(venv=VENV):
93 print 'Installing dependencies with pip (this can take a while)...'
94
95 # Install greenlet by hand - just listing it in the requires file does not
96 # get it in stalled in the right order
97 venv_tool = 'tools/with_venv.sh'
98 run_command([venv_tool, 'pip', 'install', '-E', venv, '-r', PIP_REQUIRES],
99 redirect_output=False)
100
101 # Tell the virtual env how to "import quantum"
102 pthfile = os.path.join(venv, "lib", "python2.6", "site-packages",
103 "quantum.pth")
104 f = open(pthfile, 'w')
105 f.write("%s\n" % ROOT)
106
107
108def print_help():
109 help = """
110 Quantum development environment setup is complete.
111
112 Quantum development uses virtualenv to track and manage Python dependencies
113 while in development and testing.
114
115 To activate the Quantum virtualenv for the extent of your current shell
116 session you can run:
117
118 $ source .quantum-venv/bin/activate
119
120 Or, if you prefer, you can run commands in the virtualenv on a case by case
121 basis by running:
122
123 $ tools/with_venv.sh <your command>
124
125 Also, make test will automatically use the virtualenv.
126 """
127 print help
128
129
130def main(argv):
131 check_dependencies()
132 create_virtualenv()
133 install_dependencies()
134 print_help()
135
136if __name__ == '__main__':
137 main(sys.argv)
0138
=== added file 'tools/pip-requires'
--- tools/pip-requires 1970-01-01 00:00:00 +0000
+++ tools/pip-requires 2011-06-09 05:01:27 +0000
@@ -0,0 +1,10 @@
1eventlet>=0.9.12
2nose
3Paste
4PasteDeploy
5pep8==0.5.0
6python-gflags
7routes
8simplejson
9webob
10webtest
011
=== added file 'tools/with_venv.sh'
--- tools/with_venv.sh 1970-01-01 00:00:00 +0000
+++ tools/with_venv.sh 2011-06-09 05:01:27 +0000
@@ -0,0 +1,21 @@
1#!/bin/bash
2# vim: tabstop=4 shiftwidth=4 softtabstop=4
3
4# Copyright 2011 OpenStack LLC.
5# All Rights Reserved.
6#
7# Licensed under the Apache License, Version 2.0 (the "License"); you may
8# not use this file except in compliance with the License. You may obtain
9# a copy of the License at
10#
11# http://www.apache.org/licenses/LICENSE-2.0
12#
13# Unless required by applicable law or agreed to in writing, software
14# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
15# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
16# License for the specific language governing permissions and limitations
17# under the License.
18
19TOOLS=`dirname $0`
20VENV=$TOOLS/../.quantum-venv
21source $VENV/bin/activate && $@

Subscribers

People subscribed via source and target branches