details: Reword the CPU model UI a bit more

- Add options in the model drop down for clear, hvdefault, and app default
- Make 'copy host' a check box, when enabled it hides the model dropdown
- Detect if the VM CPU is a copy of the host CPU
- Undocumented bit that allows passing in host-model/host-passthrough
    to the model field for people that really want those settings.
This commit is contained in:
Cole Robinson 2014-01-31 13:40:09 -05:00
parent 021ffd17e2
commit 90c9b3ca2e
5 changed files with 146 additions and 196 deletions

View File

@ -1738,132 +1738,61 @@
<property name="left_padding">21</property>
<property name="right_padding">12</property>
<child>
<object class="GtkVBox" id="vbox220">
<object class="GtkGrid" id="table15">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="spacing">6</property>
<property name="halign">start</property>
<property name="valign">start</property>
<property name="row_spacing">3</property>
<property name="column_spacing">12</property>
<child>
<object class="GtkTable" id="table15">
<object class="GtkLabel" id="label52">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="halign">start</property>
<property name="valign">start</property>
<property name="n_rows">2</property>
<property name="n_columns">2</property>
<property name="column_spacing">12</property>
<property name="row_spacing">3</property>
<child>
<object class="GtkLabel" id="label52">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="xalign">0</property>
<property name="label" translatable="yes">Model:</property>
<property name="xalign">0</property>
<property name="label" translatable="yes">Model:</property>
</object>
<packing>
<property name="left_attach">0</property>
<property name="top_attach">1</property>
<property name="width">1</property>
<property name="height">1</property>
</packing>
</child>
<child>
<object class="GtkCheckButton" id="cpu-copy-host">
<property name="label" translatable="yes">Copy host CPU configuration</property>
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="receives_default">False</property>
<property name="xalign">0</property>
<property name="draw_indicator">True</property>
<signal name="clicked" handler="on_cpu_copy_host_clicked" swapped="no"/>
</object>
<packing>
<property name="left_attach">0</property>
<property name="top_attach">0</property>
<property name="width">2</property>
<property name="height">1</property>
</packing>
</child>
<child>
<object class="GtkComboBox" id="cpu-model">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="has_entry">True</property>
<signal name="changed" handler="on_cpu_model_changed" swapped="no"/>
<child internal-child="entry">
<object class="GtkEntry" id="combobox-entry33">
<property name="can_focus">True</property>
</object>
<packing>
<property name="x_options">GTK_FILL</property>
<property name="y_options">GTK_FILL</property>
</packing>
</child>
<child>
<object class="GtkHBox" id="hbox21">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="spacing">20</property>
<child>
<object class="GtkAlignment" id="alignment33">
<property name="visible">True</property>
<property name="can_focus">False</property>
<child>
<object class="GtkComboBox" id="cpu-model">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="has_entry">True</property>
<signal name="changed" handler="on_cpu_model_changed" swapped="no"/>
<child internal-child="entry">
<object class="GtkEntry" id="combobox-entry33">
<property name="can_focus">False</property>
</object>
</child>
</object>
</child>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">False</property>
<property name="position">1</property>
</packing>
</child>
</object>
<packing>
<property name="left_attach">1</property>
<property name="right_attach">2</property>
<property name="y_options">GTK_FILL</property>
</packing>
</child>
<child>
<object class="GtkAlignment" id="alignment37">
<property name="visible">True</property>
<property name="can_focus">False</property>
<child>
<placeholder/>
</child>
</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">GTK_FILL</property>
</packing>
</child>
<child>
<object class="GtkHBox" id="hbox25">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="spacing">6</property>
<child>
<object class="GtkButton" id="cpu-copy-host">
<property name="label" translatable="yes">Copy host CPU configuration</property>
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="receives_default">True</property>
<property name="valign">start</property>
<signal name="clicked" handler="on_cpu_copy_host_clicked" swapped="no"/>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="position">0</property>
</packing>
</child>
<child>
<object class="GtkButton" id="cpu-clear">
<property name="label" translatable="yes">Clear CPU configuration</property>
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="receives_default">True</property>
<signal name="clicked" handler="on_cpu_clear_clicked" swapped="no"/>
</object>
<packing>
<property name="expand">True</property>
<property name="fill">True</property>
<property name="position">1</property>
</packing>
</child>
</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="x_options">GTK_FILL</property>
<property name="y_options">GTK_FILL</property>
</packing>
</child>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="position">0</property>
<property name="left_attach">1</property>
<property name="top_attach">1</property>
<property name="width">1</property>
<property name="height">1</property>
</packing>
</child>
</object>

View File

@ -378,7 +378,6 @@ class vmmDetails(vmmGObjectUI):
self.ignorePause = False
self.ignoreDetails = False
self._cpu_copy_host = False
from virtManager.console import vmmConsolePages
self.console = vmmConsolePages(self.vm, self.builder, self.topwin)
@ -481,11 +480,10 @@ class vmmDetails(vmmGObjectUI):
"on_config_vcpupin_changed": lambda *x: self.enable_apply(x, EDIT_CPUSET),
"on_config_vcpupin_generate_clicked": self.config_vcpupin_generate,
"on_cpu_model_changed": lambda *x: self.enable_apply(x, EDIT_CPU),
"on_cpu_copy_host_clicked": self.on_cpu_copy_host_clicked,
"on_cpu_cores_changed": lambda *x: self.enable_apply(x, EDIT_TOPOLOGY),
"on_cpu_sockets_changed": lambda *x: self.enable_apply(x, EDIT_TOPOLOGY),
"on_cpu_threads_changed": lambda *x: self.enable_apply(x, EDIT_TOPOLOGY),
"on_cpu_copy_host_clicked": self.config_cpu_copy_host,
"on_cpu_clear_clicked": self.config_cpu_clear,
"on_cpu_topology_enable_toggled": self.config_cpu_topology_enable,
"on_config_memory_changed": self.config_memory_changed,
@ -860,20 +858,27 @@ class vmmDetails(vmmGObjectUI):
try:
cpu_values = caps.get_cpu_values(self.vm.get_arch())
cpu_names = sorted([c.model for c in cpu_values.cpus],
key=str.lower)
except:
logging.exception("Error populating CPU model list")
# CPU model combo
cpu_model = self.widget("cpu-model")
model = Gtk.ListStore(str, object)
def sep_func(model, it, ignore):
return model[it][3]
# [label, sortkey, idstring, is sep]
model = Gtk.ListStore(str, str, str, bool)
cpu_model.set_model(model)
cpu_model.set_entry_text_column(0)
model.set_sort_column_id(0, Gtk.SortType.ASCENDING)
for name in cpu_names:
model.append([name, cpu_values.get_cpu(name)])
cpu_model.set_row_separator_func(sep_func, None)
model.set_sort_column_id(2, Gtk.SortType.ASCENDING)
model.append([_("Application Default"), "1", "appdefault", False])
model.append([_("Hypervisor Default"), "2", "hypdefault", False])
model.append([_("Clear CPU configuration"), "3", "clearcpu", False])
model.append([None, None, None, True])
for name in [c.model for c in cpu_values.cpus]:
model.append([name, name, name, False])
# Disk cache combo
disk_cache = self.widget("disk-cache")
@ -1420,13 +1425,45 @@ class vmmDetails(vmmGObjectUI):
def get_config_cpu_model(self):
cpu_list = self.widget("cpu-model")
model = cpu_list.get_child().get_text()
text = cpu_list.get_child().get_text()
model = None
mode = None
copy_host = bool(self.widget("cpu-copy-host").get_active())
key = None
for row in cpu_list.get_model():
if model == row[0]:
return model, row[1].vendor
row = None
for r in cpu_list.get_model():
if text == r[0]:
row = r
break
if row:
key = row[2]
elif text == "host-model" or text == "host-passthrough":
mode = text
else:
model = text
if key == "hypdefault" or key == "clearcpu":
# Clear the whole CPU
pass
elif key == "appdefault":
cpu_type = self.config.get_default_cpu_setting()
if cpu_type == "hv-default":
pass
elif cpu_type == "host-cpu-model":
if self.vm.conn.caps.host.cpu.model:
model = self.vm.conn.caps.host.cpu.model
elif cpu_type == "host-model":
copy_host = True
else:
raise RuntimeError("Unknown cpu default '%s'" % cpu_type)
elif key:
model = key
return model, mode, copy_host
return model, None
##############################
# Details/Hardware listeners #
@ -1537,27 +1574,10 @@ class vmmDetails(vmmGObjectUI):
def config_maxvcpus_changed(self, ignore):
self.enable_apply(EDIT_VCPUS)
def config_cpu_copy_host(self, src_ignore):
# Update UI with output copied from host
try:
CPU = virtinst.CPU(self.vm.conn.get_backend())
CPU.copy_host_cpu()
self._refresh_cpu_config(CPU)
self._cpu_copy_host = True
except Exception, e:
self.err.show_err(_("Error copying host CPU: %s") % str(e))
return
def config_cpu_clear(self, src_ignore):
try:
CPU = virtinst.CPU(self.vm.conn.get_backend())
CPU.clear()
self._refresh_cpu_config(CPU)
except Exception, e:
self.err.show_err(_("Error clear CPU config: %s") % str(e))
return
def on_cpu_copy_host_clicked(self, src):
uiutil.set_grid_row_visible(
self.widget("cpu-model"), not src.get_active())
self.enable_apply(EDIT_CPU)
def config_cpu_topology_enable(self, src):
do_enable = src.get_active()
@ -1829,9 +1849,9 @@ class vmmDetails(vmmGObjectUI):
add_define(self.vm.define_cpuset, cpuset)
if self.edited(EDIT_CPU):
model, vendor = self.get_config_cpu_model()
model, mode, copy_host = self.get_config_cpu_model()
add_define(self.vm.define_cpu,
model, vendor, self._cpu_copy_host)
model, mode, copy_host)
if self.edited(EDIT_TOPOLOGY):
do_top = self.widget("cpu-topology-enable").get_active()
@ -1845,10 +1865,7 @@ class vmmDetails(vmmGObjectUI):
add_define(self.vm.define_cpu_topology, sockets, cores, threads)
ret = self._change_config_helper(df, da, hf, ha)
if ret:
self._cpu_copy_host = False
return ret
return self._change_config_helper(df, da, hf, ha)
# Memory
def config_memory_apply(self):
@ -2376,9 +2393,9 @@ class vmmDetails(vmmGObjectUI):
self.disk_io_graph.set_property("data_array",
self.vm.disk_io_vector())
self.network_traffic_graph.set_property("data_array",
self.vm.network_traffic_vector())
self.vm.network_traffic_vector())
def _refresh_cpu_count(self):
def refresh_config_cpu(self):
conn = self.vm.conn
host_active_count = conn.host_active_processor_count()
maxvcpus = self.vm.vcpu_max_count()
@ -2395,39 +2412,44 @@ class vmmDetails(vmmGObjectUI):
warn = bool(self.config_get_vcpus() > host_active_count)
self.widget("config-vcpus-warn-box").set_visible(warn)
def _refresh_cpu_config(self, cpu):
model = cpu.model or ""
caps = self.vm.conn.caps
capscpu = None
try:
arch = self.vm.get_arch()
if arch:
cpu_values = caps.get_cpu_values(arch)
for c in cpu_values.cpus:
if model and c.model == model:
capscpu = c
break
except:
pass
# CPU model config
cpu = self.vm.get_cpu_config()
show_top = bool(cpu.sockets or cpu.cores or cpu.threads)
sockets = cpu.sockets or 1
cores = cpu.cores or 1
threads = cpu.threads or 1
self.widget("cpu-topology-enable").set_active(show_top)
self.widget("cpu-model").get_child().set_text(model)
self.widget("cpu-sockets").set_value(sockets)
self.widget("cpu-cores").set_value(cores)
self.widget("cpu-threads").set_value(threads)
def refresh_config_cpu(self):
self._cpu_copy_host = False
cpu = self.vm.get_cpu_config()
model = cpu.model or None
if not model:
if cpu.mode == "host-model" or cpu.mode == "host-passthrough":
model = cpu.mode
self._refresh_cpu_count()
self._refresh_cpu_config(cpu)
if model:
self.widget("cpu-model").get_child().set_text(model)
else:
uiutil.set_combo_entry(
self.widget("cpu-model"), "hypdefault", 2)
# Determine if CPU definition is just the host copy
hostcpu = self.conn.caps.host.cpu
is_host = False
if (hostcpu.model and
cpu.model == hostcpu.model and
len(cpu.features) == len(hostcpu.features.names())):
is_host = True
for feature in cpu.features:
if (feature.policy != "require" or
feature.name not in hostcpu.features.names()):
is_host = False
break
self.widget("cpu-copy-host").set_active(bool(is_host))
self.on_cpu_copy_host_clicked(self.widget("cpu-copy-host"))
def refresh_config_memory(self):
host_mem_widget = self.widget("state-host-memory")

View File

@ -535,21 +535,18 @@ class vmmDomain(vmmLibvirtObject):
cpu.cores = cores
cpu.threads = threads
return self._redefine(change)
def define_cpu(self, model, vendor, from_host):
def define_cpu(self, model, mode, copy_host):
def change(guest):
if from_host:
if copy_host:
guest.cpu.copy_host_cpu()
elif guest.cpu.model != model:
# Since we don't expose this in the UI, have host value trump
# caps value
guest.cpu.vendor = vendor
guest.cpu.model = model or None
if guest.cpu.model is None:
for f in guest.cpu.features:
guest.cpu.remove_feature(f)
return
elif mode:
guest.cpu.clear()
guest.cpu.mode = mode
elif model:
guest.cpu.model = model
guest.cpu.vendor = None
else:
guest.cpu.clear()
return self._redefine(change)
# Mem define methods

View File

@ -124,7 +124,7 @@ def set_row_selection(listwidget, prevkey):
def set_combo_entry(combo, value, rowidx=0):
"""
Search the passed combobox for value, comparing againt
Search the passed combobox for value, comparing against
rowidx. If found, select it. If not found, and
the combobox has a text entry, stick the value in their and
select it.

View File

@ -132,6 +132,8 @@ class CPUValues(object):
child = child.next
def get_arch(self, arch):
if not arch:
return None
if re.match(r'i[4-9]86', arch):
arch = "x86"
elif arch == "x86_64":