summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--nova/db/api.py5
-rw-r--r--nova/db/sqlalchemy/api.py36
-rw-r--r--nova/manager.py6
-rw-r--r--nova/network/manager.py41
-rw-r--r--nova/service.py24
-rw-r--r--nova/tests/service_unittest.py9
6 files changed, 87 insertions, 34 deletions
diff --git a/nova/db/api.py b/nova/db/api.py
index b68a0fe8f..4aea0e6a4 100644
--- a/nova/db/api.py
+++ b/nova/db/api.py
@@ -209,6 +209,11 @@ def fixed_ip_disassociate(context, address):
return IMPL.fixed_ip_disassociate(context, address)
+def fixed_ip_disassociate_all_by_timeout(context, host, time):
+ """Disassociate old fixed ips from host"""
+ return IMPL.fixed_ip_disassociate_all_by_timeout(context, host, time)
+
+
def fixed_ip_get_by_address(context, address):
"""Get a fixed ip by address or raise if it does not exist."""
return IMPL.fixed_ip_get_by_address(context, address)
diff --git a/nova/db/sqlalchemy/api.py b/nova/db/sqlalchemy/api.py
index e63d3b18e..4aa3c693d 100644
--- a/nova/db/sqlalchemy/api.py
+++ b/nova/db/sqlalchemy/api.py
@@ -19,8 +19,6 @@
Implementation of SQLAlchemy backend
"""
-import sys
-
from nova import db
from nova import exception
from nova import flags
@@ -340,18 +338,32 @@ def fixed_ip_disassociate(_context, address):
fixed_ip_ref.save(session=session)
+def fixed_ip_disassociate_all_by_timeout(_context, host, time):
+ session = get_session()
+ # NOTE(vish): The nested select is because sqlite doesn't support
+ # JOINs in UPDATEs.
+ result = session.execute('UPDATE fixed_ips SET instance_id = NULL, '
+ 'leased = 0 '
+ 'WHERE network_id IN (SELECT id FROM networks '
+ 'WHERE host = :host) '
+ 'AND updated_at < :time '
+ 'AND instance_id IS NOT NULL '
+ 'AND allocated = 0',
+ {'host': host,
+ 'time': time.isoformat()})
+ return result.rowcount
+
+
def fixed_ip_get_by_address(_context, address):
session = get_session()
- with session.begin():
- try:
- return session.query(models.FixedIp
- ).options(joinedload_all('instance')
- ).filter_by(address=address
- ).filter_by(deleted=False
- ).one()
- except NoResultFound:
- new_exc = exception.NotFound("No model for address %s" % address)
- raise new_exc.__class__, new_exc, sys.exc_info()[2]
+ result = session.query(models.FixedIp
+ ).options(joinedload_all('instance')
+ ).filter_by(address=address
+ ).filter_by(deleted=False
+ ).first()
+ if not result:
+ raise exception.NotFound("No model for address %s" % address)
+ return result
def fixed_ip_get_instance(_context, address):
diff --git a/nova/manager.py b/nova/manager.py
index 94e4ae959..56ba7d3f6 100644
--- a/nova/manager.py
+++ b/nova/manager.py
@@ -22,6 +22,7 @@ Base class for managers of different parts of the system
from nova import utils
from nova import flags
+from twisted.internet import defer
FLAGS = flags.FLAGS
flags.DEFINE_string('db_driver', 'nova.db.api',
@@ -38,6 +39,11 @@ class Manager(object):
db_driver = FLAGS.db_driver
self.db = utils.import_object(db_driver) # pylint: disable-msg=C0103
+ @defer.inlineCallbacks
+ def periodic_tasks(self, context=None):
+ """Tasks to be run at a periodic interval"""
+ yield
+
def init_host(self):
"""Do any initialization that needs to be run if this is a standalone service.
diff --git a/nova/network/manager.py b/nova/network/manager.py
index a7126ea4f..1325c300b 100644
--- a/nova/network/manager.py
+++ b/nova/network/manager.py
@@ -20,10 +20,12 @@
Network Hosts are responsible for allocating ips and setting up network
"""
+import datetime
import logging
import math
import IPy
+from twisted.internet import defer
from nova import db
from nova import exception
@@ -62,7 +64,9 @@ flags.DEFINE_integer('cnt_vpn_clients', 5,
flags.DEFINE_string('network_driver', 'nova.network.linux_net',
'Driver to use for network creation')
flags.DEFINE_bool('update_dhcp_on_disassociate', False,
- 'Whether to update dhcp when fixed_ip is disassocated')
+ 'Whether to update dhcp when fixed_ip is disassociated')
+flags.DEFINE_integer('fixed_ip_disassociate_timeout', 600,
+ 'Seconds after which a deallocated ip is disassociated')
class AddressAlreadyAllocated(exception.Error):
@@ -219,6 +223,19 @@ class FlatManager(NetworkManager):
class VlanManager(NetworkManager):
"""Vlan network with dhcp"""
+ @defer.inlineCallbacks
+ def periodic_tasks(self, context=None):
+ """Tasks to be run at a periodic interval"""
+ yield super(VlanManager, self).periodic_tasks(context)
+ now = datetime.datetime.utcnow()
+ timeout = FLAGS.fixed_ip_disassociate_timeout
+ time = now - datetime.timedelta(seconds=timeout)
+ num = self.db.fixed_ip_disassociate_all_by_timeout(self,
+ self.host,
+ time)
+ if num:
+ logging.debug("Dissassociated %s stale fixed ip(s)", num)
+
def init_host(self):
"""Do any initialization that needs to be run if this is a
standalone service.
@@ -242,14 +259,6 @@ class VlanManager(NetworkManager):
"""Returns a fixed ip to the pool"""
self.db.fixed_ip_update(context, address, {'allocated': False})
fixed_ip_ref = self.db.fixed_ip_get_by_address(context, address)
- if not fixed_ip_ref['leased']:
- self.db.fixed_ip_disassociate(context, address)
- # NOTE(vish): dhcp server isn't updated until next setup, this
- # 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_ref = self.db.fixed_ip_get_network(context, address)
- self.driver.update_dhcp(context, network_ref['id'])
def setup_fixed_ip(self, context, address):
@@ -266,9 +275,6 @@ class VlanManager(NetworkManager):
"""Called by dhcp-bridge when ip is leased"""
logging.debug("Leasing IP %s", address)
fixed_ip_ref = self.db.fixed_ip_get_by_address(context, address)
- if not fixed_ip_ref['allocated']:
- logging.warn("IP %s leased that was already deallocated", address)
- return
instance_ref = fixed_ip_ref['instance']
if not instance_ref:
raise exception.Error("IP %s leased that isn't associated" %
@@ -279,14 +285,13 @@ class VlanManager(NetworkManager):
self.db.fixed_ip_update(context,
fixed_ip_ref['address'],
{'leased': True})
+ if not fixed_ip_ref['allocated']:
+ logging.warn("IP %s leased that was already deallocated", address)
def release_fixed_ip(self, context, mac, address):
"""Called by dhcp-bridge when ip is released"""
logging.debug("Releasing IP %s", address)
fixed_ip_ref = self.db.fixed_ip_get_by_address(context, address)
- if not fixed_ip_ref['leased']:
- logging.warn("IP %s released that was not leased", address)
- return
instance_ref = fixed_ip_ref['instance']
if not instance_ref:
raise exception.Error("IP %s released that isn't associated" %
@@ -294,7 +299,11 @@ class VlanManager(NetworkManager):
if instance_ref['mac_address'] != mac:
raise exception.Error("IP %s released from bad mac %s vs %s" %
(address, instance_ref['mac_address'], mac))
- self.db.fixed_ip_update(context, address, {'leased': False})
+ if not fixed_ip_ref['leased']:
+ logging.warn("IP %s released that was not leased", address)
+ self.db.fixed_ip_update(context,
+ fixed_ip_ref['str_id'],
+ {'leased': False})
if not fixed_ip_ref['allocated']:
self.db.fixed_ip_disassociate(context, address)
# NOTE(vish): dhcp server isn't updated until next setup, this
diff --git a/nova/service.py b/nova/service.py
index dcd2a09ef..a6c186896 100644
--- a/nova/service.py
+++ b/nova/service.py
@@ -37,7 +37,11 @@ from nova import utils
FLAGS = flags.FLAGS
flags.DEFINE_integer('report_interval', 10,
- 'seconds between nodes reporting state to cloud',
+ 'seconds between nodes reporting state to datastore',
+ lower_bound=1)
+
+flags.DEFINE_integer('periodic_interval', 60,
+ 'seconds between running periodic tasks',
lower_bound=1)
@@ -81,7 +85,8 @@ class Service(object, service.Service):
binary=None,
topic=None,
manager=None,
- report_interval=None):
+ report_interval=None,
+ periodic_interval=None):
"""Instantiates class and passes back application object.
Args:
@@ -90,6 +95,7 @@ class Service(object, service.Service):
topic, defaults to bin_name - "nova-" part
manager, defaults to FLAGS.<topic>_manager
report_interval, defaults to FLAGS.report_interval
+ periodic_interval, defaults to FLAGS.periodic_interval
"""
if not host:
host = FLAGS.host
@@ -101,6 +107,8 @@ class Service(object, service.Service):
manager = FLAGS.get('%s_manager' % topic, None)
if not report_interval:
report_interval = FLAGS.report_interval
+ if not periodic_interval:
+ periodic_interval = FLAGS.periodic_interval
logging.warn("Starting %s node", topic)
service_obj = cls(host, binary, topic, manager)
conn = rpc.Connection.instance()
@@ -113,11 +121,14 @@ class Service(object, service.Service):
topic='%s.%s' % (topic, host),
proxy=service_obj)
+ consumer_all.attach_to_twisted()
+ consumer_node.attach_to_twisted()
+
pulse = task.LoopingCall(service_obj.report_state)
pulse.start(interval=report_interval, now=False)
- consumer_all.attach_to_twisted()
- consumer_node.attach_to_twisted()
+ pulse = task.LoopingCall(service_obj.periodic_tasks)
+ pulse.start(interval=periodic_interval, now=False)
# This is the parent service that twistd will be looking for when it
# parses this file, return it so that we can get it into globals.
@@ -133,6 +144,11 @@ class Service(object, service.Service):
logging.warn("Service killed that has no database entry")
@defer.inlineCallbacks
+ def periodic_tasks(self, context=None):
+ """Tasks to be run at a periodic interval"""
+ yield self.manager.periodic_tasks(context)
+
+ @defer.inlineCallbacks
def report_state(self, context=None):
"""Update the state of this service in the datastore."""
try:
diff --git a/nova/tests/service_unittest.py b/nova/tests/service_unittest.py
index 01da0eb8a..06f80e82c 100644
--- a/nova/tests/service_unittest.py
+++ b/nova/tests/service_unittest.py
@@ -65,15 +65,20 @@ class ServiceTestCase(test.BaseTestCase):
proxy=mox.IsA(service.Service)).AndReturn(
rpc.AdapterConsumer)
+ rpc.AdapterConsumer.attach_to_twisted()
+ rpc.AdapterConsumer.attach_to_twisted()
+
# Stub out looping call a bit needlessly since we don't have an easy
# way to cancel it (yet) when the tests finishes
service.task.LoopingCall(mox.IgnoreArg()).AndReturn(
service.task.LoopingCall)
service.task.LoopingCall.start(interval=mox.IgnoreArg(),
now=mox.IgnoreArg())
+ service.task.LoopingCall(mox.IgnoreArg()).AndReturn(
+ service.task.LoopingCall)
+ service.task.LoopingCall.start(interval=mox.IgnoreArg(),
+ now=mox.IgnoreArg())
- rpc.AdapterConsumer.attach_to_twisted()
- rpc.AdapterConsumer.attach_to_twisted()
service_create = {'host': host,
'binary': binary,
'topic': topic,