Copy application menu on VM clone
The qubesd daemon have no information about clone source - from that side it looks like a new VM. This means application menu is created as for a new VM. To fix this re-initialize menu with --source option as part of the clone operation. It will copy both list of available applications (if applicable) and selected applications. This fixes both qvm-clone case and rename. Fixes QubesOS/qubes-issues#3902 Fixes QubesOS/qubes-issues#4124
This commit is contained in:
parent
5078d75aa3
commit
67897e3f9f
@ -385,6 +385,26 @@ class QubesBase(qubesadmin.base.PropertyHolder):
|
|||||||
if not ignore_errors:
|
if not ignore_errors:
|
||||||
raise
|
raise
|
||||||
|
|
||||||
|
try:
|
||||||
|
# FIXME: convert to qrexec calls to dom0/GUI VM
|
||||||
|
appmenus_cmd = \
|
||||||
|
['qvm-appmenus', '--init', '--update',
|
||||||
|
'--source', src_vm.name, dst_vm.name]
|
||||||
|
subprocess.check_output(appmenus_cmd, stderr=subprocess.STDOUT)
|
||||||
|
except OSError:
|
||||||
|
# this file needs to be python 2.7 compatible,
|
||||||
|
# so no FileNotFoundError
|
||||||
|
self.log.error('Failed to clone appmenus, qvm-appmenus missing')
|
||||||
|
if not ignore_errors:
|
||||||
|
raise qubesadmin.exc.QubesException(
|
||||||
|
'Failed to clone appmenus')
|
||||||
|
except subprocess.CalledProcessError as e:
|
||||||
|
self.log.error('Failed to clone appmenus: %s',
|
||||||
|
e.output.decode())
|
||||||
|
if not ignore_errors:
|
||||||
|
raise qubesadmin.exc.QubesException(
|
||||||
|
'Failed to clone appmenus')
|
||||||
|
|
||||||
except qubesadmin.exc.QubesException:
|
except qubesadmin.exc.QubesException:
|
||||||
if not ignore_errors:
|
if not ignore_errors:
|
||||||
del self.domains[dst_vm.name]
|
del self.domains[dst_vm.name]
|
||||||
|
@ -135,6 +135,16 @@ class TC_00_VMCollection(qubesadmin.tests.QubesTestCase):
|
|||||||
|
|
||||||
|
|
||||||
class TC_10_QubesBase(qubesadmin.tests.QubesTestCase):
|
class TC_10_QubesBase(qubesadmin.tests.QubesTestCase):
|
||||||
|
def setUp(self):
|
||||||
|
super(TC_10_QubesBase, self).setUp()
|
||||||
|
self.check_output_patch = mock.patch(
|
||||||
|
'subprocess.check_output')
|
||||||
|
self.check_output_mock = self.check_output_patch.start()
|
||||||
|
|
||||||
|
def tearDown(self):
|
||||||
|
self.check_output_patch.stop()
|
||||||
|
super(TC_10_QubesBase, self).tearDown()
|
||||||
|
|
||||||
def test_010_new_simple(self):
|
def test_010_new_simple(self):
|
||||||
self.app.expected_calls[('dom0', 'admin.vm.Create.AppVM', None,
|
self.app.expected_calls[('dom0', 'admin.vm.Create.AppVM', None,
|
||||||
b'name=new-vm label=red')] = b'0\x00'
|
b'name=new-vm label=red')] = b'0\x00'
|
||||||
@ -355,6 +365,11 @@ class TC_10_QubesBase(qubesadmin.tests.QubesTestCase):
|
|||||||
'test-template', b'name=new-name label=red')] = b'0\x00'
|
'test-template', b'name=new-name label=red')] = b'0\x00'
|
||||||
new_vm = self.app.clone_vm('test-vm', 'new-name')
|
new_vm = self.app.clone_vm('test-vm', 'new-name')
|
||||||
self.assertEqual(new_vm.name, 'new-name')
|
self.assertEqual(new_vm.name, 'new-name')
|
||||||
|
self.check_output_mock.assert_called_once_with(
|
||||||
|
['qvm-appmenus', '--init', '--update',
|
||||||
|
'--source', 'test-vm', 'new-name'],
|
||||||
|
stderr=subprocess.STDOUT
|
||||||
|
)
|
||||||
self.assertAllCalled()
|
self.assertAllCalled()
|
||||||
|
|
||||||
def test_031_clone_object(self):
|
def test_031_clone_object(self):
|
||||||
|
@ -19,6 +19,8 @@
|
|||||||
# with this program; if not, see <http://www.gnu.org/licenses/>.
|
# with this program; if not, see <http://www.gnu.org/licenses/>.
|
||||||
import os
|
import os
|
||||||
import tempfile
|
import tempfile
|
||||||
|
import unittest.mock
|
||||||
|
import subprocess
|
||||||
|
|
||||||
import qubesadmin.tests
|
import qubesadmin.tests
|
||||||
import qubesadmin.tests.tools
|
import qubesadmin.tests.tools
|
||||||
@ -210,7 +212,8 @@ class TC_00_qvm_create(qubesadmin.tests.QubesTestCase):
|
|||||||
self.assertAllCalled()
|
self.assertAllCalled()
|
||||||
self.assertTrue(os.path.exists(root_file.name))
|
self.assertTrue(os.path.exists(root_file.name))
|
||||||
|
|
||||||
def test_011_standalonevm(self):
|
@unittest.mock.patch('subprocess.check_output')
|
||||||
|
def test_011_standalonevm(self, check_output_mock):
|
||||||
self.app.expected_calls[('dom0', 'admin.label.List', None, None)] = \
|
self.app.expected_calls[('dom0', 'admin.label.List', None, None)] = \
|
||||||
b'0\x00red\nblue\n'
|
b'0\x00red\nblue\n'
|
||||||
self.app.expected_calls[('dom0', 'admin.vm.List', None, None)] = \
|
self.app.expected_calls[('dom0', 'admin.vm.List', None, None)] = \
|
||||||
@ -310,6 +313,10 @@ class TC_00_qvm_create(qubesadmin.tests.QubesTestCase):
|
|||||||
qubesadmin.tools.qvm_create.main(['-C', 'StandaloneVM',
|
qubesadmin.tools.qvm_create.main(['-C', 'StandaloneVM',
|
||||||
'-t', 'template', '-l', 'red', 'new-vm'],
|
'-t', 'template', '-l', 'red', 'new-vm'],
|
||||||
app=self.app)
|
app=self.app)
|
||||||
|
check_output_mock.assert_called_once_with(
|
||||||
|
['qvm-appmenus', '--init', '--update',
|
||||||
|
'--source', 'template', 'new-vm'],
|
||||||
|
stderr=subprocess.STDOUT)
|
||||||
self.assertAllCalled()
|
self.assertAllCalled()
|
||||||
|
|
||||||
def test_012_invalid_label(self):
|
def test_012_invalid_label(self):
|
||||||
@ -321,4 +328,3 @@ class TC_00_qvm_create(qubesadmin.tests.QubesTestCase):
|
|||||||
app=self.app)
|
app=self.app)
|
||||||
self.assertIn('red, blue', stderr.getvalue())
|
self.assertIn('red, blue', stderr.getvalue())
|
||||||
self.assertAllCalled()
|
self.assertAllCalled()
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user