details: Allow renaming offline VMs

Since libvirt doesn't have an easy way to do this, we need to actually
undefine the VM and redefine it with the new name. Hopefully this doesn't
go wrong :)

Add listeners in console.py and manager.py to change the VM name accordingly
in the UI.
This commit is contained in:
Cole Robinson 2011-03-23 16:56:12 -04:00
parent ca5bd0de0a
commit 148229375a
6 changed files with 128 additions and 44 deletions

View File

@ -748,8 +748,35 @@ class vmmConnection(vmmGObject):
return net
def rename_vm(self, domainobj, origxml, newxml):
# Undefine old domain
domainobj.delete()
newobj = None
try:
try:
# Redefine new domain
newobj = self.define_domain(newxml)
except Exception, renameerr:
try:
logging.exception("Error defining new name XML")
newobj = self.define_domain(origxml)
except Exception, fixerr:
logging.exception("Failed to redefine original domain!")
raise RuntimeError(
_("Domain rename failed. Attempting to recover also "
"failed.\n\n"
"Original error: %s\n\n"
"Recover error: %s" %
(str(renameerr), str(fixerr))))
raise
finally:
if newobj:
# Reinsert handle into new domain
domainobj.change_name_backend(newobj)
def define_domain(self, xml):
self.vmm.defineXML(xml)
return self.vmm.defineXML(xml)
def define_interface(self, xml):
self.vmm.interfaceDefineXML(xml, 0)

View File

@ -485,8 +485,9 @@ class vmmConsolePages(vmmGObjectUI):
self.topwin = self.window.get_widget(self.windowname)
self.err = vmmErrorDialog(self.topwin)
self.title = vm.get_name() + " " + self.topwin.get_title()
self.topwin.set_title(self.title)
self.pointer_is_grabbed = False
self.change_title()
self.vm.connect("config-changed", self.change_title)
# State for disabling modifiers when keyboard is grabbed
self.accel_groups = gtk.accel_groups_from_object(self.topwin)
@ -533,6 +534,17 @@ class vmmConsolePages(vmmGObjectUI):
# Initialization helpers #
##########################
def change_title(self, ignore1=None):
title = self.vm.get_name() + " " + _("Virtual Machine")
if self.pointer_is_grabbed and self.viewer:
keystr = self.viewer.get_grab_keys()
keymsg = _("Press %s to release pointer.") % keystr
title = keymsg + " " + title
self.topwin.set_title(title)
def viewer_focus_changed(self, ignore1=None, ignore2=None):
has_focus = self.viewer and self.viewer.get_widget() and \
self.viewer.get_widget().get_property("has-focus")
@ -546,12 +558,12 @@ class vmmConsolePages(vmmGObjectUI):
self._enable_modifiers()
def pointer_grabbed(self, src_ignore):
keystr = self.viewer.get_grab_keys()
self.topwin.set_title(_("Press %s to release pointer.") % keystr +
" " + self.title)
self.pointer_is_grabbed = True
self.change_title()
def pointer_ungrabbed(self, src_ignore):
self.topwin.set_title(self.title)
self.pointer_is_grabbed = False
self.change_title()
def _disable_modifiers(self):
if self.gtk_settings_accel is not None:

View File

@ -336,6 +336,7 @@ class vmmDetails(vmmGObjectUI):
"on_details_pages_switch_page": self.switch_page,
"on_overview_name_changed": self.config_enable_apply,
"on_overview_acpi_changed": self.config_enable_apply,
"on_overview_apic_changed": self.config_enable_apply,
"on_overview_clock_changed": self.config_enable_apply,
@ -1093,6 +1094,7 @@ class vmmDetails(vmmGObjectUI):
self.toggle_toolbar(
self.window.get_widget("details-menu-view-toolbar"))
active = vm.is_active()
destroy = vm.is_destroyable()
run = vm.is_runable()
stop = vm.is_stoppable()
@ -1121,6 +1123,8 @@ class vmmDetails(vmmGObjectUI):
finally:
self.ignorePause = False
self.window.get_widget("overview-name").set_editable(not active)
self.window.get_widget("config-vcpus").set_sensitive(not ro)
self.window.get_widget("config-vcpupin").set_sensitive(not ro)
self.window.get_widget("config-memory").set_sensitive(not ro)
@ -1610,6 +1614,9 @@ class vmmDetails(vmmGObjectUI):
# Overview section
def config_overview_apply(self):
# Overview details
name = self.window.get_widget("overview-name").get_text()
# Machine details
enable_acpi = self.window.get_widget("overview-acpi").get_active()
enable_apic = self.window.get_widget("overview-apic").get_active()
@ -1635,12 +1642,14 @@ class vmmDetails(vmmGObjectUI):
desc_widget = self.window.get_widget("overview-description")
desc = desc_widget.get_buffer().get_property("text") or ""
return self._change_config_helper([self.vm.define_acpi,
return self._change_config_helper([self.vm.define_name,
self.vm.define_acpi,
self.vm.define_apic,
self.vm.define_clock,
self.vm.define_seclabel,
self.vm.define_description],
[(enable_acpi,),
[(name,),
(enable_acpi,),
(enable_apic,),
(clock,),
(semodel, setype, selabel),

View File

@ -158,6 +158,10 @@ class vmmDomainBase(vmmLibvirtObject):
self._stats_disk_supported = True
self._stats_disk_skip = []
def change_name_backend(self, newbackend):
# Used for changing the domain object after a rename
self._backend = newbackend
# Info accessors
def get_name(self):
raise NotImplementedError()
@ -350,6 +354,29 @@ class vmmDomainBase(vmmLibvirtObject):
return self._redefine_guest(change)
def define_name(self, newname):
# Do this, so that _guest_to_define has original inactive XML
self._invalidate_xml()
guest = self._get_guest_to_define()
if guest.name == newname:
return
if self.is_active():
raise RuntimeError(_("Cannot rename an active guest"))
logging.debug("Changing guest name to '%s'" % newname)
origxml = guest.get_xml_config()
guest.name = newname
newxml = guest.get_xml_config()
try:
self.get_connection().rename_vm(self, origxml, newxml)
finally:
self._invalidate_xml()
self.emit("config-changed")
def define_acpi(self, newvalue):
def change(guest):
guest.features["acpi"] = bool(newvalue)

View File

@ -697,6 +697,7 @@ class vmmManager(vmmGObjectUI):
vm = connection.get_vm(vmuuid)
vm.connect("status-changed", self.vm_status_changed)
vm.connect("resources-sampled", self.vm_resources_sampled)
vm.connect("config-changed", self.vm_resources_sampled)
vmlist = self.window.get_widget("vm-list")
model = vmlist.get_model()
@ -763,12 +764,7 @@ class vmmManager(vmmGObjectUI):
statetext = "<span size='smaller'>%s</span>" % row[ROW_STATUS]
return domtext + "\n" + statetext
def _append_vm(self, model, vm, conn):
row_key = self.vm_row_key(vm)
if row_key in self.rows:
return
parent = self.rows[conn.get_uri()].iter
def _build_vm_row(self, vm):
row = []
row.insert(ROW_HANDLE, vm)
row.insert(ROW_NAME, vm.get_name())
@ -785,11 +781,23 @@ class vmmManager(vmmGObjectUI):
row[ROW_MARKUP] = self._build_vm_markup(vm, row)
return row
def _append_vm(self, model, vm, conn):
row_key = self.vm_row_key(vm)
if row_key in self.rows:
return
row = self._build_vm_row(vm)
parent = self.rows[conn.get_uri()].iter
_iter = model.append(parent, row)
path = model.get_path(_iter)
self.rows[row_key] = model[path]
# Expand a connection when adding a vm to it
self.window.get_widget("vm-list").expand_row(model.get_path(parent), False)
self.window.get_widget("vm-list").expand_row(model.get_path(parent),
False)
def _append_connection(self, model, conn):
row = []
@ -894,6 +902,7 @@ class vmmManager(vmmGObjectUI):
return
row = self.rows[self.vm_row_key(vm)]
row[ROW_NAME] = vm.get_name()
row[ROW_STATUS] = vm.run_status()
row[ROW_STATUS_ICON] = vm.run_status_icon_large()
row[ROW_IS_VM_RUNNING] = vm.is_active()

View File

@ -1008,7 +1008,9 @@
<widget class="GtkLabel" id="label43">
<property name="visible">True</property>
<property name="xalign">0</property>
<property name="label" translatable="yes">Name:</property>
<property name="label" translatable="yes">_Name:</property>
<property name="use_underline">True</property>
<property name="mnemonic_widget">overview-name</property>
</widget>
<packing>
<property name="x_options">GTK_FILL</property>
@ -1065,20 +1067,6 @@
<property name="y_options"></property>
</packing>
</child>
<child>
<widget class="GtkLabel" id="overview-name">
<property name="visible">True</property>
<property name="xalign">0</property>
<property name="label">test-vm</property>
<property name="selectable">True</property>
</widget>
<packing>
<property name="left_attach">1</property>
<property name="right_attach">2</property>
<property name="x_options">GTK_FILL</property>
<property name="y_options">GTK_FILL</property>
</packing>
</child>
<child>
<widget class="GtkLabel" id="label24">
<property name="visible">True</property>
@ -1127,6 +1115,18 @@
<property name="x_options">GTK_FILL</property>
</packing>
</child>
<child>
<widget class="GtkEntry" id="overview-name">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="invisible_char">●</property>
<signal name="changed" handler="on_overview_name_changed"/>
</widget>
<packing>
<property name="left_attach">1</property>
<property name="right_attach">2</property>
</packing>
</child>
</widget>
</child>
</widget>
@ -3785,7 +3785,7 @@ I/O:</property>
<widget class="GtkVBox" id="network-source-box">
<property name="visible">True</property>
<child>
<widget class="GtkHBox" id="hbox19">
<widget class="GtkHBox" id="hbox7">
<property name="visible">True</property>
<child>
<widget class="GtkComboBox" id="network-source-combo">
@ -4039,7 +4039,7 @@ I/O:</property>
</packing>
</child>
<child>
<widget class="GtkAlignment" id="alignment29">
<widget class="GtkAlignment" id="alignment5">
<property name="visible">True</property>
<child>
<placeholder/>
@ -4052,7 +4052,7 @@ I/O:</property>
</widget>
</child>
<child>
<widget class="GtkLabel" id="label5">
<widget class="GtkLabel" id="label6">
<property name="visible">True</property>
<property name="label" translatable="yes">Virtual port</property>
<property name="use_markup">True</property>
@ -4783,7 +4783,7 @@ I/O:</property>
<property name="label_xalign">0</property>
<property name="shadow_type">none</property>
<child>
<widget class="GtkAlignment" id="alignment5">
<widget class="GtkAlignment" id="alignment10">
<property name="visible">True</property>
<property name="top_padding">3</property>
<property name="left_padding">12</property>
@ -4843,7 +4843,7 @@ I/O:</property>
</packing>
</child>
<child>
<widget class="GtkLabel" id="label6">
<widget class="GtkLabel" id="label15">
<property name="visible">True</property>
<property name="label">phy</property>
</widget>
@ -4862,7 +4862,7 @@ I/O:</property>
<property name="label_xalign">0</property>
<property name="shadow_type">none</property>
<child>
<widget class="GtkAlignment" id="alignment10">
<widget class="GtkAlignment" id="alignment22">
<property name="visible">True</property>
<property name="top_padding">3</property>
<property name="left_padding">12</property>
@ -4942,7 +4942,7 @@ I/O:</property>
</packing>
</child>
<child>
<widget class="GtkHBox" id="hbox7">
<widget class="GtkHBox" id="hbox12">
<property name="visible">True</property>
<property name="spacing">6</property>
<child>
@ -5000,7 +5000,7 @@ I/O:</property>
</packing>
</child>
<child>
<widget class="GtkLabel" id="label15">
<widget class="GtkLabel" id="label25">
<property name="visible">True</property>
<property name="label">vid</property>
</widget>
@ -5019,7 +5019,7 @@ I/O:</property>
<property name="label_xalign">0</property>
<property name="shadow_type">none</property>
<child>
<widget class="GtkAlignment" id="alignment22">
<widget class="GtkAlignment" id="alignment30">
<property name="visible">True</property>
<property name="top_padding">3</property>
<property name="left_padding">12</property>
@ -5059,7 +5059,7 @@ I/O:</property>
</packing>
</child>
<child>
<widget class="GtkHBox" id="hbox12">
<widget class="GtkHBox" id="hbox13">
<property name="visible">True</property>
<property name="spacing">6</property>
<child>
@ -5093,7 +5093,7 @@ I/O:</property>
</packing>
</child>
<child>
<widget class="GtkHBox" id="hbox13">
<widget class="GtkHBox" id="hbox20">
<property name="visible">True</property>
<property name="spacing">6</property>
<child>
@ -5149,7 +5149,7 @@ I/O:</property>
</packing>
</child>
<child>
<widget class="GtkLabel" id="label25">
<widget class="GtkLabel" id="label35">
<property name="visible">True</property>
<property name="label">wdog</property>
</widget>
@ -5165,7 +5165,7 @@ I/O:</property>
<property name="label_xalign">0</property>
<property name="shadow_type">none</property>
<child>
<widget class="GtkAlignment" id="alignment30">
<widget class="GtkAlignment" id="alignment34">
<property name="visible">True</property>
<property name="top_padding">3</property>
<property name="left_padding">12</property>