2010-02-26 03:35:01 +03:00
#
2013-10-28 00:59:46 +04:00
# Copyright (C) 2010, 2013 Red Hat, Inc.
2010-02-26 03:35:01 +03:00
# Copyright (C) 2010 Cole Robinson <crobinso@redhat.com>
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
# MA 02110-1301 USA.
#
2012-05-14 17:24:56 +04:00
from gi . repository import GObject
2010-02-26 03:35:01 +03:00
import logging
2014-09-13 00:10:45 +04:00
from . baseclass import vmmGObject
2010-02-26 03:35:01 +03:00
2013-04-13 22:34:52 +04:00
2010-12-09 20:37:48 +03:00
class vmmLibvirtObject ( vmmGObject ) :
2012-05-14 17:24:56 +04:00
__gsignals__ = {
2015-04-10 01:02:42 +03:00
" state-changed " : ( GObject . SignalFlags . RUN_FIRST , None , [ ] ) ,
2015-04-10 21:08:25 +03:00
" initialized " : ( GObject . SignalFlags . RUN_FIRST , None , [ ] ) ,
2012-05-14 17:24:56 +04:00
}
2015-04-10 01:02:42 +03:00
_STATUS_ACTIVE = 1
_STATUS_INACTIVE = 2
2013-09-23 16:34:50 +04:00
def __init__ ( self , conn , backend , key , parseclass ) :
2010-12-09 20:37:48 +03:00
vmmGObject . __init__ ( self )
2011-07-23 00:43:26 +04:00
self . _conn = conn
2013-07-07 16:42:57 +04:00
self . _backend = backend
self . _key = key
2013-09-10 01:14:16 +04:00
self . _parseclass = parseclass
2010-02-26 03:35:01 +03:00
2015-04-10 21:08:25 +03:00
self . _initialized = False
2015-04-10 01:02:42 +03:00
self . __status = self . _STATUS_ACTIVE
self . _support_isactive = None
2013-09-10 01:14:16 +04:00
self . _xmlobj = None
self . _xmlobj_to_define = None
2015-04-07 21:53:28 +03:00
self . _is_xml_valid = False
2013-09-10 01:14:16 +04:00
2010-02-26 03:35:01 +03:00
# These should be set by the child classes if necessary
self . _inactive_xml_flags = 0
self . _active_xml_flags = 0
2015-04-10 21:08:25 +03: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-03 01:17:47 +04:00
self . _name = None
self . get_name ( )
2014-01-27 02:42:24 +04:00
@staticmethod
def log_redefine_xml_diff ( obj , origxml , newxml ) :
objname = " < %s name= %s > " % ( obj . __class__ . __name__ , obj . get_name ( ) )
if origxml == newxml :
logging . debug ( " Redefine requested for %s , but XML didn ' t change! " ,
objname )
return
import difflib
diff = " " . join ( difflib . unified_diff ( origxml . splitlines ( 1 ) ,
newxml . splitlines ( 1 ) ,
fromfile = " Original XML " ,
tofile = " New XML " ) )
logging . debug ( " Redefining %s with XML diff: \n %s " , objname , diff )
2015-04-10 01:27:45 +03: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 16:15:44 +03:00
poll_param = self . _conn_tick_poll_param ( ) # pylint: disable=protected-access
tick_kwargs = { poll_param : True }
2015-04-10 01:27:45 +03:00
self . conn . schedule_priority_tick ( * * tick_kwargs )
return ret
return newfn
2011-07-24 05:16:54 +04:00
def _cleanup ( self ) :
pass
2011-07-23 00:43:26 +04:00
def _get_conn ( self ) :
return self . _conn
conn = property ( _get_conn )
2010-02-26 03:35:01 +03:00
2013-07-07 16:42:57 +04:00
def get_backend ( self ) :
return self . _backend
2014-06-03 01:17:47 +04:00
def get_connkey ( self ) :
2013-07-07 16:42:57 +04:00
return self . _key
2013-09-29 20:14:00 +04:00
def change_name_backend ( self , newbackend ) :
# Used for changing the backing object after a rename
self . _backend = newbackend
2014-09-21 00:55:11 +04:00
def define_name ( self , newname ) :
2013-09-29 20:14:00 +04:00
oldname = self . get_xmlobj ( ) . name
self . _invalidate_xml ( )
xmlobj = self . _get_xmlobj_to_define ( )
if xmlobj . name == newname :
return
logging . debug ( " Changing %s name from %s to %s " ,
2014-09-21 00:55:11 +04:00
self . __class__ , oldname , newname )
2013-09-29 20:14:00 +04:00
origxml = xmlobj . get_xml_config ( )
xmlobj . name = newname
newxml = xmlobj . get_xml_config ( )
try :
2014-09-21 00:55:11 +04:00
self . _key = newname
self . conn . rename_object ( self , origxml , newxml , oldname , newname )
except :
self . _key = oldname
raise
2013-09-29 20:14:00 +04:00
finally :
self . _invalidate_xml ( )
2015-04-10 01:02:42 +03:00
self . emit ( " state-changed " )
2013-09-29 20:14:00 +04:00
2013-09-23 16:34:50 +04:00
2010-02-26 03:35:01 +03:00
#############################################################
# Functions that should probably be overridden in sub class #
#############################################################
def _XMLDesc ( self , flags ) :
2010-12-09 20:37:48 +03:00
raise NotImplementedError ( )
2015-04-10 16:15:44 +03: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 19:52:42 +03:00
def reports_stats ( self ) :
return False
2014-02-11 21:07:13 +04:00
def _using_events ( self ) :
return False
2015-04-10 01:02:42 +03:00
def _check_supports_isactive ( self ) :
return False
def _get_backend_status ( self ) :
raise NotImplementedError ( )
2010-02-26 03:35:01 +03:00
def _define ( self , xml ) :
ignore = xml
return
2013-09-30 23:23:14 +04:00
def delete ( self , force = True ) :
ignore = force
2014-06-03 01: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 16:15:44 +03:00
def tick ( self , stats_update = True ) :
raise NotImplementedError ( )
2015-04-10 21:08:25 +03: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 .
"""
if self . _initialized :
return
try :
self . _init_libvirt_state ( )
finally :
self . _initialized = True
self . idle_emit ( " initialized " )
2013-07-07 16:05:23 +04:00
2015-04-10 01:02:42 +03: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 16:15:44 +03:00
def run_status ( self ) :
if self . is_active ( ) :
return " Active "
return " Inactive "
2015-04-10 02:03:27 +03:00
def refresh_status_from_event_loop ( self ) :
2015-04-10 01:02:42 +03:00
"""
2015-04-10 02:03:27 +03:00
Updates VM status , because we received a status event from libvirt ' s
event implementations . That ' s the only time this should be used.
"""
return self . _refresh_status ( skip_if_have_events = False )
def _refresh_status ( self , skip_if_have_events = True , newstatus = None ) :
"""
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 .
: param skip_if_have_events : If this object is served by libvirt
events , we want this to be a no - op for most usages , like
from tick ( ) , so don ' t do anything.
2015-04-10 01:02:42 +03:00
: param newstatus : Used by vmmDomain as a small optimization to
2015-04-10 02:03:27 +03:00
avoid polling info ( ) twice
2015-04-10 01:02:42 +03:00
"""
2015-04-10 02:03:27 +03:00
if ( self . _using_events ( ) and
skip_if_have_events and
2015-04-10 21:08:25 +03:00
self . _initialized ) :
2015-04-10 01:02:42 +03:00
return
try :
status = newstatus
if newstatus is None :
status = self . _get_backend_status ( )
if status == self . __status :
return
self . __status = status
# This will send state-change for us
self . refresh_xml ( forcesignal = True )
2015-04-10 01:27:45 +03:00
except Exception , e :
2015-04-10 01:02:42 +03:00
# If we hit an exception here, it's often that the object
# disappeared, so request the poll loop to be updated
2015-04-10 01:27:45 +03:00
logging . debug ( " Error polling status for %s : %s " , self , e )
2015-04-10 16:15:44 +03:00
poll_param = self . _conn_tick_poll_param ( )
if poll_param :
kwargs = { " force " : True , poll_param : True }
2015-04-10 01:27:45 +03:00
logging . debug ( " Scheduling priority tick with: %s " , kwargs )
2015-04-10 01:02:42 +03:00
self . conn . schedule_priority_tick ( * * kwargs )
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-26 03:35:01 +03:00
##################
# Public XML API #
##################
2015-04-07 21:53:28 +03:00
def refresh_xml ( self , forcesignal = False ) :
2010-02-26 03:35:01 +03:00
"""
2015-04-10 01:02:42 +03:00
Force an xml update . Signal ' state-changed ' if domain xml has
2015-04-07 21:53:28 +03:00
changed since last refresh
2015-04-10 01:02:42 +03:00
: param forcesignal : Send state - changed unconditionally
2010-02-26 03:35:01 +03:00
"""
2015-04-07 21:53:28 +03:00
origxml = None
if self . _xmlobj :
origxml = self . _xmlobj . get_xml_config ( )
2013-09-23 16:34:50 +04:00
2015-04-07 21:53:28 +03: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
if forcesignal or origxml != active_xml :
2015-04-10 01:02:42 +03:00
self . idle_emit ( " state-changed " )
2013-09-23 16:34:50 +04:00
2015-04-07 21:53:28 +03: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 16:34:50 +04:00
if inactive :
2015-04-07 21:53:28 +03:00
# If inactive XML requested, always return a fresh object even if
2013-09-23 16: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 21:53:28 +03: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 ) ) :
self . refresh_xml ( )
2013-09-23 16:34:50 +04:00
return self . _xmlobj
2010-02-26 03:35:01 +03:00
2015-04-07 21:12:00 +03:00
@property
def xmlobj ( self ) :
return self . get_xmlobj ( )
2015-04-07 21:53:28 +03:00
def redefine_cached ( self ) :
"""
Redefine the _xmlobj_to_define cache .
2010-02-26 03:35:01 +03:00
2015-04-07 21:53:28 +03:00
Used by places like details . py and addhardware . py to queue a bunch
of XML changes via vmmDomain functions , but only call ' define '
once .
"""
if not self . _xmlobj_to_define :
logging . debug ( " No cached XML to define, skipping. " )
return
2015-04-09 20:01:38 +03:00
try :
self . _redefine_object ( self . _xmlobj_to_define )
except :
# If something fails here, we need to drop the cached object,
# since some edits like addhardware.py may not be idempotent
self . _invalidate_xml ( )
raise
2010-02-26 03:35:01 +03:00
2013-07-07 16:05:23 +04:00
2015-04-07 21:53:28 +03:00
#########################
# Internal XML routines #
#########################
2010-02-26 03:35:01 +03:00
def _invalidate_xml ( self ) :
2015-04-07 21:53:28 +03:00
"""
Mark cached XML as invalid . Subclasses may extend this
to invalidate any specific caches of their own
"""
2010-02-26 03:35:01 +03:00
self . _is_xml_valid = False
2013-09-10 01:14:16 +04:00
self . _xmlobj_to_define = None
2014-09-21 01:21:16 +04:00
self . _name = None
2010-02-26 03:35:01 +03:00
2015-04-07 21:53:28 +03:00
def _make_xmlobj_to_define ( self ) :
2013-09-10 01:14:16 +04:00
"""
2015-04-07 21:53:28 +03:00
Build an xmlobj that should be used for defining new XML .
2013-09-10 01:14:16 +04:00
2015-04-07 21:53:28 +03:00
Most subclasses shouldn ' t touch this, but vmmDomainVirtinst needs to.
"""
return self . get_xmlobj ( inactive = True )
2013-09-10 01:14:16 +04:00
2015-04-07 21:53:28 +03:00
def _get_xmlobj_to_define ( self ) :
"""
Return the XML object that should be used to queue up new XML changes .
This is what is flushed with redefine_cached .
2010-02-26 03:35:01 +03:00
2015-04-07 21:53:28 +03:00
Most subclasses shouldn ' t touch this, but vmmDomainVirtinst needs to.
"""
2013-09-10 01:14:16 +04:00
if not self . _xmlobj_to_define :
2015-04-07 21:53:28 +03:00
self . _xmlobj_to_define = self . _make_xmlobj_to_define ( )
return self . _xmlobj_to_define
2013-09-10 01:14:16 +04:00
2015-04-07 21:53:28 +03:00
def _redefine_object ( self , xmlobj , origxml = None ) :
"""
Redefine the passed object . This is called by redefine_cached and
shouldn ' t be called directly.
2013-09-10 01:14:16 +04:00
2015-04-07 21:53:28 +03:00
Most subclasses shouldn ' t touch this, but vmmDomainVirtinst needs to.
2013-09-10 01:14:16 +04:00
2015-04-07 21:53:28 +03:00
: param origxml : vmmDomainVirtinst uses that field to make sure
we detect the actual XML change and log it correctly .
"""
if not origxml :
origxml = self . _make_xmlobj_to_define ( ) . get_xml_config ( )
2013-09-10 01:14:16 +04:00
2015-04-07 21:53:28 +03:00
newxml = xmlobj . get_xml_config ( )
2014-01-27 02:42:24 +04:00
self . log_redefine_xml_diff ( self , origxml , newxml )
2010-02-26 03:35:01 +03:00
2013-09-30 22:28:43 +04:00
if origxml != newxml :
2010-09-09 01:53:51 +04:00
self . _define ( newxml )
2014-02-11 21:07:13 +04:00
if not self . _using_events ( ) :
# Make sure we have latest XML
self . refresh_xml ( forcesignal = True )