2013-10-27 21:59:46 +01:00
# Copyright (C) 2010, 2013 Red Hat, Inc.
2010-02-25 19:35:01 -05:00
# Copyright (C) 2010 Cole Robinson <crobinso@redhat.com>
#
2018-04-04 14:35:41 +01:00
# This work is licensed under the GNU GPLv2 or later.
2018-03-20 15:00:02 -04:00
# See the COPYING file in the top-level directory.
2010-02-25 19:35:01 -05:00
import logging
2014-09-12 16:10:45 -04:00
from . baseclass import vmmGObject
2010-02-25 19:35:01 -05:00
2013-04-13 14:34:52 -04:00
2010-12-09 12:37:48 -05:00
class vmmLibvirtObject ( vmmGObject ) :
2012-05-14 14:24:56 +01:00
__gsignals__ = {
2018-03-15 08:10:09 -04:00
" state-changed " : ( vmmGObject . RUN_FIRST , None , [ ] ) ,
" initialized " : ( vmmGObject . RUN_FIRST , None , [ bool ] ) ,
2012-05-14 14:24:56 +01:00
}
2015-04-09 18:02:42 -04:00
_STATUS_ACTIVE = 1
_STATUS_INACTIVE = 2
2013-09-23 08:34:50 -04:00
def __init__ ( self , conn , backend , key , parseclass ) :
2010-12-09 12:37:48 -05:00
vmmGObject . __init__ ( self )
2011-07-22 16:43:26 -04:00
self . _conn = conn
2013-07-07 08:42:57 -04:00
self . _backend = backend
self . _key = key
2013-09-09 17:14:16 -04:00
self . _parseclass = parseclass
2010-02-25 19:35:01 -05:00
2015-04-10 18:03:56 -04:00
self . __initialized = False
self . __status = None
2015-04-09 18:02:42 -04:00
self . _support_isactive = None
2013-09-09 17:14:16 -04:00
self . _xmlobj = None
self . _xmlobj_to_define = None
2015-04-07 14:53:28 -04:00
self . _is_xml_valid = False
2013-09-09 17:14:16 -04:00
2010-02-25 19:35:01 -05:00
# These should be set by the child classes if necessary
self . _inactive_xml_flags = 0
self . _active_xml_flags = 0
2015-04-10 14:08:25 -04:00
# Cache object name. We may need to do this even
# before init_libvirt_state since it might be needed ahead of time.
2014-06-02 17:17:47 -04:00
self . _name = None
self . get_name ( )
2014-01-26 17:42:24 -05:00
@staticmethod
def log_redefine_xml_diff ( obj , origxml , newxml ) :
if origxml == newxml :
logging . debug ( " Redefine requested for %s , but XML didn ' t change! " ,
2017-03-29 11:41:50 -04:00
obj )
2014-01-26 17:42:24 -05:00
return
import difflib
diff = " " . join ( difflib . unified_diff ( origxml . splitlines ( 1 ) ,
newxml . splitlines ( 1 ) ,
fromfile = " Original XML " ,
tofile = " New XML " ) )
2017-03-29 11:41:50 -04:00
logging . debug ( " Redefining %s with XML diff: \n %s " , obj , diff )
2014-01-26 17:42:24 -05:00
2015-04-09 18:27:45 -04:00
@staticmethod
def lifecycle_action ( fn ) :
"""
Decorator for object lifecycle actions like start , stop , delete .
Will make sure any necessary state is updated accordingly .
"""
def newfn ( self , * args , * * kwargs ) :
ret = fn ( self , * args , * * kwargs )
# If events are supported, this is a no-op, but the event loop
# will trigger force_status_update, which will refresh_xml as well.
#
# If events aren't supported, the priority tick will call
# self.tick(), which will call force_status_update
2015-04-10 09:15:44 -04:00
poll_param = self . _conn_tick_poll_param ( ) # pylint: disable=protected-access
tick_kwargs = { poll_param : True }
2015-04-09 18:27:45 -04:00
self . conn . schedule_priority_tick ( * * tick_kwargs )
return ret
return newfn
2017-03-29 11:41:50 -04:00
def __repr__ ( self ) :
try :
name = self . get_name ( )
2017-07-24 09:26:48 +01:00
except Exception :
2017-03-29 11:41:50 -04:00
name = " "
return " < %s name= %s id= %s > " % (
self . __class__ . __name__ , name , hex ( id ( self ) ) )
2011-07-23 21:16:54 -04:00
def _cleanup ( self ) :
2018-03-15 07:43:56 -04:00
self . _backend = None
2011-07-23 21:16:54 -04:00
2011-07-22 16:43:26 -04:00
def _get_conn ( self ) :
return self . _conn
conn = property ( _get_conn )
2010-02-25 19:35:01 -05:00
2013-07-07 08:42:57 -04:00
def get_backend ( self ) :
return self . _backend
2014-06-02 17:17:47 -04:00
def get_connkey ( self ) :
2013-07-07 08:42:57 -04:00
return self . _key
2018-10-13 17:21:22 -04:00
def is_domain ( self ) :
return self . class_name ( ) == " domain "
def is_network ( self ) :
return self . class_name ( ) == " network "
def is_pool ( self ) :
return self . class_name ( ) == " pool "
def is_nodedev ( self ) :
return self . class_name ( ) == " nodedev "
def is_interface ( self ) :
return self . class_name ( ) == " interface "
2013-09-29 12:14:00 -04:00
def change_name_backend ( self , newbackend ) :
# Used for changing the backing object after a rename
self . _backend = newbackend
2014-09-20 16:55:11 -04:00
def define_name ( self , newname ) :
2015-11-03 15:56:39 -05:00
oldconnkey = self . get_connkey ( )
2013-09-29 12:14:00 -04:00
oldname = self . get_xmlobj ( ) . name
2015-04-10 17:50:06 -04:00
self . ensure_latest_xml ( )
2015-04-10 17:23:11 -04:00
xmlobj = self . _make_xmlobj_to_define ( )
2013-09-29 12:14:00 -04:00
if xmlobj . name == newname :
return
logging . debug ( " Changing %s name from %s to %s " ,
2017-03-29 11:41:50 -04:00
self , oldname , newname )
2018-08-31 16:52:02 -04:00
origxml = xmlobj . get_xml ( )
2013-09-29 12:14:00 -04:00
xmlobj . name = newname
2018-08-31 16:52:02 -04:00
newxml = xmlobj . get_xml ( )
2013-09-29 12:14:00 -04:00
try :
2014-09-20 16:55:11 -04:00
self . _key = newname
2015-11-03 15:56:39 -05:00
self . conn . rename_object ( self , origxml , newxml , oldconnkey )
2017-07-24 09:26:48 +01:00
except Exception :
2014-09-20 16:55:11 -04:00
self . _key = oldname
raise
2013-09-29 12:14:00 -04:00
finally :
2015-04-13 18:02:16 -04:00
self . __force_refresh_xml ( )
2013-09-29 12:14:00 -04:00
2013-09-23 08:34:50 -04:00
2010-02-25 19:35:01 -05:00
#############################################################
# Functions that should probably be overridden in sub class #
#############################################################
def _XMLDesc ( self , flags ) :
2010-12-09 12:37:48 -05:00
raise NotImplementedError ( )
2015-04-10 09:15:44 -04:00
def class_name ( self ) :
raise NotImplementedError ( )
def _conn_tick_poll_param ( self ) :
# The parameter name for conn.tick() object polling. So
# for vmmDomain == "pollvm"
raise NotImplementedError ( )
2015-04-10 12:52:42 -04:00
def reports_stats ( self ) :
return False
2014-02-11 12:07:13 -05:00
def _using_events ( self ) :
return False
2015-04-09 18:02:42 -04:00
def _check_supports_isactive ( self ) :
return False
def _get_backend_status ( self ) :
raise NotImplementedError ( )
2010-02-25 19:35:01 -05:00
def _define ( self , xml ) :
ignore = xml
return
2013-09-30 15:23:14 -04:00
def delete ( self , force = True ) :
ignore = force
2014-06-02 17:17:47 -04:00
def get_name ( self ) :
if self . _name is None :
self . _name = self . _backend_get_name ( )
return self . _name
def _backend_get_name ( self ) :
return self . _backend . name ( )
2015-04-10 09:15:44 -04:00
def tick ( self , stats_update = True ) :
raise NotImplementedError ( )
2015-04-10 14:08:25 -04:00
def _init_libvirt_state ( self ) :
raise NotImplementedError ( )
def init_libvirt_state ( self ) :
"""
Function called by vmmConnection to populate initial state when
a new object appears .
"""
2015-04-10 18:03:56 -04:00
if self . __initialized :
2015-04-10 14:08:25 -04:00
return
2015-09-17 15:48:42 -04:00
initialize_failed = False
2015-04-10 14:08:25 -04:00
try :
self . _init_libvirt_state ( )
2017-07-24 09:26:48 +01:00
except Exception :
2015-04-24 15:34:03 -04:00
logging . debug ( " Error initializing libvirt state for %s " , self ,
exc_info = True )
2015-09-17 15:48:42 -04:00
initialize_failed = True
2015-04-24 15:34:03 -04:00
self . __initialized = True
2015-09-17 15:48:42 -04:00
self . idle_emit ( " initialized " , initialize_failed )
2015-04-10 14:08:25 -04:00
2013-07-07 08:05:23 -04:00
2015-04-09 18:02:42 -04:00
###################
# Status handling #
###################
def _get_status ( self ) :
return self . __status
def is_active ( self ) :
# vmmDomain overwrites this since it has more fine grained statuses
return self . _get_status ( ) == self . _STATUS_ACTIVE
2015-04-10 09:15:44 -04:00
def run_status ( self ) :
if self . is_active ( ) :
2016-02-05 16:18:16 +01:00
return _ ( " Active " )
return _ ( " Inactive " )
2015-04-10 09:15:44 -04:00
2015-04-19 09:26:16 -04:00
def _refresh_status ( self , newstatus = None , cansignal = True ) :
2015-04-09 19:03:27 -04:00
"""
Grab the object status / active state from libvirt , and if the
status has changed , update the XML cache . Typically called from
object tick functions for manually updating the object state .
2015-04-09 18:02:42 -04:00
: param newstatus : Used by vmmDomain as a small optimization to
2015-04-09 19:03:27 -04:00
avoid polling info ( ) twice
2015-04-19 09:26:16 -04:00
: param cansignal : If True , this function will signal state - changed
if required .
: returns : True if status changed , false otherwise
2015-04-09 18:02:42 -04:00
"""
2015-04-09 19:03:27 -04:00
if ( self . _using_events ( ) and
2015-04-10 18:03:56 -04:00
self . __status is not None ) :
2015-04-19 09:26:16 -04:00
return False
2015-04-09 18:02:42 -04:00
2015-04-13 18:02:16 -04:00
if newstatus is None :
newstatus = self . _get_backend_status ( )
status = newstatus
if status == self . __status :
2015-04-19 09:26:16 -04:00
return False
2015-04-13 18:02:16 -04:00
self . __status = status
self . ensure_latest_xml ( nosignal = True )
2015-04-19 09:26:16 -04:00
if cansignal :
self . idle_emit ( " state-changed " )
return True
2015-04-09 18:02:42 -04:00
def _backend_get_active ( self ) :
if self . _support_isactive is None :
self . _support_isactive = self . _check_supports_isactive ( )
if not self . _support_isactive :
return self . _STATUS_ACTIVE
return ( bool ( self . _backend . isActive ( ) ) and
self . _STATUS_ACTIVE or
self . _STATUS_INACTIVE )
2010-02-25 19:35:01 -05:00
##################
# Public XML API #
##################
2016-06-23 14:48:04 -04:00
def recache_from_event_loop ( self ) :
2015-04-10 17:50:06 -04:00
"""
2015-04-11 17:18:25 -04:00
Updates the VM status and XML , because we received an event from
libvirt ' s event implementations. That ' s the only time this should
be used .
We refresh status and XML because they are tied together in subtle
ways , like runtime XML changing when a VM is started .
2015-04-10 17:50:06 -04:00
"""
2015-04-13 18:02:16 -04:00
try :
self . __force_refresh_xml ( nosignal = True )
# status = None forces a signal to be emitted
self . __status = None
self . _refresh_status ( )
2017-05-05 12:47:21 -04:00
except Exception as e :
2015-04-13 18:02:16 -04:00
# If we hit an exception here, it's often that the object
# disappeared, so request the poll loop to be updated
logging . debug ( " Error refreshing %s from events: %s " , self , e )
poll_param = self . _conn_tick_poll_param ( )
if poll_param :
kwargs = { " force " : True , poll_param : True }
logging . debug ( " Scheduling priority tick with: %s " , kwargs )
self . conn . schedule_priority_tick ( * * kwargs )
2015-04-10 17:50:06 -04:00
2015-04-10 18:03:56 -04:00
def ensure_latest_xml ( self , nosignal = False ) :
2015-04-10 17:50:06 -04:00
"""
Refresh XML if it isn ' t up to date, basically if we aren ' t using
events .
"""
if ( self . _using_events ( ) and
self . _xmlobj and
self . _is_xml_valid ) :
return
2015-04-13 18:02:16 -04:00
self . __force_refresh_xml ( nosignal = nosignal )
2015-04-10 17:50:06 -04:00
2015-04-13 18:02:16 -04:00
def __force_refresh_xml ( self , nosignal = False ) :
2010-02-25 19:35:01 -05:00
"""
2015-04-09 18:02:42 -04:00
Force an xml update . Signal ' state-changed ' if domain xml has
2015-04-07 14:53:28 -04:00
changed since last refresh
2015-04-10 17:50:06 -04:00
: param nosignal : If true , don ' t send state-changed. Used by
callers that are going to send it anyways .
2010-02-25 19:35:01 -05:00
"""
2015-04-07 14:53:28 -04:00
origxml = None
if self . _xmlobj :
2018-08-31 16:52:02 -04:00
origxml = self . _xmlobj . get_xml ( )
2013-09-23 08:34:50 -04:00
2015-04-07 14:53:28 -04:00
self . _invalidate_xml ( )
active_xml = self . _XMLDesc ( self . _active_xml_flags )
self . _xmlobj = self . _parseclass ( self . conn . get_backend ( ) ,
parsexml = active_xml )
self . _is_xml_valid = True
2015-04-10 17:50:06 -04:00
if not nosignal and origxml != active_xml :
2015-04-09 18:02:42 -04:00
self . idle_emit ( " state-changed " )
2013-09-23 08:34:50 -04:00
2015-04-07 14:53:28 -04:00
def get_xmlobj ( self , inactive = False , refresh_if_nec = True ) :
"""
Get object xml , return it wrapped in a virtinst object .
If cached xml is invalid , update .
: param inactive : Return persistent XML , not the running config .
No effect if domain is not running . Use this flag
if the XML will be used for redefining a guest
: param refresh_if_nec : Check if XML is out of date , and if so ,
refresh it ( default behavior ) . Skipping a refresh is
useful to prevent updating xml in the tick loop when
it ' s not that important (disk/net stats)
"""
2013-09-23 08:34:50 -04:00
if inactive :
2015-04-07 14:53:28 -04:00
# If inactive XML requested, always return a fresh object even if
2013-09-23 08:34:50 -04:00
# the current object is inactive XML (like when the domain is
# stopped). Callers that request inactive are basically expecting
# a new copy.
2015-04-07 14:53:28 -04:00
inactive_xml = self . _XMLDesc ( self . _inactive_xml_flags )
return self . _parseclass ( self . conn . get_backend ( ) ,
parsexml = inactive_xml )
if ( self . _xmlobj is None or
( refresh_if_nec and not self . _is_xml_valid ) ) :
2015-04-10 17:50:06 -04:00
self . ensure_latest_xml ( )
2013-09-23 08:34:50 -04:00
return self . _xmlobj
2010-02-25 19:35:01 -05:00
2015-04-07 14:12:00 -04:00
@property
def xmlobj ( self ) :
return self . get_xmlobj ( )
2013-07-07 08:05:23 -04:00
2015-04-07 14:53:28 -04:00
#########################
# Internal XML routines #
#########################
2010-02-25 19:35:01 -05:00
def _invalidate_xml ( self ) :
2015-04-07 14:53:28 -04:00
"""
Mark cached XML as invalid . Subclasses may extend this
to invalidate any specific caches of their own
"""
2014-09-20 17:21:16 -04:00
self . _name = None
2010-02-25 19:35:01 -05:00
2015-04-10 17:50:06 -04:00
# While for events we do want to clear cached XML values like
# _name, the XML is never invalid.
self . _is_xml_valid = self . _using_events ( )
2015-04-07 14:53:28 -04:00
def _make_xmlobj_to_define ( self ) :
2013-09-09 17:14:16 -04:00
"""
2015-04-07 14:53:28 -04:00
Build an xmlobj that should be used for defining new XML .
2013-09-09 17:14:16 -04:00
2015-04-07 14:53:28 -04:00
Most subclasses shouldn ' t touch this, but vmmDomainVirtinst needs to.
"""
return self . get_xmlobj ( inactive = True )
2013-09-09 17:14:16 -04:00
2015-04-10 17:23:11 -04:00
def _redefine_xmlobj ( self , xmlobj , origxml = None ) :
2015-04-07 14:53:28 -04:00
"""
2015-04-10 17:23:11 -04:00
Redefine the passed xmlobj , which should be generated with
self . _make_xmlobj_to_define ( )
2013-09-09 17:14:16 -04:00
2015-04-07 14:53:28 -04:00
Most subclasses shouldn ' t touch this, but vmmDomainVirtinst needs to.
2013-09-09 17:14:16 -04:00
2015-04-07 14:53:28 -04:00
: param origxml : vmmDomainVirtinst uses that field to make sure
we detect the actual XML change and log it correctly .
"""
if not origxml :
2018-08-31 16:52:02 -04:00
origxml = self . _make_xmlobj_to_define ( ) . get_xml ( )
2013-09-09 17:14:16 -04:00
2018-08-31 16:52:02 -04:00
newxml = xmlobj . get_xml ( )
2014-01-26 17:42:24 -05:00
self . log_redefine_xml_diff ( self , origxml , newxml )
2010-02-25 19:35:01 -05:00
2013-09-30 14:28:43 -04:00
if origxml != newxml :
2010-09-08 17:53:51 -04:00
self . _define ( newxml )
2015-04-10 17:50:06 -04:00
if self . _using_events ( ) :
return
# Make sure we have latest XML.
2015-04-10 18:03:56 -04:00
self . ensure_latest_xml ( nosignal = True )
2015-04-10 17:50:06 -04:00
# We force a signal even if XML didn't change, so the details
# window is correctly refreshed.
self . idle_emit ( " state-changed " )