Browse Source

Fire property-reset event when default value might change

Those are only some cases, the most obvious ones:
 - defaults inherited from a template
 - xid and start_time on domain start/stop
 - IP related properties
 - icon

QubesOS/qubes-issues#5834
Marek Marczykowski-Górecki 4 years ago
parent
commit
d61d24b055
4 changed files with 89 additions and 0 deletions
  1. 4 0
      qubes/ext/core_features.py
  2. 53 0
      qubes/vm/mix/net.py
  3. 8 0
      qubes/vm/qubesvm.py
  4. 24 0
      qubes/vm/templatevm.py

+ 4 - 0
qubes/ext/core_features.py

@@ -67,8 +67,12 @@ class CoreFeatures(qubes.ext.Extension):
     def set_servicevm_feature(self, subject):
         if getattr(subject, 'provides_network', False):
             subject.features['servicevm'] = 1
+            # icon is calculated based on this feature
+            subject.fire_event('property-reset:icon', name='icon')
         elif 'servicevm' in subject.features:
             del subject.features['servicevm']
+            # icon is calculated based on this feature
+            subject.fire_event('property-reset:icon', name='icon')
 
     @qubes.ext.handler('property-set:provides_network')
     def on_property_set(self, subject, event, name, newvalue, oldvalue=None):

+ 53 - 0
qubes/vm/mix/net.py

@@ -475,6 +475,59 @@ class NetVMMixin(qubes.events.Emitter):
         # pylint: disable=unused-argument
         self.reload_firewall_for_vm(vm)
 
+    @qubes.events.handler('property-set:ip', 'property-reset:ip')
+    def on_property_set_ip(self, _event, name, newvalue=None, oldvalue=None):
+        # pylint: disable=unused-argument
+        if newvalue == oldvalue:
+            return
+        if self.provides_network:
+            self.fire_event('property-reset:gateway', name='gateway')
+        self.fire_event('property-reset:visible_ip', name='visible_ip')
+        for vm in self.connected_vms:
+            vm.fire_event(
+                'property-reset:visible_gateway', name='visible_gateway')
+
+    @qubes.events.handler('property-set:ip6', 'property-reset:ipv6')
+    def on_property_set_ip6(self, _event, name, newvalue=None, oldvalue=None):
+        # pylint: disable=unused-argument
+        if newvalue == oldvalue:
+            return
+        if self.provides_network:
+            self.fire_event('property-reset:gateway6', name='gateway6')
+        self.fire_event('property-reset:visible_ip6', name='visible_ip6')
+        for vm in self.connected_vms:
+            vm.fire_event(
+                'property-reset:visible_gateway6', name='visible_gateway6')
+
+    @qubes.events.handler('feature-set:net.fake-ip')
+    def on_feature_set_net_fake_ip(self, event, name, newvalue, oldvalue=None):
+        # pylint: disable=unused-argument
+        if oldvalue == newvalue:
+            return
+        self.fire_event('property-reset:visible_ip', name='visible_ip')
+        for vm in self.connected_vms:
+            vm.fire_event(
+                'property-reset:visible_gateway', name='visible_gateway')
+
+    @qubes.events.handler('feature-set:ipv6')
+    def on_feature_set_ipv6(self, event, name, newvalue, oldvalue=None):
+        # pylint: disable=unused-argument
+        if oldvalue == newvalue:
+            return
+        self.fire_event('property-reset:visible_ip6', name='visible_ip6')
+        for vm in self.connected_vms:
+            vm.fire_event(
+                'property-reset:visible_gateway6', name='visible_gateway6')
+
+    @qubes.events.handler('property-set:provides_network')
+    def on_property_set_provides(
+            self, _event, name, newvalue, oldvalue=None):
+        # pylint: disable=unused-argument
+        if newvalue == oldvalue:
+            return
+        self.fire_event('property-reset:gateway', name='gateway')
+        self.fire_event('property-reset:gateway6', name='gateway6')
+
     @qubes.events.handler('domain-qdb-create')
     def on_domain_qdb_create(self, event):
         ''' Fills the QubesDB with firewall entries. '''

+ 8 - 0
qubes/vm/qubesvm.py

@@ -965,6 +965,9 @@ class QubesVM(qubes.vm.mix.net.NetVMMixin, qubes.vm.BaseVM):
             else:
                 shutil.copy(newvalue.icon_path, self.icon_path)
 
+        # icon is calculated based on label
+        self.fire_event('property-reset:icon', name='icon')
+
     @qubes.events.handler('property-pre-set:kernel')
     def on_property_pre_set_kernel(self, event, name, newvalue, oldvalue=None):
         # pylint: disable=unused-argument
@@ -1142,6 +1145,9 @@ class QubesVM(qubes.vm.mix.net.NetVMMixin, qubes.vm.BaseVM):
                 self.libvirt_domain.createWithFlags(
                     libvirt.VIR_DOMAIN_START_PAUSED)
 
+                # the above allocates xid, lets announce that
+                self.fire_event('property-reset:xid', name='xid')
+                self.fire_event('property-reset:start_time', name='start_time')
             except libvirt.libvirtError as exc:
                 # missing IOMMU?
                 if self.virt_mode == 'hvm' and \
@@ -1250,6 +1256,8 @@ class QubesVM(qubes.vm.mix.net.NetVMMixin, qubes.vm.BaseVM):
         except qubes.storage.StoragePoolException:
             self.log.exception('Failed to stop storage for domain %s',
                                self.name)
+        self.fire_event('property-reset:xid', name='xid')
+        self.fire_event('property-reset:start_time', name='start_time')
 
     @asyncio.coroutine
     def shutdown(self, force=False, wait=False, timeout=None):

+ 24 - 0
qubes/vm/templatevm.py

@@ -85,3 +85,27 @@ class TemplateVM(QubesVM):
             }
         }
         super(TemplateVM, self).__init__(*args, **kwargs)
+
+    @qubes.events.handler('property-set:default_user',
+                          'property-set:kernel',
+                          'property-set:kernelopts',
+                          'property-set:vcpus',
+                          'property-set:memory',
+                          'property-set:maxmem',
+                          'property-set:qrexec_timeout',
+                          'property-set:shutdown_timeout',
+                          'property-set:management_dispvm')
+    def on_property_set_child(self, _event, name, newvalue, oldvalue=None):
+        """Send event about default value change to child VMs
+           (which use default inherited from the template).
+
+           This handler is supposed to be set for properties using
+           `_default_with_template()` function for the default value.
+           """
+        if newvalue == oldvalue:
+            return
+
+        for vm in self.appvms:
+            if not vm.property_is_default(name):
+                continue
+            vm.fire_event('property-reset:' + name, name=name)