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.
#
import logging
2016-06-07 18:33:21 +03:00
from gi . repository import GObject
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-09-17 22:48:42 +03:00
" initialized " : ( GObject . SignalFlags . RUN_FIRST , None , [ bool ] ) ,
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-11 01:03:56 +03:00
self . __initialized = False
self . __status = None
2015-04-10 01:02:42 +03:00
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 ) :
2015-11-03 23:56:39 +03:00
oldconnkey = self . get_connkey ( )
2013-09-29 20:14:00 +04:00
oldname = self . get_xmlobj ( ) . name
2015-04-11 00:50:06 +03:00
self . ensure_latest_xml ( )
2015-04-11 00:23:11 +03:00
xmlobj = self . _make_xmlobj_to_define ( )
2013-09-29 20:14:00 +04:00
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
2015-11-03 23:56:39 +03:00
self . conn . rename_object ( self , origxml , newxml , oldconnkey )
2014-09-21 00:55:11 +04:00
except :
self . _key = oldname
raise
2013-09-29 20:14:00 +04:00
finally :
2015-04-14 01:02:16 +03:00
self . __force_refresh_xml ( )
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 .
"""
2015-04-11 01:03:56 +03:00
if self . __initialized :
2015-04-10 21:08:25 +03:00
return
2015-09-17 22:48:42 +03:00
initialize_failed = False
2015-04-10 21:08:25 +03:00
try :
self . _init_libvirt_state ( )
2015-04-24 22:34:03 +03:00
except :
logging . debug ( " Error initializing libvirt state for %s " , self ,
exc_info = True )
2015-09-17 22:48:42 +03:00
initialize_failed = True
2015-04-24 22:34:03 +03:00
self . __initialized = True
2015-09-17 22:48:42 +03:00
self . idle_emit ( " initialized " , initialize_failed )
2015-04-10 21:08:25 +03:00
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 ( ) :
2016-02-05 18:18:16 +03:00
return _ ( " Active " )
return _ ( " Inactive " )
2015-04-10 16:15:44 +03:00
2015-04-19 16:26:16 +03:00
def _refresh_status ( self , newstatus = None , cansignal = True ) :
2015-04-10 02:03:27 +03: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-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-19 16:26:16 +03:00
: param cansignal : If True , this function will signal state - changed
if required .
: returns : True if status changed , false otherwise
2015-04-10 01:02:42 +03:00
"""
2015-04-10 02:03:27 +03:00
if ( self . _using_events ( ) and
2015-04-11 01:03:56 +03:00
self . __status is not None ) :
2015-04-19 16:26:16 +03:00
return False
2015-04-10 01:02:42 +03:00
2015-04-14 01:02:16 +03:00
if newstatus is None :
newstatus = self . _get_backend_status ( )
status = newstatus
if status == self . __status :
2015-04-19 16:26:16 +03:00
return False
2015-04-14 01:02:16 +03:00
self . __status = status
self . ensure_latest_xml ( nosignal = True )
2015-04-19 16:26:16 +03:00
if cansignal :
self . idle_emit ( " state-changed " )
return True
2015-04-10 01:02:42 +03: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-26 03:35:01 +03:00
##################
# Public XML API #
##################
2016-06-23 21:48:04 +03:00
def recache_from_event_loop ( self ) :
2015-04-11 00:50:06 +03:00
"""
2015-04-12 00:18:25 +03: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-11 00:50:06 +03:00
"""
2015-04-14 01:02:16 +03:00
try :
self . __force_refresh_xml ( nosignal = True )
# status = None forces a signal to be emitted
self . __status = None
self . _refresh_status ( )
except Exception , e :
# 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-11 00:50:06 +03:00
2015-04-11 01:03:56 +03:00
def ensure_latest_xml ( self , nosignal = False ) :
2015-04-11 00:50:06 +03: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-14 01:02:16 +03:00
self . __force_refresh_xml ( nosignal = nosignal )
2015-04-11 00:50:06 +03:00
2015-04-14 01:02:16 +03:00
def __force_refresh_xml ( self , nosignal = 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-11 00:50:06 +03:00
: param nosignal : If true , don ' t send state-changed. Used by
callers that are going to send it anyways .
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
2015-04-11 00:50:06 +03:00
if not nosignal and 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 ) ) :
2015-04-11 00:50:06 +03:00
self . ensure_latest_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 ( )
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
"""
2014-09-21 01:21:16 +04:00
self . _name = None
2010-02-26 03:35:01 +03:00
2015-04-11 00:50:06 +03: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 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-11 00:23:11 +03:00
def _redefine_xmlobj ( self , xmlobj , origxml = None ) :
2015-04-07 21:53:28 +03:00
"""
2015-04-11 00:23:11 +03:00
Redefine the passed xmlobj , which should be generated with
self . _make_xmlobj_to_define ( )
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 )
2015-04-11 00:50:06 +03:00
if self . _using_events ( ) :
return
# Make sure we have latest XML.
2015-04-11 01:03:56 +03:00
self . ensure_latest_xml ( nosignal = True )
2015-04-11 00:50:06 +03:00
# We force a signal even if XML didn't change, so the details
# window is correctly refreshed.
self . idle_emit ( " state-changed " )