migrate: Add XML editor support

Allows the user to tweak the XML at the destination, which is already
something that libvirt supports

Signed-off-by: Cole Robinson <crobinso@redhat.com>
This commit is contained in:
Cole Robinson 2020-09-02 16:56:08 -04:00
parent 16ebab2230
commit 5e495ebd46
5 changed files with 551 additions and 486 deletions

View File

@ -102,7 +102,6 @@ class VMMMigrate(uiutils.UITestCase):
uiutils.check(lambda: not progwin.showing, timeout=5) uiutils.check(lambda: not progwin.showing, timeout=5)
uiutils.check(lambda: not mig.showing) uiutils.check(lambda: not mig.showing)
def testMigrateConnMismatch(self): def testMigrateConnMismatch(self):
# Add a possible target but disconnect it # Add a possible target but disconnect it
self.app.uri = utils.URIs.test_default self.app.uri = utils.URIs.test_default
@ -120,3 +119,35 @@ class VMMMigrate(uiutils.UITestCase):
mig.find("conn-combo").find("No usable", "menu item") mig.find("conn-combo").find("No usable", "menu item")
mig.keyCombo("<alt>F4") mig.keyCombo("<alt>F4")
uiutils.check(lambda: not mig.showing) uiutils.check(lambda: not mig.showing)
def testMigrateXMLEditor(self):
self.app.open(xmleditor_enabled=True)
manager = self.app.topwin
# Add an additional connection
self._add_conn("test:///default")
# Run it and check some values
vmname = "test-many-devices"
win = self._open_migrate(vmname)
win.find("address-text").set_text("TESTSUITE-FAKE")
# Create a new obj with XML edited name, verify it worked
newname = "aafroofroo"
win.find("XML", "page tab").click()
xmleditor = win.find("XML editor")
newtext = xmleditor.text.replace(
">%s<" % vmname, ">%s<" % newname)
xmleditor.set_text(newtext)
win.find("Migrate", "push button").click()
uiutils.check(lambda: not win.showing, timeout=10)
manager.find(newname, "table cell")
# Do standard xmleditor tests
win = self._open_migrate(vmname)
win.find("address-text").set_text("TESTSUITE-FAKE")
finish = win.find("Migrate", "push button")
self._test_xmleditor_interactions(win, finish)
win.find("Cancel", "push button").click()
uiutils.check(lambda: not win.visible)

View File

@ -265,7 +265,9 @@ def drag(win, x, y):
""" """
Drag a window to the x/y coordinates Drag a window to the x/y coordinates
""" """
win.click() time.sleep(.5)
win.click_title()
time.sleep(.5)
clickX, clickY = _title_coordinates(win) clickX, clickY = _title_coordinates(win)
dogtail.rawinput.drag((clickX, clickY), (x, y)) dogtail.rawinput.drag((clickX, clickY), (x, y))

File diff suppressed because it is too large Load Diff

View File

@ -17,6 +17,7 @@ from .asyncjob import vmmAsyncJob
from .baseclass import vmmGObjectUI from .baseclass import vmmGObjectUI
from .connmanager import vmmConnectionManager from .connmanager import vmmConnectionManager
from .object.domain import vmmDomain from .object.domain import vmmDomain
from .xmleditor import vmmXMLEditor
NUM_COLS = 3 NUM_COLS = 3
@ -40,6 +41,12 @@ class vmmMigrateDialog(vmmGObjectUI):
vmmGObjectUI.__init__(self, "migrate.ui", "vmm-migrate") vmmGObjectUI.__init__(self, "migrate.ui", "vmm-migrate")
self.vm = None self.vm = None
self._xmleditor = vmmXMLEditor(self.builder, self.topwin,
self.widget("details-box-align"),
self.widget("details-box"))
self._xmleditor.connect("xml-requested",
self._xmleditor_xml_requested_cb)
self.builder.connect_signals({ self.builder.connect_signals({
"on_vmm_migrate_delete_event": self._delete_event, "on_vmm_migrate_delete_event": self._delete_event,
"on_migrate_cancel_clicked": self._cancel_clicked, "on_migrate_cancel_clicked": self._cancel_clicked,
@ -58,6 +65,8 @@ class vmmMigrateDialog(vmmGObjectUI):
def _cleanup(self): def _cleanup(self):
self.vm = None self.vm = None
self._xmleditor.cleanup()
self._xmleditor = None
@property @property
def _connobjs(self): def _connobjs(self):
@ -147,6 +156,8 @@ class vmmMigrateDialog(vmmGObjectUI):
self.widget("migrate-temporary-label").get_tooltip_text()) self.widget("migrate-temporary-label").get_tooltip_text())
def _reset_state(self): def _reset_state(self):
self._xmleditor.reset_state()
title_str = _("<span size='large'>Migrate '%(vm)s'</span>") % { title_str = _("<span size='large'>Migrate '%(vm)s'</span>") % {
"vm": xmlutil.xml_escape(self.vm.get_name()), "vm": xmlutil.xml_escape(self.vm.get_name()),
} }
@ -348,6 +359,11 @@ class vmmMigrateDialog(vmmGObjectUI):
def _finish(self): def _finish(self):
try: try:
xml = None
if self._xmleditor.is_xml_selected():
xml = self._xmleditor.get_xml()
log.debug("Using XML from xmleditor:\n%s", xml)
row = uiutil.get_list_selected_row(self.widget("migrate-dest")) row = uiutil.get_list_selected_row(self.widget("migrate-dest"))
destlabel = row[COL_LABEL] destlabel = row[COL_LABEL]
destconn = self._connobjs.get(row[COL_URI]) destconn = self._connobjs.get(row[COL_URI])
@ -378,7 +394,7 @@ class vmmMigrateDialog(vmmGObjectUI):
progWin = vmmAsyncJob( progWin = vmmAsyncJob(
self._async_migrate, self._async_migrate,
[self.vm, destconn, uri, tunnel, unsafe, temporary], [self.vm, destconn, uri, tunnel, unsafe, temporary, xml],
self._finish_cb, [destconn], self._finish_cb, [destconn],
_("Migrating VM '%s'") % self.vm.get_name(), _("Migrating VM '%s'") % self.vm.get_name(),
(_("Migrating VM '%(name)s' to %(host)s. This may take a while.") % (_("Migrating VM '%(name)s' to %(host)s. This may take a while.") %
@ -398,7 +414,7 @@ class vmmMigrateDialog(vmmGObjectUI):
asyncjob.job_canceled = True # pragma: no cover asyncjob.job_canceled = True # pragma: no cover
def _async_migrate(self, asyncjob, def _async_migrate(self, asyncjob,
origvm, origdconn, migrate_uri, tunnel, unsafe, temporary): origvm, origdconn, migrate_uri, tunnel, unsafe, temporary, xml):
meter = asyncjob.get_meter() meter = asyncjob.get_meter()
srcconn = origvm.conn srcconn = origvm.conn
@ -410,5 +426,12 @@ class vmmMigrateDialog(vmmGObjectUI):
log.debug("Migrating vm=%s from %s to %s", vm.get_name(), log.debug("Migrating vm=%s from %s to %s", vm.get_name(),
srcconn.get_uri(), dstconn.get_uri()) srcconn.get_uri(), dstconn.get_uri())
vm.migrate(dstconn, migrate_uri, tunnel, unsafe, temporary, vm.migrate(dstconn, migrate_uri, tunnel, unsafe, temporary, xml,
meter=meter) meter=meter)
################
# UI listeners #
################
def _xmleditor_xml_requested_cb(self, src):
self._xmleditor.set_xml(self.vm.xmlobj.get_xml())

View File

@ -1388,7 +1388,7 @@ class vmmDomain(vmmLibvirtObject):
def migrate(self, destconn, dest_uri=None, def migrate(self, destconn, dest_uri=None,
tunnel=False, unsafe=False, temporary=False, meter=None): tunnel=False, unsafe=False, temporary=False, xml=None, meter=None):
self._cancel_set_time() self._cancel_set_time()
self._install_abort = True self._install_abort = True
@ -1417,12 +1417,15 @@ class vmmDomain(vmmLibvirtObject):
params = {} params = {}
if dest_uri and not tunnel: if dest_uri and not tunnel:
params[libvirt.VIR_MIGRATE_PARAM_URI] = dest_uri params[libvirt.VIR_MIGRATE_PARAM_URI] = dest_uri
if xml:
params[libvirt.VIR_MIGRATE_PARAM_DEST_XML] = xml
if self.conn.is_test() and "TESTSUITE-FAKE" in (dest_uri or ""): if self.conn.is_test() and "TESTSUITE-FAKE" in (dest_uri or ""):
# If using the test driver and a special URI, fake successful # If using the test driver and a special URI, fake successful
# migration so we can test more of the migration wizard # migration so we can test more of the migration wizard
time.sleep(3) time.sleep(3)
xml = self.get_xml_to_define() if not xml:
xml = self.get_xml_to_define()
destconn.define_domain(xml).create() destconn.define_domain(xml).create()
self.delete() self.delete()
elif tunnel: elif tunnel: