virt-manager/virtManager/util.py
Cole Robinson 4201ef9e68 details: Multiple screenshot improvements and fixes
- Fix screenshot after gtk3 conversion
- Report errors to the user if something goes wrong
- Drop the 'screenshot saved' success dialog
- Capture the screenshot data before asking for the file path, so things
  aren't out of date.
- Give a default recommended screenshot path to save.

First bit fixes:
https://bugzilla.redhat.com/show_bug.cgi?id=969410
2013-06-12 21:00:11 -04:00

413 lines
12 KiB
Python

#
# Copyright (C) 2008 Red Hat, Inc.
# Copyright (C) 2008 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 libvirt
import logging
import os.path
import virtManager
import virtinst
running_config = None
xml_escape = virtinst.util.xml_escape
def get_default_pool_path(conn):
if conn.is_session_uri():
return os.path.expanduser("~/VirtualMachines")
return "/var/lib/libvirt/images"
def get_default_pool_name(conn):
ignore = conn
return "default"
def build_default_pool(vmmconn):
"""
Helper to build the 'default' storage pool
"""
conn = vmmconn.vmm
path = get_default_pool_path(vmmconn)
name = get_default_pool_name(vmmconn)
pool = None
try:
pool = conn.storagePoolLookupByName(name)
except libvirt.libvirtError:
pass
if pool:
return
try:
logging.debug("Attempting to build default pool with target '%s'",
path)
defpool = virtinst.Storage.DirectoryPool(conn=conn,
name=name,
target_path=path)
newpool = defpool.install(build=True, create=True)
newpool.setAutostart(True)
except Exception, e:
raise RuntimeError(_("Couldn't create default storage pool '%s': %s") %
(path, str(e)))
def get_ideal_path_info(conn, name):
path = get_default_dir(conn)
suffix = ".img"
return (path, name, suffix)
def get_ideal_path(conn, name):
target, name, suffix = get_ideal_path_info(conn, name)
return os.path.join(target, name) + suffix
def get_default_pool(conn):
pool = None
default_name = get_default_pool_name(conn)
for uuid in conn.list_pool_uuids():
p = conn.get_pool(uuid)
if p.get_name() == default_name:
pool = p
return pool
def get_default_dir(conn):
pool = get_default_pool(conn)
if pool:
return pool.get_target_path()
else:
return running_config.get_default_image_dir(conn)
def get_default_path(conn, name, collidelist=None):
collidelist = collidelist or []
pool = get_default_pool(conn)
default_dir = get_default_dir(conn)
def path_exists(p):
return os.path.exists(p) or p in collidelist
if not pool:
# Use old generating method
origf = os.path.join(default_dir, name + ".img")
f = origf
n = 1
while path_exists(f) and n < 100:
f = os.path.join(default_dir, name +
"-" + str(n) + ".img")
n += 1
if path_exists(f):
f = origf
path = f
else:
target, ignore, suffix = get_ideal_path_info(conn, name)
# Sanitize collidelist to work with the collision checker
newcollidelist = []
for c in collidelist:
if c and os.path.dirname(c) == pool.get_target_path():
newcollidelist.append(os.path.basename(c))
path = virtinst.Storage.StorageVolume.find_free_name(name,
pool_object=pool.pool, suffix=suffix,
collidelist=newcollidelist)
path = os.path.join(target, path)
return path
def xml_parse_wrapper(xml, parse_func, *args, **kwargs):
"""
Parse the passed xml string into an xpath context, which is passed
to parse_func, along with any extra arguments.
"""
import libxml2
doc = None
ctx = None
ret = None
try:
doc = libxml2.parseDoc(xml)
ctx = doc.xpathNewContext()
ret = parse_func(doc, ctx, *args, **kwargs)
finally:
if ctx is not None:
ctx.xpathFreeContext()
if doc is not None:
doc.freeDoc()
return ret
def browse_local(parent, dialog_name, conn, start_folder=None,
_type=None, dialog_type=None,
confirm_func=None, browse_reason=None,
choose_button=None, default_name=None):
"""
Helper function for launching a filechooser
@param parent: Parent window for the filechooser
@param dialog_name: String to use in the title bar of the filechooser.
@param conn: vmmConnection used by calling class
@param start_folder: Folder the filechooser is viewing at startup
@param _type: File extension to filter by (e.g. "iso", "png")
@param dialog_type: Maps to FileChooserDialog 'action'
@param confirm_func: Optional callback function if file is chosen.
@param browse_reason: The vmmConfig.CONFIG_DIR* reason we are browsing.
If set, this will override the 'folder' parameter with the gconf
value, and store the user chosen path.
"""
from gi.repository import Gtk # pylint: disable=E0611
# Initial setup
overwrite_confirm = False
if dialog_type is None:
dialog_type = Gtk.FileChooserAction.OPEN
if dialog_type == Gtk.FileChooserAction.SAVE:
if choose_button is None:
choose_button = Gtk.STOCK_SAVE
overwrite_confirm = True
if choose_button is None:
choose_button = Gtk.STOCK_OPEN
fcdialog = Gtk.FileChooserDialog(title=dialog_name,
parent=parent,
action=dialog_type,
buttons=(Gtk.STOCK_CANCEL,
Gtk.ResponseType.CANCEL,
choose_button,
Gtk.ResponseType.ACCEPT))
fcdialog.set_default_response(Gtk.ResponseType.ACCEPT)
if default_name:
fcdialog.set_current_name(default_name)
# If confirm is set, warn about a file overwrite
if confirm_func:
overwrite_confirm = True
fcdialog.connect("confirm-overwrite", confirm_func)
fcdialog.set_do_overwrite_confirmation(overwrite_confirm)
# Set file match pattern (ex. *.png)
if _type is not None:
pattern = _type
name = None
if type(_type) is tuple:
pattern = _type[0]
name = _type[1]
f = Gtk.FileFilter()
f.add_pattern("*." + pattern)
if name:
f.set_name(name)
fcdialog.set_filter(f)
# Set initial dialog folder
if browse_reason:
start_folder = running_config.get_default_directory(conn,
browse_reason)
if start_folder is not None:
if os.access(start_folder, os.R_OK):
fcdialog.set_current_folder(start_folder)
# Run the dialog and parse the response
ret = None
if fcdialog.run() == Gtk.ResponseType.ACCEPT:
ret = fcdialog.get_filename()
fcdialog.destroy()
# Store the chosen directory in gconf if necessary
if ret and browse_reason and not ret.startswith("/dev"):
running_config.set_default_directory(os.path.dirname(ret),
browse_reason)
return ret
def dup_lib_conn(libconn):
conn = _dup_all_conn(None, libconn)
if isinstance(conn, virtManager.connection.vmmConnection):
return conn.vmm
return conn
def dup_conn(conn):
return _dup_all_conn(conn, None)
def _dup_all_conn(conn, libconn):
is_readonly = False
if libconn:
uri = libconn.getURI()
is_test = uri.startswith("test")
vmm = libconn
else:
is_test = conn.is_test_conn()
is_readonly = conn.is_read_only()
uri = conn.get_uri()
vmm = conn.vmm
if is_test:
# Skip duplicating a test conn, since it doesn't maintain state
# between instances
return conn or vmm
if running_config.support_threading:
# Libvirt 0.6.0 implemented client side request threading: this
# removes the need to actually duplicate the connection.
return conn or vmm
logging.debug("Duplicating connection for async operation.")
newconn = virtManager.connection.vmmConnection(uri, readOnly=is_readonly)
newconn.open(sync=True)
return newconn
def pretty_hv(gtype, domtype):
"""
Convert XML <domain type='foo'> and <os><type>bar</type>
into a more human relevant string.
"""
gtype = gtype.lower()
domtype = domtype.lower()
label = domtype
if domtype == "kvm":
if gtype == "xen":
label = "xenner"
elif domtype == "xen":
if gtype == "xen":
label = "xen (paravirt)"
elif gtype == "hvm":
label = "xen (fullvirt)"
elif domtype == "test":
if gtype == "xen":
label = "test (xen)"
elif gtype == "hvm":
label = "test (hvm)"
return label
def uuidstr(rawuuid):
hx = ['0', '1', '2', '3', '4', '5', '6', '7',
'8', '9', 'a', 'b', 'c', 'd', 'e', 'f']
uuid = []
for i in range(16):
uuid.append(hx[((ord(rawuuid[i]) >> 4) & 0xf)])
uuid.append(hx[(ord(rawuuid[i]) & 0xf)])
if i == 3 or i == 5 or i == 7 or i == 9:
uuid.append('-')
return "".join(uuid)
def iface_in_use_by(conn, name):
use_str = ""
for i in conn.list_interface_names():
iface = conn.get_interface(i)
if name in iface.get_slave_names():
if use_str:
use_str += ", "
use_str += iface.get_name()
return use_str
def pretty_mem(val):
val = int(val)
if val > (10 * 1024 * 1024):
return "%2.2f GB" % (val / (1024.0 * 1024.0))
else:
return "%2.0f MB" % (val / 1024.0)
def pretty_bytes(val):
val = int(val)
if val > (1024 * 1024 * 1024):
return "%2.2f GB" % (val / (1024.0 * 1024.0 * 1024.0))
else:
return "%2.2f MB" % (val / (1024.0 * 1024.0))
xpath = virtinst.util.get_xml_path
def chkbox_helper(src, getcb, setcb, text1, text2=None,
alwaysrecord=False,
default=True,
chktext=_("Don't ask me again")):
"""
Helper to prompt user about proceeding with an operation
Returns True if the 'yes' or 'ok' button was selected, False otherwise
@alwaysrecord: Don't require user to select 'yes' to record chkbox value
@default: What value to return if getcb tells us not to prompt
"""
from gi.repository import Gtk # pylint: disable=E0611
do_prompt = getcb()
if not do_prompt:
return default
res = src.err.warn_chkbox(text1=text1, text2=text2,
chktext=chktext,
buttons=Gtk.ButtonsType.YES_NO)
response, skip_prompt = res
if alwaysrecord or response:
setcb(not skip_prompt)
return response
def get_list_selection(widget):
selection = widget.get_selection()
active = selection.get_selected()
treestore, treeiter = active
if treeiter is not None:
return treestore[treeiter]
return None
def set_list_selection(widget, rownum):
path = str(rownum)
selection = widget.get_selection()
selection.unselect_all()
widget.set_cursor(path)
selection.select_path(path)