1# Copyright (C) 2010, 2013 Red Hat, Inc. 2# Copyright (C) 2010 Cole Robinson <crobinso@redhat.com> 3# 4# This work is licensed under the GNU GPLv2 or later. 5# See the COPYING file in the top-level directory. 6 7from virtinst import log 8from virtinst import xmlutil 9 10from ..baseclass import vmmGObject 11 12 13class vmmLibvirtObject(vmmGObject): 14 __gsignals__ = { 15 "state-changed": (vmmGObject.RUN_FIRST, None, []), 16 "initialized": (vmmGObject.RUN_FIRST, None, [bool]), 17 } 18 19 _STATUS_ACTIVE = 1 20 _STATUS_INACTIVE = 2 21 22 def __init__(self, conn, backend, name, parseclass): 23 vmmGObject.__init__(self) 24 self._conn = conn 25 self._backend = backend 26 self._parseclass = parseclass 27 self._name = name 28 29 self.__initialized = False 30 self.__status = None 31 32 self._xmlobj = None 33 self._xmlobj_to_define = None 34 self._is_xml_valid = False 35 36 # These should be set by the child classes if necessary 37 self._inactive_xml_flags = 0 38 self._active_xml_flags = 0 39 40 @staticmethod 41 def log_redefine_xml_diff(obj, origxml, newxml): 42 if origxml == newxml: 43 log.debug("Redefine requested for %s, but XML didn't change!", 44 obj) 45 return 46 47 diff = xmlutil.diff(origxml, newxml, "Original XML", "New XML") 48 log.debug("Redefining %s with XML diff:\n%s", obj, diff) 49 50 @staticmethod 51 def lifecycle_action(fn): 52 """ 53 Decorator for object lifecycle actions like start, stop, delete. 54 Will make sure any necessary state is updated accordingly. 55 """ 56 def newfn(self, *args, **kwargs): 57 ret = fn(self, *args, **kwargs) 58 59 # If events are supported, this is a no-op, but the event loop 60 # will trigger force_status_update, which will refresh_xml as well. 61 # 62 # If events aren't supported, the priority tick will call 63 # self.tick(), which will call force_status_update 64 poll_param = self._conn_tick_poll_param() # pylint: disable=protected-access 65 tick_kwargs = {poll_param: True} 66 self.conn.schedule_priority_tick(**tick_kwargs) 67 68 return ret 69 return newfn 70 71 def __repr__(self): 72 # pylint: disable=arguments-differ 73 try: 74 name = self.get_name() 75 except Exception: 76 name = "" 77 return "<%s name=%s id=%s>" % ( 78 self.__class__.__name__, name, hex(id(self))) 79 80 def _cleanup(self): 81 self._backend = None 82 83 def _get_conn(self): 84 return self._conn 85 conn = property(_get_conn) 86 87 def get_backend(self): 88 return self._backend 89 90 def is_domain(self): 91 return self.class_name() == "domain" 92 def is_network(self): 93 return self.class_name() == "network" 94 def is_pool(self): 95 return self.class_name() == "pool" 96 def is_nodedev(self): 97 return self.class_name() == "nodedev" 98 99 def change_name_backend(self, newbackend): 100 # Used for changing the backing object after a rename 101 self._backend = newbackend 102 103 def define_name(self, newname): 104 oldname = self.get_xmlobj().name 105 106 self.ensure_latest_xml() 107 xmlobj = self._make_xmlobj_to_define() 108 if xmlobj.name == newname: 109 return # pragma: no cover 110 111 log.debug("Changing %s name from %s to %s", 112 self, oldname, newname) 113 origxml = xmlobj.get_xml() 114 xmlobj.name = newname 115 newxml = xmlobj.get_xml() 116 117 try: 118 self._name = newname 119 self.conn.rename_object(self, origxml, newxml) 120 except Exception: # pragma: no cover 121 self._name = oldname 122 raise 123 finally: 124 self.__force_refresh_xml() 125 126 127 ############################################################# 128 # Functions that should probably be overridden in sub class # 129 ############################################################# 130 131 def _XMLDesc(self, flags): 132 raise NotImplementedError() 133 def class_name(self): 134 raise NotImplementedError() 135 def _conn_tick_poll_param(self): 136 # The parameter name for conn.tick() object polling. So 137 # for vmmDomain == "pollvm" 138 raise NotImplementedError() 139 140 def reports_stats(self): 141 return False 142 def _using_events(self): 143 return False 144 def _get_backend_status(self): 145 raise NotImplementedError() 146 147 def _define(self, xml): # pragma: no cover 148 ignore = xml 149 return 150 151 def delete(self, force=True): # pragma: no cover 152 ignore = force 153 154 def get_name(self): 155 return self._name 156 157 def tick(self, stats_update=True): 158 ignore = stats_update 159 self._refresh_status() 160 161 def _init_libvirt_state(self): 162 self.tick() 163 164 def init_libvirt_state(self): 165 """ 166 Function called by vmmConnection to populate initial state when 167 a new object appears. 168 """ 169 if self.__initialized: 170 return # pragma: no cover 171 172 initialize_failed = False 173 try: 174 if self.config.CLITestOptions.object_denylist == self._name: 175 raise RuntimeError("fake initialization error") 176 177 self._init_libvirt_state() 178 except Exception: # pragma: no cover 179 log.debug("Error initializing libvirt state for %s", self, 180 exc_info=True) 181 initialize_failed = True 182 183 self.__initialized = True 184 self.idle_emit("initialized", initialize_failed) 185 186 187 ################### 188 # Status handling # 189 ################### 190 191 def _get_status(self): 192 return self.__status 193 194 def is_active(self): 195 # vmmDomain overwrites this since it has more fine grained statuses 196 return self._get_status() == self._STATUS_ACTIVE 197 198 def run_status(self): 199 if self.is_active(): 200 return _("Active") 201 return _("Inactive") 202 203 def _refresh_status(self, newstatus=None, cansignal=True): 204 """ 205 Grab the object status/active state from libvirt, and if the 206 status has changed, update the XML cache. Typically called from 207 object tick functions for manually updating the object state. 208 209 :param newstatus: Used by vmmDomain as a small optimization to 210 avoid polling info() twice 211 :param cansignal: If True, this function will signal state-changed 212 if required. 213 :returns: True if status changed, false otherwise 214 """ 215 if (self._using_events() and 216 self.__status is not None): 217 return False 218 219 if newstatus is None: 220 newstatus = self._get_backend_status() 221 status = newstatus 222 if status == self.__status: 223 return False 224 self.__status = status 225 226 self.ensure_latest_xml(nosignal=True) 227 if cansignal: 228 self.idle_emit("state-changed") 229 return True 230 231 232 ################## 233 # Public XML API # 234 ################## 235 236 def recache_from_event_loop(self): 237 """ 238 Updates the VM status and XML, because we received an event from 239 libvirt's event implementations. That's the only time this should 240 be used. 241 242 We refresh status and XML because they are tied together in subtle 243 ways, like runtime XML changing when a VM is started. 244 """ 245 try: 246 self.__force_refresh_xml(nosignal=True) 247 # status = None forces a signal to be emitted 248 self.__status = None 249 self._refresh_status() 250 except Exception as e: 251 # If we hit an exception here, it's often that the object 252 # disappeared, so request the poll loop to be updated 253 log.debug("Error refreshing %s from events: %s", self, e) 254 poll_param = self._conn_tick_poll_param() 255 if poll_param: 256 kwargs = {"force": True, poll_param: True} 257 log.debug("Scheduling priority tick with: %s", kwargs) 258 self.conn.schedule_priority_tick(**kwargs) 259 260 def ensure_latest_xml(self, nosignal=False): 261 """ 262 Refresh XML if it isn't up to date, basically if we aren't using 263 events. 264 """ 265 if (self._using_events() and 266 self._xmlobj and 267 self._is_xml_valid): 268 return 269 self.__force_refresh_xml(nosignal=nosignal) 270 271 def __force_refresh_xml(self, nosignal=False): 272 """ 273 Force an xml update. Signal 'state-changed' if domain xml has 274 changed since last refresh 275 276 :param nosignal: If true, don't send state-changed. Used by 277 callers that are going to send it anyways. 278 """ 279 origxml = None 280 if self._xmlobj: 281 origxml = self._xmlobj.get_xml() 282 283 self._invalidate_xml() 284 active_xml = self._XMLDesc(self._active_xml_flags) 285 self._xmlobj = self._parseclass(self.conn.get_backend(), 286 parsexml=active_xml) 287 self._is_xml_valid = True 288 289 if not nosignal and origxml != active_xml: 290 self.idle_emit("state-changed") 291 292 def get_xmlobj(self, inactive=False, refresh_if_nec=True): 293 """ 294 Get object xml, return it wrapped in a virtinst object. 295 If cached xml is invalid, update. 296 297 :param inactive: Return persistent XML, not the running config. 298 No effect if domain is not running. Use this flag 299 if the XML will be used for redefining a guest 300 :param refresh_if_nec: Check if XML is out of date, and if so, 301 refresh it (default behavior). Skipping a refresh is 302 useful to prevent updating xml in the tick loop when 303 it's not that important (disk/net stats) 304 """ 305 if inactive: 306 # If inactive XML requested, always return a fresh object even if 307 # the current object is inactive XML (like when the domain is 308 # stopped). Callers that request inactive are basically expecting 309 # a new copy. 310 inactive_xml = self._XMLDesc(self._inactive_xml_flags) 311 return self._parseclass(self.conn.get_backend(), 312 parsexml=inactive_xml) 313 314 if (self._xmlobj is None or 315 (refresh_if_nec and not self._is_xml_valid)): 316 self.ensure_latest_xml() 317 318 return self._xmlobj 319 320 @property 321 def xmlobj(self): 322 return self.get_xmlobj() 323 324 def get_xml_to_define(self): 325 """ 326 Return the raw inactive XML we would use to alter/define an 327 object. Used by the xmleditor UI 328 """ 329 return self._make_xmlobj_to_define().get_xml() 330 331 def define_xml(self, xml): 332 """ 333 Define the passed in XML, and log a diff against the current XML. 334 Generally subclasses should use _redefine_xmlobj with higher 335 level wrappers, but this is needed for the XML editor 336 """ 337 origxml = self.get_xml_to_define() 338 newxml = xml 339 self._redefine_xml_internal(origxml, newxml) 340 341 342 ######################### 343 # Internal XML routines # 344 ######################### 345 346 def _invalidate_xml(self): 347 """ 348 Mark cached XML as invalid. Subclasses may extend this 349 to invalidate any specific caches of their own 350 """ 351 # While for events we do want to clear cached XML values like 352 # _name, the XML is never invalid. 353 self._is_xml_valid = self._using_events() 354 355 def _make_xmlobj_to_define(self): 356 """ 357 Build an xmlobj that should be used for defining new XML. 358 359 Most subclasses shouldn't touch this, but vmmDomainVirtinst needs to. 360 """ 361 return self.get_xmlobj(inactive=True) 362 363 def _redefine_xml_internal(self, origxml, newxml): 364 self.log_redefine_xml_diff(self, origxml, newxml) 365 366 self._define(newxml) 367 if self._using_events(): 368 return 369 370 self.ensure_latest_xml(nosignal=True) 371 self.idle_emit("state-changed") 372 373 def _redefine_xmlobj(self, xmlobj): 374 """ 375 Redefine the passed xmlobj, which should be generated with 376 self._make_xmlobj_to_define() and which has accumulated edits 377 from UI fields. 378 379 Most subclasses shouldn't alter this, but vmmDomainVirtinst needs to. 380 """ 381 origxml = self._make_xmlobj_to_define().get_xml() 382 newxml = xmlobj.get_xml() 383 self._redefine_xml_internal(origxml, newxml) 384