Merge lp:~danwent/neutron/ovs-plugin-fixes into lp:neutron/diablo
- ovs-plugin-fixes
- Merge into diablo
Status: | Merged |
---|---|
Merge reported by: | Somik Behera |
Merged at revision: | not available |
Proposed branch: | lp:~danwent/neutron/ovs-plugin-fixes |
Merge into: | lp:neutron/diablo |
Diff against target: |
547 lines (+239/-110) 10 files modified
quantum/cli.py (+3/-1) quantum/db/api.py (+10/-9) quantum/plugins/openvswitch/README (+7/-2) quantum/plugins/openvswitch/agent/ovs_quantum_agent.py (+42/-43) quantum/plugins/openvswitch/agent/set_external_ids.sh (+0/-15) quantum/plugins/openvswitch/ovs_db.py (+0/-18) quantum/plugins/openvswitch/ovs_models.py (+0/-17) quantum/plugins/openvswitch/ovs_quantum_plugin.ini (+3/-3) quantum/plugins/openvswitch/ovs_quantum_plugin.py (+0/-2) tools/batch_config.py (+174/-0) |
To merge this branch: | bzr merge lp:~danwent/neutron/ovs-plugin-fixes |
Related bugs: |
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Somik Behera | netstack-core | Approve | |
Brad Hall (community) | Approve | ||
Review via email: mp+66024@code.launchpad.net |
Commit message
Description of the change
This branch started out just aiming to tweak the OVS plugin so it could run on hypervisors other than XenServer. In the course, I fixed a couple of bugs in the OVS plugin and in the DB models created for the OVS plugin.
I also introduced a new tool called "batch_config.py" that simplifies testing by making it easy to create network topologies using a single CLI command, instead of copying and pasting UUIDs from individual calls.
Other than batch_config.py, there should be no new functionality, only fixes.
This code has been pretty heavily tested with libvirt, and Brad has tested it with XenServer.
dan wendlandt (danwent) wrote : | # |
Somik Behera (somikbehera) wrote : | # |
1) There is 1 pep8 violation, trunk is pep8 clean, so we should make sure to get this merge cleaned up too.
./quantum/
2) quantum/cli.py should raise an exception when it cant set attachement
except exc.NoResultFound:
pass
But, this bug was pre-existing, I'll file something for this.
3) Otherwise, everything looks good! If you can just fix the pep8 change, we can get this bug fix merged in!
- 23. By dan wendlandt
-
fix pep8 introduced by trunk merge
Somik Behera (somikbehera) wrote : | # |
Thanks for fixing that pep8 change that had slipped in!
Got to keep the trunk pep8 clean.
Preview Diff
1 | === modified file 'quantum/cli.py' |
2 | --- quantum/cli.py 2011-06-24 18:20:03 +0000 |
3 | +++ quantum/cli.py 2011-06-27 23:11:53 +0000 |
4 | @@ -240,6 +240,7 @@ |
5 | |
6 | def delete_port(manager, *args): |
7 | tid, nid, pid = args |
8 | + manager.delete_port(tid, nid, pid) |
9 | LOG.info("Deleted Virtual Port:%s " \ |
10 | "on Virtual Network:%s" % (pid, nid)) |
11 | |
12 | @@ -318,7 +319,8 @@ |
13 | output = res.read() |
14 | LOG.debug(output) |
15 | if res.status != 202: |
16 | - LOG.error("Failed to unplug iface from port \"%s\": %s" % (pid, output)) |
17 | + LOG.error("Failed to unplug iface from port \"%s\": %s" % \ |
18 | + (pid, output)) |
19 | return |
20 | print "Unplugged interface from port:%s on network:%s" % (pid, nid) |
21 | |
22 | |
23 | === modified file 'quantum/db/api.py' |
24 | --- quantum/db/api.py 2011-06-08 10:21:47 +0000 |
25 | +++ quantum/db/api.py 2011-06-27 23:11:53 +0000 |
26 | @@ -72,7 +72,7 @@ |
27 | net = None |
28 | try: |
29 | net = session.query(models.Network).\ |
30 | - filter_by(name=name).\ |
31 | + filter_by(tenant_id=tenant_id, name=name).\ |
32 | one() |
33 | raise Exception("Network with name \"%s\" already exists" % name) |
34 | except exc.NoResultFound: |
35 | @@ -104,7 +104,7 @@ |
36 | session = get_session() |
37 | try: |
38 | res = session.query(models.Network).\ |
39 | - filter_by(name=new_name).\ |
40 | + filter_by(tenant_id=tenant_id, name=new_name).\ |
41 | one() |
42 | except exc.NoResultFound: |
43 | net = network_get(net_id) |
44 | @@ -156,13 +156,14 @@ |
45 | |
46 | def port_set_attachment(port_id, new_interface_id): |
47 | session = get_session() |
48 | - ports = None |
49 | - try: |
50 | - ports = session.query(models.Port).\ |
51 | - filter_by(interface_id=new_interface_id).\ |
52 | - all() |
53 | - except exc.NoResultFound: |
54 | - pass |
55 | + ports = [] |
56 | + if new_interface_id != "": |
57 | + try: |
58 | + ports = session.query(models.Port).\ |
59 | + filter_by(interface_id=new_interface_id).\ |
60 | + all() |
61 | + except exc.NoResultFound: |
62 | + pass |
63 | if len(ports) == 0: |
64 | port = port_get(port_id) |
65 | port.interface_id = new_interface_id |
66 | |
67 | === modified file 'quantum/plugins/openvswitch/README' |
68 | --- quantum/plugins/openvswitch/README 2011-06-09 14:42:19 +0000 |
69 | +++ quantum/plugins/openvswitch/README 2011-06-27 23:11:53 +0000 |
70 | @@ -62,20 +62,25 @@ |
71 | distribution tarball (see below) and the agent will use the credentials here |
72 | to access the database. |
73 | |
74 | -# -- Agent configuration |
75 | +# -- XenServer Agent configuration |
76 | |
77 | - Create the agent distribution tarball |
78 | |
79 | $ make agent-dist |
80 | - Copy the resulting tarball to your xenserver(s) (copy to dom0, not the nova |
81 | compute node) |
82 | -- Unpack the tarball and run install.sh. This will install all of the |
83 | +- Unpack the tarball and run xenserver_install.sh. This will install all of the |
84 | necessary pieces into /etc/xapi.d/plugins. It will also spit out the name |
85 | of the integration bridge that you'll need for your nova configuration. |
86 | Make sure to specify this in your nova flagfile as --flat_network_bridge. |
87 | - Run the agent [on your hypervisor (dom0)]: |
88 | $ /etc/xapi.d/plugins/ovs_quantum_agent.py /etc/xapi.d/plugins/ovs_quantum_plugin.ini |
89 | |
90 | +# -- KVM Agent configuration |
91 | + |
92 | +- Copy ovs_quantum_agent.py and ovs_quantum_plugin.ini to the Linux host and run: |
93 | +$ python ovs_quantum_agent.py ovs_quantum_plugin.ini |
94 | + |
95 | # -- Getting quantum up and running |
96 | |
97 | - Start quantum [on the quantum service host]: |
98 | |
99 | === modified file 'quantum/plugins/openvswitch/agent/ovs_quantum_agent.py' |
100 | --- quantum/plugins/openvswitch/agent/ovs_quantum_agent.py 2011-06-08 10:21:47 +0000 |
101 | +++ quantum/plugins/openvswitch/agent/ovs_quantum_agent.py 2011-06-27 23:11:53 +0000 |
102 | @@ -130,40 +130,40 @@ |
103 | def get_port_stats(self, port_name): |
104 | return self.db_get_map("Interface", port_name, "statistics") |
105 | |
106 | + # this is a hack that should go away once nova properly reports bindings |
107 | + # to quantum. We have this here for now as it lets us work with |
108 | + # unmodified nova |
109 | + def xapi_get_port(self, name): |
110 | + external_ids = self.db_get_map("Interface", name, "external_ids") |
111 | + if "attached-mac" not in external_ids: |
112 | + return None |
113 | + vm_uuid = external_ids.get("xs-vm-uuid", "") |
114 | + if len(vm_uuid) == 0: |
115 | + return None |
116 | + LOG.debug("iface-id not set, got xs-vm-uuid: %s" % vm_uuid) |
117 | + res = os.popen("xe vm-list uuid=%s params=name-label --minimal" \ |
118 | + % vm_uuid).readline().strip() |
119 | + if len(res) == 0: |
120 | + return None |
121 | + external_ids["iface-id"] = res |
122 | + LOG.info("Setting interface \"%s\" iface-id to \"%s\"" % (name, res)) |
123 | + self.set_db_attribute("Interface", name, |
124 | + "external-ids:iface-id", res) |
125 | + ofport = self.db_get_val("Interface", name, "ofport") |
126 | + return VifPort(name, ofport, external_ids["iface-id"], |
127 | + external_ids["attached-mac"], self) |
128 | + |
129 | # returns a VIF object for each VIF port |
130 | def get_vif_ports(self): |
131 | edge_ports = [] |
132 | port_names = self.get_port_name_list() |
133 | for name in port_names: |
134 | external_ids = self.db_get_map("Interface", name, "external_ids") |
135 | - if "iface-id" in external_ids and "attached-mac" in external_ids: |
136 | - ofport = self.db_get_val("Interface", name, "ofport") |
137 | - p = VifPort(name, ofport, external_ids["iface-id"], |
138 | - external_ids["attached-mac"], self) |
139 | - edge_ports.append(p) |
140 | - else: |
141 | - # iface-id might not be set. See if we can figure it out and |
142 | - # set it here. |
143 | - external_ids = self.db_get_map("Interface", name, |
144 | - "external_ids") |
145 | - if "attached-mac" not in external_ids: |
146 | - continue |
147 | - vif_uuid = external_ids.get("xs-vif-uuid", "") |
148 | - if len(vif_uuid) == 0: |
149 | - continue |
150 | - LOG.debug("iface-id not set, got vif-uuid: %s" % vif_uuid) |
151 | - res = os.popen("xe vif-param-get param-name=other-config " |
152 | - "uuid=%s | grep nicira-iface-id | " |
153 | - "awk '{print $2}'" |
154 | - % vif_uuid).readline() |
155 | - res = res.strip() |
156 | - if len(res) == 0: |
157 | - continue |
158 | - external_ids["iface-id"] = res |
159 | - LOG.info("Setting interface \"%s\" iface-id to \"%s\"" |
160 | - % (name, res)) |
161 | - self.set_db_attribute("Interface", name, |
162 | - "external-ids:iface-id", res) |
163 | + if "xs-vm-uuid" in external_ids: |
164 | + p = xapi_get_port(name) |
165 | + if p is not None: |
166 | + edge_ports.append(p) |
167 | + elif "iface-id" in external_ids and "attached-mac" in external_ids: |
168 | ofport = self.db_get_val("Interface", name, "ofport") |
169 | p = VifPort(name, ofport, external_ids["iface-id"], |
170 | external_ids["attached-mac"], self) |
171 | @@ -171,13 +171,15 @@ |
172 | return edge_ports |
173 | |
174 | |
175 | -class OVSNaaSPlugin: |
176 | +class OVSQuantumAgent: |
177 | + |
178 | def __init__(self, integ_br): |
179 | self.setup_integration_br(integ_br) |
180 | |
181 | def port_bound(self, port, vlan_id): |
182 | self.int_br.set_db_attribute("Port", port.port_name, "tag", |
183 | - str(vlan_id)) |
184 | + str(vlan_id)) |
185 | + self.int_br.delete_flows(match="in_port=%s" % port.ofport) |
186 | |
187 | def port_unbound(self, port, still_exists): |
188 | if still_exists: |
189 | @@ -186,13 +188,8 @@ |
190 | def setup_integration_br(self, integ_br): |
191 | self.int_br = OVSBridge(integ_br) |
192 | self.int_br.remove_all_flows() |
193 | - # drop all traffic on the 'dead vlan' |
194 | - self.int_br.add_flow(priority=2, match="dl_vlan=4095", actions="drop") |
195 | - # switch all other traffic using L2 learning |
196 | + # switch all traffic using L2 learning |
197 | self.int_br.add_flow(priority=1, actions="normal") |
198 | - # FIXME send broadcast everywhere, regardless of tenant |
199 | - #int_br.add_flow(priority=3, match="dl_dst=ff:ff:ff:ff:ff:ff", |
200 | - # actions="normal") |
201 | |
202 | def daemon_loop(self, conn): |
203 | self.local_vlan_map = {} |
204 | @@ -201,7 +198,7 @@ |
205 | |
206 | while True: |
207 | cursor = conn.cursor() |
208 | - cursor.execute("SELECT * FROM network_bindings") |
209 | + cursor.execute("SELECT * FROM ports") |
210 | rows = cursor.fetchall() |
211 | cursor.close() |
212 | all_bindings = {} |
213 | @@ -226,22 +223,26 @@ |
214 | else: |
215 | # no binding, put him on the 'dead vlan' |
216 | self.int_br.set_db_attribute("Port", p.port_name, "tag", |
217 | - "4095") |
218 | + "4095") |
219 | + self.int_br.add_flow(priority=2, |
220 | + match="in_port=%s" % p.ofport, actions="drop") |
221 | + |
222 | old_b = old_local_bindings.get(p.vif_id, None) |
223 | new_b = new_local_bindings.get(p.vif_id, None) |
224 | + |
225 | if old_b != new_b: |
226 | if old_b is not None: |
227 | LOG.info("Removing binding to net-id = %s for %s" |
228 | % (old_b, str(p))) |
229 | self.port_unbound(p, True) |
230 | if new_b is not None: |
231 | - LOG.info("Adding binding to net-id = %s for %s" \ |
232 | - % (new_b, str(p))) |
233 | # If we don't have a binding we have to stick it on |
234 | # the dead vlan |
235 | vlan_id = vlan_bindings.get(all_bindings[p.vif_id], |
236 | "4095") |
237 | self.port_bound(p, vlan_id) |
238 | + LOG.info("Adding binding to net-id = %s " \ |
239 | + "for %s on vlan %s" % (new_b, str(p), vlan_id)) |
240 | for vif_id in old_vif_ports.keys(): |
241 | if vif_id not in new_vif_ports: |
242 | LOG.info("Port Disappeared: %s" % vif_id) |
243 | @@ -251,8 +252,6 @@ |
244 | |
245 | old_vif_ports = new_vif_ports |
246 | old_local_bindings = new_local_bindings |
247 | - self.int_br.run_cmd(["bash", |
248 | - "/etc/xapi.d/plugins/set_external_ids.sh"]) |
249 | time.sleep(2) |
250 | |
251 | if __name__ == "__main__": |
252 | @@ -291,7 +290,7 @@ |
253 | LOG.info("Connecting to database \"%s\" on %s" % (db_name, db_host)) |
254 | conn = MySQLdb.connect(host=db_host, user=db_user, |
255 | passwd=db_pass, db=db_name) |
256 | - plugin = OVSNaaSPlugin(integ_br) |
257 | + plugin = OVSQuantumAgent(integ_br) |
258 | plugin.daemon_loop(conn) |
259 | finally: |
260 | if conn: |
261 | |
262 | === removed file 'quantum/plugins/openvswitch/agent/set_external_ids.sh' |
263 | --- quantum/plugins/openvswitch/agent/set_external_ids.sh 2011-06-04 03:59:49 +0000 |
264 | +++ quantum/plugins/openvswitch/agent/set_external_ids.sh 1970-01-01 00:00:00 +0000 |
265 | @@ -1,15 +0,0 @@ |
266 | -#!/bin/sh |
267 | -VIFLIST=`xe vif-list params=uuid --minimal | sed s/,/" "/g` |
268 | -for VIF_UUID in $VIFLIST; do |
269 | -DEVICE_NUM=`xe vif-list params=device uuid=$VIF_UUID --minimal` |
270 | - VM_NAME=`xe vif-list params=vm-name-label uuid=$VIF_UUID --minimal` |
271 | - NAME="$VM_NAME-eth$DEVICE_NUM" |
272 | - echo "Vif: $VIF_UUID is '$NAME'" |
273 | - xe vif-param-set uuid=$VIF_UUID other-config:nicira-iface-id="$NAME" |
274 | -done |
275 | - |
276 | -ps auxw | grep -v grep | grep ovs-xapi-sync > /dev/null 2>&1 |
277 | -if [ $? -eq 0 ]; then |
278 | - killall -HUP ovs-xapi-sync |
279 | -fi |
280 | - |
281 | |
282 | === renamed file 'quantum/plugins/openvswitch/agent/install.sh' => 'quantum/plugins/openvswitch/agent/xenserver_install.sh' |
283 | === modified file 'quantum/plugins/openvswitch/ovs_db.py' |
284 | --- quantum/plugins/openvswitch/ovs_db.py 2011-06-08 10:21:47 +0000 |
285 | +++ quantum/plugins/openvswitch/ovs_db.py 2011-06-27 23:11:53 +0000 |
286 | @@ -56,21 +56,3 @@ |
287 | except exc.NoResultFound: |
288 | pass |
289 | session.flush() |
290 | - |
291 | - |
292 | -def update_network_binding(netid, ifaceid): |
293 | - session = db.get_session() |
294 | - # Add to or delete from the bindings table |
295 | - if ifaceid == None: |
296 | - try: |
297 | - binding = session.query(ovs_models.NetworkBinding).\ |
298 | - filter_by(network_id=netid).\ |
299 | - one() |
300 | - session.delete(binding) |
301 | - except exc.NoResultFound: |
302 | - raise Exception("No binding found with network_id = %s" % netid) |
303 | - else: |
304 | - binding = ovs_models.NetworkBinding(netid, ifaceid) |
305 | - session.add(binding) |
306 | - |
307 | - session.flush() |
308 | |
309 | === modified file 'quantum/plugins/openvswitch/ovs_models.py' |
310 | --- quantum/plugins/openvswitch/ovs_models.py 2011-06-08 10:21:47 +0000 |
311 | +++ quantum/plugins/openvswitch/ovs_models.py 2011-06-27 23:11:53 +0000 |
312 | @@ -26,23 +26,6 @@ |
313 | from quantum.db.models import BASE |
314 | |
315 | |
316 | -class NetworkBinding(BASE): |
317 | - """Represents a binding of network_id, vif_id""" |
318 | - __tablename__ = 'network_bindings' |
319 | - |
320 | - id = Column(Integer, primary_key=True, autoincrement=True) |
321 | - network_id = Column(String(255)) |
322 | - vif_id = Column(String(255)) |
323 | - |
324 | - def __init__(self, network_id, vif_id): |
325 | - self.network_id = network_id |
326 | - self.vif_id = vif_id |
327 | - |
328 | - def __repr__(self): |
329 | - return "<NetworkBinding(%s,%s)>" % \ |
330 | - (self.network_id, self.vif_id) |
331 | - |
332 | - |
333 | class VlanBinding(BASE): |
334 | """Represents a binding of network_id, vlan_id""" |
335 | __tablename__ = 'vlan_bindings' |
336 | |
337 | === modified file 'quantum/plugins/openvswitch/ovs_quantum_plugin.ini' |
338 | --- quantum/plugins/openvswitch/ovs_quantum_plugin.ini 2011-06-04 03:56:32 +0000 |
339 | +++ quantum/plugins/openvswitch/ovs_quantum_plugin.ini 2011-06-27 23:11:53 +0000 |
340 | @@ -1,9 +1,9 @@ |
341 | [DATABASE] |
342 | -name = ovs_naas |
343 | +name = ovs_quantum |
344 | user = root |
345 | -pass = foobar |
346 | +pass = nova |
347 | host = 127.0.0.1 |
348 | port = 3306 |
349 | |
350 | [OVS] |
351 | -integration-bridge = xapi1 |
352 | +integration-bridge = br100 |
353 | |
354 | === modified file 'quantum/plugins/openvswitch/ovs_quantum_plugin.py' |
355 | --- quantum/plugins/openvswitch/ovs_quantum_plugin.py 2011-06-08 10:21:47 +0000 |
356 | +++ quantum/plugins/openvswitch/ovs_quantum_plugin.py 2011-06-27 23:11:53 +0000 |
357 | @@ -200,11 +200,9 @@ |
358 | |
359 | def plug_interface(self, tenant_id, net_id, port_id, remote_iface_id): |
360 | db.port_set_attachment(port_id, remote_iface_id) |
361 | - ovs_db.update_network_binding(net_id, remote_iface_id) |
362 | |
363 | def unplug_interface(self, tenant_id, net_id, port_id): |
364 | db.port_set_attachment(port_id, "") |
365 | - ovs_db.update_network_binding(net_id, None) |
366 | |
367 | def get_interface_details(self, tenant_id, net_id, port_id): |
368 | res = db.port_get(port_id) |
369 | |
370 | === added file 'tools/batch_config.py' |
371 | --- tools/batch_config.py 1970-01-01 00:00:00 +0000 |
372 | +++ tools/batch_config.py 2011-06-27 23:11:53 +0000 |
373 | @@ -0,0 +1,174 @@ |
374 | +# vim: tabstop=4 shiftwidth=4 softtabstop=4 |
375 | + |
376 | +# Copyright 2011 Nicira Networks, Inc. |
377 | +# |
378 | +# Licensed under the Apache License, Version 2.0 (the "License"); you may |
379 | +# not use this file except in compliance with the License. You may obtain |
380 | +# a copy of the License at |
381 | +# |
382 | +# http://www.apache.org/licenses/LICENSE-2.0 |
383 | +# |
384 | +# Unless required by applicable law or agreed to in writing, software |
385 | +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT |
386 | +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the |
387 | +# License for the specific language governing permissions and limitations |
388 | +# under the License. |
389 | +# @author: Dan Wendlandt, Nicira Networks, Inc. |
390 | + |
391 | +import httplib |
392 | +import logging as LOG |
393 | +import json |
394 | +import socket |
395 | +import sys |
396 | +import urllib |
397 | + |
398 | +from quantum.manager import QuantumManager |
399 | +from optparse import OptionParser |
400 | +from quantum.common.wsgi import Serializer |
401 | +from quantum.cli import MiniClient |
402 | + |
403 | +FORMAT = "json" |
404 | +CONTENT_TYPE = "application/" + FORMAT |
405 | + |
406 | + |
407 | +def delete_all_nets(client, tenant_id): |
408 | + res = client.do_request(tenant_id, 'GET', "/networks." + FORMAT) |
409 | + resdict = json.loads(res.read()) |
410 | + LOG.debug(resdict) |
411 | + for n in resdict["networks"]: |
412 | + nid = n["id"] |
413 | + |
414 | + res = client.do_request(tenant_id, 'GET', |
415 | + "/networks/%s/ports.%s" % (nid, FORMAT)) |
416 | + output = res.read() |
417 | + if res.status != 200: |
418 | + LOG.error("Failed to list ports: %s" % output) |
419 | + continue |
420 | + rd = json.loads(output) |
421 | + LOG.debug(rd) |
422 | + for port in rd["ports"]: |
423 | + pid = port["id"] |
424 | + |
425 | + data = {'port': {'attachment-id': ''}} |
426 | + body = Serializer().serialize(data, CONTENT_TYPE) |
427 | + res = client.do_request(tenant_id, 'DELETE', |
428 | + "/networks/%s/ports/%s/attachment.%s" % \ |
429 | + (nid, pid, FORMAT), body=body) |
430 | + output = res.read() |
431 | + LOG.debug(output) |
432 | + if res.status != 202: |
433 | + LOG.error("Failed to unplug iface from port \"%s\": %s" % (vid, |
434 | + pid, output)) |
435 | + continue |
436 | + LOG.info("Unplugged interface from port:%s on network:%s" % (pid, |
437 | + nid)) |
438 | + |
439 | + res = client.do_request(tenant_id, 'DELETE', |
440 | + "/networks/%s/ports/%s.%s" % (nid, pid, FORMAT)) |
441 | + output = res.read() |
442 | + if res.status != 202: |
443 | + LOG.error("Failed to delete port: %s" % output) |
444 | + continue |
445 | + print "Deleted Virtual Port:%s " \ |
446 | + "on Virtual Network:%s" % (pid, nid) |
447 | + |
448 | + res = client.do_request(tenant_id, 'DELETE', |
449 | + "/networks/" + nid + "." + FORMAT) |
450 | + status = res.status |
451 | + if status != 202: |
452 | + Log.error("Failed to delete network: %s" % nid) |
453 | + output = res.read() |
454 | + print output |
455 | + else: |
456 | + print "Deleted Virtual Network with ID:%s" % nid |
457 | + |
458 | + |
459 | +def create_net_with_attachments(net_name, iface_ids): |
460 | + data = {'network': {'network-name': '%s' % net_name}} |
461 | + body = Serializer().serialize(data, CONTENT_TYPE) |
462 | + res = client.do_request(tenant_id, 'POST', |
463 | + "/networks." + FORMAT, body=body) |
464 | + rd = json.loads(res.read()) |
465 | + LOG.debug(rd) |
466 | + nid = rd["networks"]["network"]["id"] |
467 | + print "Created a new Virtual Network %s with ID:%s" % (net_name, nid) |
468 | + |
469 | + for iface_id in iface_ids: |
470 | + res = client.do_request(tenant_id, 'POST', |
471 | + "/networks/%s/ports.%s" % (nid, FORMAT)) |
472 | + output = res.read() |
473 | + if res.status != 200: |
474 | + LOG.error("Failed to create port: %s" % output) |
475 | + continue |
476 | + rd = json.loads(output) |
477 | + new_port_id = rd["ports"]["port"]["id"] |
478 | + print "Created Virtual Port:%s " \ |
479 | + "on Virtual Network:%s" % (new_port_id, nid) |
480 | + data = {'port': {'attachment-id': '%s' % iface_id}} |
481 | + body = Serializer().serialize(data, CONTENT_TYPE) |
482 | + res = client.do_request(tenant_id, 'PUT', |
483 | + "/networks/%s/ports/%s/attachment.%s" %\ |
484 | + (nid, new_port_id, FORMAT), body=body) |
485 | + output = res.read() |
486 | + LOG.debug(output) |
487 | + if res.status != 202: |
488 | + LOG.error("Failed to plug iface \"%s\" to port \"%s\": %s" % \ |
489 | + (iface_id, new_port_id, output)) |
490 | + continue |
491 | + print "Plugged interface \"%s\" to port:%s on network:%s" % \ |
492 | + (iface_id, new_port_id, nid) |
493 | + |
494 | +if __name__ == "__main__": |
495 | + usagestr = "Usage: %prog [OPTIONS] <tenant-id> <config-string> [args]\n" \ |
496 | + "Example config-string: net1=instance-1,instance-2"\ |
497 | + ":net2=instance-3,instance-4\n" \ |
498 | + "This string would create two networks: \n" \ |
499 | + "'net1' would have two ports, with iface-ids "\ |
500 | + "instance-1 and instance-2 attached\n" \ |
501 | + "'net2' would have two ports, with iface-ids"\ |
502 | + " instance-3 and instance-4 attached\n" |
503 | + parser = OptionParser(usage=usagestr) |
504 | + parser.add_option("-H", "--host", dest="host", |
505 | + type="string", default="127.0.0.1", help="ip address of api host") |
506 | + parser.add_option("-p", "--port", dest="port", |
507 | + type="int", default=9696, help="api poort") |
508 | + parser.add_option("-s", "--ssl", dest="ssl", |
509 | + action="store_true", default=False, help="use ssl") |
510 | + parser.add_option("-v", "--verbose", dest="verbose", |
511 | + action="store_true", default=False, help="turn on verbose logging") |
512 | + parser.add_option("-d", "--delete", dest="delete", |
513 | + action="store_true", default=False, \ |
514 | + help="delete existing tenants networks") |
515 | + |
516 | + options, args = parser.parse_args() |
517 | + |
518 | + if options.verbose: |
519 | + LOG.basicConfig(level=LOG.DEBUG) |
520 | + else: |
521 | + LOG.basicConfig(level=LOG.WARN) |
522 | + |
523 | + if len(args) < 1: |
524 | + parser.print_help() |
525 | + help() |
526 | + sys.exit(1) |
527 | + |
528 | + nets = {} |
529 | + tenant_id = args[0] |
530 | + if len(args) > 1: |
531 | + config_str = args[1] |
532 | + for net_str in config_str.split(":"): |
533 | + arr = net_str.split("=") |
534 | + net_name = arr[0] |
535 | + nets[net_name] = arr[1].split(",") |
536 | + |
537 | + print "nets: %s" % str(nets) |
538 | + |
539 | + client = MiniClient(options.host, options.port, options.ssl) |
540 | + |
541 | + if options.delete: |
542 | + delete_all_nets(client, tenant_id) |
543 | + |
544 | + for net_name, iface_ids in nets.items(): |
545 | + create_net_with_attachments(net_name, iface_ids) |
546 | + |
547 | + sys.exit(0) |
One comment, you will notice a change that makes it permissible for multiple ports to have the empty string as an attachment. This was because the CLI actually performs an unplug using the empty string:
def api_unplug_ iface(client, *args):
tid, nid, pid = args
data = {'port': {'attachment-id': ''}}
However, I actually think it would be cleaner if this happened by setting attachment-id to a null/none value. We should check with Salvatore on this.