connection: Allow setting a custom 'pretty name' (bz 784701)

We've had multiple requests over the years for something similar. People
might have to connect to multiple IP addresses, or really large hostnames,
that become difficult to distinguish in the UI.

Add a field in the host details page that allows setting a custom name,
and store it in gsettings.
This commit is contained in:
Cole Robinson 2015-04-11 13:39:25 -04:00
parent 4781ad6cd6
commit 3d2afbaf6f
6 changed files with 157 additions and 64 deletions

View File

@ -27,6 +27,17 @@
</key>
</schema>
<!-- Relocatable per conn schema -->
<schema id="org.virt-manager.virt-manager.connection">
<key name="pretty-name" type="s">
<default>''</default>
<summary>Custom connection description</summary>
<description>Custom connection description, used in the manager window. If empty, the app generates a default on demand.</description>
</key>
</schema>
<schema id="org.virt-manager.virt-manager"
path="/org/virt-manager/virt-manager/">

View File

@ -130,14 +130,12 @@
<property name="can_focus">False</property>
<property name="left_padding">20</property>
<child>
<object class="GtkTable" id="table1">
<object class="GtkGrid" id="table1">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="border_width">3</property>
<property name="n_rows">4</property>
<property name="n_columns">2</property>
<property name="column_spacing">6</property>
<property name="row_spacing">3</property>
<property name="column_spacing">6</property>
<child>
<object class="GtkLabel" id="label6">
<property name="visible">True</property>
@ -146,10 +144,8 @@
<property name="xalign">1</property>
</object>
<packing>
<property name="top_attach">1</property>
<property name="bottom_attach">2</property>
<property name="x_options">GTK_FILL</property>
<property name="y_options"/>
<property name="left_attach">0</property>
<property name="top_attach">2</property>
</packing>
</child>
<child>
@ -160,10 +156,8 @@
<property name="xalign">1</property>
</object>
<packing>
<property name="top_attach">2</property>
<property name="bottom_attach">3</property>
<property name="x_options">GTK_FILL</property>
<property name="y_options"/>
<property name="left_attach">0</property>
<property name="top_attach">3</property>
</packing>
</child>
<child>
@ -177,10 +171,7 @@
</object>
<packing>
<property name="left_attach">1</property>
<property name="right_attach">2</property>
<property name="top_attach">1</property>
<property name="bottom_attach">2</property>
<property name="y_options"/>
<property name="top_attach">2</property>
</packing>
</child>
<child>
@ -192,23 +183,20 @@
</object>
<packing>
<property name="left_attach">1</property>
<property name="right_attach">2</property>
<property name="top_attach">2</property>
<property name="bottom_attach">3</property>
<property name="y_options"/>
<property name="top_attach">3</property>
</packing>
</child>
<child>
<object class="GtkLabel" id="label71">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="label" translatable="yes">Connection URI:</property>
<property name="label" translatable="yes">Libvirt URI:</property>
<property name="lines">1</property>
<property name="xalign">1</property>
</object>
<packing>
<property name="x_options">GTK_FILL</property>
<property name="y_options"/>
<property name="left_attach">0</property>
<property name="top_attach">1</property>
</packing>
</child>
<child>
@ -222,25 +210,7 @@
</object>
<packing>
<property name="left_attach">1</property>
<property name="right_attach">2</property>
<property name="x_options">GTK_FILL</property>
<property name="y_options"/>
</packing>
</child>
<child>
<object class="GtkLabel" id="label72">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="label" translatable="yes">A_utoconnect:</property>
<property name="use_underline">True</property>
<property name="mnemonic_widget">config-autoconnect</property>
<property name="xalign">1</property>
</object>
<packing>
<property name="top_attach">3</property>
<property name="bottom_attach">4</property>
<property name="x_options">GTK_FILL</property>
<property name="y_options"/>
<property name="top_attach">1</property>
</packing>
</child>
<child>
@ -255,11 +225,48 @@
</object>
<packing>
<property name="left_attach">1</property>
<property name="right_attach">2</property>
<property name="top_attach">3</property>
<property name="bottom_attach">4</property>
<property name="x_options">GTK_FILL</property>
<property name="y_options"/>
<property name="top_attach">4</property>
</packing>
</child>
<child>
<object class="GtkEntry" id="overview-name">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="max_width_chars">30</property>
<signal name="changed" handler="on_overview_name_changed" swapped="no"/>
</object>
<packing>
<property name="left_attach">1</property>
<property name="top_attach">0</property>
</packing>
</child>
<child>
<object class="GtkLabel" id="label8">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="label" translatable="yes">_Name:</property>
<property name="use_underline">True</property>
<property name="mnemonic_widget">overview-name</property>
<property name="lines">1</property>
<property name="xalign">1</property>
</object>
<packing>
<property name="left_attach">0</property>
<property name="top_attach">0</property>
</packing>
</child>
<child>
<object class="GtkLabel" id="label72">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="label" translatable="yes">A_utoconnect:</property>
<property name="use_underline">True</property>
<property name="mnemonic_widget">config-autoconnect</property>
<property name="xalign">1</property>
</object>
<packing>
<property name="left_attach">0</property>
<property name="top_attach">4</property>
</packing>
</child>
</object>

View File

@ -50,15 +50,32 @@ class SettingsWrapper(object):
return settingskey, value
def make_vm_settings(self, key):
"""
Initialize per-VM relocatable schema if necessary
"""
settingskey = self._parse_key(key)[0]
if settingskey in self._settingsmap:
return True
schema = self._root + ".vm"
path = "/" + self._root.replace(".", "/") + key.rsplit("/", 1)[0] + "/"
self._settingsmap[settingskey] = Gio.Settings.new_with_path(schema,
path)
self._settingsmap[settingskey] = Gio.Settings.new_with_path(
schema, path)
return True
def make_conn_settings(self, key):
"""
Initialize per-conn relocatable schema if necessary
"""
settingskey = self._parse_key(key)[0]
if settingskey in self._settingsmap:
return True
schema = self._root + ".connection"
path = "/" + self._root.replace(".", "/") + key.rsplit("/", 1)[0] + "/"
print schema, path
self._settingsmap[settingskey] = Gio.Settings.new_with_path(
schema, path)
return True
def _find_settings(self, key):
@ -212,6 +229,11 @@ class vmmConfig(object):
def get_objects(self):
return self._objects[:]
#####################################
# Wrappers for setting per-VM value #
#####################################
def _make_pervm_key(self, uuid, key):
return "/vms/%s%s" % (uuid.replace("-", ""), key)
@ -232,6 +254,30 @@ class vmmConfig(object):
return self.conf.get(key)
########################################
# Wrappers for setting per-conn values #
########################################
def _make_perconn_key(self, uri, key):
return "/conns/%s%s" % (uri.replace("/", ""), key)
def listen_perconn(self, uri, key, *args, **kwargs):
key = self._make_perconn_key(uri, key)
self.conf.make_conn_settings(key)
return self.conf.notify_add(key, *args, **kwargs)
def set_perconn(self, uri, key, *args, **kwargs):
key = self._make_perconn_key(uri, key)
self.conf.make_conn_settings(key)
ret = self.conf.set(key, *args, **kwargs)
return ret
def get_perconn(self, uri, key):
key = self._make_perconn_key(uri, key)
self.conf.make_conn_settings(key)
return self.conf.get(key)
###################
# General helpers #
###################

View File

@ -191,6 +191,10 @@ class vmmConnection(vmmGObject):
self.record = []
self.hostinfo = None
self.add_gsettings_handle(
self._on_config_pretty_name_changed(
self._config_pretty_name_changed_cb))
self._init_virtconn()
@ -368,6 +372,8 @@ class vmmConnection(vmmGObject):
Return a pretty label for use in the manager view, and various
connection lists.
"""
if self._get_config_pretty_name():
return self._get_config_pretty_name()
if self._backend.fake_name():
return self._backend.fake_name()
@ -811,11 +817,6 @@ class vmmConnection(vmmGObject):
# Connection closing/opening methods #
######################################
def get_autoconnect(self):
return self.config.get_conn_autoconnect(self.get_uri())
def set_autoconnect(self, val):
self.config.set_conn_autoconnect(self.get_uri(), val)
def _schedule_close(self):
self._closing = True
self.idle_add(self.close)
@ -1351,3 +1352,23 @@ class vmmConnection(vmmGObject):
return self.disk_read_rate() + self.disk_write_rate()
def disk_io_max_rate(self):
return self._get_record_helper("diskMaxRate")
###########################
# Per-conn config helpers #
###########################
def get_autoconnect(self):
return self.config.get_conn_autoconnect(self.get_uri())
def set_autoconnect(self, val):
self.config.set_conn_autoconnect(self.get_uri(), val)
def set_config_pretty_name(self, value):
self.config.set_perconn(self.get_uri(), "/pretty-name", value)
def _get_config_pretty_name(self):
return self.config.get_perconn(self.get_uri(), "/pretty-name")
def _on_config_pretty_name_changed(self, *args, **kwargs):
return self.config.listen_perconn(self.get_uri(), "/pretty-name",
*args, **kwargs)
def _config_pretty_name_changed_cb(self):
self.emit("state-changed")

View File

@ -60,9 +60,7 @@ class vmmHost(vmmGObjectUI):
vmmGObjectUI.__init__(self, "host.ui", "vmm-host")
self.conn = conn
self.title = conn.get_pretty_desc() + " " + self.topwin.get_title()
self.topwin.set_title(self.title)
self._orig_title = self.topwin.get_title()
self.ICON_RUNNING = "state_running"
self.ICON_SHUTOFF = "state_shutoff"
@ -107,6 +105,7 @@ class vmmHost(vmmGObjectUI):
"on_interface_apply_clicked" : (lambda *x: self.interface_apply()),
"on_interface_list_changed": self.interface_selected,
"on_overview_name_changed": self._overview_name_changed,
"on_config_autoconnect_toggled": self.toggle_autoconnect,
"on_qos_inbound_average_changed": (lambda *x:
@ -325,6 +324,12 @@ class vmmHost(vmmGObjectUI):
def conn_state_changed(self, ignore1=None):
conn_active = self.conn.is_active()
self.topwin.set_title(
self.conn.get_pretty_desc() + " " + self._orig_title)
if not self.widget("overview-name").has_focus():
self.widget("overview-name").set_text(self.conn.get_pretty_desc())
self.widget("menu_file_restore_saved").set_sensitive(conn_active)
self.widget("net-add").set_sensitive(conn_active and
self.conn.is_network_capable())
@ -356,13 +361,17 @@ class vmmHost(vmmGObjectUI):
if self.addnet:
self.addnet.close()
def _overview_name_changed(self, src):
src = self.widget("overview-name")
self.conn.set_config_pretty_name(src.get_text())
def toggle_autoconnect(self, src):
self.conn.set_autoconnect(src.get_active())
# -------------------------
# Virtual Network functions
# -------------------------
#############################
# Virtual Network functions #
#############################
def delete_network(self, src_ignore):
net = self.current_network()

View File

@ -804,10 +804,9 @@ class vmmManager(vmmGObjectUI):
it = model.iter_next(it)
def conn_state_changed(self, conn, newname=None):
def conn_state_changed(self, conn):
row = self.rows[conn.get_uri()]
if newname:
row[ROW_SORT_KEY] = newname
row[ROW_SORT_KEY] = conn.get_pretty_desc()
row[ROW_MARKUP] = self._build_conn_markup(conn, row[ROW_SORT_KEY])
row[ROW_IS_CONN_CONNECTED] = not conn.is_disconnected()
row[ROW_COLOR] = self._build_conn_color(conn)