Skip to content

Commit

Permalink
qvm-run will unpause paused VMs by defaults
Browse files Browse the repository at this point in the history
If qvm-run is run with the autostart option (true by default), it will
also unpause paused VMs.

fixes QubesOS/qubes-issues#5967
  • Loading branch information
marmarta committed Aug 5, 2020
1 parent a078e1f commit 4a6b5db
Show file tree
Hide file tree
Showing 2 changed files with 92 additions and 26 deletions.
96 changes: 72 additions & 24 deletions qubesadmin/tests/tools/qvm_run.py
Original file line number Diff line number Diff line change
Expand Up @@ -45,9 +45,9 @@ def test_000_run_single(self):
self.app.expected_calls[
('test-vm', 'admin.vm.feature.CheckWithTemplate', 'os', None)] = \
b'2\x00QubesFeatureNotFoundError\x00\x00Feature \'os\' not set\x00'
# self.app.expected_calls[
# ('test-vm', 'admin.vm.List', None, None)] = \
# b'0\x00test-vm class=AppVM state=Running\n'
self.app.expected_calls[
('test-vm', 'admin.vm.CurrentState', None, None)] = \
b'0\x00power_state=Running'
ret = qubesadmin.tools.qvm_run.main(
['--no-gui', 'test-vm', 'command'],
app=self.app)
Expand Down Expand Up @@ -110,6 +110,9 @@ def test_002_passio(self):
self.app.expected_calls[
('test-vm', 'admin.vm.feature.CheckWithTemplate', 'os', None)] = \
b'2\x00QubesFeatureNotFoundError\x00\x00Feature \'os\' not set\x00'
self.app.expected_calls[
('test-vm', 'admin.vm.CurrentState', None, None)] = \
b'0\x00power_state=Running'
# self.app.expected_calls[
# ('test-vm', 'admin.vm.List', None, None)] = \
# b'0\x00test-vm class=AppVM state=Running\n'
Expand Down Expand Up @@ -139,9 +142,9 @@ def test_002_passio_service(self):
self.app.expected_calls[
('dom0', 'admin.vm.List', None, None)] = \
b'0\x00test-vm class=AppVM state=Running\n'
# self.app.expected_calls[
# ('test-vm', 'admin.vm.List', None, None)] = \
# b'0\x00test-vm class=AppVM state=Running\n'
self.app.expected_calls[
('test-vm', 'admin.vm.CurrentState', None, None)] = \
b'0\x00power_state=Running'
echo = subprocess.Popen(['echo', 'some-data'], stdout=subprocess.PIPE)
with unittest.mock.patch('sys.stdin', echo.stdout):
ret = qubesadmin.tools.qvm_run.main(
Expand Down Expand Up @@ -276,9 +279,9 @@ def test_005_localcmd(self, mock_popen):
self.app.expected_calls[
('test-vm', 'admin.vm.feature.CheckWithTemplate', 'os', None)] = \
b'2\x00QubesFeatureNotFoundError\x00\x00Feature \'os\' not set\x00'
# self.app.expected_calls[
# ('test-vm', 'admin.vm.List', None, None)] = \
# b'0\x00test-vm class=AppVM state=Running\n'
self.app.expected_calls[
('test-vm', 'admin.vm.CurrentState', None, None)] = \
b'0\x00power_state=Running'
mock_popen.return_value.wait.return_value = 0
ret = qubesadmin.tools.qvm_run.main(
['--no-gui', '--pass-io', '--localcmd', 'local-command',
Expand Down Expand Up @@ -309,9 +312,9 @@ def test_006_run_single_with_gui(self):
self.app.expected_calls[
('test-vm', 'admin.vm.feature.CheckWithTemplate', 'os', None)] = \
b'2\x00QubesFeatureNotFoundError\x00\x00Feature \'os\' not set\x00'
# self.app.expected_calls[
# ('test-vm', 'admin.vm.List', None, None)] = \
# b'0\x00test-vm class=AppVM state=Running\n'
self.app.expected_calls[
('test-vm', 'admin.vm.CurrentState', None, None)] = \
b'0\x00power_state=Running'
ret = qubesadmin.tools.qvm_run.main(
['test-vm', 'command'],
app=self.app)
Expand Down Expand Up @@ -339,9 +342,9 @@ def test_007_run_service_with_gui(self):
self.app.expected_calls[
('test-vm', 'admin.vm.property.Get', 'default_user', None)] = \
b'0\x00default=yes type=str user'
# self.app.expected_calls[
# ('test-vm', 'admin.vm.List', None, None)] = \
# b'0\x00test-vm class=AppVM state=Running\n'
self.app.expected_calls[
('test-vm', 'admin.vm.CurrentState', None, None)] = \
b'0\x00power_state=Running'
ret = qubesadmin.tools.qvm_run.main(
['--service', 'test-vm', 'service.name'],
app=self.app)
Expand All @@ -363,6 +366,9 @@ def test_007_run_service_with_gui(self):
self.assertAllCalled()

def test_008_dispvm_remote(self):
self.app.expected_calls[
('$dispvm', 'admin.vm.CurrentState', None, None)] = \
b'0\x00power_state=Running'
ret = qubesadmin.tools.qvm_run.main(
['--dispvm', '--service', 'test.service'], app=self.app)
self.assertEqual(ret, 0)
Expand All @@ -377,6 +383,9 @@ def test_008_dispvm_remote(self):
self.assertAllCalled()

def test_009_dispvm_remote_specific(self):
self.app.expected_calls[
('$dispvm:test-vm', 'admin.vm.CurrentState', None, None)] = \
b'0\x00power_state=Running'
ret = qubesadmin.tools.qvm_run.main(
['--dispvm=test-vm', '--service', 'test.service'], app=self.app)
self.assertEqual(ret, 0)
Expand All @@ -400,6 +409,9 @@ def test_010_dispvm_local(self):
self.app.expected_calls[
('disp123', 'admin.vm.property.Get', 'qrexec_timeout', None)] = \
b'0\0default=yes type=int 30'
self.app.expected_calls[
('$dispvm', 'admin.vm.CurrentState', None, None)] = \
b'0\x00power_state=Running'
ret = qubesadmin.tools.qvm_run.main(
['--dispvm', '--service', 'test.service'], app=self.app)
self.assertEqual(ret, 0)
Expand All @@ -424,6 +436,9 @@ def test_011_dispvm_local_specific(self):
self.app.expected_calls[
('disp123', 'admin.vm.property.Get', 'qrexec_timeout', None)] = \
b'0\0default=yes type=int 30'
self.app.expected_calls[
('$dispvm:test-vm', 'admin.vm.CurrentState', None, None)] = \
b'0\x00power_state=Running'
ret = qubesadmin.tools.qvm_run.main(
['--dispvm=test-vm', '--service', 'test.service'], app=self.app)
self.assertEqual(ret, 0)
Expand Down Expand Up @@ -496,6 +511,9 @@ def test_014_dispvm_local_gui(self):
self.app.expected_calls[
('disp123', 'admin.vm.feature.CheckWithTemplate', 'os', None)] = \
b'2\x00QubesFeatureNotFoundError\x00\x00Feature \'os\' not set\x00'
self.app.expected_calls[
('$dispvm', 'admin.vm.CurrentState', None, None)] = \
b'0\x00power_state=Running'
ret = qubesadmin.tools.qvm_run.main(
['--dispvm', '--', 'test.command'], app=self.app)
self.assertEqual(ret, 0)
Expand Down Expand Up @@ -524,6 +542,9 @@ def test_015_dispvm_local_no_gui(self):
self.app.expected_calls[
('disp123', 'admin.vm.feature.CheckWithTemplate', 'os', None)] = \
b'2\x00QubesFeatureNotFoundError\x00\x00Feature \'os\' not set\x00'
self.app.expected_calls[
('$dispvm', 'admin.vm.CurrentState', None, None)] = \
b'0\x00power_state=Running'
ret = qubesadmin.tools.qvm_run.main(
['--dispvm', '--no-gui', 'test.command'], app=self.app)
self.assertEqual(ret, 0)
Expand All @@ -545,9 +566,9 @@ def test_016_run_single_windows(self):
self.app.expected_calls[
('test-vm', 'admin.vm.feature.CheckWithTemplate', 'os', None)] = \
b'0\x00Windows'
# self.app.expected_calls[
# ('test-vm', 'admin.vm.List', None, None)] = \
# b'0\x00test-vm class=AppVM state=Running\n'
self.app.expected_calls[
('test-vm', 'admin.vm.CurrentState', None, None)] = \
b'0\x00power_state=Running'
ret = qubesadmin.tools.qvm_run.main(
['--no-gui', 'test-vm', 'command'],
app=self.app)
Expand All @@ -572,9 +593,9 @@ def test_020_run_exec_with_vmexec_not_supported(self):
self.app.expected_calls[
('test-vm', 'admin.vm.feature.CheckWithTemplate', 'vmexec', None)] = \
b'2\x00QubesFeatureNotFoundError\x00\x00Feature \'vmexec\' not set\x00'
# self.app.expected_calls[
# ('test-vm', 'admin.vm.List', None, None)] = \
# b'0\x00test-vm class=AppVM state=Running\n'
self.app.expected_calls[
('test-vm', 'admin.vm.CurrentState', None, None)] = \
b'0\x00power_state=Running'
ret = qubesadmin.tools.qvm_run.main(
['--no-gui', 'test-vm', 'command', 'arg'],
app=self.app)
Expand All @@ -597,9 +618,9 @@ def test_020_run_exec_with_vmexec_supported(self):
('test-vm', 'admin.vm.feature.CheckWithTemplate',
'vmexec', None)] = \
b'0\x001'
# self.app.expected_calls[
# ('test-vm', 'admin.vm.List', None, None)] = \
# b'0\x00test-vm class=AppVM state=Running\n'
self.app.expected_calls[
('test-vm', 'admin.vm.CurrentState', None, None)] = \
b'0\x00power_state=Running'
ret = qubesadmin.tools.qvm_run.main(
['--no-gui', 'test-vm', 'command', 'arg'],
app=self.app)
Expand All @@ -613,3 +634,30 @@ def test_020_run_exec_with_vmexec_supported(self):
('test-vm', 'qubes.VMExec+command+arg', b'')
])
self.assertAllCalled()

def test_021_paused_vm(self):
self.app.expected_calls[
('dom0', 'admin.vm.List', None, None)] = \
b'0\x00test-vm class=AppVM state=Paused\n'
self.app.expected_calls[
('test-vm', 'admin.vm.feature.CheckWithTemplate', 'os', None)] = \
b'2\x00QubesFeatureNotFoundError\x00\x00Feature \'os\' not set\x00'
self.app.expected_calls[
('test-vm', 'admin.vm.CurrentState', None, None)] = \
b'0\x00power_state=Paused'
self.app.expected_calls[
('test-vm', 'admin.vm.Unpause', None, None)] = \
b'0\x00'
ret = qubesadmin.tools.qvm_run.main(
['--no-gui', 'test-vm', 'command'],
app=self.app)
self.assertEqual(ret, 0)
self.assertEqual(self.app.service_calls, [
('test-vm', 'qubes.VMShell', {
'stdout': subprocess.DEVNULL,
'stderr': subprocess.DEVNULL,
'user': None,
}),
('test-vm', 'qubes.VMShell', b'command; exit\n')
])
self.assertAllCalled()
22 changes: 20 additions & 2 deletions qubesadmin/tools/qvm_run.py
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@

parser.add_argument('--no-autostart', '--no-auto', '-n',
action='store_false', dest='autostart',
help='do not autostart qube')
help='do not autostart/unpause qube')

parser.add_argument('--pass-io', '-p',
action='store_true', dest='passio', default=False,
Expand Down Expand Up @@ -270,9 +270,27 @@ def main(args=None, app=None):
if not args.autostart and not vm.is_running():
if verbose > 0:
print_no_color('Qube \'{}\' not started'.format(vm.name),
file=sys.stderr, color=args.color_stderr)
file=sys.stderr, color=args.color_stderr)
retcode = max(retcode, 1)
continue
if vm.is_paused():
if not args.autostart:
if verbose > 0:
print_no_color(
'Qube \'{}\' is paused'.format(vm.name),
file=sys.stderr, color=args.color_stderr)
retcode = max(retcode, 1)
continue
try:
vm.unpause()
except qubesadmin.exc.QubesException:
if verbose > 0:
print_no_color(
'Qube \'{}\' cannot be unpaused'.format(
vm.name),
file=sys.stderr, color=args.color_stderr)
retcode = max(retcode, 1)
continue
try:
if verbose > 0:
print_no_color(
Expand Down

0 comments on commit 4a6b5db

Please sign in to comment.