def ensure_vms_present(): for vm_name in generate_vm_names(): q = qubesadmin.Qubes() if vm_name not in q.domains: x = qmgr.HexagonQube(vm_name) x.reconcile() q = qubesadmin.Qubes() assert vm_name in q.domains
def ensure_vms_absent(): for vm_name in generate_vm_names(): q = qubesadmin.Qubes() if vm_name in q.domains: subprocess.check_call(["qvm-shutdown", "--wait", vm_name]) subprocess.check_call(["qvm-remove", "-f", vm_name]) time.sleep(1) q = qubesadmin.Qubes() assert vm_name not in q.domains
def test_default_netvm_is_none(): vm_name = "hexagon-test-3" q = qubesadmin.Qubes() assert vm_name in q.domains vm = qmgr.HexagonQube(vm_name, template="debian-10") q = qubesadmin.Qubes() assert vm_name in q.domains vm.reconcile() q = qubesadmin.Qubes() assert vm.name in q.domains assert str(vm.vm.netvm) == "None"
def __init__(self, name, *args, **kwargs): self.name = name # Don't clobber existing VM config unless explicitly requested if self.exists(): self.vm = qubesadmin.Qubes().domains[self.name] self.desired_config = {**kwargs} else: self.desired_config = {**CONFIG_DEFAULTS, **kwargs} self.pending_changes = [] self.reboot_required = False self.rebuild_required = False new_template = self.desired_config.get("template", "") if new_template and new_template not in qubesadmin.Qubes().domains: msg = "Target TemplateVM does not exist: {}".format(new_template) raise Exception(msg)
def run_asynchronous(app_name, icon_name, window_class): qt_app = QtWidgets.QApplication(sys.argv) qt_app.setOrganizationName("The Qubes Project") qt_app.setOrganizationDomain("http://qubes-os.org") qt_app.setApplicationName(app_name) qt_app.setWindowIcon(QIcon.fromTheme(icon_name)) qt_app.lastWindowClosed.connect(loop_shutdown) qubes_app = qubesadmin.Qubes() loop = quamash.QEventLoop(qt_app) asyncio.set_event_loop(loop) dispatcher = events.EventsDispatcher(qubes_app) window = window_class(qt_app, qubes_app, dispatcher) window.show() try: loop.run_until_complete( asyncio.ensure_future(dispatcher.listen_for_events())) except asyncio.CancelledError: pass except Exception: # pylint: disable=broad-except loop_shutdown() exc_type, exc_value, exc_traceback = sys.exc_info()[:3] handle_exception(exc_type, exc_value, exc_traceback)
def main(): qapp = qubesadmin.Qubes() dispatcher = qubesadmin.events.EventsDispatcher(qapp) app = UpdatesTray('org.qubes.qui.tray.Updates', qapp, dispatcher) app.run() loop = asyncio.get_event_loop() done, _unused = loop.run_until_complete( asyncio.ensure_future(dispatcher.listen_for_events())) exit_code = 0 for d in done: # pylint: disable=invalid-name try: d.result() except Exception: # pylint: disable=broad-except exc_type, exc_value = sys.exc_info()[:2] dialog = Gtk.MessageDialog(None, 0, Gtk.MessageType.ERROR, Gtk.ButtonsType.OK) dialog.set_title(_("Houston, we have a problem...")) dialog.set_markup( _("<b>Whoops. A critical error in Updates Widget has occured.</b>" " This is most likely a bug in the widget. To restart the " "widget, run 'qui-updates' in dom0.")) dialog.format_secondary_markup("\n<b>{}</b>: {}\n{}".format( exc_type.__name__, exc_value, traceback.format_exc(limit=10))) dialog.run() exit_code = 1 return exit_code
def run_synchronous(window_class): qt_app = QtWidgets.QApplication(sys.argv) translator = QtCore.QTranslator(qt_app) locale = QtCore.QLocale.system().name() i18n_dir = os.path.join(os.path.dirname(os.path.realpath(__file__)), 'i18n') translator.load("qubesmanager_{!s}.qm".format(locale), i18n_dir) qt_app.installTranslator(translator) QtCore.QCoreApplication.installTranslator(translator) qt_app.setOrganizationName("The Qubes Project") qt_app.setOrganizationDomain("http://qubes-os.org") sys.excepthook = handle_exception qubes_app = qubesadmin.Qubes() window = window_class(qt_app, qubes_app) if hasattr(window, "setup_application"): window.setup_application() window.show() qt_app.exec_() qt_app.exit()
def __init__(self) -> None: self.app = qubesadmin.Qubes() qubes_data = qubesdbus.serialize.qubes_data(self.app) # type: DBusProperties bus = dbus.SessionBus() bus_name = dbus.service.BusName(SERVICE_NAME, bus=bus, allow_replacement=True, replace_existing=True) super().__init__(bus_name, SERVICE_PATH, INTERFACE, qubes_data) self.bus_name = bus_name self.bus = bus self.signal_matches = {} # type: Dict[str, List[DBusSignalMatch]] self.state_signals = { 'Starting': self.Starting, 'Started': self.Started, 'Failed': self.Failed, 'Halting': self.Halting, 'Halted': self.Halted, 'Unknown': lambda _, __: None, } self.domains = { vm.name: self._proxify_domain(vm) for vm in self.app.domains } self.events_dispatcher.add_handler('domain-add', self._domain_add) self.events_dispatcher.add_handler('domain-delete', self._domain_delete) self.events_dispatcher.add_handler('domain-spawn', self._domain_spawn) self.events_dispatcher.add_handler('domain-start', self._domain_start) self.events_dispatcher.add_handler('domain-pre-shutdown', self._domain_pre_shutdown) self.events_dispatcher.add_handler('domain-shutdown', self._domain_shutdown) self.stats_dispatcher = EventsDispatcher(self.app, api_method='admin.vm.Stats') self.stats_dispatcher.add_handler('vm-stats', self._update_stats)
def main(): ''' main function ''' qapp = qubesadmin.Qubes() dispatcher = qubesadmin.events.EventsDispatcher(qapp) stats_dispatcher = qubesadmin.events.EventsDispatcher( qapp, api_method='admin.vm.Stats') app = DomainTray('org.qubes.qui.tray.Domains', qapp, dispatcher, stats_dispatcher) app.run() loop = asyncio.get_event_loop() tasks = [ asyncio.ensure_future(dispatcher.listen_for_events()), asyncio.ensure_future(stats_dispatcher.listen_for_events()), ] done, _ = loop.run_until_complete( asyncio.wait(tasks, return_when=asyncio.FIRST_EXCEPTION)) for d in done: try: d.result() except Exception as ex: exc_type, exc_value = sys.exc_info()[:2] dialog = Gtk.MessageDialog(None, 0, Gtk.MessageType.ERROR, Gtk.ButtonsType.OK) dialog.set_title("Houston, we have a problem...") dialog.set_markup( "<b>Whoops. A critical error in Domains Widget has occured.</b>" " This is most likely a bug in the widget. To restart the " "widget, run 'qui-domains' in dom0.") dialog.format_secondary_markup("\n<b>{}</b>: {}\n{}".format( exc_type.__name__, exc_value, traceback.format_exc(limit=10))) dialog.run()
def ext_pillar(minion_id, pillar, *args, **kwargs): app = qubesadmin.Qubes() try: vm = app.domains[minion_id] except KeyError: return {} qvm_pillar = {} if vm.qid == 0: qvm_pillar['type'] = 'admin' elif vm.klass == 'TemplateVM': qvm_pillar['type'] = 'template' elif vm.klass == 'StandaloneVM': qvm_pillar['type'] = 'standalone' else: qvm_pillar['type'] = 'app' if hasattr(vm, 'template'): qvm_pillar['template'] = vm.template.name if hasattr(vm, 'netvm'): qvm_pillar['netvm'] = str(vm.netvm) # TODO: consider other properties; target VM will learn them! return {'qubes': qvm_pillar}
def test_template_change_while_halted(): vm = qmgr.HexagonQube("hexagon-test-1", template="debian-10") q = qubesadmin.Qubes() assert vm.name in q.domains assert not vm.pending_changes assert vm.desired_config["template"] == "debian-10" vm.desired_config["template"] = "fedora-31" vm.reconcile() assert vm.desired_config["template"] == "fedora-31"
def RunService(self, service): app = qubesadmin.Qubes() name = str(self.name) vm = app.domains[name] vm.run_service(service, stdin=subprocess.DEVNULL, stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL) return True
def test_netvm_change_while_running(): vm = qmgr.HexagonQube("hexagon-test-4", template="debian-10", autostart=True) q = qubesadmin.Qubes() assert vm.name in q.domains assert str(vm.vm.netvm) == "None" vm.desired_config["netvm"] = "sys-firewall" vm.reconcile() assert vm.desired_config["netvm"] == "sys-firewall"
def run_synchronous(app_name, window_class): qt_app = QtWidgets.QApplication(sys.argv) qt_app.setOrganizationName("The Qubes Project") qt_app.setOrganizationDomain("http://qubes-os.org") qt_app.setApplicationName(app_name) sys.excepthook = handle_exception qubes_app = qubesadmin.Qubes() window = window_class(qt_app, qubes_app) window.show() qt_app.exec_() qt_app.exit()
def reconcile(self): """ Apply all outstanding config changes to VM. If VM does not exist, it will be created. Handles VM roughly, including rebooting despite attached network clients if a netvm. """ # Logging is not mandatory, but calling changes_required is, # since it populates the pending_changes attribute. if not self.changes_required(): logging.debug("{} requires no changes".format(self)) else: logging.debug("{} requires changes: {}".format( self, self.pending_changes)) # Make sure VM exists self.create() # We'll restore the original power state when done was_running = self.vm.is_running() reboot_required = False if self.is_outdated(): reboot_required = True if any([c.reboot_required for c in self.pending_changes]): reboot_required = True if reboot_required: # The ensure_halted operation blocks, so we can update settings self.ensure_halted() if self.rebuild_required: self.recreate() for c in self.pending_changes: # logging.debug("Applying config change for {}: {}".format(self, c)) c.apply(self.vm) if self.vm.autostart or was_running: if not self.vm.is_running(): self.vm.start() else: if self.vm.is_running(): self.ensure_halted() # Finally, update the vm attribute was latest info self.vm = qubesadmin.Qubes().domains[self.name]
def main(args=None, app=None): """Main routine of :program:`qvm-block`.""" basename = os.path.basename(sys.argv[0]) devclass = None if basename.startswith('qvm-') and basename != 'qvm-device': devclass = basename[4:] args = get_parser(devclass).parse_args(args, app=app) if args.list_device_classes: print('\n'.join(qubesadmin.Qubes().list_deviceclass())) return 0 try: args.func(args) except qubesadmin.exc.QubesException as e: print(str(e), file=sys.stderr) return 1 return 0
def clicked(self): "Button clicked" vmname = self.cbox.currentText() self.button.setEnabled(False) self.button.repaint() if not self.devicestatus: assignment = qubesadmin.devices.DeviceAssignment( self.device.backend_domain, self.device.ident, persistent=False ) dname = self.cbox.currentText() app = qubesadmin.Qubes() domain = app.domains[dname] domain.devices[self.devclass].attach(assignment) self.domain = domain self.vmname = dname self.assignment = assignment self.devicestatus = True self.msg_callback( "Connected {0} to {1}".format(self.device.description, vmname) ) self.button.setText("Disconnect") self.button.setStyleSheet( "QPushButton { width: 50px; height: 40px; color: red; }" ) self.cbox.setEnabled(False) else: self.domain.devices[self.devclass].detach(self.assignment) self.domain = None self.vmname = "" self.assignment = None self.devicestatus = False self.msg_callback( "Disconnected {0} from {1}".format(self.device.description, vmname) ) self.button.setText("Connect") self.button.setStyleSheet( "QPushButton { width: 50px; height: 40px; color: green; }" ) self.cbox.setEnabled(True) self.button.setEnabled(True) self.button.repaint()
def main(): qapp = qubesadmin.Qubes() dispatcher = qubesadmin.events.EventsDispatcher(qapp) lz = LazyWorker(qapp, dispatcher) loop = asyncio.get_event_loop() done, _ = loop.run_until_complete( asyncio.ensure_future(dispatcher.listen_for_events())) exit_code = 0 for d in done: try: d.result() except Exception: exit_code = 1 return exit_code
def test_template_change_while_running(): vm_name = "hexagon-test-2" vm = qmgr.HexagonQube(vm_name, template="debian-10", autostart=True) q = qubesadmin.Qubes() assert vm.name in q.domains assert not vm.pending_changes assert vm.vm.start_time == "" assert get_pref(vm_name, "start_time") == "" vm.vm.start() assert get_pref(vm_name, "start_time") != "" time_before_reconcile = get_pref(vm_name, "start_time") assert vm.desired_config["template"] == "debian-10" vm.desired_config["template"] = "fedora-31" vm.reconcile() assert get_pref(vm_name, "template") == "fedora-31" # TODO: figure out why this reboot cycle is required # Maybe the reconcile isn't rebooting? vm.ensure_halted() vm.vm.start() time_after_reconcile = get_pref(vm_name, "start_time") assert time_after_reconcile > time_before_reconcile
def main(): ''' main function ''' qapp = qubesadmin.Qubes() dispatcher = qubesadmin.events.EventsDispatcher(qapp) stats_dispatcher = qubesadmin.events.EventsDispatcher( qapp, api_method='admin.vm.Stats') app = DomainTray('org.qubes.qui.tray.Domains', qapp, dispatcher, stats_dispatcher) app.run() loop = asyncio.get_event_loop() tasks = [ asyncio.ensure_future(dispatcher.listen_for_events()), asyncio.ensure_future(stats_dispatcher.listen_for_events()), ] done, _unused = loop.run_until_complete( asyncio.wait(tasks, return_when=asyncio.FIRST_EXCEPTION)) exit_code = 0 for d in done: # pylint: disable=invalid-name try: d.result() except Exception as _ex: # pylint: disable=broad-except exc_type, exc_value = sys.exc_info()[:2] dialog = Gtk.MessageDialog(None, 0, Gtk.MessageType.ERROR, Gtk.ButtonsType.OK) dialog.set_title(_("Houston, we have a problem...")) dialog.set_markup( _("<b>Whoops. A critical error in Qubes Domains has occurred.</b>" " This is most likely a bug in the widget. Qubes Domains" " will restart itself.")) exc_description = "\n<b>{}</b>: {}\n{}".format( exc_type.__name__, exc_value, traceback.format_exc(limit=10)) dialog.format_secondary_markup(escape(exc_description)) dialog.run() exit_code = 1 return exit_code
def run_asynchronous(window_class): qt_app = QtWidgets.QApplication(sys.argv) translator = QtCore.QTranslator(qt_app) locale = QtCore.QLocale.system().name() i18n_dir = os.path.join(os.path.dirname(os.path.realpath(__file__)), 'i18n') translator.load("qubesmanager_{!s}.qm".format(locale), i18n_dir) qt_app.installTranslator(translator) QtCore.QCoreApplication.installTranslator(translator) qt_app.setOrganizationName("The Qubes Project") qt_app.setOrganizationDomain("http://qubes-os.org") qt_app.lastWindowClosed.connect(loop_shutdown) qubes_app = qubesadmin.Qubes() loop = quamash.QEventLoop(qt_app) asyncio.set_event_loop(loop) dispatcher = events.EventsDispatcher(qubes_app) window = window_class(qt_app, qubes_app, dispatcher) if hasattr(window, "setup_application"): window.setup_application() window.show() try: loop.run_until_complete( asyncio.ensure_future(dispatcher.listen_for_events())) except asyncio.CancelledError: pass except Exception: # pylint: disable=broad-except loop_shutdown() exc_type, exc_value, exc_traceback = sys.exc_info()[:3] handle_exception(exc_type, exc_value, exc_traceback)
def __init__(self): QMainWindow.__init__(self) self.app = qubesadmin.Qubes() # To hold the devices and their status self.devices = {} self.running_vms = [] # widgets to refresh self.widgets = [] self.setMinimumSize(QSize(600, 400)) self.setWindowTitle("Qubes Devices") self.centralWidget = QWidget(self) self.setCentralWidget(self.centralWidget) self.gridLayout = QGridLayout(self) self.centralWidget.setLayout(self.gridLayout) self.refreshButton = QPushButton("Refresh") self.refreshButton.setStyleSheet("QPushButton { height: 50px; }") self.refreshButton.clicked.connect(self.refresh_view) self.gridLayout.addWidget(self.refreshButton, 0, 0) self.gridLayout.addWidget(QLabel("Devices on the system", self), 1, 0) self.trayIcon = QSystemTrayIcon(self) self.trayIcon.setIcon(self.style().standardIcon(QStyle.SP_ComputerIcon)) self.quitAction = QAction("Exit", self) self.quitAction.triggered.connect(qApp.quit) self.trayMenu = QMenu() self.trayMenu.addAction(self.quitAction) self.trayIcon.setContextMenu(self.trayMenu) self.trayIcon.activated.connect(self.view_toggle) self.trayIcon.show() self.refresh_view()
dbus.mainloop.glib.DBusGMainLoop(set_as_default=True) # isort:skip import gi gi.require_version('Gtk', '3.0') # isort:skip gi.require_version('AppIndicator3', '0.1') # isort:skip from gi.repository import Gtk # isort:skip from gi.repository import AppIndicator3 as appindicator # isort:skip import qubesadmin import qui.decorators import qui.models.qubes DOMAINS = qui.models.qubes.DomainManager() LABELS = qui.models.qubes.LabelsManager() QUBES_APP = qubesadmin.Qubes() DBUS = dbus.SessionBus() # TODO Replace pci with usb & mic when they are ready DEV_TYPES = ['block', 'usb', 'mic'] class DomainMenuItem(Gtk.ImageMenuItem): ''' A submenu item for the device menu. Allows attaching and detaching the device to a domain. ''' def __init__(self, dev: qui.models.qubes.Device, dbus_vm: qui.models.qubes.Domain, *args, **kwargs): super().__init__(*args, **kwargs) self.dbus_vm = dbus_vm
def main(): args = parse_args() q = qubesadmin.Qubes() vms = args.vms # TODO: support --max-concurrency flag, defaulting to 4 # for "update" behavior, but for e.g. ls it's ok to raise n_proc = len(vms) or 4 if args.command == "reconcile": # Handle helper args, maybe belongs in parse_args for property_alias in ("template", "netvm", "label"): alias_value = getattr(args, property_alias) if alias_value: args.property.append([property_alias, alias_value]) if not vms: logging.error("No VMs were declared") # TODO: It'd be grand to read from a config file msg = "Reconcile must target specific VMs" raise NotImplementedError(msg) func = reconcile_vm elif args.command == "ls": logging.debug("Listing VMs...") if vms: vms = [HexagonQube(x.name) for x in q.domains if x.name in vms] else: vms = [HexagonQube(x.name) for x in q.domains] n_proc = len(vms) or 5 if args.tags: # TODO: support csv tags vms = [x for x in vms if args.tags in x.vm.tags] if args.template: vms = [ x for x in vms if getattr(x.vm, "template", "") == args.template ] if args.updatable: vms = [ x for x in vms if x.vm.features.get("updates-available", "0") == "1" ] if args.outdated: vms = [x for x in vms if x.is_outdated()] for vm in vms: print(vm.name) sys.exit(0) elif args.command == "reboot": if vms: vms = [HexagonQube(x.name) for x in q.domains if x.name in vms] if args.outdated and vms: vms = [x for x in vms if x.is_outdated()] elif args.outdated and not vms: vms = [HexagonQube(x.name) for x in q.domains] vms = [x for x in vms if x.is_outdated()] vms = [x.name for x in vms] func = reboot_vm elif args.command == "update": n_proc = args.max_concurrency if not vms: vms = [ x for x in q.domains if x.features.get("updates-available", "0") == "1" ] vms = [x.name for x in vms] # TODO: handle dom0 separately, before all domUs func = update_vm elif args.command == "shutdown": requested_vms = len(vms) if requested_vms > 0: vms = [HexagonQube(x.name) for x in q.domains if x.name in vms] if len(vms) != requested_vms: msg = "Some VMs could not be found" raise Exception(msg) else: logging.error("No VMs were declared") # TODO: It'd be grand to read from a config file msg = "Shutdown must target specific VMs" raise NotImplementedError(msg) func = lambda args, x: x.ensure_halted() elif args.command == "start": requested_vms = len(vms) if requested_vms > 0: vms = [HexagonQube(x.name) for x in q.domains if x.name in vms] if len(vms) != requested_vms: msg = "Some VMs could not be found" raise Exception(msg) else: logging.error("No VMs were declared") # TODO: It'd be grand to read from a config file msg = "Start must target specific VMs" raise NotImplementedError(msg) func = lambda args, x: x.vm.start() else: msg = "Action not supported: {}".format(args.command) raise NotImplementedError(msg) if args.dry_run: logging.debug("Would {} VMs: {}".format(args.command, vms)) sys.exit(0) logging.debug("Performing {} of VMs: {}".format(args.command, vms)) with concurrent.futures.ThreadPoolExecutor(max_workers=n_proc) as executor: results = list(map(lambda x: executor.submit(func, args, x), vms)) errors = 0 for i, r in enumerate(results): vm = vms[i] try: r.result() logging.debug("VM operation {} completed: {}".format( args.command, vm)) except Exception as e: errors += 1 logging.debug("VM operation {} failed: {}, error: {}".format( args.command, vm, repr(e))) logging.debug("All VM {} operations finished, with {} errors".format( args.command, errors)) if errors: sys.exit(1)
def __init__(self, module): self.module = module self.app = qubesadmin.Qubes()
def Shutdown(self): app = qubesadmin.Qubes() name = str(self.name) vm = app.domains[name] vm.shutdown() return True
def Kill(self): app = qubesadmin.Qubes() name = str(self.name) vm = app.domains[name] vm.kill() return True
def Start(self): app = qubesadmin.Qubes() name = str(self.name) vm = app.domains[name] vm.start() return True
def create(self): if not self.exists(): self.vm = qubesadmin.Qubes().add_new_vm( self.desired_config["klass"], self.name, self.desired_config["label"])
def exists(self): return self.name in qubesadmin.Qubes().domains