Package virtualization :: Module poller_state_cache
[hide private]
[frames] | no frames]

Source Code for Module virtualization.poller_state_cache

  1  # 
  2  # Copyright (c) 2008--2013 Red Hat, Inc. 
  3  # 
  4  # This software is licensed to you under the GNU General Public License, 
  5  # version 2 (GPLv2). There is NO WARRANTY for this software, express or 
  6  # implied, including the implied warranties of MERCHANTABILITY or FITNESS 
  7  # FOR A PARTICULAR PURPOSE. You should have received a copy of GPLv2 
  8  # along with this software; if not, see 
  9  # http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt. 
 10  # 
 11  # Red Hat trademarks are not licensed under GPLv2. No permission is 
 12  # granted to use or replicate Red Hat trademarks that are incorporated 
 13  # in this software or its documentation. 
 14  # 
 15   
 16  import sys 
 17  import os 
 18  try: 
 19      # python2 
 20      import cPickle 
 21  except ImportError: 
 22      import pickle as cPickle 
 23  import time 
 24  import traceback 
 25   
 26  from spacewalk.common.usix import LongType 
 27   
 28  ############################################################################### 
 29  # Constants 
 30  ############################################################################### 
 31   
 32  CACHE_DATA_PATH = '/var/cache/rhn/virt_state.cache' 
 33  CACHE_EXPIRE_SECS = 60 * 60 * 6   # 6 hours, in seconds 
 34   
 35  ############################################################################### 
 36  # PollerStateCache Class 
 37  ############################################################################### 
 38   
39 -class PollerStateCache:
40 41 ########################################################################### 42 # Public Interface 43 ########################################################################### 44
45 - def __init__(self, domain_data, debug = 0):
46 """ 47 This method creates a new poller state based on the provided domain 48 list. The domain_data list should be in the form returned from 49 poller.poll_hypervisor. That is, 50 51 { uuid : { 'name' : '...', 52 'uuid' : '...', 53 'virt_type' : '...', 54 'memory_size' : '...', 55 'vcpus' : '...', 56 'state' : '...' }, ... } 57 """ 58 self.__debug = debug 59 60 # Start by loading the old state, if necessary. 61 self._load_state() 62 self.__new_domain_data = domain_data 63 64 # Now compare the given domain_data against the one loaded in the old 65 # state. 66 self._compare_domain_data() 67 68 self._log_debug("Added: %s" % repr(self.__added)) 69 self._log_debug("Removed: %s" % repr(self.__removed)) 70 self._log_debug("Modified: %s" % repr(self.__modified))
71
72 - def save(self):
73 """ 74 Updates the cache on disk with the latest domain data. 75 """ 76 self._save_state()
77
78 - def is_expired(self):
79 """ 80 Returns true if this cache is expired. 81 """ 82 if self.__expire_time is None: 83 return False 84 else: 85 return LongType(time.time()) >= self.__expire_time
86
87 - def is_changed(self):
88 return self.__added or self.__removed or self.__modified
89
90 - def get_added(self):
91 """ 92 Returns a list of uuids for each domain that has been added since the 93 last state poll. 94 """ 95 return self.__added
96
97 - def get_modified(self):
98 """ 99 Returns a list of uuids for each domain that has been modified since 100 the last state poll. 101 """ 102 return self.__modified
103
104 - def get_removed(self):
105 """ 106 Returns a list of uuids for each domain that has been removed since 107 the last state poll. 108 """ 109 return self.__removed
110 111 ########################################################################### 112 # Helper Methods 113 ########################################################################### 114
115 - def _load_state(self):
116 """ 117 Loads the last hypervisor state from disk. 118 """ 119 # Attempt to open up the cache file. 120 cache_file = None 121 try: 122 cache_file = open(CACHE_DATA_PATH, 'rb') 123 except IOError: 124 ioe = sys.exc_info()[1] 125 # Couldn't open the cache file. That's ok, there might not be one. 126 # We'll only complain if debugging is enabled. 127 self._log_debug("Could not open cache file '%s': %s" % \ 128 (CACHE_DATA_PATH, str(ioe))) 129 130 # Now, if a previous state was cached, load it. 131 state = {} 132 if cache_file: 133 try: 134 state = cPickle.load(cache_file) 135 except cPickle.PickleError: 136 pe = sys.exc_info()[1] 137 # Strange. Possibly, the file is corrupt. We'll load an empty 138 # state instead. 139 self._log_debug("Error occurred while loading state: %s" % \ 140 str(pe)) 141 except EOFError: 142 self._log_debug("Unexpected EOF. Probably an empty file.") 143 cache_file.close() 144 145 cache_file.close() 146 147 if state: 148 self._log_debug("Loaded state: %s" % repr(state)) 149 150 self.__expire_time = LongType(state['expire_time']) 151 152 # If the cache is expired, set the old data to None so we force 153 # a refresh. 154 if self.is_expired(): 155 self.__old_domain_data = None 156 os.unlink(CACHE_DATA_PATH) 157 else: 158 self.__old_domain_data = state['domain_data'] 159 160 else: 161 self.__old_domain_data = None 162 self.__expire_time = None
163
164 - def _save_state(self):
165 """ 166 Saves the given polling state to disk. 167 """ 168 # First, ensure that the proper parent directory is created. 169 cache_dir_path = os.path.dirname(CACHE_DATA_PATH) 170 if not os.path.exists(cache_dir_path): 171 os.makedirs(cache_dir_path, int('0700', 8)) 172 173 state = {} 174 state['domain_data'] = self.__new_domain_data 175 if self.__expire_time is None or self.is_expired(): 176 state['expire_time'] = LongType(time.time()) + CACHE_EXPIRE_SECS 177 else: 178 state['expire_time'] = self.__expire_time 179 180 # Now attempt to open the file for writing. We'll just overwrite 181 # whatever's already there. Also, let any exceptions bounce out. 182 cache_file = open(CACHE_DATA_PATH, "wb") 183 cPickle.dump(state, cache_file) 184 cache_file.close()
185
186 - def _compare_domain_data(self):
187 """ 188 Compares the old domain_data to the new domain_data. Returns a tuple 189 of lists, relative to the new domain_data: 190 191 (added, removed, modified) 192 """ 193 self.__added = {} 194 self.__removed = {} 195 self.__modified = {} 196 197 # First, figure out the modified and added uuids. 198 if self.__new_domain_data: 199 for (uuid, new_properties) in self.__new_domain_data.items(): 200 if not self.__old_domain_data or uuid not in self.__old_domain_data: 201 202 self.__added[uuid] = self.__new_domain_data[uuid] 203 else: 204 old_properties = self.__old_domain_data[uuid] 205 if old_properties != new_properties: 206 self.__modified[uuid] = self.__new_domain_data[uuid] 207 208 # Now, figure out the removed uuids. 209 if self.__old_domain_data: 210 for uuid in self.__old_domain_data.keys(): 211 if not self.__new_domain_data or uuid not in self.__new_domain_data: 212 213 self.__removed[uuid] = self.__old_domain_data[uuid]
214
215 - def _log_debug(self, msg, include_trace = 0):
216 if self.__debug: 217 print("DEBUG: " + str(msg)) 218 if include_trace: 219 e_info = sys.exc_info() 220 traceback.print_exception(e_info[0], e_info[1], e_info[2])
221