Skip to content
Snippets Groups Projects
Commit ca3a36aa authored by Anael Beutot's avatar Anael Beutot
Browse files

Handle libvirt connection with errors.

Try to connect to libvirt while no exception is thrown.

Clean global variable for libvirt connection.
parent 5821177e
No related branches found
No related tags found
No related merge requests found
...@@ -17,26 +17,46 @@ from ccnode.hypervisor.domains import VirtualMachine ...@@ -17,26 +17,46 @@ from ccnode.hypervisor.domains import VirtualMachine
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
# FIXME find a way to refactor Handler and Hypervisor class
class Handler(HostHandler): class Handler(HostHandler):
def __init__(self, *args, **kwargs): def __init__(self, *args, **kwargs):
""" """
:param loop: MainLoop instance :param loop: MainLoop instance
:param hypervisor_name: hypervisor name :param hypervisor_name: hypervisor name
""" """
hypervisor_name = kwargs.pop('hypervisor_name') self.hypervisor_name = kwargs.pop('hypervisor_name')
HostHandler.__init__(self, *args, **kwargs) HostHandler.__init__(self, *args, **kwargs)
#: keep index of asynchronous calls #: keep index of asynchronous calls
self.async_calls = dict() self.async_calls = dict()
self.timer = self.main.evloop.timer(.0, 5., self.virt_connect_cb)
self.hypervisor = None
self.virt_connected = False
def start(self):
self.timer.start()
HostHandler.start(self)
def stop(self):
self.timer.stop()
if self.hypervisor is not None:
self.hypervisor.stop()
HostHandler.stop(self)
def virt_connect_cb(self, *args):
# initialize hypervisor instance # initialize hypervisor instance
# FIXME this may block try:
self.hypervisor = Hypervisor( self.hypervisor = Hypervisor(
name=hypervisor_name, name=self.hypervisor_name,
loop=self.main, handler=self,
) )
except libvirt.libvirtError:
logger.exception('Error while connecting to libvirt')
return
self.virt_connected = True
# FIXME this may block
# register hypervisor storage tags # register hypervisor storage tags
for name, storage in self.hypervisor.storage.storages.iteritems(): for name, storage in self.hypervisor.storage.storages.iteritems():
for t in ( for t in (
...@@ -47,6 +67,7 @@ class Handler(HostHandler): ...@@ -47,6 +67,7 @@ class Handler(HostHandler):
lambda: storage.capacity - storage.available, 5), lambda: storage.capacity - storage.available, 5),
): ):
self.tag_db['__main__'][t.name] = t self.tag_db['__main__'][t.name] = t
self.main.reset_tag(t)
# register domains # register domains
for dom in self.hypervisor.domains.itervalues(): for dom in self.hypervisor.domains.itervalues():
...@@ -59,9 +80,12 @@ class Handler(HostHandler): ...@@ -59,9 +80,12 @@ class Handler(HostHandler):
'vm', 'vm',
)] = name )] = name
self.tag_db['__main__'].update(dict( hv_tags = dict(
(t.name, t) for t in tag_inspector(tags, self), (t.name, t) for t in tag_inspector(tags, self),
)) )
self.tag_db['__main__'].update(hv_tags)
for tag in hv_tags.itervalues():
self.main.reset_tag(tag)
self.rpc_handler.update(dict( self.rpc_handler.update(dict(
vm_define=self.vm_define, vm_define=self.vm_define,
...@@ -72,8 +96,53 @@ class Handler(HostHandler): ...@@ -72,8 +96,53 @@ class Handler(HostHandler):
vm_suspend=self.vm_suspend, vm_suspend=self.vm_suspend,
vm_resume=self.vm_resume, vm_resume=self.vm_resume,
)) ))
self.main.reset_handler('vm_define', self.vm_define)
self.main.reset_handler('vm_undefine', self.vm_undefine)
self.main.reset_handler('vm_export', self.vm_export)
self.main.reset_handler('vm_stop', self.vm_stop)
self.main.reset_handler('vm_start', self.vm_start)
self.main.reset_handler('vm_suspend', self.vm_suspend)
self.main.reset_handler('vm_resume', self.vm_resume)
# if everything went fine, unregister the timer
self.timer.stop()
def virt_connect_restart(self):
"""Restart libvirt connection.
This method might be called when libvirt connection is lost.
"""
if not self.virt_connected:
return
logger.error('Connection to libvirt lost, trying to restart')
# update connection state
self.virt_connected = False
# unregister tags that will be re register later
for storage in self.hypervisor.storage.storages:
self.main.remove_tag('sto%s_state' % storage)
self.main.remove_tag('sto%s_size' % storage)
self.main.remove_tag('sto%s_free' % storage)
self.main.remove_tag('sto%s_used' % storage)
self.tag_db['__main__'].pop('sto%s_state' % storage)
self.tag_db['__main__'].pop('sto%s_size' % storage)
self.tag_db['__main__'].pop('sto%s_free' % storage)
self.tag_db['__main__'].pop('sto%s_used' % storage)
# unregister sub objects (for the same reason)
for sub_id in self.tag_db.keys():
if sub_id == '__main__':
continue
self.main.remove_sub_object(sub_id)
self.tag_db.pop(sub_id)
# stop and delete hypervisor instance
self.hypervisor.stop()
self.hypervisor = None
# launch connection timer
self.timer.start()
# FIXME duplicate code
def register_domain_cb(self, call_id, response=None, error=None): def register_domain_cb(self, call_id, response=None, error=None):
"""RPC callback used when registering a domain on the cc-server."""
name = self.async_calls.pop(call_id) name = self.async_calls.pop(call_id)
if error is not None: if error is not None:
logger.error('Error while registering domain, %s("%s")', logger.error('Error while registering domain, %s("%s")',
...@@ -149,14 +218,13 @@ class Handler(HostHandler): ...@@ -149,14 +218,13 @@ class Handler(HostHandler):
class Hypervisor(object): class Hypervisor(object):
"""Container for all hypervisor related state.""" """Container for all hypervisor related state."""
def __init__(self, name, loop): def __init__(self, name, handler):
""" """
:param str name: name of hypervisor instance :param str name: name of hypervisor instance
:param loop: MainLoop instance :param Handler handler: hypervisor handler
""" """
#: parent MainLoop self.handler = weakref.proxy(handler)
self.main = weakref.proxy(loop) self.rpc_con = handler.main.rpc_con
self.rpc_con = loop.rpc_con
self.async_calls = dict() self.async_calls = dict()
#: hv attributes #: hv attributes
...@@ -164,7 +232,7 @@ class Hypervisor(object): ...@@ -164,7 +232,7 @@ class Hypervisor(object):
self.type = u'kvm' self.type = u'kvm'
# libvirt event loop abstraction # libvirt event loop abstraction
self.vir_event_loop = VirEventLoop(self.main.evloop) self.vir_event_loop = VirEventLoop(self.handler.main.evloop)
# This tells libvirt what event loop implementation it # This tells libvirt what event loop implementation it
# should use # should use
libvirt.virEventRegisterImpl( libvirt.virEventRegisterImpl(
...@@ -241,6 +309,7 @@ class Hypervisor(object): ...@@ -241,6 +309,7 @@ class Hypervisor(object):
vm.state = state vm.state = state
def register_cb(self, call_id, response=None, error=None): def register_cb(self, call_id, response=None, error=None):
"""RPC callback for registering domain to cc-server."""
vm = self.domains[self.async_calls.pop(call_id)] vm = self.domains[self.async_calls.pop(call_id)]
if error is not None: if error is not None:
logger.error('Error while registering domain to server, %s("%s")', logger.error('Error while registering domain to server, %s("%s")',
...@@ -248,15 +317,16 @@ class Hypervisor(object): ...@@ -248,15 +317,16 @@ class Hypervisor(object):
logger.info('Add domain: %s (%s)', vm.name, vm.uuid) logger.info('Add domain: %s (%s)', vm.name, vm.uuid)
# add tags # add tags
for tag in vm.tags.itervalues(): for tag in vm.tags.itervalues():
self.main.reset_sub_tag(vm.name, tag) self.handler.main.reset_sub_tag(vm.name, tag)
def unregister_cb(self, call_id, response=None, error=None): def unregister_cb(self, call_id, response=None, error=None):
"""RPC callback for unregistering domain to cc-server."""
vm_name = self.async_calls.pop(call_id) vm_name = self.async_calls.pop(call_id)
if error is not None: if error is not None:
logger.error('Error while unregistering domain to server, %s("%s")', logger.error('Error while unregistering domain to server, %s("%s")',
error['exception'], error['message']) error['exception'], error['message'])
logger.info('Delete domain: %s', vm_name) logger.info('Delete domain: %s', vm_name)
self.main.remove_sub_object(vm_name) self.handler.main.remove_sub_object(vm_name)
def vm_define(self, xml_desc): def vm_define(self, xml_desc):
"""Create a VM on the Hypervisor """Create a VM on the Hypervisor
......
import logging
from functools import wraps
from xml.etree import cElementTree as et from xml.etree import cElementTree as et
from StringIO import StringIO from StringIO import StringIO
import libvirt
from ccnode.tags import ttl, refresh from ccnode.tags import ttl, refresh
logger = logging.getLogger(__name__)
def _vir_tag(func):
"""Catches libvirt related exception.
Decorator used for tag declarations that interacts with libvirt.
"""
@wraps(func)
def decorated(dom):
if not dom.hypervisor.handler.virt_connected:
return
try:
func(dom)
except libvirt.libvirtError:
logger.exception('Unexpected libvirt error')
dom.hypervisor.handler.virt_connect_restart()
return decorated
def uuid(dom): def uuid(dom):
"""Unique identifier of the domain.""" """Unique identifier of the domain."""
return dom.uuid return dom.uuid
...@@ -23,6 +49,7 @@ def htype(dom): ...@@ -23,6 +49,7 @@ def htype(dom):
return dom.hypervisor.type return dom.hypervisor.type
@_vir_tag
def arch(dom): def arch(dom):
"""VM CPU architecture.""" """VM CPU architecture."""
try: try:
...@@ -37,6 +64,7 @@ def h(dom): ...@@ -37,6 +64,7 @@ def h(dom):
return dom.name return dom.name
@_vir_tag
def cpu(dom): def cpu(dom):
"""Number of CPU of the VM.""" """Number of CPU of the VM."""
return dom.lv_dom.info()[3] return dom.lv_dom.info()[3]
...@@ -48,16 +76,19 @@ def cpuuse(): ...@@ -48,16 +76,19 @@ def cpuuse():
pass pass
@_vir_tag
def mem(dom): def mem(dom):
"""Memory currently allocated.""" """Memory currently allocated."""
return dom.lv_dom.info()[2] * 1024 return dom.lv_dom.info()[2] * 1024
@_vir_tag
def memmax(dom): def memmax(dom):
"""Maximum memory allocation.""" """Maximum memory allocation."""
return dom.lv_dom.info()[1] * 1024 return dom.lv_dom.info()[1] * 1024
@_vir_tag
def vncport(dom): def vncport(dom):
"""VNC port for the VM console access.""" """VNC port for the VM console access."""
try: try:
...@@ -70,6 +101,7 @@ def vncport(dom): ...@@ -70,6 +101,7 @@ def vncport(dom):
@ttl(10) @ttl(10)
@refresh(10) @refresh(10)
@_vir_tag
def disk(dom): def disk(dom):
"""Get backend disks.""" """Get backend disks."""
return u' '.join(map(str, xrange(len(dom.disks)))) or None return u' '.join(map(str, xrange(len(dom.disks)))) or None
...@@ -77,6 +109,7 @@ def disk(dom): ...@@ -77,6 +109,7 @@ def disk(dom):
@ttl(10) @ttl(10)
@refresh(10) @refresh(10)
@_vir_tag
def nic(dom): def nic(dom):
"""VM network interfaces.""" """VM network interfaces."""
return u' '.join(map(str, xrange(len(dom.nics)))) or None return u' '.join(map(str, xrange(len(dom.nics)))) or None
...@@ -2,6 +2,7 @@ ...@@ -2,6 +2,7 @@
import logging import logging
from itertools import chain from itertools import chain
from functools import wraps
import pyev import pyev
import libvirt import libvirt
...@@ -10,11 +11,6 @@ import libvirt ...@@ -10,11 +11,6 @@ import libvirt
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
# FIXME bad global variable
#: connection to the libvirt
connection = None # TODO create a watcher thread for this
#: corresponding name for enum state of libvirt domains #: corresponding name for enum state of libvirt domains
# see http://libvirt.org/html/libvirt-libvirt.html#virDomainState # see http://libvirt.org/html/libvirt-libvirt.html#virDomainState
DOMAIN_STATES = ( DOMAIN_STATES = (
...@@ -52,6 +48,25 @@ EVENTS = ( ...@@ -52,6 +48,25 @@ EVENTS = (
) )
def vir_tag(func):
"""Catches libvirt related exception.
Decorator used for tag declarations that interacts with libvirt.
"""
@wraps(func)
def decorated(handl):
if not handl.virt_connected:
return
try:
func(handl)
except libvirt.libvirtError:
logger.exception('Unexpected libvirt error')
handl.vir_con_restart()
return decorated
# following event loop implementation was inspired by libvirt python example # following event loop implementation was inspired by libvirt python example
# but updated to work with libev # but updated to work with libev
class LoopHandler(object): class LoopHandler(object):
......
from ccnode.utils import and_ from ccnode.utils import and_
from ccnode.hypervisor.lib import vir_tag
# hypervisor related tags # hypervisor related tags
...@@ -38,11 +39,13 @@ def hvm(): ...@@ -38,11 +39,13 @@ def hvm():
return None return None
@vir_tag
def hvver(handl): def hvver(handl):
"""Hypervisor version.""" """Hypervisor version."""
return handl.hypervisor.vir_con.getVersion() return handl.hypervisor.vir_con.getVersion()
@vir_tag
def libvirtver(handl): def libvirtver(handl):
"""Version of running libvirt.""" """Version of running libvirt."""
return handl.hypervisor.vir_con.getLibVersion() return handl.hypervisor.vir_con.getLibVersion()
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment