[gnome-shell] network: add support for virtual device types (vlan, bond, bridge)



commit c49bb5aa0390b7cf1763f1757d95d8a4aade76eb
Author: Dan Winship <danw gnome org>
Date:   Tue Dec 4 08:49:34 2012 -0500

    network: add support for virtual device types (vlan, bond, bridge)
    
    Virtual devices may not actually exist until their corresponding
    connections are brought up. So we need new code to create fake device
    wrapper objects for them based on the corresponding NMConnections.
    
    https://bugzilla.gnome.org/show_bug.cgi?id=677144
    https://bugzilla.gnome.org/show_bug.cgi?id=677146
    https://bugzilla.gnome.org/show_bug.cgi?id=677148

 js/ui/status/network.js |  265 ++++++++++++++++++++++++++++++++++++-----------
 1 files changed, 205 insertions(+), 60 deletions(-)
---
diff --git a/js/ui/status/network.js b/js/ui/status/network.js
index 4bc2335..73f5fc8 100644
--- a/js/ui/status/network.js
+++ b/js/ui/status/network.js
@@ -26,6 +26,7 @@ const Util = imports.misc.util;
 const NMConnectionCategory = {
     INVALID: 'invalid',
     WIRED: 'wired',
+    VIRTUAL: 'virtual',
     WIRELESS: 'wireless',
     WWAN: 'wwan',
     VPN: 'vpn'
@@ -307,13 +308,10 @@ const NMDevice = new Lang.Class({
     Extends: NMConnectionBased,
 
     _init: function(client, device, connections) {
-        this.device = device;
-        this.device._delegate = this;
-        this._stateChangedId = this.device.connect('state-changed', Lang.bind(this, this._deviceStateChanged));
-
-        // protected
         this._client = client;
+        this._setDevice(device);
         this.parent(connections);
+
         this._activeConnection = null;
         this._activeConnectionItem = null;
         this._autoConnectionItem = null;
@@ -338,24 +336,7 @@ const NMDevice = new Lang.Class({
     },
 
     destroy: function() {
-        if (this.device)
-            this.device._delegate = null;
-
-        if (this._stateChangedId) {
-            // Need to go through GObject.Object.prototype because
-            // nm_device_disconnect conflicts with g_signal_disconnect
-            GObject.Object.prototype.disconnect.call(this.device, this._stateChangedId);
-            this._stateChangedId = 0;
-        }
-        if (this._carrierChangedId) {
-            // see above for why this is needed
-            GObject.Object.prototype.disconnect.call(this.device, this._carrierChangedId);
-            this._carrierChangedId = 0;
-        }
-        if (this._firmwareChangedId) {
-            GObject.Object.prototype.disconnect.call(this.device, this._firmwareChangedId);
-            this._firmwareChangedId = 0;
-        }
+        this._setDevice(null);
 
         if (this._deferredWorkId) {
             // Just clear out, the actual removal is handled when the
@@ -369,6 +350,33 @@ const NMDevice = new Lang.Class({
         this.section.destroy();
     },
 
+    _setDevice: function(device) {
+        if (device) {
+            this.device = device;
+            this.device._delegate = this;
+            this._stateChangedId = this.device.connect('state-changed', Lang.bind(this, this._deviceStateChanged));
+        } else if (this.device) {
+            this.device._delegate = null;
+
+            if (this._stateChangedId) {
+                // Need to go through GObject.Object.prototype because
+                // nm_device_disconnect conflicts with g_signal_disconnect
+                GObject.Object.prototype.disconnect.call(this.device, this._stateChangedId);
+                this._stateChangedId = 0;
+            }
+            if (this._carrierChangedId) {
+                GObject.Object.prototype.disconnect.call(this.device, this._carrierChangedId);
+                this._carrierChangedId = 0;
+            }
+            if (this._firmwareChangedId) {
+                GObject.Object.prototype.disconnect.call(this.device, this._firmwareChangedId);
+                this._firmwareChangedId = 0;
+            }
+
+            this.device = null;
+        }
+    },
+
     deactivate: function() {
         this.device.disconnect(null);
         return true;
@@ -383,7 +391,7 @@ const NMDevice = new Lang.Class({
         // Otherwise, if no connection is currently configured,
         // try automatic configuration (or summon the config dialog)
         if (this._connections.length == 1) {
-            this._client.activate_connection(this._connections[0].connection, this.device, null, null);
+            this._client.activate_connection(this._connections[0].connection, this.device || null, null, null);
             return true;
         } else if (this._connections.length == 0) {
             return this._activateAutomaticConnection();
@@ -403,7 +411,7 @@ const NMDevice = new Lang.Class({
     },
 
     get connected() {
-        return this.device.state == NetworkManager.DeviceState.ACTIVATED;
+        return this.device && this.device.state == NetworkManager.DeviceState.ACTIVATED;
     },
 
     clearActiveConnection: function(activeConnection) {
@@ -436,6 +444,9 @@ const NMDevice = new Lang.Class({
     },
 
     getStatusLabel: function() {
+        if (!this.device)
+            return null;
+
         switch(this.device.state) {
         case NetworkManager.DeviceState.DISCONNECTED:
         case NetworkManager.DeviceState.ACTIVATED:
@@ -486,7 +497,7 @@ const NMDevice = new Lang.Class({
     },
 
     syncDescription: function() {
-        if (this.device._description)
+        if (this.device && this.device._description)
             this.statusItem.label.text = this.device._description;
     },
 
@@ -1439,6 +1450,56 @@ const NMDeviceWireless = new Lang.Class({
     },
 });
 
+const NMDeviceVirtual = new Lang.Class({
+    Name: 'NMDeviceVirtual',
+    Extends: NMDeviceSimple,
+
+    _init: function(client, iface, connections) {
+        this.iface = iface;
+        this.parent(client, null, connections);
+        this.category = NMConnectionCategory.VIRTUAL;
+    },
+
+    _shouldShowConnectionList: function() {
+        return this.hasConnections();
+    },
+
+    connectionValid: function(connection) {
+        return connection.get_virtual_iface_name() == this.iface;
+    },
+
+    addConnection: function(connection) {
+        if (!this.device && !this.hasConnections())
+            this.statusItem.label.text = NMGtk.utils_get_connection_device_name(connection);
+
+        this.parent(connection);
+    },
+
+    adoptDevice: function(device) {
+        if (device.get_iface() == this.iface) {
+            this._setDevice(device);
+            if (device._description)
+                this.syncDescription();
+            this._updateStatusItem();
+            this.emit('state-changed');
+            return true;
+        } else
+            return false;
+    },
+
+    removeDevice: function(device) {
+        if (device == this.device) {
+            this._setDevice(null);
+            this._updateStatusItem();
+            this.emit('state-changed');
+        }
+    },
+
+    hasConnections: function() {
+        return this._connections.length != 0;
+    }
+});
+
 const NMVPNSection = new Lang.Class({
     Name: 'NMVPNSection',
     Extends: NMConnectionBased,
@@ -1619,6 +1680,7 @@ const NMApplet = new Lang.Class({
 
         this._nmDevices = [];
         this._devices = { };
+        this._virtualDevices = [ ];
 
         this._devices.wired = {
             section: new PopupMenu.PopupMenuSection(),
@@ -1629,6 +1691,15 @@ const NMApplet = new Lang.Class({
         this.menu.addMenuItem(this._devices.wired.section);
         this.menu.addMenuItem(new PopupMenu.PopupSeparatorMenuItem());
 
+        this._devices.virtual = {
+            section: new PopupMenu.PopupMenuSection(),
+            devices: [ ],
+        };
+
+        this._devices.virtual.section.actor.hide();
+        this.menu.addMenuItem(this._devices.virtual.section);
+        this.menu.addMenuItem(new PopupMenu.PopupSeparatorMenuItem());
+
         this._devices.wireless = {
             section: new PopupMenu.PopupMenuSection(),
             devices: [ ],
@@ -1662,6 +1733,12 @@ const NMApplet = new Lang.Class({
         this._dtypes[NetworkManager.DeviceType.INFINIBAND] = NMDeviceSimple;
         // TODO: WiMax support
 
+        // Virtual device types
+        this._vtypes = { };
+        this._vtypes[NetworkManager.SETTING_VLAN_SETTING_NAME] = NMDeviceVirtual;
+        this._vtypes[NetworkManager.SETTING_BOND_SETTING_NAME] = NMDeviceVirtual;
+        this._vtypes[NetworkManager.SETTING_BRIDGE_SETTING_NAME] = NMDeviceVirtual;
+
         // Connection types
         this._ctypes = { };
         this._ctypes[NetworkManager.SETTING_WIRELESS_SETTING_NAME] = NMConnectionCategory.WIRELESS;
@@ -1672,6 +1749,9 @@ const NMApplet = new Lang.Class({
         this._ctypes[NetworkManager.SETTING_CDMA_SETTING_NAME] = NMConnectionCategory.WWAN;
         this._ctypes[NetworkManager.SETTING_GSM_SETTING_NAME] = NMConnectionCategory.WWAN;
         this._ctypes[NetworkManager.SETTING_INFINIBAND_SETTING_NAME] = NMConnectionCategory.WIRED;
+        this._ctypes[NetworkManager.SETTING_VLAN_SETTING_NAME] = NMConnectionCategory.VIRTUAL;
+        this._ctypes[NetworkManager.SETTING_BOND_SETTING_NAME] = NMConnectionCategory.VIRTUAL;
+        this._ctypes[NetworkManager.SETTING_BRIDGE_SETTING_NAME] = NMConnectionCategory.VIRTUAL;
         this._ctypes[NetworkManager.SETTING_VPN_SETTING_NAME] = NMConnectionCategory.VPN;
 
         this._settings = NMClient.RemoteSettings.new(null);
@@ -1723,7 +1803,14 @@ const NMApplet = new Lang.Class({
         let devices = this._devices[category].devices;
         let item = this._devices[category].item;
         let section = this._devices[category].section;
-        if (devices.length == 0)
+
+        let visible;
+        if (category == NMConnectionCategory.VIRTUAL)
+            visible = !section.isEmpty();
+        else
+            visible = devices.length > 0;
+
+        if (!visible)
             section.actor.hide();
         else {
             section.actor.show();
@@ -1784,30 +1871,14 @@ const NMApplet = new Lang.Class({
                               MessageTray.Urgency.HIGH);
     },
 
-    _makeWrapperDevice: function(wrapperClass, device) {
-        let wrapper = new wrapperClass(this._client, device, this._connections);
-
-        wrapper._activationFailedId = wrapper.connect('activation-failed',
-                                                      Lang.bind(this, this._onActivationFailed));
-        wrapper._deviceStateChangedId = wrapper.connect('state-changed', Lang.bind(this, function(dev) {
-            this._syncSectionTitle(dev.category);
-        }));
-        wrapper._destroyId = wrapper.connect('destroy', function(wrapper) {
-            wrapper.disconnect(wrapper._activationFailedId);
-            wrapper.disconnect(wrapper._deviceStateChangedId);
-            wrapper.disconnect(wrapper._destroyId);
-        });
-
-        return wrapper;
-    },
-
     _syncDeviceNames: function() {
         if (NMGtk) {
             let names = NMGtk.utils_disambiguate_device_names(this._nmDevices);
             for (let i = 0; i < this._nmDevices.length; i++) {
                 let device = this._nmDevices[i];
                 device._description = names[i];
-                device._delegate.syncDescription();
+                if (device._delegate)
+                    device._delegate.syncDescription();
             }
         } else {
             for (let i = 0; i < this._nmDevices.length; i++) {
@@ -1822,41 +1893,75 @@ const NMApplet = new Lang.Class({
             // already seen, not adding again
             return;
         }
+
+        for (let i = 0; i < this._virtualDevices.length; i++) {
+            if (this._virtualDevices[i].adoptDevice(device)) {
+                this._nmDevices.push(device);
+                if (!skipSyncDeviceNames)
+                    this._syncDeviceNames();
+                return;
+            }
+        }
+
         let wrapperClass = this._dtypes[device.get_device_type()];
         if (wrapperClass) {
-            let wrapper = this._makeWrapperDevice(wrapperClass, device);
-            let section = this._devices[wrapper.category].section;
-            let devices = this._devices[wrapper.category].devices;
-
-            section.addMenuItem(wrapper.statusItem);
-            section.addMenuItem(wrapper.section);
-            devices.push(wrapper);
+            let wrapper = new wrapperClass(this._client, device, this._connections);
+            this._addDeviceWrapper(wrapper);
 
             this._nmDevices.push(device);
             if (!skipSyncDeviceNames)
                 this._syncDeviceNames();
-
-            this._syncSectionTitle(wrapper.category);
         }
     },
 
+    _addDeviceWrapper: function(wrapper) {
+        wrapper._activationFailedId = wrapper.connect('activation-failed',
+                                                      Lang.bind(this, this._onActivationFailed));
+        wrapper._deviceStateChangedId = wrapper.connect('state-changed', Lang.bind(this, function(dev) {
+            this._syncSectionTitle(dev.category);
+        }));
+        wrapper._destroyId = wrapper.connect('destroy', function(wrapper) {
+            wrapper.disconnect(wrapper._activationFailedId);
+            wrapper.disconnect(wrapper._deviceStateChangedId);
+            wrapper.disconnect(wrapper._destroyId);
+        });
+
+        let section = this._devices[wrapper.category].section;
+        section.addMenuItem(wrapper.statusItem);
+        section.addMenuItem(wrapper.section);
+
+        this._syncSectionTitle(wrapper.category);
+
+        let devices = this._devices[wrapper.category].devices;
+        devices.push(wrapper);
+    },
+
     _deviceRemoved: function(client, device) {
-        if (!device._delegate) {
+        let pos = this._nmDevices.indexOf(device);
+        if (pos != -1) {
+            this._nmDevices.splice(pos, 1);
+            this._syncDeviceNames();
+        }
+
+        let wrapper = device._delegate;
+        if (!wrapper) {
             log('Removing a network device that was not added');
             return;
         }
 
-        let wrapper = device._delegate;
+        if (wrapper instanceof NMDeviceVirtual)
+            wrapper.removeDevice(device);
+        else
+            this._removeDeviceWrapper(wrapper);
+    },
+
+    _removeDeviceWrapper: function(wrapper) {
         wrapper.destroy();
 
         let devices = this._devices[wrapper.category].devices;
         let pos = devices.indexOf(wrapper);
         devices.splice(pos, 1);
 
-        pos = this._nmDevices.indexOf(device);
-        this._nmDevices.splice(pos, 1);
-        this._syncDeviceNames();
-
         this._syncSectionTitle(wrapper.category)
     },
 
@@ -2051,6 +2156,13 @@ const NMApplet = new Lang.Class({
                 devices[i].removeConnection(connection);
         }
 
+        if (section == NMConnectionCategory.VIRTUAL) {
+            let iface = connection.get_virtual_iface_name();
+            let wrapper = this._findVirtualDevice(iface);
+            if (wrapper && !wrapper.hasConnections())
+                this._removeDeviceWrapper(wrapper);
+        }
+
         connection.disconnect(connection._removedId);
         connection.disconnect(connection._updatedId);
         connection._removedId = connection._updatedId = 0;
@@ -2064,6 +2176,27 @@ const NMApplet = new Lang.Class({
 
         let section = connection._section;
 
+        if (section == NMConnectionCategory.VIRTUAL) {
+            let wrapperClass = this._vtypes[connection._type];
+            if (!wrapperClass)
+                return;
+
+            let iface = connection.get_virtual_iface_name();
+            let wrapper = this._findVirtualDevice(iface);
+            if (!wrapper) {
+                wrapper = new wrapperClass(this._client, iface, this._connections);
+                this._addDeviceWrapper(wrapper);
+                this._virtualDevices.push(wrapper);
+
+                // We might already have a device for this connection
+                for (let i = 0; i < this._nmDevices.length; i++) {
+                    let device = this._nmDevices[i];
+                    if (wrapper.adoptDevice(device))
+                        break;
+                }
+            }
+        }
+
         if (section == NMConnectionCategory.INVALID)
             return;
         if (section == NMConnectionCategory.VPN) {
@@ -2076,6 +2209,15 @@ const NMApplet = new Lang.Class({
         }
     },
 
+    _findVirtualDevice: function(iface) {
+        for (let i = 0; i < this._virtualDevices.length; i++) {
+            if (this._virtualDevices[i].iface == iface)
+                return this._virtualDevices[i];
+        }
+
+        return null;
+    },
+
     _hideDevices: function() {
         this._devicesHidden = true;
 
@@ -2091,6 +2233,7 @@ const NMApplet = new Lang.Class({
         this._statusSection.actor.hide();
 
         this._syncSectionTitle(NMConnectionCategory.WIRED);
+        this._syncSectionTitle(NMConnectionCategory.VIRTUAL);
         this._syncSectionTitle(NMConnectionCategory.WIRELESS);
         this._syncSectionTitle(NMConnectionCategory.WWAN);
     },
@@ -2128,6 +2271,7 @@ const NMApplet = new Lang.Class({
                 this.setIcon('network-wireless-acquiring-symbolic');
                 break;
             case NMConnectionCategory.WIRED:
+            case NMConnectionCategory.VIRTUAL:
                 this.setIcon('network-wired-acquiring-symbolic');
                 break;
             default:
@@ -2167,6 +2311,7 @@ const NMApplet = new Lang.Class({
                     break;
                 }
             case NMConnectionCategory.WIRED:
+            case NMConnectionCategory.VIRTUAL:
                 this.setIcon('network-wired-symbolic');
                 break;
             case NMConnectionCategory.WWAN:



[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]