1# Copyright (C) 2008, 2013 Red Hat, Inc. 2# Copyright (C) 2008 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 7import time 8 9from virtinst import log 10from virtinst import pollhelpers 11from virtinst import StoragePool, StorageVolume 12 13from .libvirtobject import vmmLibvirtObject 14 15 16def _pretty_bytes(val): 17 val = int(val) 18 if val > (1024 * 1024 * 1024): 19 return "%2.2f GiB" % (val / (1024.0 * 1024.0 * 1024.0)) 20 else: 21 return "%2.2f MiB" % (val / (1024.0 * 1024.0)) 22 23 24POOL_TYPE_DESCS = { 25 StoragePool.TYPE_DIR: _("Filesystem Directory"), 26 StoragePool.TYPE_FS: _("Pre-Formatted Block Device"), 27 StoragePool.TYPE_NETFS: _("Network Exported Directory"), 28 StoragePool.TYPE_LOGICAL: _("LVM Volume Group"), 29 StoragePool.TYPE_DISK: _("Physical Disk Device"), 30 StoragePool.TYPE_ISCSI: _("iSCSI Target"), 31 StoragePool.TYPE_SCSI: _("SCSI Host Adapter"), 32 StoragePool.TYPE_MPATH: _("Multipath Device Enumerator"), 33 StoragePool.TYPE_GLUSTER: _("Gluster Filesystem"), 34 StoragePool.TYPE_RBD: _("RADOS Block Device/Ceph"), 35 StoragePool.TYPE_SHEEPDOG: _("Sheepdog Filesystem"), 36 StoragePool.TYPE_ZFS: _("ZFS Pool"), 37} 38 39 40class vmmStorageVolume(vmmLibvirtObject): 41 def __init__(self, conn, backend, key): 42 vmmLibvirtObject.__init__(self, conn, backend, key, StorageVolume) 43 44 45 ########################## 46 # Required class methods # 47 ########################## 48 49 def _conn_tick_poll_param(self): 50 return None # pragma: no cover 51 def class_name(self): 52 return "volume" # pragma: no cover 53 54 def _XMLDesc(self, flags): 55 try: 56 return self._backend.XMLDesc(flags) 57 except Exception as e: # pragma: no cover 58 log.debug("XMLDesc for vol=%s failed: %s", 59 self._backend.key(), e) 60 raise 61 62 def _get_backend_status(self): 63 return self._STATUS_ACTIVE 64 65 66 ########### 67 # Actions # 68 ########### 69 70 def get_parent_pool(self): 71 name = self._backend.storagePoolLookupByVolume().name() 72 for pool in self.conn.list_pools(): 73 if pool.get_name() == name: 74 return pool 75 76 def delete(self, force=True): 77 ignore = force 78 self._backend.delete(0) 79 self._backend = None 80 81 82 ################# 83 # XML accessors # 84 ################# 85 86 def get_key(self): 87 return self.get_xmlobj().key or "" 88 def get_target_path(self): 89 return self.get_xmlobj().target_path or "" 90 def get_format(self): 91 return self.get_xmlobj().format 92 def get_capacity(self): 93 return self.get_xmlobj().capacity 94 95 def get_pretty_capacity(self): 96 return _pretty_bytes(self.get_capacity()) 97 98 def get_pretty_name(self, pooltype): 99 name = self.get_name() 100 if pooltype != "iscsi": 101 return name 102 103 key = self.get_key() 104 ret = name 105 if key: 106 ret += " (%s)" % key 107 return ret 108 109 110class vmmStoragePool(vmmLibvirtObject): 111 __gsignals__ = { 112 "refreshed": (vmmLibvirtObject.RUN_FIRST, None, []) 113 } 114 115 @staticmethod 116 def supports_volume_creation(pool_type, clone=False): 117 """ 118 Returns if pool supports volume creation. If @clone is set to True 119 returns if pool supports volume cloning (virVolCreateXMLFrom). 120 """ 121 supported = [ 122 StoragePool.TYPE_DIR, 123 StoragePool.TYPE_FS, 124 StoragePool.TYPE_NETFS, 125 StoragePool.TYPE_DISK, 126 StoragePool.TYPE_LOGICAL, 127 StoragePool.TYPE_RBD, 128 ] 129 if not clone: 130 supported.extend([ 131 StoragePool.TYPE_SHEEPDOG, 132 StoragePool.TYPE_ZFS, 133 ]) 134 return pool_type in supported 135 136 @staticmethod 137 def pretty_type(pool_type): 138 return POOL_TYPE_DESCS.get(pool_type, "%s pool" % pool_type) 139 140 @staticmethod 141 def list_types(): 142 return sorted(list(POOL_TYPE_DESCS.keys())) 143 144 def __init__(self, conn, backend, key): 145 vmmLibvirtObject.__init__(self, conn, backend, key, StoragePool) 146 147 self._last_refresh_time = 0 148 self._volumes = None 149 150 151 ########################## 152 # Required class methods # 153 ########################## 154 155 def _conn_tick_poll_param(self): 156 return "pollpool" 157 def class_name(self): 158 return "pool" 159 160 def _XMLDesc(self, flags): 161 return self._backend.XMLDesc(flags) 162 def _define(self, xml): 163 return self.conn.define_pool(xml) 164 def _using_events(self): 165 return self.conn.using_storage_pool_events 166 def _get_backend_status(self): 167 return (bool(self._backend.isActive()) and 168 self._STATUS_ACTIVE or 169 self._STATUS_INACTIVE) 170 171 def _init_libvirt_state(self): 172 super()._init_libvirt_state() 173 if not self.conn.is_active(): 174 # We only want to refresh a pool on initial conn startup, 175 # since the pools may be out of date. But if a storage pool 176 # shows up while the conn is connected, this means it was 177 # just 'defined' recently and doesn't need to be refreshed. 178 self.refresh(_from_object_init=True) 179 for vol in self.get_volumes(): 180 vol.init_libvirt_state() 181 182 def _invalidate_xml(self): 183 vmmLibvirtObject._invalidate_xml(self) 184 self._volumes = None 185 186 def _cleanup(self): 187 vmmLibvirtObject._cleanup(self) 188 for vol in self._volumes: 189 vol.cleanup() 190 self._volumes = None 191 192 193 ########### 194 # Actions # 195 ########### 196 197 @vmmLibvirtObject.lifecycle_action 198 def start(self): 199 self._backend.create(0) 200 201 @vmmLibvirtObject.lifecycle_action 202 def stop(self): 203 self._backend.destroy() 204 205 @vmmLibvirtObject.lifecycle_action 206 def delete(self, force=True): 207 ignore = force 208 self._backend.undefine() 209 self._backend = None 210 211 def refresh(self, _from_object_init=False): 212 """ 213 :param _from_object_init: Only used for the refresh() call from 214 _init_libvirt_state. Tells us to not refresh the XML, since 215 we just updated it. 216 """ 217 if not self.is_active(): 218 return # pragma: no cover 219 220 self._backend.refresh(0) 221 if self._using_events() and not _from_object_init: 222 # If we are using events, we let the event loop trigger 223 # the cache update for us. Except if from init_libvirt_state, 224 # we want the update to be done immediately 225 return 226 227 self.refresh_pool_cache_from_event_loop( 228 _from_object_init=_from_object_init) 229 230 def refresh_pool_cache_from_event_loop(self, _from_object_init=False): 231 if not _from_object_init: 232 self.recache_from_event_loop() 233 self._update_volumes(force=True) 234 self.idle_emit("refreshed") 235 self._last_refresh_time = time.time() 236 237 def secs_since_last_refresh(self): 238 return time.time() - self._last_refresh_time 239 240 241 ################### 242 # Volume handling # 243 ################### 244 245 def get_volume_by_name(self, name): 246 for vol in self.get_volumes(): 247 if vol.get_name() == name: 248 return vol 249 250 def get_volumes(self): 251 self._update_volumes(force=False) 252 return self._volumes[:] 253 254 def _update_volumes(self, force): 255 if not self.is_active(): 256 self._volumes = [] 257 return 258 if not force and self._volumes is not None: 259 return 260 261 keymap = dict((o.get_name(), o) for o in self._volumes or []) 262 def cb(obj, key): 263 return vmmStorageVolume(self.conn, obj, key) 264 (dummy1, dummy2, allvols) = pollhelpers.fetch_volumes( 265 self.conn.get_backend(), self.get_backend(), keymap, cb) 266 self._volumes = allvols 267 268 269 ######################### 270 # XML/config operations # 271 ######################### 272 273 def set_autostart(self, value): 274 self._backend.setAutostart(value) 275 def get_autostart(self): 276 return self._backend.autostart() 277 278 def get_type(self): 279 return self.get_xmlobj().type 280 def get_target_path(self): 281 return self.get_xmlobj().target_path or "" 282 283 def get_allocation(self): 284 return self.get_xmlobj().allocation 285 def get_available(self): 286 return self.get_xmlobj().available 287 def get_capacity(self): 288 return self.get_xmlobj().capacity 289 290 def get_pretty_allocation(self): 291 return _pretty_bytes(self.get_allocation()) 292 def get_pretty_available(self): 293 return _pretty_bytes(self.get_available()) 294