Merge lp:~vishvananda/nova/ha-net into lp:~hudson-openstack/nova/trunk

Proposed by Vish Ishaya
Status: Merged
Approved by: Devin Carlen
Approved revision: 1266
Merged at revision: 1293
Proposed branch: lp:~vishvananda/nova/ha-net
Merge into: lp:~hudson-openstack/nova/trunk
Diff against target: 1104 lines (+280/-175)
15 files modified
bin/nova-dhcpbridge (+1/-1)
bin/nova-manage (+12/-7)
nova/compute/manager.py (+9/-8)
nova/db/api.py (+12/-6)
nova/db/sqlalchemy/api.py (+32/-11)
nova/db/sqlalchemy/migrate_repo/versions/033_ha_network.py (+44/-0)
nova/db/sqlalchemy/models.py (+2/-0)
nova/exception.py (+5/-0)
nova/network/api.py (+14/-4)
nova/network/linux_net.py (+20/-20)
nova/network/manager.py (+121/-100)
nova/tests/__init__.py (+2/-1)
nova/tests/test_libvirt.py (+1/-0)
nova/tests/test_network.py (+4/-15)
nova/virt/libvirt/connection.py (+1/-2)
To merge this branch: bzr merge lp:~vishvananda/nova/ha-net
Reviewer Review Type Date Requested Status
Devin Carlen (community) Approve
Dan Prince (community) Approve
Koji Iida (community) Needs Information
Review via email: mp+67078@code.launchpad.net

Commit message

Adds HA networking (multi_host) option to networks.

* Adds extra flag for network creation: multi_host.
* Multi hosts networks will send all network related commands to the host that the vm is on.
* Requires nova-network to be run on every compute host.
* Non multi_host networks work the same way.
* Moved extra db access out of linux_net (there is still a little in the dhcp leases part that should probably be moved)
* Fixed the logic on auto_assign which was messed up

Description of the change

Adds HA networking option to networks.

* Adds extra flag for network creation: multi_host.
* Multi hosts networks will send all network related commands to the host that the vm is on.
* Requires nova-network to be run on every compute host.
* Non multi_host networks work the same way.

Other cleanup:

* Moved extra db access out of linux_net (there is still a little in the dhcp leases part that should probably be moved)
* Fixed the logic on auto_assign which was messed up

Potential issues:
* The flag version means I'm checking for multi_host in a number of places. Ideas for changes here welcome.
* Nova-manage positional changes are annoying, we need the kwarg version in place
* No tests added. Open to suggestions on some specific tests that could be added.

To post a comment you must log in.
Revision history for this message
Jason Kölker (jason-koelker) wrote :

48: multi_host = multi_host == 'T'

I think something like:

multi_host = multi_host.lower()[:1] == 't'

so it can accept any "truthy" value would be more user friendly.

Revision history for this message
Mandell (mdegerne) wrote :

I like Jason's suggestion, although I would want to check for "y" as well.

I'm a bit concerned about the way which _on_set_network_host is being called. It seems to imply that all hosts are in a single HA network. Is it possible that there are multiple, disparate, HA networks?

Revision history for this message
Lucky (luckythetourist) wrote :

Other than the above I have a single nit:

On L583-584 the order of operations should be explicit

e.g.,

    (foo and bar) or baz

or

    foo and (bar or baz)

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

On Jul 7, 2011, at 3:13 PM, Mandell wrote:

> I like Jason's suggestion, although I would want to check for "y" as well.
>

Will fix. I was following the use of T in a few other places, so switching the others should be done in the branch that is adding in better opt parsing

> I'm a bit concerned about the way which _on_set_network_host is being called. It seems to imply that all hosts are in a single HA network. Is it possible that there are multiple, disparate, HA networks?

My current mental model says HA networks belong to all hosts. I think this is the most common use case. That said, I can change the init host to look for ha networks that already have an ip on this host, and only on_set_network_host there, then I can also call it from the associate call in _get_dhcp_ip. That should make the network only show up on the host if it is being used.

Vish

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

Cleaned up stuff and made changes to make setup of networks automatic if an ip is allocated and the network has not been setup yet.

Revision history for this message
Koji Iida (iida-koji) wrote :

I have a question.

I can't boot a instance under VlanManager and multi_host set to false.

2011-07-16 13:21:37,094 DEBUG nova.network.manager [H40YHKSORHMMV047X7BB admin admin] floating IP allocation for instance |1| from (pid=9900) allocate_for_instance /opt2/ha-net/nova/network/manager.py:195
2011-07-16 13:21:37,094 DEBUG nova.network.manager [H40YHKSORHMMV047X7BB admin admin] network allocations for instance 1 from (pid=9900) allocate_for_instance /opt2/ha-net/nova/network/manager.py:378
2011-07-16 13:21:37,102 ERROR nova [-] Exception during message handling
(nova): TRACE: Traceback (most recent call last):
(nova): TRACE: File "/opt2/ha-net/nova/rpc.py", line 232, in _process_data
(nova): TRACE: rval = node_func(context=ctxt, **node_args)
(nova): TRACE: File "/opt2/ha-net/nova/network/manager.py", line 199, in allocate_for_instance
(nova): TRACE: ips = super(FloatingIP, self).allocate_for_instance(context, **kwargs)
(nova): TRACE: File "/opt2/ha-net/nova/network/manager.py", line 380, in allocate_for_instance
(nova): TRACE: project_id)
(nova): TRACE: File "/opt2/ha-net/nova/network/manager.py", line 847, in _get_networks_for_instance
(nova): TRACE: return self.db.project_get_networks(context, project_id)
(nova): TRACE: File "/opt2/ha-net/nova/db/api.py", line 1234, in project_get_networks
(nova): TRACE: return IMPL.project_get_networks(context, project_id, associate)
(nova): TRACE: File "/opt2/ha-net/nova/db/sqlalchemy/api.py", line 115, in wrapper
(nova): TRACE: return f(*args, **kwargs)
(nova): TRACE: File "/opt2/ha-net/nova/db/sqlalchemy/api.py", line 2749, in project_get_networks
(nova): TRACE: return [network_associate(context, project_id)]
(nova): TRACE: File "/opt2/ha-net/nova/db/sqlalchemy/api.py", line 97, in wrapper
(nova): TRACE: return f(*args, **kwargs)
(nova): TRACE: File "/opt2/ha-net/nova/db/sqlalchemy/api.py", line 1516, in network_associate
(nova): TRACE: raise db.NoMoreNetworks()
(nova): TRACE: NoMoreNetworks: None
(nova): TRACE:

615 - # setup any new networks which have been created
616 - self.set_network_hosts(context)

set_network_hosts() is removed from periodic_tasks.
Are there any other mechanisms to allocate host to network?

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

Hmm, this could be a bug. It is supposed to set_network_host in the allocate_for_instance call. I must have missed one of the code paths. Will check.

Vish

On Jul 15, 2011, at 11:33 PM, Koji Iida wrote:

> Review: Needs Information
> I have a question.
>
> I can't boot a instance under VlanManager and multi_host set to false.
>
> 2011-07-16 13:21:37,094 DEBUG nova.network.manager [H40YHKSORHMMV047X7BB admin admin] floating IP allocation for instance |1| from (pid=9900) allocate_for_instance /opt2/ha-net/nova/network/manager.py:195
> 2011-07-16 13:21:37,094 DEBUG nova.network.manager [H40YHKSORHMMV047X7BB admin admin] network allocations for instance 1 from (pid=9900) allocate_for_instance /opt2/ha-net/nova/network/manager.py:378
> 2011-07-16 13:21:37,102 ERROR nova [-] Exception during message handling
> (nova): TRACE: Traceback (most recent call last):
> (nova): TRACE: File "/opt2/ha-net/nova/rpc.py", line 232, in _process_data
> (nova): TRACE: rval = node_func(context=ctxt, **node_args)
> (nova): TRACE: File "/opt2/ha-net/nova/network/manager.py", line 199, in allocate_for_instance
> (nova): TRACE: ips = super(FloatingIP, self).allocate_for_instance(context, **kwargs)
> (nova): TRACE: File "/opt2/ha-net/nova/network/manager.py", line 380, in allocate_for_instance
> (nova): TRACE: project_id)
> (nova): TRACE: File "/opt2/ha-net/nova/network/manager.py", line 847, in _get_networks_for_instance
> (nova): TRACE: return self.db.project_get_networks(context, project_id)
> (nova): TRACE: File "/opt2/ha-net/nova/db/api.py", line 1234, in project_get_networks
> (nova): TRACE: return IMPL.project_get_networks(context, project_id, associate)
> (nova): TRACE: File "/opt2/ha-net/nova/db/sqlalchemy/api.py", line 115, in wrapper
> (nova): TRACE: return f(*args, **kwargs)
> (nova): TRACE: File "/opt2/ha-net/nova/db/sqlalchemy/api.py", line 2749, in project_get_networks
> (nova): TRACE: return [network_associate(context, project_id)]
> (nova): TRACE: File "/opt2/ha-net/nova/db/sqlalchemy/api.py", line 97, in wrapper
> (nova): TRACE: return f(*args, **kwargs)
> (nova): TRACE: File "/opt2/ha-net/nova/db/sqlalchemy/api.py", line 1516, in network_associate
> (nova): TRACE: raise db.NoMoreNetworks()
> (nova): TRACE: NoMoreNetworks: None
> (nova): TRACE:
>
>
> 615 - # setup any new networks which have been created
> 616 - self.set_network_hosts(context)
>
> set_network_hosts() is removed from periodic_tasks.
> Are there any other mechanisms to allocate host to network?
>
>
>
>
> --
> https://code.launchpad.net/~vishvananda/nova/ha-net/+merge/67078
> You are the owner of lp:~vishvananda/nova/ha-net.

lp:~vishvananda/nova/ha-net updated
1259. By Vish Ishaya

fix issues that were breaking vlan mode

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

koji. I fixed the issue with vlan mode. Can you try again?

lp:~vishvananda/nova/ha-net updated
1260. By Vish Ishaya

merged trunk

1261. By Vish Ishaya

change migration number

1262. By Vish Ishaya

fix bad merge

1263. By Vish Ishaya

missed the vpn kwarg in rpc

Revision history for this message
Dan Prince (dan-prince) wrote :

Hey Vish,

Would you be up for adding a 'downgrade' to this migration. I know not all of our migrations have them but I'd prefer to follow that model.

Otherwise looks good. All functional tests appear to be passing on Smokestack too.

lp:~vishvananda/nova/ha-net updated
1264. By Vish Ishaya

add downgrade

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

Hey Dan,

Added a downgrade.

Revision history for this message
Dan Prince (dan-prince) wrote :

Great. Looks good.

review: Approve
lp:~vishvananda/nova/ha-net updated
1265. By Vish Ishaya

merged trunk

1266. By Vish Ishaya

pep8

Revision history for this message
Devin Carlen (devcamcar) wrote :

lgtm

review: Approve
Revision history for this message
Do Dinh Thang (thangdd-it49) wrote :

Hi Vish,
I setup & run nova-network in multi_host mode.
when I restart service nova-network on Node1, i must rerun "euca-associate-address" to associate floating IP to instance on Node1.
Is it a bug?

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

It should reassociate the floating ips on reboot. This sounds like a bug.

On Aug 16, 2011, at 2:11 AM, Do Dinh Thang wrote:

> Hi Vish,
> I setup & run nova-network in multi_host mode.
> when I restart service nova-network on Node1, i must rerun "euca-associate-address" to associate floating IP to instance on Node1.
> Is it a bug?
> --
> https://code.launchpad.net/~vishvananda/nova/ha-net/+merge/67078
> You are the owner of lp:~vishvananda/nova/ha-net.

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'bin/nova-dhcpbridge'
2--- bin/nova-dhcpbridge 2011-07-11 20:31:39 +0000
3+++ bin/nova-dhcpbridge 2011-07-20 18:28:31 +0000
4@@ -91,7 +91,7 @@
5 """Get the list of hosts for an interface."""
6 ctxt = context.get_admin_context()
7 network_ref = db.network_get_by_bridge(ctxt, interface)
8- return linux_net.get_dhcp_leases(ctxt, network_ref['id'])
9+ return linux_net.get_dhcp_leases(ctxt, network_ref)
10
11
12 def main():
13
14=== modified file 'bin/nova-manage'
15--- bin/nova-manage 2011-07-18 19:09:39 +0000
16+++ bin/nova-manage 2011-07-20 18:28:31 +0000
17@@ -479,7 +479,7 @@
18 except db.api.NoMoreNetworks:
19 print _('No more networks available. If this is a new '
20 'installation, you need\nto call something like this:\n\n'
21- ' nova-manage network create 10.0.0.0/8 10 64\n\n')
22+ ' nova-manage network create pvt 10.0.0.0/8 10 64\n\n')
23 except exception.ProcessExecutionError, e:
24 print e
25 print _("The above error may show that the certificate db has not "
26@@ -500,7 +500,7 @@
27 if host is None:
28 fixed_ips = db.fixed_ip_get_all(ctxt)
29 else:
30- fixed_ips = db.fixed_ip_get_all_by_host(ctxt, host)
31+ fixed_ips = db.fixed_ip_get_all_by_instance_host(ctxt, host)
32 except exception.NotFound as ex:
33 print "error: %s" % ex
34 sys.exit(2)
35@@ -564,17 +564,17 @@
36 """Class for managing networks."""
37
38 def create(self, label=None, fixed_range=None, num_networks=None,
39- network_size=None, vlan_start=None,
40+ network_size=None, multi_host=None, vlan_start=None,
41 vpn_start=None, fixed_range_v6=None, gateway_v6=None,
42 flat_network_bridge=None, bridge_interface=None):
43 """Creates fixed ips for host by range
44 arguments: label, fixed_range, [num_networks=FLAG],
45- [network_size=FLAG], [vlan_start=FLAG],
46+ [network_size=FLAG], [multi_host=FLAG], [vlan_start=FLAG],
47 [vpn_start=FLAG], [fixed_range_v6=FLAG], [gateway_v6=FLAG],
48 [flat_network_bridge=FLAG], [bridge_interface=FLAG]
49- If you wish to use a later argument fill in the gaps with 0s
50- Ex: network create private 10.0.0.0/8 1 15 0 0 0 0 xenbr1 eth1
51- network create private 10.0.0.0/8 1 15
52+ If you wish to use a later argument fill in the gaps with ""s
53+ Ex: network create private 10.0.0.0/8 1 16 T "" "" "" "" xenbr1 eth1
54+ network create private 10.0.0.0/8 1 16
55 """
56 if not label:
57 msg = _('a label (ex: public) is required to create networks.')
58@@ -589,6 +589,10 @@
59 num_networks = FLAGS.num_networks
60 if not network_size:
61 network_size = FLAGS.network_size
62+ if not multi_host:
63+ multi_host = FLAGS.multi_host
64+ else:
65+ multi_host = multi_host == 'T'
66 if not vlan_start:
67 vlan_start = FLAGS.vlan_start
68 if not vpn_start:
69@@ -607,6 +611,7 @@
70 net_manager.create_networks(context.get_admin_context(),
71 label=label,
72 cidr=fixed_range,
73+ multi_host=multi_host,
74 num_networks=int(num_networks),
75 network_size=int(network_size),
76 vlan_start=int(vlan_start),
77
78=== modified file 'nova/compute/manager.py'
79--- nova/compute/manager.py 2011-07-19 20:21:39 +0000
80+++ nova/compute/manager.py 2011-07-20 18:28:31 +0000
81@@ -77,8 +77,6 @@
82 flags.DEFINE_integer("rescue_timeout", 0,
83 "Automatically unrescue an instance after N seconds."
84 " Set to 0 to disable.")
85-flags.DEFINE_bool('auto_assign_floating_ip', False,
86- 'Autoassigning floating ip to VM')
87 flags.DEFINE_integer('host_state_interval', 120,
88 'Interval in seconds for querying the host status')
89
90@@ -274,16 +272,19 @@
91 """Launch a new instance with specified options."""
92 context = context.elevated()
93 instance = self.db.instance_get(context, instance_id)
94- instance.injected_files = kwargs.get('injected_files', [])
95- instance.admin_pass = kwargs.get('admin_password', None)
96 if instance['name'] in self.driver.list_instances():
97 raise exception.Error(_("Instance has already been created"))
98 LOG.audit(_("instance %s: starting..."), instance_id,
99 context=context)
100- self.db.instance_update(context,
101- instance_id,
102- {'host': self.host, 'launched_on': self.host})
103-
104+ updates = {}
105+ updates['host'] = self.host
106+ updates['launched_on'] = self.host
107+ # NOTE(vish): used by virt but not in database
108+ updates['injected_files'] = kwargs.get('injected_files', [])
109+ updates['admin_pass'] = kwargs.get('admin_password', None)
110+ instance = self.db.instance_update(context,
111+ instance_id,
112+ updates)
113 self.db.instance_set_state(context,
114 instance_id,
115 power_state.NOSTATE,
116
117=== modified file 'nova/db/api.py'
118--- nova/db/api.py 2011-07-08 03:07:58 +0000
119+++ nova/db/api.py 2011-07-20 18:28:31 +0000
120@@ -332,13 +332,14 @@
121 return IMPL.fixed_ip_associate(context, address, instance_id)
122
123
124-def fixed_ip_associate_pool(context, network_id, instance_id):
125- """Find free ip in network and associate it to instance.
126+def fixed_ip_associate_pool(context, network_id, instance_id=None, host=None):
127+ """Find free ip in network and associate it to instance or host.
128
129 Raises if one is not available.
130
131 """
132- return IMPL.fixed_ip_associate_pool(context, network_id, instance_id)
133+ return IMPL.fixed_ip_associate_pool(context, network_id,
134+ instance_id, host)
135
136
137 def fixed_ip_create(context, values):
138@@ -361,9 +362,9 @@
139 return IMPL.fixed_ip_get_all(context)
140
141
142-def fixed_ip_get_all_by_host(context, host):
143- """Get all defined fixed ips used by a host."""
144- return IMPL.fixed_ip_get_all_by_host(context, host)
145+def fixed_ip_get_all_by_instance_host(context, host):
146+ """Get all allocated fixed ips filtered by instance host."""
147+ return IMPL.fixed_ip_get_all_instance_by_host(context, host)
148
149
150 def fixed_ip_get_by_address(context, address):
151@@ -376,6 +377,11 @@
152 return IMPL.fixed_ip_get_by_instance(context, instance_id)
153
154
155+def fixed_ip_get_by_network_host(context, network_id, host):
156+ """Get fixed ip for a host in a network."""
157+ return IMPL.fixed_ip_get_by_network_host(context, network_id, host)
158+
159+
160 def fixed_ip_get_by_virtual_interface(context, vif_id):
161 """Get fixed ips by virtual interface or raise if none exist."""
162 return IMPL.fixed_ip_get_by_virtual_interface(context, vif_id)
163
164=== modified file 'nova/db/sqlalchemy/api.py'
165--- nova/db/sqlalchemy/api.py 2011-07-18 23:42:02 +0000
166+++ nova/db/sqlalchemy/api.py 2011-07-20 18:28:31 +0000
167@@ -18,7 +18,6 @@
168 """
169 Implementation of SQLAlchemy backend.
170 """
171-import traceback
172 import warnings
173
174 from nova import db
175@@ -33,7 +32,6 @@
176 from sqlalchemy.exc import IntegrityError
177 from sqlalchemy.orm import joinedload
178 from sqlalchemy.orm import joinedload_all
179-from sqlalchemy.sql import exists
180 from sqlalchemy.sql import func
181 from sqlalchemy.sql.expression import literal_column
182
183@@ -672,7 +670,7 @@
184
185
186 @require_admin_context
187-def fixed_ip_associate_pool(context, network_id, instance_id):
188+def fixed_ip_associate_pool(context, network_id, instance_id=None, host=None):
189 session = get_session()
190 with session.begin():
191 network_or_none = or_(models.FixedIp.network_id == network_id,
192@@ -682,6 +680,7 @@
193 filter_by(reserved=False).\
194 filter_by(deleted=False).\
195 filter_by(instance=None).\
196+ filter_by(host=None).\
197 with_lockmode('update').\
198 first()
199 # NOTE(vish): if with_lockmode isn't supported, as in sqlite,
200@@ -692,9 +691,12 @@
201 fixed_ip_ref.network = network_get(context,
202 network_id,
203 session=session)
204- fixed_ip_ref.instance = instance_get(context,
205- instance_id,
206- session=session)
207+ if instance_id:
208+ fixed_ip_ref.instance = instance_get(context,
209+ instance_id,
210+ session=session)
211+ if host:
212+ fixed_ip_ref.host = host
213 session.add(fixed_ip_ref)
214 return fixed_ip_ref['address']
215
216@@ -750,7 +752,7 @@
217
218
219 @require_admin_context
220-def fixed_ip_get_all_by_host(context, host=None):
221+def fixed_ip_get_all_by_instance_host(context, host=None):
222 session = get_session()
223
224 result = session.query(models.FixedIp).\
225@@ -800,6 +802,20 @@
226
227
228 @require_context
229+def fixed_ip_get_by_network_host(context, network_id, host):
230+ session = get_session()
231+ rv = session.query(models.FixedIp).\
232+ filter_by(network_id=network_id).\
233+ filter_by(host=host).\
234+ filter_by(deleted=False).\
235+ first()
236+ if not rv:
237+ raise exception.FixedIpNotFoundForNetworkHost(network_id=network_id,
238+ host=host)
239+ return rv
240+
241+
242+@require_context
243 def fixed_ip_get_by_virtual_interface(context, vif_id):
244 session = get_session()
245 rv = session.query(models.FixedIp).\
246@@ -1480,8 +1496,6 @@
247 called by project_get_networks under certain conditions
248 and network manager add_network_to_project()
249
250- only associates projects with networks that have configured hosts
251-
252 only associate if the project doesn't already have a network
253 or if force is True
254
255@@ -1497,7 +1511,6 @@
256 def network_query(project_filter):
257 return session.query(models.Network).\
258 filter_by(deleted=False).\
259- filter(models.Network.host != None).\
260 filter_by(project_id=project_filter).\
261 with_lockmode('update').\
262 first()
263@@ -1704,9 +1717,16 @@
264 def network_get_all_by_host(context, host):
265 session = get_session()
266 with session.begin():
267+ # NOTE(vish): return networks that have host set
268+ # or that have a fixed ip with host set
269+ host_filter = or_(models.Network.host == host,
270+ models.FixedIp.host == host)
271+
272 return session.query(models.Network).\
273 filter_by(deleted=False).\
274- filter_by(host=host).\
275+ join(models.Network.fixed_ips).\
276+ filter(host_filter).\
277+ filter_by(deleted=False).\
278 all()
279
280
281@@ -1738,6 +1758,7 @@
282 network_ref = network_get(context, network_id, session=session)
283 network_ref.update(values)
284 network_ref.save(session=session)
285+ return network_ref
286
287
288 ###################
289
290=== added file 'nova/db/sqlalchemy/migrate_repo/versions/033_ha_network.py'
291--- nova/db/sqlalchemy/migrate_repo/versions/033_ha_network.py 1970-01-01 00:00:00 +0000
292+++ nova/db/sqlalchemy/migrate_repo/versions/033_ha_network.py 2011-07-20 18:28:31 +0000
293@@ -0,0 +1,44 @@
294+# vim: tabstop=4 shiftwidth=4 softtabstop=4
295+
296+# Copyright (c) 2011 OpenStack, LLC.
297+# All Rights Reserved.
298+#
299+# Licensed under the Apache License, Version 2.0 (the "License"); you may
300+# not use this file except in compliance with the License. You may obtain
301+# a copy of the License at
302+#
303+# http://www.apache.org/licenses/LICENSE-2.0
304+#
305+# Unless required by applicable law or agreed to in writing, software
306+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
307+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
308+# License for the specific language governing permissions and limitations
309+# under the License.
310+
311+from sqlalchemy import Column, Table, MetaData, Boolean, String
312+
313+meta = MetaData()
314+
315+fixed_ips_host = Column('host', String(255))
316+
317+networks_multi_host = Column('multi_host', Boolean, default=False)
318+
319+
320+def upgrade(migrate_engine):
321+ meta.bind = migrate_engine
322+
323+ fixed_ips = Table('fixed_ips', meta, autoload=True)
324+ fixed_ips.create_column(fixed_ips_host)
325+
326+ networks = Table('networks', meta, autoload=True)
327+ networks.create_column(networks_multi_host)
328+
329+
330+def downgrade(migrate_engine):
331+ meta.bind = migrate_engine
332+
333+ fixed_ips = Table('fixed_ips', meta, autoload=True)
334+ fixed_ips.drop_column(fixed_ips_host)
335+
336+ networks = Table('networks', meta, autoload=True)
337+ networks.drop_column(networks_multi_host)
338
339=== modified file 'nova/db/sqlalchemy/models.py'
340--- nova/db/sqlalchemy/models.py 2011-07-08 03:07:58 +0000
341+++ nova/db/sqlalchemy/models.py 2011-07-20 18:28:31 +0000
342@@ -545,6 +545,7 @@
343 injected = Column(Boolean, default=False)
344 cidr = Column(String(255), unique=True)
345 cidr_v6 = Column(String(255), unique=True)
346+ multi_host = Column(Boolean, default=False)
347
348 gateway_v6 = Column(String(255))
349 netmask_v6 = Column(String(255))
350@@ -603,6 +604,7 @@
351 # leased means dhcp bridge has leased the ip
352 leased = Column(Boolean, default=False)
353 reserved = Column(Boolean, default=False)
354+ host = Column(String(255))
355
356
357 class FloatingIp(BASE, NovaBase):
358
359=== modified file 'nova/exception.py'
360--- nova/exception.py 2011-07-13 18:19:32 +0000
361+++ nova/exception.py 2011-07-20 18:28:31 +0000
362@@ -408,6 +408,11 @@
363 message = _("Instance %(instance_id)s has zero fixed ips.")
364
365
366+class FixedIpNotFoundForNetworkHost(FixedIpNotFound):
367+ message = _("Network host %(host)s has zero fixed ips "
368+ "in network %(network_id)s.")
369+
370+
371 class FixedIpNotFoundForSpecificInstance(FixedIpNotFound):
372 message = _("Instance %(instance_id)s doesn't have fixed ip '%(ip)s'.")
373
374
375=== modified file 'nova/network/api.py'
376--- nova/network/api.py 2011-07-19 20:49:05 +0000
377+++ nova/network/api.py 2011-07-20 18:28:31 +0000
378@@ -18,7 +18,6 @@
379
380 """Handles all requests relating to instances (guest vms)."""
381
382-from nova import db
383 from nova import exception
384 from nova import flags
385 from nova import log as logging
386@@ -108,7 +107,11 @@
387 '(%(project)s)') %
388 {'address': floating_ip['address'],
389 'project': context.project_id})
390- host = fixed_ip['network']['host']
391+ # NOTE(vish): if we are multi_host, send to the instances host
392+ if fixed_ip['network']['multi_host']:
393+ host = fixed_ip['instance']['host']
394+ else:
395+ host = fixed_ip['network']['host']
396 rpc.cast(context,
397 self.db.queue_get_for(context, FLAGS.network_topic, host),
398 {'method': 'associate_floating_ip',
399@@ -123,7 +126,11 @@
400 return
401 if not floating_ip.get('fixed_ip'):
402 raise exception.ApiError('Address is not associated.')
403- host = floating_ip['fixed_ip']['network']['host']
404+ # NOTE(vish): if we are multi_host, send to the instances host
405+ if floating_ip['fixed_ip']['network']['multi_host']:
406+ host = floating_ip['fixed_ip']['instance']['host']
407+ else:
408+ host = floating_ip['fixed_ip']['network']['host']
409 rpc.call(context,
410 self.db.queue_get_for(context, FLAGS.network_topic, host),
411 {'method': 'disassociate_floating_ip',
412@@ -137,7 +144,9 @@
413 args = kwargs
414 args['instance_id'] = instance['id']
415 args['project_id'] = instance['project_id']
416+ args['host'] = instance['host']
417 args['instance_type_id'] = instance['instance_type_id']
418+
419 return rpc.call(context, FLAGS.network_topic,
420 {'method': 'allocate_for_instance',
421 'args': args})
422@@ -176,7 +185,8 @@
423 def get_instance_nw_info(self, context, instance):
424 """Returns all network info related to an instance."""
425 args = {'instance_id': instance['id'],
426- 'instance_type_id': instance['instance_type_id']}
427+ 'instance_type_id': instance['instance_type_id'],
428+ 'host': instance['host']}
429 return rpc.call(context, FLAGS.network_topic,
430 {'method': 'get_instance_nw_info',
431 'args': args})
432
433=== modified file 'nova/network/linux_net.py'
434--- nova/network/linux_net.py 2011-06-27 21:48:03 +0000
435+++ nova/network/linux_net.py 2011-07-20 18:28:31 +0000
436@@ -497,7 +497,7 @@
437 suffix = net_attrs['cidr'].rpartition('/')[2]
438 out, err = _execute('sudo', 'ip', 'addr', 'add',
439 '%s/%s' %
440- (net_attrs['gateway'], suffix),
441+ (net_attrs['dhcp_server'], suffix),
442 'brd',
443 net_attrs['broadcast'],
444 'dev',
445@@ -551,21 +551,27 @@
446 bridge)
447
448
449-def get_dhcp_leases(context, network_id):
450+def get_dhcp_leases(context, network_ref):
451 """Return a network's hosts config in dnsmasq leasefile format."""
452 hosts = []
453- for fixed_ip_ref in db.network_get_associated_fixed_ips(context,
454- network_id):
455- hosts.append(_host_lease(fixed_ip_ref))
456+ for fixed_ref in db.network_get_associated_fixed_ips(context,
457+ network_ref['id']):
458+ host = fixed_ref['instance']['host']
459+ if network_ref['multi_host'] and FLAGS.host != host:
460+ continue
461+ hosts.append(_host_lease(fixed_ref))
462 return '\n'.join(hosts)
463
464
465-def get_dhcp_hosts(context, network_id):
466+def get_dhcp_hosts(context, network_ref):
467 """Get network's hosts config in dhcp-host format."""
468 hosts = []
469- for fixed_ip_ref in db.network_get_associated_fixed_ips(context,
470- network_id):
471- hosts.append(_host_dhcp(fixed_ip_ref))
472+ for fixed_ref in db.network_get_associated_fixed_ips(context,
473+ network_ref['id']):
474+ host = fixed_ref['instance']['host']
475+ if network_ref['multi_host'] and FLAGS.host != host:
476+ continue
477+ hosts.append(_host_dhcp(fixed_ref))
478 return '\n'.join(hosts)
479
480
481@@ -573,18 +579,16 @@
482 # configuration options (like dchp-range, vlan, ...)
483 # aren't reloaded.
484 @utils.synchronized('dnsmasq_start')
485-def update_dhcp(context, network_id):
486+def update_dhcp(context, network_ref):
487 """(Re)starts a dnsmasq server for a given network.
488
489 If a dnsmasq instance is already running then send a HUP
490 signal causing it to reload, otherwise spawn a new instance.
491
492 """
493- network_ref = db.network_get(context, network_id)
494-
495 conffile = _dhcp_file(network_ref['bridge'], 'conf')
496 with open(conffile, 'w') as f:
497- f.write(get_dhcp_hosts(context, network_id))
498+ f.write(get_dhcp_hosts(context, network_ref))
499
500 # Make sure dnsmasq can actually read it (it setuid()s to "nobody")
501 os.chmod(conffile, 0644)
502@@ -612,9 +616,7 @@
503
504
505 @utils.synchronized('radvd_start')
506-def update_ra(context, network_id):
507- network_ref = db.network_get(context, network_id)
508-
509+def update_ra(context, network_ref):
510 conffile = _ra_file(network_ref['bridge'], 'conf')
511 with open(conffile, 'w') as f:
512 conf_str = """
513@@ -650,9 +652,6 @@
514 LOG.debug(_('Pid %d is stale, relaunching radvd'), pid)
515 command = _ra_cmd(network_ref)
516 _execute(*command)
517- db.network_update(context, network_id,
518- {'gateway_v6':
519- utils.get_my_linklocal(network_ref['bridge'])})
520
521
522 def _host_lease(fixed_ip_ref):
523@@ -701,10 +700,11 @@
524 cmd = ['sudo', '-E', 'dnsmasq',
525 '--strict-order',
526 '--bind-interfaces',
527+ '--interface=%s' % net['bridge'],
528 '--conf-file=%s' % FLAGS.dnsmasq_config_file,
529 '--domain=%s' % FLAGS.dhcp_domain,
530 '--pid-file=%s' % _dhcp_file(net['bridge'], 'pid'),
531- '--listen-address=%s' % net['gateway'],
532+ '--listen-address=%s' % net['dhcp_server'],
533 '--except-interface=lo',
534 '--dhcp-range=%s,static,120s' % net['dhcp_start'],
535 '--dhcp-lease-max=%s' % len(netaddr.IPNetwork(net['cidr'])),
536
537=== modified file 'nova/network/manager.py'
538--- nova/network/manager.py 2011-07-19 19:29:44 +0000
539+++ nova/network/manager.py 2011-07-20 18:28:31 +0000
540@@ -28,7 +28,6 @@
541 :flat_network_bridge: Bridge device for simple network instances
542 :flat_interface: FlatDhcp will bridge into this interface if set
543 :flat_network_dns: Dns for simple network
544-:flat_network_dhcp_start: Dhcp start for FlatDhcp
545 :vlan_start: First VLAN for private networks
546 :vpn_ip: Public IP for the cloudpipe VPN servers
547 :vpn_start: First Vpn port for private networks
548@@ -49,7 +48,6 @@
549 import math
550 import netaddr
551 import socket
552-import pickle
553 from eventlet import greenpool
554
555 from nova import context
556@@ -78,8 +76,6 @@
557 'Whether to attempt to inject network setup into guest')
558 flags.DEFINE_string('flat_interface', None,
559 'FlatDhcp will bridge into this interface if set')
560-flags.DEFINE_string('flat_network_dhcp_start', '10.0.0.2',
561- 'Dhcp start for FlatDhcp')
562 flags.DEFINE_integer('vlan_start', 100, 'First VLAN for private networks')
563 flags.DEFINE_string('vlan_interface', None,
564 'vlans will bridge into this interface if set')
565@@ -87,6 +83,8 @@
566 flags.DEFINE_string('vpn_ip', '$my_ip',
567 'Public IP for the cloudpipe VPN servers')
568 flags.DEFINE_integer('vpn_start', 1000, 'First Vpn port for private networks')
569+flags.DEFINE_bool('multi_host', False,
570+ 'Default value for multi_host in networks')
571 flags.DEFINE_integer('network_size', 256,
572 'Number of addresses in each private subnet')
573 flags.DEFINE_string('floating_range', '4.4.4.0/24',
574@@ -104,7 +102,8 @@
575 'Seconds after which a deallocated ip is disassociated')
576 flags.DEFINE_integer('create_unique_mac_address_attempts', 5,
577 'Number of attempts to create unique mac address')
578-
579+flags.DEFINE_bool('auto_assign_floating_ip', False,
580+ 'Autoassigning floating ip to VM')
581 flags.DEFINE_bool('use_ipv6', False,
582 'use the ipv6')
583 flags.DEFINE_string('network_host', socket.gethostname(),
584@@ -124,16 +123,26 @@
585 used since they share code to RPC.call allocate_fixed_ip on the
586 correct network host to configure dnsmasq
587 """
588- def _allocate_fixed_ips(self, context, instance_id, networks, **kwargs):
589+ def _allocate_fixed_ips(self, context, instance_id, host, networks,
590+ **kwargs):
591 """Calls allocate_fixed_ip once for each network."""
592 green_pool = greenpool.GreenPool()
593
594 vpn = kwargs.pop('vpn')
595 for network in networks:
596- if network['host'] != self.host:
597+ # NOTE(vish): if we are not multi_host pass to the network host
598+ if not network['multi_host']:
599+ host = network['host']
600+ # NOTE(vish): if there is no network host, set one
601+ if host == None:
602+ host = rpc.call(context, FLAGS.network_topic,
603+ {'method': 'set_network_host',
604+ 'args': {'network_ref': network}})
605+ if host != self.host:
606 # need to call allocate_fixed_ip to correct network host
607- topic = self.db.queue_get_for(context, FLAGS.network_topic,
608- network['host'])
609+ topic = self.db.queue_get_for(context,
610+ FLAGS.network_topic,
611+ host)
612 args = {}
613 args['instance_id'] = instance_id
614 args['network_id'] = network['id']
615@@ -149,12 +158,13 @@
616 # wait for all of the allocates (if any) to finish
617 green_pool.waitall()
618
619- def _rpc_allocate_fixed_ip(self, context, instance_id, network_id):
620+ def _rpc_allocate_fixed_ip(self, context, instance_id, network_id,
621+ **kwargs):
622 """Sits in between _allocate_fixed_ips and allocate_fixed_ip to
623 perform network lookup on the far side of rpc.
624 """
625 network = self.db.network_get(context, network_id)
626- self.allocate_fixed_ip(context, instance_id, network)
627+ self.allocate_fixed_ip(context, instance_id, network, **kwargs)
628
629
630 class FloatingIP(object):
631@@ -193,7 +203,7 @@
632 # which is currently the NetworkManager version
633 # do this first so fixed ip is already allocated
634 ips = super(FloatingIP, self).allocate_for_instance(context, **kwargs)
635- if hasattr(FLAGS, 'auto_assign_floating_ip'):
636+ if FLAGS.auto_assign_floating_ip:
637 # allocate a floating ip (public_ip is just the address string)
638 public_ip = self.allocate_floating_ip(context, project_id)
639 # set auto_assigned column to true for the floating ip
640@@ -300,15 +310,36 @@
641 super(NetworkManager, self).__init__(service_name='network',
642 *args, **kwargs)
643
644+ @utils.synchronized('get_dhcp')
645+ def _get_dhcp_ip(self, context, network_ref, host=None):
646+ """Get the proper dhcp address to listen on."""
647+ # NOTE(vish): this is for compatibility
648+ if not network_ref['multi_host']:
649+ return network_ref['gateway']
650+
651+ if not host:
652+ host = self.host
653+ network_id = network_ref['id']
654+ try:
655+ fip = self.db.fixed_ip_get_by_network_host(context,
656+ network_id,
657+ host)
658+ return fip['address']
659+ except exception.FixedIpNotFoundForNetworkHost:
660+ elevated = context.elevated()
661+ return self.db.fixed_ip_associate_pool(elevated,
662+ network_id,
663+ host=host)
664+
665 def init_host(self):
666 """Do any initialization that needs to be run if this is a
667 standalone service.
668 """
669- # Set up this host for networks in which it's already
670- # the designated network host.
671+ # NOTE(vish): Set up networks for which this host already has
672+ # an ip address.
673 ctxt = context.get_admin_context()
674 for network in self.db.network_get_all_by_host(ctxt, self.host):
675- self._on_set_network_host(ctxt, network['id'])
676+ self._setup_network(ctxt, network)
677
678 def periodic_tasks(self, context=None):
679 """Tasks to be run at a periodic interval."""
680@@ -323,32 +354,13 @@
681 if num:
682 LOG.debug(_('Dissassociated %s stale fixed ip(s)'), num)
683
684- # setup any new networks which have been created
685- self.set_network_hosts(context)
686-
687- def set_network_host(self, context, network_id):
688+ def set_network_host(self, context, network_ref):
689 """Safely sets the host of the network."""
690 LOG.debug(_('setting network host'), context=context)
691 host = self.db.network_set_host(context,
692- network_id,
693+ network_ref['id'],
694 self.host)
695- if host == self.host:
696- self._on_set_network_host(context, network_id)
697-
698- def set_network_hosts(self, context):
699- """Set the network hosts for any networks which are unset."""
700- try:
701- networks = self.db.network_get_all(context)
702- except exception.NoNetworksFound:
703- # no networks found, nothing to do
704- return
705-
706- for network in networks:
707- host = network['host']
708- if not host:
709- # break so worker will only grab 1 (to help scale flatter)
710- self.set_network_host(context, network['id'])
711- break
712+ return host
713
714 def _get_networks_for_instance(self, context, instance_id, project_id):
715 """Determine & return which networks an instance should connect to."""
716@@ -361,9 +373,9 @@
717 # we don't care if no networks are found
718 pass
719
720- # return only networks which are not vlan networks and have host set
721+ # return only networks which are not vlan networks
722 return [network for network in networks if
723- not network['vlan'] and network['host']]
724+ not network['vlan']]
725
726 def allocate_for_instance(self, context, **kwargs):
727 """Handles allocating the various network resources for an instance.
728@@ -371,6 +383,7 @@
729 rpc.called by network_api
730 """
731 instance_id = kwargs.pop('instance_id')
732+ host = kwargs.pop('host')
733 project_id = kwargs.pop('project_id')
734 type_id = kwargs.pop('instance_type_id')
735 vpn = kwargs.pop('vpn')
736@@ -379,9 +392,11 @@
737 context=context)
738 networks = self._get_networks_for_instance(admin_context, instance_id,
739 project_id)
740+ LOG.warn(networks)
741 self._allocate_mac_addresses(context, instance_id, networks)
742- self._allocate_fixed_ips(admin_context, instance_id, networks, vpn=vpn)
743- return self.get_instance_nw_info(context, instance_id, type_id)
744+ self._allocate_fixed_ips(admin_context, instance_id, host, networks,
745+ vpn=vpn)
746+ return self.get_instance_nw_info(context, instance_id, type_id, host)
747
748 def deallocate_for_instance(self, context, **kwargs):
749 """Handles deallocating various network resources for an instance.
750@@ -401,7 +416,8 @@
751 # deallocate vifs (mac addresses)
752 self.db.virtual_interface_delete_by_instance(context, instance_id)
753
754- def get_instance_nw_info(self, context, instance_id, instance_type_id):
755+ def get_instance_nw_info(self, context, instance_id,
756+ instance_type_id, host):
757 """Creates network info list for instance.
758
759 called by allocate_for_instance and netowrk_api
760@@ -445,9 +461,16 @@
761 'cidr': network['cidr'],
762 'cidr_v6': network['cidr_v6'],
763 'injected': network['injected']}
764+ if network['multi_host']:
765+ dhcp_server = self._get_dhcp_ip(context, network, host)
766+ else:
767+ dhcp_server = self._get_dhcp_ip(context,
768+ network,
769+ network['host'])
770 info = {
771 'label': network['label'],
772 'gateway': network['gateway'],
773+ 'dhcp_server': dhcp_server,
774 'broadcast': network['broadcast'],
775 'mac': vif['address'],
776 'rxtx_cap': flavor['rxtx_cap'],
777@@ -487,10 +510,10 @@
778 random.randint(0x00, 0xff)]
779 return ':'.join(map(lambda x: "%02x" % x, mac))
780
781- def add_fixed_ip_to_instance(self, context, instance_id, network_id):
782+ def add_fixed_ip_to_instance(self, context, instance_id, host, network_id):
783 """Adds a fixed ip to an instance from specified network."""
784 networks = [self.db.network_get(context, network_id)]
785- self._allocate_fixed_ips(context, instance_id, networks)
786+ self._allocate_fixed_ips(context, instance_id, host, networks)
787
788 def remove_fixed_ip_from_instance(self, context, instance_id, address):
789 """Removes a fixed ip from an instance from specified network."""
790@@ -517,6 +540,7 @@
791 values = {'allocated': True,
792 'virtual_interface_id': vif['id']}
793 self.db.fixed_ip_update(context, address, values)
794+ self._setup_network(context, network)
795 return address
796
797 def deallocate_fixed_ip(self, context, address, **kwargs):
798@@ -562,10 +586,10 @@
799 # means there will stale entries in the conf file
800 # the code below will update the file if necessary
801 if FLAGS.update_dhcp_on_disassociate:
802- network = self.db.fixed_ip_get_network(context, address)
803- self.driver.update_dhcp(context, network['id'])
804+ network_ref = self.db.fixed_ip_get_network(context, address)
805+ self._setup_network(context, network_ref)
806
807- def create_networks(self, context, label, cidr, num_networks,
808+ def create_networks(self, context, label, cidr, multi_host, num_networks,
809 network_size, cidr_v6, gateway_v6, bridge,
810 bridge_interface, **kwargs):
811 """Create networks based on parameters."""
812@@ -584,6 +608,7 @@
813 net['bridge_interface'] = bridge_interface
814 net['dns'] = FLAGS.flat_network_dns
815 net['cidr'] = cidr
816+ net['multi_host'] = multi_host
817 net['netmask'] = str(project_net.netmask)
818 net['gateway'] = str(project_net[1])
819 net['broadcast'] = str(project_net.broadcast)
820@@ -659,12 +684,13 @@
821 'address': address,
822 'reserved': reserved})
823
824- def _allocate_fixed_ips(self, context, instance_id, networks, **kwargs):
825+ def _allocate_fixed_ips(self, context, instance_id, host, networks,
826+ **kwargs):
827 """Calls allocate_fixed_ip once for each network."""
828 raise NotImplementedError()
829
830- def _on_set_network_host(self, context, network_id):
831- """Called when this host becomes the host for a network."""
832+ def _setup_network(self, context, network_ref):
833+ """Sets up network on this host."""
834 raise NotImplementedError()
835
836 def setup_compute_network(self, context, instance_id):
837@@ -706,7 +732,8 @@
838
839 timeout_fixed_ips = False
840
841- def _allocate_fixed_ips(self, context, instance_id, networks, **kwargs):
842+ def _allocate_fixed_ips(self, context, instance_id, host, networks,
843+ **kwargs):
844 """Calls allocate_fixed_ip once for each network."""
845 for network in networks:
846 self.allocate_fixed_ip(context, instance_id, network)
847@@ -724,12 +751,12 @@
848 """
849 pass
850
851- def _on_set_network_host(self, context, network_id):
852- """Called when this host becomes the host for a network."""
853+ def _setup_network(self, context, network_ref):
854+ """Setup Network on this host."""
855 net = {}
856 net['injected'] = FLAGS.flat_injected
857 net['dns'] = FLAGS.flat_network_dns
858- self.db.network_update(context, network_id, net)
859+ self.db.network_update(context, network_ref['id'], net)
860
861
862 class FlatDHCPManager(FloatingIP, RPCAllocateFixedIP, NetworkManager):
863@@ -760,30 +787,23 @@
864 """
865 networks = db.network_get_all_by_instance(context, instance_id)
866 for network in networks:
867- self.driver.ensure_bridge(network['bridge'],
868- network['bridge_interface'])
869-
870- def allocate_fixed_ip(self, context, instance_id, network, **kwargs):
871- """Allocate flat_network fixed_ip, then setup dhcp for this network."""
872- address = super(FlatDHCPManager, self).allocate_fixed_ip(context,
873- instance_id,
874- network)
875- if not FLAGS.fake_network:
876- self.driver.update_dhcp(context, network['id'])
877-
878- def _on_set_network_host(self, context, network_id):
879- """Called when this host becomes the host for a project."""
880- net = {}
881- net['dhcp_start'] = FLAGS.flat_network_dhcp_start
882- self.db.network_update(context, network_id, net)
883- network = db.network_get(context, network_id)
884- self.driver.ensure_bridge(network['bridge'],
885- network['bridge_interface'],
886- network)
887- if not FLAGS.fake_network:
888- self.driver.update_dhcp(context, network_id)
889+ if not network['multi_host']:
890+ self.driver.ensure_bridge(network['bridge'],
891+ network['bridge_interface'])
892+
893+ def _setup_network(self, context, network_ref):
894+ """Sets up network on this host."""
895+ network_ref['dhcp_server'] = self._get_dhcp_ip(context, network_ref)
896+ self.driver.ensure_bridge(network_ref['bridge'],
897+ network_ref['bridge_interface'],
898+ network_ref)
899+ if not FLAGS.fake_network:
900+ self.driver.update_dhcp(context, network_ref)
901 if(FLAGS.use_ipv6):
902- self.driver.update_ra(context, network_id)
903+ self.driver.update_ra(context, network_ref)
904+ gateway = utils.get_my_linklocal(network_ref['bridge'])
905+ self.db.network_update(context, network_ref['id'],
906+ {'gateway_v6': gateway})
907
908
909 class VlanManager(RPCAllocateFixedIP, FloatingIP, NetworkManager):
910@@ -832,8 +852,8 @@
911 values = {'allocated': True,
912 'virtual_interface_id': vif['id']}
913 self.db.fixed_ip_update(context, address, values)
914- if not FLAGS.fake_network:
915- self.driver.update_dhcp(context, network['id'])
916+ self._setup_network(context, network)
917+ return address
918
919 def add_network_to_project(self, context, project_id):
920 """Force adds another network to a project."""
921@@ -845,17 +865,15 @@
922 """
923 networks = self.db.network_get_all_by_instance(context, instance_id)
924 for network in networks:
925- self.driver.ensure_vlan_bridge(network['vlan'],
926- network['bridge'],
927- network['bridge_interface'])
928+ if not network['multi_host']:
929+ self.driver.ensure_vlan_bridge(network['vlan'],
930+ network['bridge'],
931+ network['bridge_interface'])
932
933 def _get_networks_for_instance(self, context, instance_id, project_id):
934 """Determine which networks an instance should connect to."""
935 # get networks associated with project
936- networks = self.db.project_get_networks(context, project_id)
937-
938- # return only networks which have host set
939- return [network for network in networks if network['host']]
940+ return self.db.project_get_networks(context, project_id)
941
942 def create_networks(self, context, **kwargs):
943 """Create networks based on parameters."""
944@@ -874,32 +892,35 @@
945
946 NetworkManager.create_networks(self, context, vpn=True, **kwargs)
947
948- def _on_set_network_host(self, context, network_id):
949- """Called when this host becomes the host for a network."""
950- network = self.db.network_get(context, network_id)
951- if not network['vpn_public_address']:
952+ def _setup_network(self, context, network_ref):
953+ """Sets up network on this host."""
954+ network_ref['dhcp_server'] = self._get_dhcp_ip(context, network_ref)
955+ if not network_ref['vpn_public_address']:
956 net = {}
957 address = FLAGS.vpn_ip
958 net['vpn_public_address'] = address
959- db.network_update(context, network_id, net)
960+ network_ref = db.network_update(context, network_ref['id'], net)
961 else:
962- address = network['vpn_public_address']
963- self.driver.ensure_vlan_bridge(network['vlan'],
964- network['bridge'],
965- network['bridge_interface'],
966- network)
967+ address = network_ref['vpn_public_address']
968+ self.driver.ensure_vlan_bridge(network_ref['vlan'],
969+ network_ref['bridge'],
970+ network_ref['bridge_interface'],
971+ network_ref)
972
973 # NOTE(vish): only ensure this forward if the address hasn't been set
974 # manually.
975 if address == FLAGS.vpn_ip and hasattr(self.driver,
976 "ensure_vlan_forward"):
977 self.driver.ensure_vlan_forward(FLAGS.vpn_ip,
978- network['vpn_public_port'],
979- network['vpn_private_address'])
980+ network_ref['vpn_public_port'],
981+ network_ref['vpn_private_address'])
982 if not FLAGS.fake_network:
983- self.driver.update_dhcp(context, network_id)
984+ self.driver.update_dhcp(context, network_ref)
985 if(FLAGS.use_ipv6):
986- self.driver.update_ra(context, network_id)
987+ self.driver.update_ra(context, network_ref)
988+ gateway = utils.get_my_linklocal(network_ref['bridge'])
989+ self.db.network_update(context, network_ref['id'],
990+ {'gateway_v6': gateway})
991
992 @property
993 def _bottom_reserved_ips(self):
994
995=== modified file 'nova/tests/__init__.py'
996--- nova/tests/__init__.py 2011-06-29 17:58:10 +0000
997+++ nova/tests/__init__.py 2011-07-20 18:28:31 +0000
998@@ -59,6 +59,7 @@
999 network.create_networks(ctxt,
1000 label='test',
1001 cidr=FLAGS.fixed_range,
1002+ multi_host=FLAGS.multi_host,
1003 num_networks=FLAGS.num_networks,
1004 network_size=FLAGS.network_size,
1005 cidr_v6=FLAGS.fixed_range_v6,
1006@@ -68,7 +69,7 @@
1007 vpn_start=FLAGS.vpn_start,
1008 vlan_start=FLAGS.vlan_start)
1009 for net in db.network_get_all(ctxt):
1010- network.set_network_host(ctxt, net['id'])
1011+ network.set_network_host(ctxt, net)
1012
1013 cleandb = os.path.join(FLAGS.state_path, FLAGS.sqlite_clean_db)
1014 shutil.copyfile(testdb, cleandb)
1015
1016=== modified file 'nova/tests/test_libvirt.py'
1017--- nova/tests/test_libvirt.py 2011-06-28 16:20:02 +0000
1018+++ nova/tests/test_libvirt.py 2011-07-20 18:28:31 +0000
1019@@ -58,6 +58,7 @@
1020 'cidr': fake_ip,
1021 'cidr_v6': fake_ip}
1022 mapping = {'mac': fake,
1023+ 'dhcp_server': fake,
1024 'gateway': fake,
1025 'gateway6': fake,
1026 'ips': [{'ip': fake_ip}, {'ip': fake_ip}]}
1027
1028=== modified file 'nova/tests/test_network.py'
1029--- nova/tests/test_network.py 2011-07-11 14:49:34 +0000
1030+++ nova/tests/test_network.py 2011-07-20 18:28:31 +0000
1031@@ -45,6 +45,7 @@
1032 networks = [{'id': 0,
1033 'label': 'test0',
1034 'injected': False,
1035+ 'multi_host': False,
1036 'cidr': '192.168.0.0/24',
1037 'cidr_v6': '2001:db8::/64',
1038 'gateway_v6': '2001:db8::1',
1039@@ -62,6 +63,7 @@
1040 {'id': 1,
1041 'label': 'test1',
1042 'injected': False,
1043+ 'multi_host': False,
1044 'cidr': '192.168.1.0/24',
1045 'cidr_v6': '2001:db9::/64',
1046 'gateway_v6': '2001:db9::1',
1047@@ -122,20 +124,6 @@
1048 self.network = network_manager.FlatManager(host=HOST)
1049 self.network.db = db
1050
1051- def test_set_network_hosts(self):
1052- self.mox.StubOutWithMock(db, 'network_get_all')
1053- self.mox.StubOutWithMock(db, 'network_set_host')
1054- self.mox.StubOutWithMock(db, 'network_update')
1055-
1056- db.network_get_all(mox.IgnoreArg()).AndReturn([networks[0]])
1057- db.network_set_host(mox.IgnoreArg(),
1058- networks[0]['id'],
1059- mox.IgnoreArg()).AndReturn(HOST)
1060- db.network_update(mox.IgnoreArg(), mox.IgnoreArg(), mox.IgnoreArg())
1061- self.mox.ReplayAll()
1062-
1063- self.network.set_network_hosts(None)
1064-
1065 def test_get_instance_nw_info(self):
1066 self.mox.StubOutWithMock(db, 'fixed_ip_get_by_instance')
1067 self.mox.StubOutWithMock(db, 'virtual_interface_get_by_instance')
1068@@ -149,7 +137,7 @@
1069 mox.IgnoreArg()).AndReturn(flavor)
1070 self.mox.ReplayAll()
1071
1072- nw_info = self.network.get_instance_nw_info(None, 0, 0)
1073+ nw_info = self.network.get_instance_nw_info(None, 0, 0, None)
1074
1075 self.assertTrue(nw_info)
1076
1077@@ -164,6 +152,7 @@
1078 self.assertDictMatch(nw[0], check)
1079
1080 check = {'broadcast': '192.168.%s.255' % i,
1081+ 'dhcp_server': '192.168.%s.1' % i,
1082 'dns': 'DONTCARE',
1083 'gateway': '192.168.%s.1' % i,
1084 'gateway6': '2001:db%s::1' % i8,
1085
1086=== modified file 'nova/virt/libvirt/connection.py'
1087--- nova/virt/libvirt/connection.py 2011-07-20 16:17:44 +0000
1088+++ nova/virt/libvirt/connection.py 2011-07-20 18:28:31 +0000
1089@@ -928,7 +928,6 @@
1090
1091 def _get_nic_for_xml(self, network, mapping):
1092 # Assume that the gateway also acts as the dhcp server.
1093- dhcp_server = mapping['gateway']
1094 gateway6 = mapping.get('gateway6')
1095 mac_id = mapping['mac'].replace(':', '')
1096
1097@@ -951,7 +950,7 @@
1098 'bridge_name': network['bridge'],
1099 'mac_address': mapping['mac'],
1100 'ip_address': mapping['ips'][0]['ip'],
1101- 'dhcp_server': dhcp_server,
1102+ 'dhcp_server': mapping['dhcp_server'],
1103 'extra_params': extra_params,
1104 }
1105