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
=== modified file 'bin/nova-dhcpbridge'
--- bin/nova-dhcpbridge 2011-07-11 20:31:39 +0000
+++ bin/nova-dhcpbridge 2011-07-20 18:28:31 +0000
@@ -91,7 +91,7 @@
91 """Get the list of hosts for an interface."""91 """Get the list of hosts for an interface."""
92 ctxt = context.get_admin_context()92 ctxt = context.get_admin_context()
93 network_ref = db.network_get_by_bridge(ctxt, interface)93 network_ref = db.network_get_by_bridge(ctxt, interface)
94 return linux_net.get_dhcp_leases(ctxt, network_ref['id'])94 return linux_net.get_dhcp_leases(ctxt, network_ref)
9595
9696
97def main():97def main():
9898
=== modified file 'bin/nova-manage'
--- bin/nova-manage 2011-07-18 19:09:39 +0000
+++ bin/nova-manage 2011-07-20 18:28:31 +0000
@@ -479,7 +479,7 @@
479 except db.api.NoMoreNetworks:479 except db.api.NoMoreNetworks:
480 print _('No more networks available. If this is a new '480 print _('No more networks available. If this is a new '
481 'installation, you need\nto call something like this:\n\n'481 'installation, you need\nto call something like this:\n\n'
482 ' nova-manage network create 10.0.0.0/8 10 64\n\n')482 ' nova-manage network create pvt 10.0.0.0/8 10 64\n\n')
483 except exception.ProcessExecutionError, e:483 except exception.ProcessExecutionError, e:
484 print e484 print e
485 print _("The above error may show that the certificate db has not "485 print _("The above error may show that the certificate db has not "
@@ -500,7 +500,7 @@
500 if host is None:500 if host is None:
501 fixed_ips = db.fixed_ip_get_all(ctxt)501 fixed_ips = db.fixed_ip_get_all(ctxt)
502 else:502 else:
503 fixed_ips = db.fixed_ip_get_all_by_host(ctxt, host)503 fixed_ips = db.fixed_ip_get_all_by_instance_host(ctxt, host)
504 except exception.NotFound as ex:504 except exception.NotFound as ex:
505 print "error: %s" % ex505 print "error: %s" % ex
506 sys.exit(2)506 sys.exit(2)
@@ -564,17 +564,17 @@
564 """Class for managing networks."""564 """Class for managing networks."""
565565
566 def create(self, label=None, fixed_range=None, num_networks=None,566 def create(self, label=None, fixed_range=None, num_networks=None,
567 network_size=None, vlan_start=None,567 network_size=None, multi_host=None, vlan_start=None,
568 vpn_start=None, fixed_range_v6=None, gateway_v6=None,568 vpn_start=None, fixed_range_v6=None, gateway_v6=None,
569 flat_network_bridge=None, bridge_interface=None):569 flat_network_bridge=None, bridge_interface=None):
570 """Creates fixed ips for host by range570 """Creates fixed ips for host by range
571 arguments: label, fixed_range, [num_networks=FLAG],571 arguments: label, fixed_range, [num_networks=FLAG],
572 [network_size=FLAG], [vlan_start=FLAG],572 [network_size=FLAG], [multi_host=FLAG], [vlan_start=FLAG],
573 [vpn_start=FLAG], [fixed_range_v6=FLAG], [gateway_v6=FLAG],573 [vpn_start=FLAG], [fixed_range_v6=FLAG], [gateway_v6=FLAG],
574 [flat_network_bridge=FLAG], [bridge_interface=FLAG]574 [flat_network_bridge=FLAG], [bridge_interface=FLAG]
575 If you wish to use a later argument fill in the gaps with 0s575 If you wish to use a later argument fill in the gaps with ""s
576 Ex: network create private 10.0.0.0/8 1 15 0 0 0 0 xenbr1 eth1576 Ex: network create private 10.0.0.0/8 1 16 T "" "" "" "" xenbr1 eth1
577 network create private 10.0.0.0/8 1 15577 network create private 10.0.0.0/8 1 16
578 """578 """
579 if not label:579 if not label:
580 msg = _('a label (ex: public) is required to create networks.')580 msg = _('a label (ex: public) is required to create networks.')
@@ -589,6 +589,10 @@
589 num_networks = FLAGS.num_networks589 num_networks = FLAGS.num_networks
590 if not network_size:590 if not network_size:
591 network_size = FLAGS.network_size591 network_size = FLAGS.network_size
592 if not multi_host:
593 multi_host = FLAGS.multi_host
594 else:
595 multi_host = multi_host == 'T'
592 if not vlan_start:596 if not vlan_start:
593 vlan_start = FLAGS.vlan_start597 vlan_start = FLAGS.vlan_start
594 if not vpn_start:598 if not vpn_start:
@@ -607,6 +611,7 @@
607 net_manager.create_networks(context.get_admin_context(),611 net_manager.create_networks(context.get_admin_context(),
608 label=label,612 label=label,
609 cidr=fixed_range,613 cidr=fixed_range,
614 multi_host=multi_host,
610 num_networks=int(num_networks),615 num_networks=int(num_networks),
611 network_size=int(network_size),616 network_size=int(network_size),
612 vlan_start=int(vlan_start),617 vlan_start=int(vlan_start),
613618
=== modified file 'nova/compute/manager.py'
--- nova/compute/manager.py 2011-07-19 20:21:39 +0000
+++ nova/compute/manager.py 2011-07-20 18:28:31 +0000
@@ -77,8 +77,6 @@
77flags.DEFINE_integer("rescue_timeout", 0,77flags.DEFINE_integer("rescue_timeout", 0,
78 "Automatically unrescue an instance after N seconds."78 "Automatically unrescue an instance after N seconds."
79 " Set to 0 to disable.")79 " Set to 0 to disable.")
80flags.DEFINE_bool('auto_assign_floating_ip', False,
81 'Autoassigning floating ip to VM')
82flags.DEFINE_integer('host_state_interval', 120,80flags.DEFINE_integer('host_state_interval', 120,
83 'Interval in seconds for querying the host status')81 'Interval in seconds for querying the host status')
8482
@@ -274,16 +272,19 @@
274 """Launch a new instance with specified options."""272 """Launch a new instance with specified options."""
275 context = context.elevated()273 context = context.elevated()
276 instance = self.db.instance_get(context, instance_id)274 instance = self.db.instance_get(context, instance_id)
277 instance.injected_files = kwargs.get('injected_files', [])
278 instance.admin_pass = kwargs.get('admin_password', None)
279 if instance['name'] in self.driver.list_instances():275 if instance['name'] in self.driver.list_instances():
280 raise exception.Error(_("Instance has already been created"))276 raise exception.Error(_("Instance has already been created"))
281 LOG.audit(_("instance %s: starting..."), instance_id,277 LOG.audit(_("instance %s: starting..."), instance_id,
282 context=context)278 context=context)
283 self.db.instance_update(context,279 updates = {}
284 instance_id,280 updates['host'] = self.host
285 {'host': self.host, 'launched_on': self.host})281 updates['launched_on'] = self.host
286282 # NOTE(vish): used by virt but not in database
283 updates['injected_files'] = kwargs.get('injected_files', [])
284 updates['admin_pass'] = kwargs.get('admin_password', None)
285 instance = self.db.instance_update(context,
286 instance_id,
287 updates)
287 self.db.instance_set_state(context,288 self.db.instance_set_state(context,
288 instance_id,289 instance_id,
289 power_state.NOSTATE,290 power_state.NOSTATE,
290291
=== modified file 'nova/db/api.py'
--- nova/db/api.py 2011-07-08 03:07:58 +0000
+++ nova/db/api.py 2011-07-20 18:28:31 +0000
@@ -332,13 +332,14 @@
332 return IMPL.fixed_ip_associate(context, address, instance_id)332 return IMPL.fixed_ip_associate(context, address, instance_id)
333333
334334
335def fixed_ip_associate_pool(context, network_id, instance_id):335def fixed_ip_associate_pool(context, network_id, instance_id=None, host=None):
336 """Find free ip in network and associate it to instance.336 """Find free ip in network and associate it to instance or host.
337337
338 Raises if one is not available.338 Raises if one is not available.
339339
340 """340 """
341 return IMPL.fixed_ip_associate_pool(context, network_id, instance_id)341 return IMPL.fixed_ip_associate_pool(context, network_id,
342 instance_id, host)
342343
343344
344def fixed_ip_create(context, values):345def fixed_ip_create(context, values):
@@ -361,9 +362,9 @@
361 return IMPL.fixed_ip_get_all(context)362 return IMPL.fixed_ip_get_all(context)
362363
363364
364def fixed_ip_get_all_by_host(context, host):365def fixed_ip_get_all_by_instance_host(context, host):
365 """Get all defined fixed ips used by a host."""366 """Get all allocated fixed ips filtered by instance host."""
366 return IMPL.fixed_ip_get_all_by_host(context, host)367 return IMPL.fixed_ip_get_all_instance_by_host(context, host)
367368
368369
369def fixed_ip_get_by_address(context, address):370def fixed_ip_get_by_address(context, address):
@@ -376,6 +377,11 @@
376 return IMPL.fixed_ip_get_by_instance(context, instance_id)377 return IMPL.fixed_ip_get_by_instance(context, instance_id)
377378
378379
380def fixed_ip_get_by_network_host(context, network_id, host):
381 """Get fixed ip for a host in a network."""
382 return IMPL.fixed_ip_get_by_network_host(context, network_id, host)
383
384
379def fixed_ip_get_by_virtual_interface(context, vif_id):385def fixed_ip_get_by_virtual_interface(context, vif_id):
380 """Get fixed ips by virtual interface or raise if none exist."""386 """Get fixed ips by virtual interface or raise if none exist."""
381 return IMPL.fixed_ip_get_by_virtual_interface(context, vif_id)387 return IMPL.fixed_ip_get_by_virtual_interface(context, vif_id)
382388
=== modified file 'nova/db/sqlalchemy/api.py'
--- nova/db/sqlalchemy/api.py 2011-07-18 23:42:02 +0000
+++ nova/db/sqlalchemy/api.py 2011-07-20 18:28:31 +0000
@@ -18,7 +18,6 @@
18"""18"""
19Implementation of SQLAlchemy backend.19Implementation of SQLAlchemy backend.
20"""20"""
21import traceback
22import warnings21import warnings
2322
24from nova import db23from nova import db
@@ -33,7 +32,6 @@
33from sqlalchemy.exc import IntegrityError32from sqlalchemy.exc import IntegrityError
34from sqlalchemy.orm import joinedload33from sqlalchemy.orm import joinedload
35from sqlalchemy.orm import joinedload_all34from sqlalchemy.orm import joinedload_all
36from sqlalchemy.sql import exists
37from sqlalchemy.sql import func35from sqlalchemy.sql import func
38from sqlalchemy.sql.expression import literal_column36from sqlalchemy.sql.expression import literal_column
3937
@@ -672,7 +670,7 @@
672670
673671
674@require_admin_context672@require_admin_context
675def fixed_ip_associate_pool(context, network_id, instance_id):673def fixed_ip_associate_pool(context, network_id, instance_id=None, host=None):
676 session = get_session()674 session = get_session()
677 with session.begin():675 with session.begin():
678 network_or_none = or_(models.FixedIp.network_id == network_id,676 network_or_none = or_(models.FixedIp.network_id == network_id,
@@ -682,6 +680,7 @@
682 filter_by(reserved=False).\680 filter_by(reserved=False).\
683 filter_by(deleted=False).\681 filter_by(deleted=False).\
684 filter_by(instance=None).\682 filter_by(instance=None).\
683 filter_by(host=None).\
685 with_lockmode('update').\684 with_lockmode('update').\
686 first()685 first()
687 # NOTE(vish): if with_lockmode isn't supported, as in sqlite,686 # NOTE(vish): if with_lockmode isn't supported, as in sqlite,
@@ -692,9 +691,12 @@
692 fixed_ip_ref.network = network_get(context,691 fixed_ip_ref.network = network_get(context,
693 network_id,692 network_id,
694 session=session)693 session=session)
695 fixed_ip_ref.instance = instance_get(context,694 if instance_id:
696 instance_id,695 fixed_ip_ref.instance = instance_get(context,
697 session=session)696 instance_id,
697 session=session)
698 if host:
699 fixed_ip_ref.host = host
698 session.add(fixed_ip_ref)700 session.add(fixed_ip_ref)
699 return fixed_ip_ref['address']701 return fixed_ip_ref['address']
700702
@@ -750,7 +752,7 @@
750752
751753
752@require_admin_context754@require_admin_context
753def fixed_ip_get_all_by_host(context, host=None):755def fixed_ip_get_all_by_instance_host(context, host=None):
754 session = get_session()756 session = get_session()
755757
756 result = session.query(models.FixedIp).\758 result = session.query(models.FixedIp).\
@@ -800,6 +802,20 @@
800802
801803
802@require_context804@require_context
805def fixed_ip_get_by_network_host(context, network_id, host):
806 session = get_session()
807 rv = session.query(models.FixedIp).\
808 filter_by(network_id=network_id).\
809 filter_by(host=host).\
810 filter_by(deleted=False).\
811 first()
812 if not rv:
813 raise exception.FixedIpNotFoundForNetworkHost(network_id=network_id,
814 host=host)
815 return rv
816
817
818@require_context
803def fixed_ip_get_by_virtual_interface(context, vif_id):819def fixed_ip_get_by_virtual_interface(context, vif_id):
804 session = get_session()820 session = get_session()
805 rv = session.query(models.FixedIp).\821 rv = session.query(models.FixedIp).\
@@ -1480,8 +1496,6 @@
1480 called by project_get_networks under certain conditions1496 called by project_get_networks under certain conditions
1481 and network manager add_network_to_project()1497 and network manager add_network_to_project()
14821498
1483 only associates projects with networks that have configured hosts
1484
1485 only associate if the project doesn't already have a network1499 only associate if the project doesn't already have a network
1486 or if force is True1500 or if force is True
14871501
@@ -1497,7 +1511,6 @@
1497 def network_query(project_filter):1511 def network_query(project_filter):
1498 return session.query(models.Network).\1512 return session.query(models.Network).\
1499 filter_by(deleted=False).\1513 filter_by(deleted=False).\
1500 filter(models.Network.host != None).\
1501 filter_by(project_id=project_filter).\1514 filter_by(project_id=project_filter).\
1502 with_lockmode('update').\1515 with_lockmode('update').\
1503 first()1516 first()
@@ -1704,9 +1717,16 @@
1704def network_get_all_by_host(context, host):1717def network_get_all_by_host(context, host):
1705 session = get_session()1718 session = get_session()
1706 with session.begin():1719 with session.begin():
1720 # NOTE(vish): return networks that have host set
1721 # or that have a fixed ip with host set
1722 host_filter = or_(models.Network.host == host,
1723 models.FixedIp.host == host)
1724
1707 return session.query(models.Network).\1725 return session.query(models.Network).\
1708 filter_by(deleted=False).\1726 filter_by(deleted=False).\
1709 filter_by(host=host).\1727 join(models.Network.fixed_ips).\
1728 filter(host_filter).\
1729 filter_by(deleted=False).\
1710 all()1730 all()
17111731
17121732
@@ -1738,6 +1758,7 @@
1738 network_ref = network_get(context, network_id, session=session)1758 network_ref = network_get(context, network_id, session=session)
1739 network_ref.update(values)1759 network_ref.update(values)
1740 network_ref.save(session=session)1760 network_ref.save(session=session)
1761 return network_ref
17411762
17421763
1743###################1764###################
17441765
=== added file 'nova/db/sqlalchemy/migrate_repo/versions/033_ha_network.py'
--- nova/db/sqlalchemy/migrate_repo/versions/033_ha_network.py 1970-01-01 00:00:00 +0000
+++ nova/db/sqlalchemy/migrate_repo/versions/033_ha_network.py 2011-07-20 18:28:31 +0000
@@ -0,0 +1,44 @@
1# vim: tabstop=4 shiftwidth=4 softtabstop=4
2
3# Copyright (c) 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
18from sqlalchemy import Column, Table, MetaData, Boolean, String
19
20meta = MetaData()
21
22fixed_ips_host = Column('host', String(255))
23
24networks_multi_host = Column('multi_host', Boolean, default=False)
25
26
27def upgrade(migrate_engine):
28 meta.bind = migrate_engine
29
30 fixed_ips = Table('fixed_ips', meta, autoload=True)
31 fixed_ips.create_column(fixed_ips_host)
32
33 networks = Table('networks', meta, autoload=True)
34 networks.create_column(networks_multi_host)
35
36
37def downgrade(migrate_engine):
38 meta.bind = migrate_engine
39
40 fixed_ips = Table('fixed_ips', meta, autoload=True)
41 fixed_ips.drop_column(fixed_ips_host)
42
43 networks = Table('networks', meta, autoload=True)
44 networks.drop_column(networks_multi_host)
045
=== modified file 'nova/db/sqlalchemy/models.py'
--- nova/db/sqlalchemy/models.py 2011-07-08 03:07:58 +0000
+++ nova/db/sqlalchemy/models.py 2011-07-20 18:28:31 +0000
@@ -545,6 +545,7 @@
545 injected = Column(Boolean, default=False)545 injected = Column(Boolean, default=False)
546 cidr = Column(String(255), unique=True)546 cidr = Column(String(255), unique=True)
547 cidr_v6 = Column(String(255), unique=True)547 cidr_v6 = Column(String(255), unique=True)
548 multi_host = Column(Boolean, default=False)
548549
549 gateway_v6 = Column(String(255))550 gateway_v6 = Column(String(255))
550 netmask_v6 = Column(String(255))551 netmask_v6 = Column(String(255))
@@ -603,6 +604,7 @@
603 # leased means dhcp bridge has leased the ip604 # leased means dhcp bridge has leased the ip
604 leased = Column(Boolean, default=False)605 leased = Column(Boolean, default=False)
605 reserved = Column(Boolean, default=False)606 reserved = Column(Boolean, default=False)
607 host = Column(String(255))
606608
607609
608class FloatingIp(BASE, NovaBase):610class FloatingIp(BASE, NovaBase):
609611
=== modified file 'nova/exception.py'
--- nova/exception.py 2011-07-13 18:19:32 +0000
+++ nova/exception.py 2011-07-20 18:28:31 +0000
@@ -408,6 +408,11 @@
408 message = _("Instance %(instance_id)s has zero fixed ips.")408 message = _("Instance %(instance_id)s has zero fixed ips.")
409409
410410
411class FixedIpNotFoundForNetworkHost(FixedIpNotFound):
412 message = _("Network host %(host)s has zero fixed ips "
413 "in network %(network_id)s.")
414
415
411class FixedIpNotFoundForSpecificInstance(FixedIpNotFound):416class FixedIpNotFoundForSpecificInstance(FixedIpNotFound):
412 message = _("Instance %(instance_id)s doesn't have fixed ip '%(ip)s'.")417 message = _("Instance %(instance_id)s doesn't have fixed ip '%(ip)s'.")
413418
414419
=== modified file 'nova/network/api.py'
--- nova/network/api.py 2011-07-19 20:49:05 +0000
+++ nova/network/api.py 2011-07-20 18:28:31 +0000
@@ -18,7 +18,6 @@
1818
19"""Handles all requests relating to instances (guest vms)."""19"""Handles all requests relating to instances (guest vms)."""
2020
21from nova import db
22from nova import exception21from nova import exception
23from nova import flags22from nova import flags
24from nova import log as logging23from nova import log as logging
@@ -108,7 +107,11 @@
108 '(%(project)s)') %107 '(%(project)s)') %
109 {'address': floating_ip['address'],108 {'address': floating_ip['address'],
110 'project': context.project_id})109 'project': context.project_id})
111 host = fixed_ip['network']['host']110 # NOTE(vish): if we are multi_host, send to the instances host
111 if fixed_ip['network']['multi_host']:
112 host = fixed_ip['instance']['host']
113 else:
114 host = fixed_ip['network']['host']
112 rpc.cast(context,115 rpc.cast(context,
113 self.db.queue_get_for(context, FLAGS.network_topic, host),116 self.db.queue_get_for(context, FLAGS.network_topic, host),
114 {'method': 'associate_floating_ip',117 {'method': 'associate_floating_ip',
@@ -123,7 +126,11 @@
123 return126 return
124 if not floating_ip.get('fixed_ip'):127 if not floating_ip.get('fixed_ip'):
125 raise exception.ApiError('Address is not associated.')128 raise exception.ApiError('Address is not associated.')
126 host = floating_ip['fixed_ip']['network']['host']129 # NOTE(vish): if we are multi_host, send to the instances host
130 if floating_ip['fixed_ip']['network']['multi_host']:
131 host = floating_ip['fixed_ip']['instance']['host']
132 else:
133 host = floating_ip['fixed_ip']['network']['host']
127 rpc.call(context,134 rpc.call(context,
128 self.db.queue_get_for(context, FLAGS.network_topic, host),135 self.db.queue_get_for(context, FLAGS.network_topic, host),
129 {'method': 'disassociate_floating_ip',136 {'method': 'disassociate_floating_ip',
@@ -137,7 +144,9 @@
137 args = kwargs144 args = kwargs
138 args['instance_id'] = instance['id']145 args['instance_id'] = instance['id']
139 args['project_id'] = instance['project_id']146 args['project_id'] = instance['project_id']
147 args['host'] = instance['host']
140 args['instance_type_id'] = instance['instance_type_id']148 args['instance_type_id'] = instance['instance_type_id']
149
141 return rpc.call(context, FLAGS.network_topic,150 return rpc.call(context, FLAGS.network_topic,
142 {'method': 'allocate_for_instance',151 {'method': 'allocate_for_instance',
143 'args': args})152 'args': args})
@@ -176,7 +185,8 @@
176 def get_instance_nw_info(self, context, instance):185 def get_instance_nw_info(self, context, instance):
177 """Returns all network info related to an instance."""186 """Returns all network info related to an instance."""
178 args = {'instance_id': instance['id'],187 args = {'instance_id': instance['id'],
179 'instance_type_id': instance['instance_type_id']}188 'instance_type_id': instance['instance_type_id'],
189 'host': instance['host']}
180 return rpc.call(context, FLAGS.network_topic,190 return rpc.call(context, FLAGS.network_topic,
181 {'method': 'get_instance_nw_info',191 {'method': 'get_instance_nw_info',
182 'args': args})192 'args': args})
183193
=== modified file 'nova/network/linux_net.py'
--- nova/network/linux_net.py 2011-06-27 21:48:03 +0000
+++ nova/network/linux_net.py 2011-07-20 18:28:31 +0000
@@ -497,7 +497,7 @@
497 suffix = net_attrs['cidr'].rpartition('/')[2]497 suffix = net_attrs['cidr'].rpartition('/')[2]
498 out, err = _execute('sudo', 'ip', 'addr', 'add',498 out, err = _execute('sudo', 'ip', 'addr', 'add',
499 '%s/%s' %499 '%s/%s' %
500 (net_attrs['gateway'], suffix),500 (net_attrs['dhcp_server'], suffix),
501 'brd',501 'brd',
502 net_attrs['broadcast'],502 net_attrs['broadcast'],
503 'dev',503 'dev',
@@ -551,21 +551,27 @@
551 bridge)551 bridge)
552552
553553
554def get_dhcp_leases(context, network_id):554def get_dhcp_leases(context, network_ref):
555 """Return a network's hosts config in dnsmasq leasefile format."""555 """Return a network's hosts config in dnsmasq leasefile format."""
556 hosts = []556 hosts = []
557 for fixed_ip_ref in db.network_get_associated_fixed_ips(context,557 for fixed_ref in db.network_get_associated_fixed_ips(context,
558 network_id):558 network_ref['id']):
559 hosts.append(_host_lease(fixed_ip_ref))559 host = fixed_ref['instance']['host']
560 if network_ref['multi_host'] and FLAGS.host != host:
561 continue
562 hosts.append(_host_lease(fixed_ref))
560 return '\n'.join(hosts)563 return '\n'.join(hosts)
561564
562565
563def get_dhcp_hosts(context, network_id):566def get_dhcp_hosts(context, network_ref):
564 """Get network's hosts config in dhcp-host format."""567 """Get network's hosts config in dhcp-host format."""
565 hosts = []568 hosts = []
566 for fixed_ip_ref in db.network_get_associated_fixed_ips(context,569 for fixed_ref in db.network_get_associated_fixed_ips(context,
567 network_id):570 network_ref['id']):
568 hosts.append(_host_dhcp(fixed_ip_ref))571 host = fixed_ref['instance']['host']
572 if network_ref['multi_host'] and FLAGS.host != host:
573 continue
574 hosts.append(_host_dhcp(fixed_ref))
569 return '\n'.join(hosts)575 return '\n'.join(hosts)
570576
571577
@@ -573,18 +579,16 @@
573# configuration options (like dchp-range, vlan, ...)579# configuration options (like dchp-range, vlan, ...)
574# aren't reloaded.580# aren't reloaded.
575@utils.synchronized('dnsmasq_start')581@utils.synchronized('dnsmasq_start')
576def update_dhcp(context, network_id):582def update_dhcp(context, network_ref):
577 """(Re)starts a dnsmasq server for a given network.583 """(Re)starts a dnsmasq server for a given network.
578584
579 If a dnsmasq instance is already running then send a HUP585 If a dnsmasq instance is already running then send a HUP
580 signal causing it to reload, otherwise spawn a new instance.586 signal causing it to reload, otherwise spawn a new instance.
581587
582 """588 """
583 network_ref = db.network_get(context, network_id)
584
585 conffile = _dhcp_file(network_ref['bridge'], 'conf')589 conffile = _dhcp_file(network_ref['bridge'], 'conf')
586 with open(conffile, 'w') as f:590 with open(conffile, 'w') as f:
587 f.write(get_dhcp_hosts(context, network_id))591 f.write(get_dhcp_hosts(context, network_ref))
588592
589 # Make sure dnsmasq can actually read it (it setuid()s to "nobody")593 # Make sure dnsmasq can actually read it (it setuid()s to "nobody")
590 os.chmod(conffile, 0644)594 os.chmod(conffile, 0644)
@@ -612,9 +616,7 @@
612616
613617
614@utils.synchronized('radvd_start')618@utils.synchronized('radvd_start')
615def update_ra(context, network_id):619def update_ra(context, network_ref):
616 network_ref = db.network_get(context, network_id)
617
618 conffile = _ra_file(network_ref['bridge'], 'conf')620 conffile = _ra_file(network_ref['bridge'], 'conf')
619 with open(conffile, 'w') as f:621 with open(conffile, 'w') as f:
620 conf_str = """622 conf_str = """
@@ -650,9 +652,6 @@
650 LOG.debug(_('Pid %d is stale, relaunching radvd'), pid)652 LOG.debug(_('Pid %d is stale, relaunching radvd'), pid)
651 command = _ra_cmd(network_ref)653 command = _ra_cmd(network_ref)
652 _execute(*command)654 _execute(*command)
653 db.network_update(context, network_id,
654 {'gateway_v6':
655 utils.get_my_linklocal(network_ref['bridge'])})
656655
657656
658def _host_lease(fixed_ip_ref):657def _host_lease(fixed_ip_ref):
@@ -701,10 +700,11 @@
701 cmd = ['sudo', '-E', 'dnsmasq',700 cmd = ['sudo', '-E', 'dnsmasq',
702 '--strict-order',701 '--strict-order',
703 '--bind-interfaces',702 '--bind-interfaces',
703 '--interface=%s' % net['bridge'],
704 '--conf-file=%s' % FLAGS.dnsmasq_config_file,704 '--conf-file=%s' % FLAGS.dnsmasq_config_file,
705 '--domain=%s' % FLAGS.dhcp_domain,705 '--domain=%s' % FLAGS.dhcp_domain,
706 '--pid-file=%s' % _dhcp_file(net['bridge'], 'pid'),706 '--pid-file=%s' % _dhcp_file(net['bridge'], 'pid'),
707 '--listen-address=%s' % net['gateway'],707 '--listen-address=%s' % net['dhcp_server'],
708 '--except-interface=lo',708 '--except-interface=lo',
709 '--dhcp-range=%s,static,120s' % net['dhcp_start'],709 '--dhcp-range=%s,static,120s' % net['dhcp_start'],
710 '--dhcp-lease-max=%s' % len(netaddr.IPNetwork(net['cidr'])),710 '--dhcp-lease-max=%s' % len(netaddr.IPNetwork(net['cidr'])),
711711
=== modified file 'nova/network/manager.py'
--- nova/network/manager.py 2011-07-19 19:29:44 +0000
+++ nova/network/manager.py 2011-07-20 18:28:31 +0000
@@ -28,7 +28,6 @@
28:flat_network_bridge: Bridge device for simple network instances28:flat_network_bridge: Bridge device for simple network instances
29:flat_interface: FlatDhcp will bridge into this interface if set29:flat_interface: FlatDhcp will bridge into this interface if set
30:flat_network_dns: Dns for simple network30:flat_network_dns: Dns for simple network
31:flat_network_dhcp_start: Dhcp start for FlatDhcp
32:vlan_start: First VLAN for private networks31:vlan_start: First VLAN for private networks
33:vpn_ip: Public IP for the cloudpipe VPN servers32:vpn_ip: Public IP for the cloudpipe VPN servers
34:vpn_start: First Vpn port for private networks33:vpn_start: First Vpn port for private networks
@@ -49,7 +48,6 @@
49import math48import math
50import netaddr49import netaddr
51import socket50import socket
52import pickle
53from eventlet import greenpool51from eventlet import greenpool
5452
55from nova import context53from nova import context
@@ -78,8 +76,6 @@
78 'Whether to attempt to inject network setup into guest')76 'Whether to attempt to inject network setup into guest')
79flags.DEFINE_string('flat_interface', None,77flags.DEFINE_string('flat_interface', None,
80 'FlatDhcp will bridge into this interface if set')78 'FlatDhcp will bridge into this interface if set')
81flags.DEFINE_string('flat_network_dhcp_start', '10.0.0.2',
82 'Dhcp start for FlatDhcp')
83flags.DEFINE_integer('vlan_start', 100, 'First VLAN for private networks')79flags.DEFINE_integer('vlan_start', 100, 'First VLAN for private networks')
84flags.DEFINE_string('vlan_interface', None,80flags.DEFINE_string('vlan_interface', None,
85 'vlans will bridge into this interface if set')81 'vlans will bridge into this interface if set')
@@ -87,6 +83,8 @@
87flags.DEFINE_string('vpn_ip', '$my_ip',83flags.DEFINE_string('vpn_ip', '$my_ip',
88 'Public IP for the cloudpipe VPN servers')84 'Public IP for the cloudpipe VPN servers')
89flags.DEFINE_integer('vpn_start', 1000, 'First Vpn port for private networks')85flags.DEFINE_integer('vpn_start', 1000, 'First Vpn port for private networks')
86flags.DEFINE_bool('multi_host', False,
87 'Default value for multi_host in networks')
90flags.DEFINE_integer('network_size', 256,88flags.DEFINE_integer('network_size', 256,
91 'Number of addresses in each private subnet')89 'Number of addresses in each private subnet')
92flags.DEFINE_string('floating_range', '4.4.4.0/24',90flags.DEFINE_string('floating_range', '4.4.4.0/24',
@@ -104,7 +102,8 @@
104 'Seconds after which a deallocated ip is disassociated')102 'Seconds after which a deallocated ip is disassociated')
105flags.DEFINE_integer('create_unique_mac_address_attempts', 5,103flags.DEFINE_integer('create_unique_mac_address_attempts', 5,
106 'Number of attempts to create unique mac address')104 'Number of attempts to create unique mac address')
107105flags.DEFINE_bool('auto_assign_floating_ip', False,
106 'Autoassigning floating ip to VM')
108flags.DEFINE_bool('use_ipv6', False,107flags.DEFINE_bool('use_ipv6', False,
109 'use the ipv6')108 'use the ipv6')
110flags.DEFINE_string('network_host', socket.gethostname(),109flags.DEFINE_string('network_host', socket.gethostname(),
@@ -124,16 +123,26 @@
124 used since they share code to RPC.call allocate_fixed_ip on the123 used since they share code to RPC.call allocate_fixed_ip on the
125 correct network host to configure dnsmasq124 correct network host to configure dnsmasq
126 """125 """
127 def _allocate_fixed_ips(self, context, instance_id, networks, **kwargs):126 def _allocate_fixed_ips(self, context, instance_id, host, networks,
127 **kwargs):
128 """Calls allocate_fixed_ip once for each network."""128 """Calls allocate_fixed_ip once for each network."""
129 green_pool = greenpool.GreenPool()129 green_pool = greenpool.GreenPool()
130130
131 vpn = kwargs.pop('vpn')131 vpn = kwargs.pop('vpn')
132 for network in networks:132 for network in networks:
133 if network['host'] != self.host:133 # NOTE(vish): if we are not multi_host pass to the network host
134 if not network['multi_host']:
135 host = network['host']
136 # NOTE(vish): if there is no network host, set one
137 if host == None:
138 host = rpc.call(context, FLAGS.network_topic,
139 {'method': 'set_network_host',
140 'args': {'network_ref': network}})
141 if host != self.host:
134 # need to call allocate_fixed_ip to correct network host142 # need to call allocate_fixed_ip to correct network host
135 topic = self.db.queue_get_for(context, FLAGS.network_topic,143 topic = self.db.queue_get_for(context,
136 network['host'])144 FLAGS.network_topic,
145 host)
137 args = {}146 args = {}
138 args['instance_id'] = instance_id147 args['instance_id'] = instance_id
139 args['network_id'] = network['id']148 args['network_id'] = network['id']
@@ -149,12 +158,13 @@
149 # wait for all of the allocates (if any) to finish158 # wait for all of the allocates (if any) to finish
150 green_pool.waitall()159 green_pool.waitall()
151160
152 def _rpc_allocate_fixed_ip(self, context, instance_id, network_id):161 def _rpc_allocate_fixed_ip(self, context, instance_id, network_id,
162 **kwargs):
153 """Sits in between _allocate_fixed_ips and allocate_fixed_ip to163 """Sits in between _allocate_fixed_ips and allocate_fixed_ip to
154 perform network lookup on the far side of rpc.164 perform network lookup on the far side of rpc.
155 """165 """
156 network = self.db.network_get(context, network_id)166 network = self.db.network_get(context, network_id)
157 self.allocate_fixed_ip(context, instance_id, network)167 self.allocate_fixed_ip(context, instance_id, network, **kwargs)
158168
159169
160class FloatingIP(object):170class FloatingIP(object):
@@ -193,7 +203,7 @@
193 # which is currently the NetworkManager version203 # which is currently the NetworkManager version
194 # do this first so fixed ip is already allocated204 # do this first so fixed ip is already allocated
195 ips = super(FloatingIP, self).allocate_for_instance(context, **kwargs)205 ips = super(FloatingIP, self).allocate_for_instance(context, **kwargs)
196 if hasattr(FLAGS, 'auto_assign_floating_ip'):206 if FLAGS.auto_assign_floating_ip:
197 # allocate a floating ip (public_ip is just the address string)207 # allocate a floating ip (public_ip is just the address string)
198 public_ip = self.allocate_floating_ip(context, project_id)208 public_ip = self.allocate_floating_ip(context, project_id)
199 # set auto_assigned column to true for the floating ip209 # set auto_assigned column to true for the floating ip
@@ -300,15 +310,36 @@
300 super(NetworkManager, self).__init__(service_name='network',310 super(NetworkManager, self).__init__(service_name='network',
301 *args, **kwargs)311 *args, **kwargs)
302312
313 @utils.synchronized('get_dhcp')
314 def _get_dhcp_ip(self, context, network_ref, host=None):
315 """Get the proper dhcp address to listen on."""
316 # NOTE(vish): this is for compatibility
317 if not network_ref['multi_host']:
318 return network_ref['gateway']
319
320 if not host:
321 host = self.host
322 network_id = network_ref['id']
323 try:
324 fip = self.db.fixed_ip_get_by_network_host(context,
325 network_id,
326 host)
327 return fip['address']
328 except exception.FixedIpNotFoundForNetworkHost:
329 elevated = context.elevated()
330 return self.db.fixed_ip_associate_pool(elevated,
331 network_id,
332 host=host)
333
303 def init_host(self):334 def init_host(self):
304 """Do any initialization that needs to be run if this is a335 """Do any initialization that needs to be run if this is a
305 standalone service.336 standalone service.
306 """337 """
307 # Set up this host for networks in which it's already338 # NOTE(vish): Set up networks for which this host already has
308 # the designated network host.339 # an ip address.
309 ctxt = context.get_admin_context()340 ctxt = context.get_admin_context()
310 for network in self.db.network_get_all_by_host(ctxt, self.host):341 for network in self.db.network_get_all_by_host(ctxt, self.host):
311 self._on_set_network_host(ctxt, network['id'])342 self._setup_network(ctxt, network)
312343
313 def periodic_tasks(self, context=None):344 def periodic_tasks(self, context=None):
314 """Tasks to be run at a periodic interval."""345 """Tasks to be run at a periodic interval."""
@@ -323,32 +354,13 @@
323 if num:354 if num:
324 LOG.debug(_('Dissassociated %s stale fixed ip(s)'), num)355 LOG.debug(_('Dissassociated %s stale fixed ip(s)'), num)
325356
326 # setup any new networks which have been created357 def set_network_host(self, context, network_ref):
327 self.set_network_hosts(context)
328
329 def set_network_host(self, context, network_id):
330 """Safely sets the host of the network."""358 """Safely sets the host of the network."""
331 LOG.debug(_('setting network host'), context=context)359 LOG.debug(_('setting network host'), context=context)
332 host = self.db.network_set_host(context,360 host = self.db.network_set_host(context,
333 network_id,361 network_ref['id'],
334 self.host)362 self.host)
335 if host == self.host:363 return host
336 self._on_set_network_host(context, network_id)
337
338 def set_network_hosts(self, context):
339 """Set the network hosts for any networks which are unset."""
340 try:
341 networks = self.db.network_get_all(context)
342 except exception.NoNetworksFound:
343 # no networks found, nothing to do
344 return
345
346 for network in networks:
347 host = network['host']
348 if not host:
349 # break so worker will only grab 1 (to help scale flatter)
350 self.set_network_host(context, network['id'])
351 break
352364
353 def _get_networks_for_instance(self, context, instance_id, project_id):365 def _get_networks_for_instance(self, context, instance_id, project_id):
354 """Determine & return which networks an instance should connect to."""366 """Determine & return which networks an instance should connect to."""
@@ -361,9 +373,9 @@
361 # we don't care if no networks are found373 # we don't care if no networks are found
362 pass374 pass
363375
364 # return only networks which are not vlan networks and have host set376 # return only networks which are not vlan networks
365 return [network for network in networks if377 return [network for network in networks if
366 not network['vlan'] and network['host']]378 not network['vlan']]
367379
368 def allocate_for_instance(self, context, **kwargs):380 def allocate_for_instance(self, context, **kwargs):
369 """Handles allocating the various network resources for an instance.381 """Handles allocating the various network resources for an instance.
@@ -371,6 +383,7 @@
371 rpc.called by network_api383 rpc.called by network_api
372 """384 """
373 instance_id = kwargs.pop('instance_id')385 instance_id = kwargs.pop('instance_id')
386 host = kwargs.pop('host')
374 project_id = kwargs.pop('project_id')387 project_id = kwargs.pop('project_id')
375 type_id = kwargs.pop('instance_type_id')388 type_id = kwargs.pop('instance_type_id')
376 vpn = kwargs.pop('vpn')389 vpn = kwargs.pop('vpn')
@@ -379,9 +392,11 @@
379 context=context)392 context=context)
380 networks = self._get_networks_for_instance(admin_context, instance_id,393 networks = self._get_networks_for_instance(admin_context, instance_id,
381 project_id)394 project_id)
395 LOG.warn(networks)
382 self._allocate_mac_addresses(context, instance_id, networks)396 self._allocate_mac_addresses(context, instance_id, networks)
383 self._allocate_fixed_ips(admin_context, instance_id, networks, vpn=vpn)397 self._allocate_fixed_ips(admin_context, instance_id, host, networks,
384 return self.get_instance_nw_info(context, instance_id, type_id)398 vpn=vpn)
399 return self.get_instance_nw_info(context, instance_id, type_id, host)
385400
386 def deallocate_for_instance(self, context, **kwargs):401 def deallocate_for_instance(self, context, **kwargs):
387 """Handles deallocating various network resources for an instance.402 """Handles deallocating various network resources for an instance.
@@ -401,7 +416,8 @@
401 # deallocate vifs (mac addresses)416 # deallocate vifs (mac addresses)
402 self.db.virtual_interface_delete_by_instance(context, instance_id)417 self.db.virtual_interface_delete_by_instance(context, instance_id)
403418
404 def get_instance_nw_info(self, context, instance_id, instance_type_id):419 def get_instance_nw_info(self, context, instance_id,
420 instance_type_id, host):
405 """Creates network info list for instance.421 """Creates network info list for instance.
406422
407 called by allocate_for_instance and netowrk_api423 called by allocate_for_instance and netowrk_api
@@ -445,9 +461,16 @@
445 'cidr': network['cidr'],461 'cidr': network['cidr'],
446 'cidr_v6': network['cidr_v6'],462 'cidr_v6': network['cidr_v6'],
447 'injected': network['injected']}463 'injected': network['injected']}
464 if network['multi_host']:
465 dhcp_server = self._get_dhcp_ip(context, network, host)
466 else:
467 dhcp_server = self._get_dhcp_ip(context,
468 network,
469 network['host'])
448 info = {470 info = {
449 'label': network['label'],471 'label': network['label'],
450 'gateway': network['gateway'],472 'gateway': network['gateway'],
473 'dhcp_server': dhcp_server,
451 'broadcast': network['broadcast'],474 'broadcast': network['broadcast'],
452 'mac': vif['address'],475 'mac': vif['address'],
453 'rxtx_cap': flavor['rxtx_cap'],476 'rxtx_cap': flavor['rxtx_cap'],
@@ -487,10 +510,10 @@
487 random.randint(0x00, 0xff)]510 random.randint(0x00, 0xff)]
488 return ':'.join(map(lambda x: "%02x" % x, mac))511 return ':'.join(map(lambda x: "%02x" % x, mac))
489512
490 def add_fixed_ip_to_instance(self, context, instance_id, network_id):513 def add_fixed_ip_to_instance(self, context, instance_id, host, network_id):
491 """Adds a fixed ip to an instance from specified network."""514 """Adds a fixed ip to an instance from specified network."""
492 networks = [self.db.network_get(context, network_id)]515 networks = [self.db.network_get(context, network_id)]
493 self._allocate_fixed_ips(context, instance_id, networks)516 self._allocate_fixed_ips(context, instance_id, host, networks)
494517
495 def remove_fixed_ip_from_instance(self, context, instance_id, address):518 def remove_fixed_ip_from_instance(self, context, instance_id, address):
496 """Removes a fixed ip from an instance from specified network."""519 """Removes a fixed ip from an instance from specified network."""
@@ -517,6 +540,7 @@
517 values = {'allocated': True,540 values = {'allocated': True,
518 'virtual_interface_id': vif['id']}541 'virtual_interface_id': vif['id']}
519 self.db.fixed_ip_update(context, address, values)542 self.db.fixed_ip_update(context, address, values)
543 self._setup_network(context, network)
520 return address544 return address
521545
522 def deallocate_fixed_ip(self, context, address, **kwargs):546 def deallocate_fixed_ip(self, context, address, **kwargs):
@@ -562,10 +586,10 @@
562 # means there will stale entries in the conf file586 # means there will stale entries in the conf file
563 # the code below will update the file if necessary587 # the code below will update the file if necessary
564 if FLAGS.update_dhcp_on_disassociate:588 if FLAGS.update_dhcp_on_disassociate:
565 network = self.db.fixed_ip_get_network(context, address)589 network_ref = self.db.fixed_ip_get_network(context, address)
566 self.driver.update_dhcp(context, network['id'])590 self._setup_network(context, network_ref)
567591
568 def create_networks(self, context, label, cidr, num_networks,592 def create_networks(self, context, label, cidr, multi_host, num_networks,
569 network_size, cidr_v6, gateway_v6, bridge,593 network_size, cidr_v6, gateway_v6, bridge,
570 bridge_interface, **kwargs):594 bridge_interface, **kwargs):
571 """Create networks based on parameters."""595 """Create networks based on parameters."""
@@ -584,6 +608,7 @@
584 net['bridge_interface'] = bridge_interface608 net['bridge_interface'] = bridge_interface
585 net['dns'] = FLAGS.flat_network_dns609 net['dns'] = FLAGS.flat_network_dns
586 net['cidr'] = cidr610 net['cidr'] = cidr
611 net['multi_host'] = multi_host
587 net['netmask'] = str(project_net.netmask)612 net['netmask'] = str(project_net.netmask)
588 net['gateway'] = str(project_net[1])613 net['gateway'] = str(project_net[1])
589 net['broadcast'] = str(project_net.broadcast)614 net['broadcast'] = str(project_net.broadcast)
@@ -659,12 +684,13 @@
659 'address': address,684 'address': address,
660 'reserved': reserved})685 'reserved': reserved})
661686
662 def _allocate_fixed_ips(self, context, instance_id, networks, **kwargs):687 def _allocate_fixed_ips(self, context, instance_id, host, networks,
688 **kwargs):
663 """Calls allocate_fixed_ip once for each network."""689 """Calls allocate_fixed_ip once for each network."""
664 raise NotImplementedError()690 raise NotImplementedError()
665691
666 def _on_set_network_host(self, context, network_id):692 def _setup_network(self, context, network_ref):
667 """Called when this host becomes the host for a network."""693 """Sets up network on this host."""
668 raise NotImplementedError()694 raise NotImplementedError()
669695
670 def setup_compute_network(self, context, instance_id):696 def setup_compute_network(self, context, instance_id):
@@ -706,7 +732,8 @@
706732
707 timeout_fixed_ips = False733 timeout_fixed_ips = False
708734
709 def _allocate_fixed_ips(self, context, instance_id, networks, **kwargs):735 def _allocate_fixed_ips(self, context, instance_id, host, networks,
736 **kwargs):
710 """Calls allocate_fixed_ip once for each network."""737 """Calls allocate_fixed_ip once for each network."""
711 for network in networks:738 for network in networks:
712 self.allocate_fixed_ip(context, instance_id, network)739 self.allocate_fixed_ip(context, instance_id, network)
@@ -724,12 +751,12 @@
724 """751 """
725 pass752 pass
726753
727 def _on_set_network_host(self, context, network_id):754 def _setup_network(self, context, network_ref):
728 """Called when this host becomes the host for a network."""755 """Setup Network on this host."""
729 net = {}756 net = {}
730 net['injected'] = FLAGS.flat_injected757 net['injected'] = FLAGS.flat_injected
731 net['dns'] = FLAGS.flat_network_dns758 net['dns'] = FLAGS.flat_network_dns
732 self.db.network_update(context, network_id, net)759 self.db.network_update(context, network_ref['id'], net)
733760
734761
735class FlatDHCPManager(FloatingIP, RPCAllocateFixedIP, NetworkManager):762class FlatDHCPManager(FloatingIP, RPCAllocateFixedIP, NetworkManager):
@@ -760,30 +787,23 @@
760 """787 """
761 networks = db.network_get_all_by_instance(context, instance_id)788 networks = db.network_get_all_by_instance(context, instance_id)
762 for network in networks:789 for network in networks:
763 self.driver.ensure_bridge(network['bridge'],790 if not network['multi_host']:
764 network['bridge_interface'])791 self.driver.ensure_bridge(network['bridge'],
765792 network['bridge_interface'])
766 def allocate_fixed_ip(self, context, instance_id, network, **kwargs):793
767 """Allocate flat_network fixed_ip, then setup dhcp for this network."""794 def _setup_network(self, context, network_ref):
768 address = super(FlatDHCPManager, self).allocate_fixed_ip(context,795 """Sets up network on this host."""
769 instance_id,796 network_ref['dhcp_server'] = self._get_dhcp_ip(context, network_ref)
770 network)797 self.driver.ensure_bridge(network_ref['bridge'],
771 if not FLAGS.fake_network:798 network_ref['bridge_interface'],
772 self.driver.update_dhcp(context, network['id'])799 network_ref)
773800 if not FLAGS.fake_network:
774 def _on_set_network_host(self, context, network_id):801 self.driver.update_dhcp(context, network_ref)
775 """Called when this host becomes the host for a project."""
776 net = {}
777 net['dhcp_start'] = FLAGS.flat_network_dhcp_start
778 self.db.network_update(context, network_id, net)
779 network = db.network_get(context, network_id)
780 self.driver.ensure_bridge(network['bridge'],
781 network['bridge_interface'],
782 network)
783 if not FLAGS.fake_network:
784 self.driver.update_dhcp(context, network_id)
785 if(FLAGS.use_ipv6):802 if(FLAGS.use_ipv6):
786 self.driver.update_ra(context, network_id)803 self.driver.update_ra(context, network_ref)
804 gateway = utils.get_my_linklocal(network_ref['bridge'])
805 self.db.network_update(context, network_ref['id'],
806 {'gateway_v6': gateway})
787807
788808
789class VlanManager(RPCAllocateFixedIP, FloatingIP, NetworkManager):809class VlanManager(RPCAllocateFixedIP, FloatingIP, NetworkManager):
@@ -832,8 +852,8 @@
832 values = {'allocated': True,852 values = {'allocated': True,
833 'virtual_interface_id': vif['id']}853 'virtual_interface_id': vif['id']}
834 self.db.fixed_ip_update(context, address, values)854 self.db.fixed_ip_update(context, address, values)
835 if not FLAGS.fake_network:855 self._setup_network(context, network)
836 self.driver.update_dhcp(context, network['id'])856 return address
837857
838 def add_network_to_project(self, context, project_id):858 def add_network_to_project(self, context, project_id):
839 """Force adds another network to a project."""859 """Force adds another network to a project."""
@@ -845,17 +865,15 @@
845 """865 """
846 networks = self.db.network_get_all_by_instance(context, instance_id)866 networks = self.db.network_get_all_by_instance(context, instance_id)
847 for network in networks:867 for network in networks:
848 self.driver.ensure_vlan_bridge(network['vlan'],868 if not network['multi_host']:
849 network['bridge'],869 self.driver.ensure_vlan_bridge(network['vlan'],
850 network['bridge_interface'])870 network['bridge'],
871 network['bridge_interface'])
851872
852 def _get_networks_for_instance(self, context, instance_id, project_id):873 def _get_networks_for_instance(self, context, instance_id, project_id):
853 """Determine which networks an instance should connect to."""874 """Determine which networks an instance should connect to."""
854 # get networks associated with project875 # get networks associated with project
855 networks = self.db.project_get_networks(context, project_id)876 return self.db.project_get_networks(context, project_id)
856
857 # return only networks which have host set
858 return [network for network in networks if network['host']]
859877
860 def create_networks(self, context, **kwargs):878 def create_networks(self, context, **kwargs):
861 """Create networks based on parameters."""879 """Create networks based on parameters."""
@@ -874,32 +892,35 @@
874892
875 NetworkManager.create_networks(self, context, vpn=True, **kwargs)893 NetworkManager.create_networks(self, context, vpn=True, **kwargs)
876894
877 def _on_set_network_host(self, context, network_id):895 def _setup_network(self, context, network_ref):
878 """Called when this host becomes the host for a network."""896 """Sets up network on this host."""
879 network = self.db.network_get(context, network_id)897 network_ref['dhcp_server'] = self._get_dhcp_ip(context, network_ref)
880 if not network['vpn_public_address']:898 if not network_ref['vpn_public_address']:
881 net = {}899 net = {}
882 address = FLAGS.vpn_ip900 address = FLAGS.vpn_ip
883 net['vpn_public_address'] = address901 net['vpn_public_address'] = address
884 db.network_update(context, network_id, net)902 network_ref = db.network_update(context, network_ref['id'], net)
885 else:903 else:
886 address = network['vpn_public_address']904 address = network_ref['vpn_public_address']
887 self.driver.ensure_vlan_bridge(network['vlan'],905 self.driver.ensure_vlan_bridge(network_ref['vlan'],
888 network['bridge'],906 network_ref['bridge'],
889 network['bridge_interface'],907 network_ref['bridge_interface'],
890 network)908 network_ref)
891909
892 # NOTE(vish): only ensure this forward if the address hasn't been set910 # NOTE(vish): only ensure this forward if the address hasn't been set
893 # manually.911 # manually.
894 if address == FLAGS.vpn_ip and hasattr(self.driver,912 if address == FLAGS.vpn_ip and hasattr(self.driver,
895 "ensure_vlan_forward"):913 "ensure_vlan_forward"):
896 self.driver.ensure_vlan_forward(FLAGS.vpn_ip,914 self.driver.ensure_vlan_forward(FLAGS.vpn_ip,
897 network['vpn_public_port'],915 network_ref['vpn_public_port'],
898 network['vpn_private_address'])916 network_ref['vpn_private_address'])
899 if not FLAGS.fake_network:917 if not FLAGS.fake_network:
900 self.driver.update_dhcp(context, network_id)918 self.driver.update_dhcp(context, network_ref)
901 if(FLAGS.use_ipv6):919 if(FLAGS.use_ipv6):
902 self.driver.update_ra(context, network_id)920 self.driver.update_ra(context, network_ref)
921 gateway = utils.get_my_linklocal(network_ref['bridge'])
922 self.db.network_update(context, network_ref['id'],
923 {'gateway_v6': gateway})
903924
904 @property925 @property
905 def _bottom_reserved_ips(self):926 def _bottom_reserved_ips(self):
906927
=== modified file 'nova/tests/__init__.py'
--- nova/tests/__init__.py 2011-06-29 17:58:10 +0000
+++ nova/tests/__init__.py 2011-07-20 18:28:31 +0000
@@ -59,6 +59,7 @@
59 network.create_networks(ctxt,59 network.create_networks(ctxt,
60 label='test',60 label='test',
61 cidr=FLAGS.fixed_range,61 cidr=FLAGS.fixed_range,
62 multi_host=FLAGS.multi_host,
62 num_networks=FLAGS.num_networks,63 num_networks=FLAGS.num_networks,
63 network_size=FLAGS.network_size,64 network_size=FLAGS.network_size,
64 cidr_v6=FLAGS.fixed_range_v6,65 cidr_v6=FLAGS.fixed_range_v6,
@@ -68,7 +69,7 @@
68 vpn_start=FLAGS.vpn_start,69 vpn_start=FLAGS.vpn_start,
69 vlan_start=FLAGS.vlan_start)70 vlan_start=FLAGS.vlan_start)
70 for net in db.network_get_all(ctxt):71 for net in db.network_get_all(ctxt):
71 network.set_network_host(ctxt, net['id'])72 network.set_network_host(ctxt, net)
7273
73 cleandb = os.path.join(FLAGS.state_path, FLAGS.sqlite_clean_db)74 cleandb = os.path.join(FLAGS.state_path, FLAGS.sqlite_clean_db)
74 shutil.copyfile(testdb, cleandb)75 shutil.copyfile(testdb, cleandb)
7576
=== modified file 'nova/tests/test_libvirt.py'
--- nova/tests/test_libvirt.py 2011-06-28 16:20:02 +0000
+++ nova/tests/test_libvirt.py 2011-07-20 18:28:31 +0000
@@ -58,6 +58,7 @@
58 'cidr': fake_ip,58 'cidr': fake_ip,
59 'cidr_v6': fake_ip}59 'cidr_v6': fake_ip}
60 mapping = {'mac': fake,60 mapping = {'mac': fake,
61 'dhcp_server': fake,
61 'gateway': fake,62 'gateway': fake,
62 'gateway6': fake,63 'gateway6': fake,
63 'ips': [{'ip': fake_ip}, {'ip': fake_ip}]}64 'ips': [{'ip': fake_ip}, {'ip': fake_ip}]}
6465
=== modified file 'nova/tests/test_network.py'
--- nova/tests/test_network.py 2011-07-11 14:49:34 +0000
+++ nova/tests/test_network.py 2011-07-20 18:28:31 +0000
@@ -45,6 +45,7 @@
45networks = [{'id': 0,45networks = [{'id': 0,
46 'label': 'test0',46 'label': 'test0',
47 'injected': False,47 'injected': False,
48 'multi_host': False,
48 'cidr': '192.168.0.0/24',49 'cidr': '192.168.0.0/24',
49 'cidr_v6': '2001:db8::/64',50 'cidr_v6': '2001:db8::/64',
50 'gateway_v6': '2001:db8::1',51 'gateway_v6': '2001:db8::1',
@@ -62,6 +63,7 @@
62 {'id': 1,63 {'id': 1,
63 'label': 'test1',64 'label': 'test1',
64 'injected': False,65 'injected': False,
66 'multi_host': False,
65 'cidr': '192.168.1.0/24',67 'cidr': '192.168.1.0/24',
66 'cidr_v6': '2001:db9::/64',68 'cidr_v6': '2001:db9::/64',
67 'gateway_v6': '2001:db9::1',69 'gateway_v6': '2001:db9::1',
@@ -122,20 +124,6 @@
122 self.network = network_manager.FlatManager(host=HOST)124 self.network = network_manager.FlatManager(host=HOST)
123 self.network.db = db125 self.network.db = db
124126
125 def test_set_network_hosts(self):
126 self.mox.StubOutWithMock(db, 'network_get_all')
127 self.mox.StubOutWithMock(db, 'network_set_host')
128 self.mox.StubOutWithMock(db, 'network_update')
129
130 db.network_get_all(mox.IgnoreArg()).AndReturn([networks[0]])
131 db.network_set_host(mox.IgnoreArg(),
132 networks[0]['id'],
133 mox.IgnoreArg()).AndReturn(HOST)
134 db.network_update(mox.IgnoreArg(), mox.IgnoreArg(), mox.IgnoreArg())
135 self.mox.ReplayAll()
136
137 self.network.set_network_hosts(None)
138
139 def test_get_instance_nw_info(self):127 def test_get_instance_nw_info(self):
140 self.mox.StubOutWithMock(db, 'fixed_ip_get_by_instance')128 self.mox.StubOutWithMock(db, 'fixed_ip_get_by_instance')
141 self.mox.StubOutWithMock(db, 'virtual_interface_get_by_instance')129 self.mox.StubOutWithMock(db, 'virtual_interface_get_by_instance')
@@ -149,7 +137,7 @@
149 mox.IgnoreArg()).AndReturn(flavor)137 mox.IgnoreArg()).AndReturn(flavor)
150 self.mox.ReplayAll()138 self.mox.ReplayAll()
151139
152 nw_info = self.network.get_instance_nw_info(None, 0, 0)140 nw_info = self.network.get_instance_nw_info(None, 0, 0, None)
153141
154 self.assertTrue(nw_info)142 self.assertTrue(nw_info)
155143
@@ -164,6 +152,7 @@
164 self.assertDictMatch(nw[0], check)152 self.assertDictMatch(nw[0], check)
165153
166 check = {'broadcast': '192.168.%s.255' % i,154 check = {'broadcast': '192.168.%s.255' % i,
155 'dhcp_server': '192.168.%s.1' % i,
167 'dns': 'DONTCARE',156 'dns': 'DONTCARE',
168 'gateway': '192.168.%s.1' % i,157 'gateway': '192.168.%s.1' % i,
169 'gateway6': '2001:db%s::1' % i8,158 'gateway6': '2001:db%s::1' % i8,
170159
=== modified file 'nova/virt/libvirt/connection.py'
--- nova/virt/libvirt/connection.py 2011-07-20 16:17:44 +0000
+++ nova/virt/libvirt/connection.py 2011-07-20 18:28:31 +0000
@@ -928,7 +928,6 @@
928928
929 def _get_nic_for_xml(self, network, mapping):929 def _get_nic_for_xml(self, network, mapping):
930 # Assume that the gateway also acts as the dhcp server.930 # Assume that the gateway also acts as the dhcp server.
931 dhcp_server = mapping['gateway']
932 gateway6 = mapping.get('gateway6')931 gateway6 = mapping.get('gateway6')
933 mac_id = mapping['mac'].replace(':', '')932 mac_id = mapping['mac'].replace(':', '')
934933
@@ -951,7 +950,7 @@
951 'bridge_name': network['bridge'],950 'bridge_name': network['bridge'],
952 'mac_address': mapping['mac'],951 'mac_address': mapping['mac'],
953 'ip_address': mapping['ips'][0]['ip'],952 'ip_address': mapping['ips'][0]['ip'],
954 'dhcp_server': dhcp_server,953 'dhcp_server': mapping['dhcp_server'],
955 'extra_params': extra_params,954 'extra_params': extra_params,
956 }955 }
957956