summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorVishvananda Ishaya <vishvananda@gmail.com>2011-07-05 11:51:46 -0700
committerVishvananda Ishaya <vishvananda@gmail.com>2011-07-05 11:51:46 -0700
commit01b9d211e606ee0be221b27edae8aab1d35096ff (patch)
treeff2f3ab250366025d093aeb51cc206608822f65e
parent6843421be9cdef1fc12d3480889bdcfd96821e1b (diff)
First round of changes for ha-flatdhcp.
* added 'host' column to fixed_ips to allow associating with a host * added 'multi_host' column to network for multi_host possibility * moved extra db access from linux_net to manager * added host parameter to network calls
-rwxr-xr-xbin/nova-manage2
-rw-r--r--nova/db/api.py19
-rw-r--r--nova/db/sqlalchemy/api.py29
-rw-r--r--nova/db/sqlalchemy/migrate_repo/versions/032_ha_network.py37
-rw-r--r--nova/db/sqlalchemy/models.py1
-rw-r--r--nova/exception.py5
-rw-r--r--nova/network/api.py13
-rw-r--r--nova/network/linux_net.py15
-rw-r--r--nova/network/manager.py73
9 files changed, 144 insertions, 50 deletions
diff --git a/bin/nova-manage b/bin/nova-manage
index 7dfe91698..71c99138f 100755
--- a/bin/nova-manage
+++ b/bin/nova-manage
@@ -494,7 +494,7 @@ class FixedIpCommands(object):
if host is None:
fixed_ips = db.fixed_ip_get_all(ctxt)
else:
- fixed_ips = db.fixed_ip_get_all_by_host(ctxt, host)
+ fixed_ips = db.fixed_ip_get_all_by_instance_host(ctxt, host)
except exception.NotFound as ex:
print "error: %s" % ex
sys.exit(2)
diff --git a/nova/db/api.py b/nova/db/api.py
index b7c5700e5..febe33374 100644
--- a/nova/db/api.py
+++ b/nova/db/api.py
@@ -332,13 +332,15 @@ def fixed_ip_associate(context, address, instance_id):
return IMPL.fixed_ip_associate(context, address, instance_id)
-def fixed_ip_associate_pool(context, network_id, instance_id):
- """Find free ip in network and associate it to instance.
+def fixed_ip_associate_pool(context, network_id, instance_id=None, host=None):
+ """Find free ip in network and associate it to instance or host.
+ If reserved is true, it will pull the ip from the reserved pool.
Raises if one is not available.
"""
- return IMPL.fixed_ip_associate_pool(context, network_id, instance_id)
+ return IMPL.fixed_ip_associate_pool(context, network_id,
+ instance_id, host, reserved)
def fixed_ip_create(context, values):
@@ -361,9 +363,9 @@ def fixed_ip_get_all(context):
return IMPL.fixed_ip_get_all(context)
-def fixed_ip_get_all_by_host(context, host):
- """Get all defined fixed ips used by a host."""
- return IMPL.fixed_ip_get_all_by_host(context, host)
+def fixed_ip_get_all_by_instance_host(context, host):
+ """Get all allocated fixed ips filtered by instance host."""
+ return IMPL.fixed_ip_get_all_instance_by_host(context, host)
def fixed_ip_get_by_address(context, address):
@@ -376,6 +378,11 @@ def fixed_ip_get_by_instance(context, instance_id):
return IMPL.fixed_ip_get_by_instance(context, instance_id)
+def fixed_ip_get_by_network_host(context, network_id, host):
+ """Get fixed ip for a host in a network."""
+ return IMPL.fixed_ip_get_by_network_host(context, network_id, host)
+
+
def fixed_ip_get_by_virtual_interface(context, vif_id):
"""Get fixed ips by virtual interface or raise if none exist."""
return IMPL.fixed_ip_get_by_virtual_interface(context, vif_id)
diff --git a/nova/db/sqlalchemy/api.py b/nova/db/sqlalchemy/api.py
index ffd009513..6db142276 100644
--- a/nova/db/sqlalchemy/api.py
+++ b/nova/db/sqlalchemy/api.py
@@ -18,7 +18,6 @@
"""
Implementation of SQLAlchemy backend.
"""
-import traceback
import warnings
from nova import db
@@ -33,7 +32,6 @@ from sqlalchemy import or_
from sqlalchemy.exc import IntegrityError
from sqlalchemy.orm import joinedload
from sqlalchemy.orm import joinedload_all
-from sqlalchemy.sql import exists
from sqlalchemy.sql import func
from sqlalchemy.sql.expression import literal_column
@@ -657,7 +655,7 @@ def fixed_ip_associate(context, address, instance_id):
@require_admin_context
-def fixed_ip_associate_pool(context, network_id, instance_id):
+def fixed_ip_associate_pool(context, network_id, instance_id=None, host=None):
session = get_session()
with session.begin():
network_or_none = or_(models.FixedIp.network_id == network_id,
@@ -677,9 +675,12 @@ def fixed_ip_associate_pool(context, network_id, instance_id):
fixed_ip_ref.network = network_get(context,
network_id,
session=session)
- fixed_ip_ref.instance = instance_get(context,
- instance_id,
- session=session)
+ if instance_id:
+ fixed_ip_ref.instance = instance_get(context,
+ instance_id,
+ session=session)
+ if host:
+ fixed_ip_ref.host = host
session.add(fixed_ip_ref)
return fixed_ip_ref['address']
@@ -735,7 +736,7 @@ def fixed_ip_get_all(context, session=None):
@require_admin_context
-def fixed_ip_get_all_by_host(context, host=None):
+def fixed_ip_get_all_by_instance_host(context, host=None):
session = get_session()
result = session.query(models.FixedIp).\
@@ -785,6 +786,20 @@ def fixed_ip_get_by_instance(context, instance_id):
@require_context
+def fixed_ip_get_by_network_host(context, network_id, host):
+ session = get_session()
+ rv = session.query(models.FixedIp).\
+ filter_by(network_id=network_id).\
+ filter_by(host=host).\
+ filter_by(deleted=False).\
+ all()
+ if not rv:
+ raise exception.FixedIpNotFoundForNetworkHost(network_id=network_id,
+ host=host)
+ return rv
+
+
+@require_context
def fixed_ip_get_by_virtual_interface(context, vif_id):
session = get_session()
rv = session.query(models.FixedIp).\
diff --git a/nova/db/sqlalchemy/migrate_repo/versions/032_ha_network.py b/nova/db/sqlalchemy/migrate_repo/versions/032_ha_network.py
new file mode 100644
index 000000000..316c36cbc
--- /dev/null
+++ b/nova/db/sqlalchemy/migrate_repo/versions/032_ha_network.py
@@ -0,0 +1,37 @@
+# vim: tabstop=4 shiftwidth=4 softtabstop=4
+
+# Copyright 2011 MORITA Kazutaka.
+# All Rights Reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License"); you may
+# not use this file except in compliance with the License. You may obtain
+# a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations
+# under the License.
+
+from sqlalchemy import Column, Table, MetaData, Boolean, String
+
+meta = MetaData()
+
+fixed_ips_host = Column('host', String(255))
+
+networks_multi_host = Column('multi_host', Boolean, default=False)
+
+
+def upgrade(migrate_engine):
+ # Upgrade operations go here. Don't create your own engine;
+ # bind migrate_engine to your metadata
+ meta.bind = migrate_engine
+
+ # Add columns to existing tables
+ fixed_ips = Table('fixed_ips', meta, autoload=True)
+ fixed_ips.create_column(fixed_ips_host)
+
+ networks = Table('networks', meta, autoload=True)
+ networks.create_column(networks_multi_host)
diff --git a/nova/db/sqlalchemy/models.py b/nova/db/sqlalchemy/models.py
index d29d3d6f1..8c86870f0 100644
--- a/nova/db/sqlalchemy/models.py
+++ b/nova/db/sqlalchemy/models.py
@@ -543,6 +543,7 @@ class Network(BASE, NovaBase):
injected = Column(Boolean, default=False)
cidr = Column(String(255), unique=True)
cidr_v6 = Column(String(255), unique=True)
+ multi_host = Column(Boolean, default=False)
gateway_v6 = Column(String(255))
netmask_v6 = Column(String(255))
diff --git a/nova/exception.py b/nova/exception.py
index a6776b64f..29a209c3e 100644
--- a/nova/exception.py
+++ b/nova/exception.py
@@ -377,6 +377,11 @@ class FixedIpNotFoundForInstance(FixedIpNotFound):
message = _("Instance %(instance_id)s has zero fixed ips.")
+class FixedIpNotFoundForNetworkHost(FixedIpNotFound):
+ message = _("Network host %(host)s has zero fixed ips "
+ "in network %(network_id)s.")
+
+
class FixedIpNotFoundForVirtualInterface(FixedIpNotFound):
message = _("Virtual interface %(vif_id)s has zero associated fixed ips.")
diff --git a/nova/network/api.py b/nova/network/api.py
index b2b96082b..c57394204 100644
--- a/nova/network/api.py
+++ b/nova/network/api.py
@@ -18,7 +18,6 @@
"""Handles all requests relating to instances (guest vms)."""
-from nova import db
from nova import exception
from nova import flags
from nova import log as logging
@@ -105,7 +104,10 @@ class API(base.Base):
'(%(project)s)') %
{'address': floating_ip['address'],
'project': context.project_id})
- host = fixed_ip['network']['host']
+ if fixed_ip['network']['multi_gateway']:
+ host = fixed_ip['instance']['host']
+ else:
+ host = fixed_ip['network']['host']
rpc.cast(context,
self.db.queue_get_for(context, FLAGS.network_topic, host),
{'method': 'associate_floating_ip',
@@ -120,7 +122,10 @@ class API(base.Base):
return
if not floating_ip.get('fixed_ip'):
raise exception.ApiError('Address is not associated.')
- host = floating_ip['fixed_ip']['network']['host']
+ if floating_ip['fixed_ip']['network']['multi_gateway']:
+ host = floating_ip['fixed_ip']['instance']['host']
+ else:
+ host = floating_ip['fixed_ip']['network']['host']
rpc.call(context,
self.db.queue_get_for(context, FLAGS.network_topic, host),
{'method': 'disassociate_floating_ip',
@@ -134,7 +139,9 @@ class API(base.Base):
args = kwargs
args['instance_id'] = instance['id']
args['project_id'] = instance['project_id']
+ args['host'] = instance['host']
args['instance_type_id'] = instance['instance_type_id']
+
return rpc.call(context, FLAGS.network_topic,
{'method': 'allocate_for_instance',
'args': args})
diff --git a/nova/network/linux_net.py b/nova/network/linux_net.py
index 283a5aca1..8d2bc5c0b 100644
--- a/nova/network/linux_net.py
+++ b/nova/network/linux_net.py
@@ -573,18 +573,16 @@ def get_dhcp_hosts(context, network_id):
# configuration options (like dchp-range, vlan, ...)
# aren't reloaded.
@utils.synchronized('dnsmasq_start')
-def update_dhcp(context, network_id):
+def update_dhcp(context, network_ref):
"""(Re)starts a dnsmasq server for a given network.
If a dnsmasq instance is already running then send a HUP
signal causing it to reload, otherwise spawn a new instance.
"""
- network_ref = db.network_get(context, network_id)
-
conffile = _dhcp_file(network_ref['bridge'], 'conf')
with open(conffile, 'w') as f:
- f.write(get_dhcp_hosts(context, network_id))
+ f.write(get_dhcp_hosts(context, network_ref['id']))
# Make sure dnsmasq can actually read it (it setuid()s to "nobody")
os.chmod(conffile, 0644)
@@ -612,9 +610,7 @@ def update_dhcp(context, network_id):
@utils.synchronized('radvd_start')
-def update_ra(context, network_id):
- network_ref = db.network_get(context, network_id)
-
+def update_ra(context, network_ref):
conffile = _ra_file(network_ref['bridge'], 'conf')
with open(conffile, 'w') as f:
conf_str = """
@@ -650,9 +646,6 @@ interface %s
LOG.debug(_('Pid %d is stale, relaunching radvd'), pid)
command = _ra_cmd(network_ref)
_execute(*command)
- db.network_update(context, network_id,
- {'gateway_v6':
- utils.get_my_linklocal(network_ref['bridge'])})
def _host_lease(fixed_ip_ref):
@@ -704,7 +697,7 @@ def _dnsmasq_cmd(net):
'--conf-file=%s' % FLAGS.dnsmasq_config_file,
'--domain=%s' % FLAGS.dhcp_domain,
'--pid-file=%s' % _dhcp_file(net['bridge'], 'pid'),
- '--listen-address=%s' % net['gateway'],
+ '--listen-address=%s' % net['dhcp_listen'],
'--except-interface=lo',
'--dhcp-range=%s,static,120s' % net['dhcp_start'],
'--dhcp-lease-max=%s' % len(netaddr.IPNetwork(net['cidr'])),
diff --git a/nova/network/manager.py b/nova/network/manager.py
index d42bc8c4e..f60763eba 100644
--- a/nova/network/manager.py
+++ b/nova/network/manager.py
@@ -124,15 +124,17 @@ class RPCAllocateFixedIP(object):
used since they share code to RPC.call allocate_fixed_ip on the
correct network host to configure dnsmasq
"""
- def _allocate_fixed_ips(self, context, instance_id, networks):
+ def _allocate_fixed_ips(self, context, instance_id, host, networks):
"""Calls allocate_fixed_ip once for each network."""
green_pool = greenpool.GreenPool()
for network in networks:
- if network['host'] != self.host:
+ # NOTE(vish): if we are multi_gateway pass to the specified host
+ if not network['multi_gateway']:
+ host = network['host']
+ if host != self.host:
# need to call allocate_fixed_ip to correct network host
- topic = self.db.queue_get_for(context, FLAGS.network_topic,
- network['host'])
+ topic = self.db.queue_get_for(context, FLAGS.network_topic, host)
args = {}
args['instance_id'] = instance_id
args['network_id'] = network['id']
@@ -298,6 +300,21 @@ class NetworkManager(manager.SchedulerDependentManager):
super(NetworkManager, self).__init__(service_name='network',
*args, **kwargs)
+ def _update_dchp(self, context, network_ref):
+ """Sets the listen address before sending update to the driver."""
+ network_ref['dhcp_listen'] = self._get_dhcp_ip()
+ return self.driver.update_dhcp(context, network_ref)
+
+ def _get_dhcp_ip(self, context, network_ref):
+ """Get the proper dhcp address to listen on.
+
+ If it is a multi_host network, get the ip assigned to this host,
+ otherwise, assume that dhcp is listening on the gateway."""
+ if network_ref['multi_host']:
+ return self.db.network_get_host_ip(context, FLAGS.host)
+ else:
+ return network_ref['gateway']
+
def init_host(self):
"""Do any initialization that needs to be run if this is a
standalone service.
@@ -360,6 +377,7 @@ class NetworkManager(manager.SchedulerDependentManager):
rpc.called by network_api
"""
instance_id = kwargs.pop('instance_id')
+ host = kwargs.pop('host')
project_id = kwargs.pop('project_id')
type_id = kwargs.pop('instance_type_id')
admin_context = context.elevated()
@@ -368,7 +386,7 @@ class NetworkManager(manager.SchedulerDependentManager):
networks = self._get_networks_for_instance(admin_context, instance_id,
project_id)
self._allocate_mac_addresses(context, instance_id, networks)
- self._allocate_fixed_ips(admin_context, instance_id, networks)
+ self._allocate_fixed_ips(admin_context, instance_id, host, networks)
return self.get_instance_nw_info(context, instance_id, type_id)
def deallocate_for_instance(self, context, **kwargs):
@@ -475,10 +493,10 @@ class NetworkManager(manager.SchedulerDependentManager):
random.randint(0x00, 0xff)]
return ':'.join(map(lambda x: "%02x" % x, mac))
- def add_fixed_ip_to_instance(self, context, instance_id, network_id):
+ def add_fixed_ip_to_instance(self, context, instance_id, host, network_id):
"""Adds a fixed ip to an instance from specified network."""
networks = [self.db.network_get(context, network_id)]
- self._allocate_fixed_ips(context, instance_id, networks)
+ self._allocate_fixed_ips(context, instance_id, host, networks)
def allocate_fixed_ip(self, context, instance_id, network, **kwargs):
"""Gets a fixed ip from the pool."""
@@ -540,8 +558,9 @@ class NetworkManager(manager.SchedulerDependentManager):
# means there will stale entries in the conf file
# the code below will update the file if necessary
if FLAGS.update_dhcp_on_disassociate:
- network = self.db.fixed_ip_get_network(context, address)
- self.driver.update_dhcp(context, network['id'])
+ network_ref = self.db.fixed_ip_get_network(context, address)
+ network_ref['dhcp_listen'] = self._get_dhcp_ip(context, network_ref)
+ self._update_dhcp(context, network_ref)
def create_networks(self, context, label, cidr, num_networks,
network_size, cidr_v6, gateway_v6, bridge,
@@ -637,7 +656,7 @@ class NetworkManager(manager.SchedulerDependentManager):
'address': address,
'reserved': reserved})
- def _allocate_fixed_ips(self, context, instance_id, networks):
+ def _allocate_fixed_ips(self, context, instance_id, host, networks):
"""Calls allocate_fixed_ip once for each network."""
raise NotImplementedError()
@@ -684,7 +703,7 @@ class FlatManager(NetworkManager):
timeout_fixed_ips = False
- def _allocate_fixed_ips(self, context, instance_id, networks):
+ def _allocate_fixed_ips(self, context, instance_id, host, networks):
"""Calls allocate_fixed_ip once for each network."""
for network in networks:
self.allocate_fixed_ip(context, instance_id, network)
@@ -738,8 +757,9 @@ class FlatDHCPManager(FloatingIP, RPCAllocateFixedIP, NetworkManager):
"""
networks = db.network_get_all_by_instance(context, instance_id)
for network in networks:
- self.driver.ensure_bridge(network['bridge'],
- network['bridge_interface'])
+ if not network['multi_host']:
+ self.driver.ensure_bridge(network['bridge'],
+ network['bridge_interface'])
def allocate_fixed_ip(self, context, instance_id, network):
"""Allocate flat_network fixed_ip, then setup dhcp for this network."""
@@ -747,7 +767,7 @@ class FlatDHCPManager(FloatingIP, RPCAllocateFixedIP, NetworkManager):
instance_id,
network)
if not FLAGS.fake_network:
- self.driver.update_dhcp(context, network['id'])
+ self._update_dhcp(context, network)
def _on_set_network_host(self, context, network_id):
"""Called when this host becomes the host for a project."""
@@ -759,9 +779,13 @@ class FlatDHCPManager(FloatingIP, RPCAllocateFixedIP, NetworkManager):
network['bridge_interface'],
network)
if not FLAGS.fake_network:
- self.driver.update_dhcp(context, network_id)
+ network_ref = self.db.network_get(context, network_id)
+ self._update_dhcp(context, network_ref)
if(FLAGS.use_ipv6):
- self.driver.update_ra(context, network_id)
+ self.driver.update_ra(context, network_ref)
+ gateway = utils.get_my_linklocal(network_ref['bridge'])
+ self.db.network_update(context, network_id,
+ {'gateway_v6': gateway})
class VlanManager(RPCAllocateFixedIP, FloatingIP, NetworkManager):
@@ -810,7 +834,7 @@ class VlanManager(RPCAllocateFixedIP, FloatingIP, NetworkManager):
'virtual_interface_id': vif['id']}
self.db.fixed_ip_update(context, address, values)
if not FLAGS.fake_network:
- self.driver.update_dhcp(context, network['id'])
+ self._update_dhcp(context, network)
def add_network_to_project(self, context, project_id):
"""Force adds another network to a project."""
@@ -822,9 +846,10 @@ class VlanManager(RPCAllocateFixedIP, FloatingIP, NetworkManager):
"""
networks = self.db.network_get_all_by_instance(context, instance_id)
for network in networks:
- self.driver.ensure_vlan_bridge(network['vlan'],
- network['bridge'],
- network['bridge_interface'])
+ if not network['multi_host']:
+ self.driver.ensure_vlan_bridge(network['vlan'],
+ network['bridge'],
+ network['bridge_interface'])
def _get_networks_for_instance(self, context, instance_id, project_id):
"""Determine which networks an instance should connect to."""
@@ -874,9 +899,13 @@ class VlanManager(RPCAllocateFixedIP, FloatingIP, NetworkManager):
network['vpn_public_port'],
network['vpn_private_address'])
if not FLAGS.fake_network:
- self.driver.update_dhcp(context, network_id)
+ network_ref = self.db.network_get(context, network_id)
+ self._update_dhcp(context, network_ref)
if(FLAGS.use_ipv6):
- self.driver.update_ra(context, network_id)
+ self.driver.update_ra(context, network_ref)
+ gateway = utils.get_my_linklocal(network_ref['bridge'])
+ self.db.network_update(context, network_id,
+ {'gateway_v6': gateway})
@property
def _bottom_reserved_ips(self):