From fc19b0fd3dfe28a13bf752068414509d8889e31a Mon Sep 17 00:00:00 2001 From: Sunil Mohan Adapa Date: Sun, 11 Oct 2015 16:16:54 +0530 Subject: [PATCH] networks: Refactor code for showing connection - Fix showing configured IP address in edit form. - Combine the retrival functions and organize them according to where the information is being retrived from connection/device/active connection/access point. - Add more fields to show such as ether speed, default ipv4/ipv6 connection, wifi mode, etc. - Re-format the page. Separate ipv4/ipv6 sections. Separate device information and connection information sections. - Take the action buttons to the top. - Make the activate/deactivate button work with new POST only CSRF requirement. - Update Firewall zone description messages. - Show all IP addresses of the device. --- plinth/modules/networks/networks.py | 90 ++-- .../networks/templates/connection_show.html | 419 +++++++++++------- plinth/network.py | 343 +++++--------- 3 files changed, 406 insertions(+), 446 deletions(-) diff --git a/plinth/modules/networks/networks.py b/plinth/modules/networks/networks.py index c4a368242..703133ee9 100644 --- a/plinth/modules/networks/networks.py +++ b/plinth/modules/networks/networks.py @@ -66,71 +66,41 @@ def show(request, uuid): 'Connection not found.')) return redirect(reverse_lazy('networks:index')) - name = connection.get_interface_name() - connectiontype = connection.get_connection_type() - settings_ipv4 = connection.get_setting_ip4_config() + # Connection status + connection_status = network.get_status_from_connection(connection) - mac = network.get_mac_from_device(name) - interface = connection.get_interface_name() - if connectiontype == '802-11-wireless': - settings_wireless = connection.get_setting_wireless() - ssid = settings_wireless.get_ssid().get_data() - rate = network.get_wifi_rate(interface, ssid) - channel = network.get_wifi_channel(interface, ssid) - strength = network.get_wifi_signal(interface, ssid) - linkstate = True + # Active connection status + try: + active_connection = network.get_active_connection(uuid) + active_connection_status = \ + network.get_status_from_active_connection(active_connection) + except network.ConnectionNotFound: + active_connection_status = {} + active_connection = None + + # Device status + if active_connection and active_connection.get_devices(): + device = active_connection.get_devices()[0] else: - ssid = "None" - rate = 0 - channel = 0 - linkstate = network.get_linkstate_from_device(name) - strength = 0 + interface_name = connection_status['interface_name'] + if interface_name: + device = network.get_device_by_interface_name(interface_name) - ip = network.get_all_ip_from_device(name) - ip6 = network.get_all_ip6_from_device(name) - dns = network.get_namesever_from_device(name) - dns6 = network.get_namesever6_from_device(name) - gateway = network.get_gateway_from_device(name) - gateway6 = network.get_gateway6_from_device(name) - method = settings_ipv4.get_method() + device_status = network.get_status_from_device(device) - zone = connection.get_setting_connection().get_zone() - active = network.connection_is_active(uuid) - - if network.get_primary_connection().get_id() == connection.get_id(): - primary = True - else: - primary = False - - if not ip: - ip.append("0.0.0.0/0") - - if not ip6: - ip6.append("::0/0") + # Access point status + access_point_status = None + if connection_status['type'] == '802-11-wireless': + access_point_status = network.get_status_from_wifi_access_point( + device, connection_status['wireless']['ssid']) return TemplateResponse(request, 'connection_show.html', {'title': _('Show Connection information'), - 'ip': ip, - 'ip6': ip6, - 'gateway': gateway, - 'gateway6': gateway6, - 'dns': dns, - 'dns6': dns6, - 'interface': interface, - 'mac': mac, - 'linkstate': linkstate, - 'zone': zone, - 'primary': primary, 'subsubmenu': subsubmenu, - 'method': method, - 'connectiontype': connectiontype, - 'ssid': ssid, - 'strength': strength, - 'rate': rate, - 'channel': channel, - 'active': active, - 'uuid': uuid, - 'name': name}) + 'connection': connection_status, + 'active_connection': active_connection_status, + 'device': device_status, + 'access_point': access_point_status}) def edit(request, uuid): @@ -203,9 +173,9 @@ def edit(request, uuid): if settings_connection.get_connection_type() != 'pppoe': settings_ipv4 = connection.get_setting_ip4_config() form_data['ipv4_method'] = settings_ipv4.get_method() - - name = connection.get_interface_name() - form_data['ipv4_address'] = network.get_ip_from_device(name) + address = network.get_first_ip_address_from_connection(connection) + if address: + form_data['ipv4_address'] = address if settings_connection.get_connection_type() == '802-11-wireless': settings_wireless = connection.get_setting_wireless() diff --git a/plinth/modules/networks/templates/connection_show.html b/plinth/modules/networks/templates/connection_show.html index f657a30da..c0e560578 100644 --- a/plinth/modules/networks/templates/connection_show.html +++ b/plinth/modules/networks/templates/connection_show.html @@ -23,164 +23,279 @@ .list-group-item .btn { margin: -5px 0; } + .form-inline { + display: inline; + } {% endblock %} {% load bootstrap %} {% block content %} - {% if active %} -

Physical Link Information

+
+
+
+ Edit - {% if linkstate and connectiontype == "802-3-ethernet" %} - cable is connected -

- {% elif connectiontype == "802-3-ethernet" %} - please check cable -

- {% endif %} -
-
-
- {% if connectiontype == "802-11-wireless" %} -
- SSID - {{ ssid }} -
-
- Speed - {{ rate }} -
-
- Signal - - - {{ strength }}% - - -
-
- Channel - {{ channel }} -
- {% endif %} -
- MAC adress - {{ mac }} -
-
- Interface - {{ interface }} -
-
-
-
+ {% if active_connection %} +
+ {% csrf_token %} + +
+ {% else %} +
+ {% csrf_token %} + +
+ {% endif %} -

IP Address Information

+ Delete +
-
-
-
- {% for addr in ip %} -
- IPv4 Address - {{ addr }} - -
- {% endfor %} - {% for addr in ip6 %} -
- IPv6 Address - {{ addr }} - -
- {% endfor %} - {% for server in dns %} -
- DNS Server - {{ server }} -
- {% endfor %} - {% for server in dns6 %} -
- DNS Server - {{ server }} -
- {% endfor %} - {% if gateway %} -
- IPv4 Default Gateway - {{ gateway }} -
- {% endif %} - {% if gateway6 %} -
- IPv6 Default Gateway - {{ gateway6 }} -
- {% endif %} - {% if method == "shared" %} -
- DHCP Server - enabled -
- {% elif method == "auto" %} -
- IP Method - {{ method }} - -
- {% endif %} - {% if primary %} -
- Default Connection - - yes - -
- {% endif %} -
-
-
-

Security Information

- {% if zone == "internal" %} - Firewall: {{ zone }}

- INFO: This interface should be connected to local network.
- If you connect this interface to a public - network, others may have access to your data. - {% elif zone == "external" %} - Firewall: {{ zone }}

- INFO: This interface should be connected to your internet upstream - connection - {% else %} - Firewall: {{ zone }}

- WARNING: This interface is not assigned to a zone which is maintained by - freedombox - {% endif %} - {% else %} - This connections is not active. Please activate connection. - {% endif %} -

- - Edit - - {% if active %} - - Deactivate - - {% else %} - - Activate - - {% endif %} - - Delete - +

Connection

+ +
+ {% if connection.primary %} +
+ Primary connection +
+ yes +
+
+ {% endif %} +
+ Name +
{{ connection.id }}
+
+
+ +

Device

+ +
+
+ State + {{ device.state }} +
+ {% if device.state_reason != 'none' %} +
+ State reason + {{ device.state_reason }} +
+ {% endif %} +
+ Type + {{ device.type }} +
+
+ MAC address + {{ device.hw_address }} +
+
+ Interface + {{ device.interface_name }} +
+
+ Description + {{ device.description }} +
+
+ +

Physical Link

+ +
+ {% if device.ethernet %} +
+ Link state +
+ {% if device.ethernet.carrier %} + cable is connected + {% else %} + please check cable + {% endif %} +
+
+
+ Speed + {{ device.ethernet.speed }} Mbit/s +
+ {% endif %} + + {% if connection.type == "802-11-wireless" %} +
+ SSID + {{ connection.wireless.ssid }} +
+
+ Speed + {{ device.wireless.bitrate }} Mbit/s +
+
+ Mode + {{ device.wireless.mode }} +
+ {% if access_point.channel %} +
+ Signal strength +
+ + {{ access_point.strength }}% + +
+
+ {% endif %} + {% if access_point.channel %} +
+ Channel + {{ access_point.channel }} +
+ {% endif %} + {% endif %} +
+ + {% if active_connection %} +

IPv4

+ +
+ {% if connection.ipv4.method %} +
+ Method + {{ connection.ipv4.method }} +
+ {% endif %} + + {% for address in device.ip4.addresses %} +
+ IP address + + {{ address.address }}/{{ address.prefix }} + +
+ {% endfor %} + + {% if device.ip4.gateway %} +
+ Gateway + {{ device.ip4.gateway }} +
+ {% endif %} + + {% for server in device.ip4.nameservers %} +
+ DNS server + {{ server }} +
+ {% endfor %} + + {% if active_connection.ip4.default %} +
+ Default + yes +
+ {% endif %} +
+ +

IPv6

+ +
+ {% if connection.ipv6.method %} +
+ Method + {{ connection.ipv6.method }} +
+ {% endif %} + + {% for address in device.ip6.addresses %} +
+ IP address + {{ address.address }}/{{ address.prefix }} +
+ {% endfor %} + + {% if device.ip6.gateway %} +
+ Gateway + {{ device.ip6.gateway }} +
+ {% endif %} + + {% for server in device.ip6.nameservers %} +
+ DNS server + {{ server }} +
+ {% endfor %} + + {% if active_connection.ip6.default %} +
+ Default + yes +
+ {% endif %} +
+ {% else %} +

Status

+ +

This connection is not active.

+ {% endif %} + +

Security

+ + {% if connection.zone == "internal" %} +
+
+ Firewall zone +
+ {{ connection.zone }} +
+
+
+ +
+ + This interface should be connected to local network/machine. If you + connect this interface to a public network, services meant to + be available only internally will become available externally. + This is a security risk. +
+ {% elif connection.zone == "external" %} +
+
+ Firewall zone +
+ {{ connection.zone }} +
+
+
+ +
+ + This interface should receive your Internet connection. If + you connect it your a local network/machine, many services + meant to available only internally will not be available. +
+ {% else %} +
+
+ Firewall zone +
+ {{ connection.zone }} +
+
+
+ +
+ + This interface is not maintained by FreedomBox. Its security + status is unknown to FreedomBox. Many FreedomBox services may + not be available on this interface. It is recommended that + you deactivate/delete this connection and re-configure it. +
+ {% endif %} +
+
{% endblock %} diff --git a/plinth/network.py b/plinth/network.py index fb2794ef8..e75192592 100644 --- a/plinth/network.py +++ b/plinth/network.py @@ -25,6 +25,7 @@ from gi.repository import NM as nm import logging import socket import struct +import subprocess import uuid @@ -76,254 +77,123 @@ def get_interface_list(device_type): return interfaces -def get_first_wifi_device(): - """Get a list of network interface available on the system.""" - interface = "empty" - for device in nm.Client.new(None).get_devices(): - if device.get_device_type() == nm.DeviceType.WIFI: - interface = device.get_iface() - return interface +def get_status_from_connection(connection): + """Return the current status of a connection.""" + status = collections.defaultdict(dict) - return interface + status['id'] = connection.get_id() + status['uuid'] = connection.get_uuid() + status['type'] = connection.get_connection_type() + status['zone'] = connection.get_setting_connection().get_zone() + status['interface_name'] = connection.get_interface_name() + + status['ipv4']['method'] = connection.get_setting_ip4_config().get_method() + status['ipv6']['method'] = connection.get_setting_ip6_config().get_method() + + if status['type'] == '802-11-wireless': + setting_wireless = connection.get_setting_wireless() + status['wireless']['ssid'] = setting_wireless.get_ssid().get_data() + + primary_connection = nm.Client.new(None).get_primary_connection() + status['primary'] = (primary_connection.get_uuid() == connection.get_uuid()) + + return status -def get_ip_from_device(devicename): - """ - Get the first ip address from the network interface. - will return "0.0.0.0" if no ip address is assiged. - IP address is a optional information, will not raise a exception - if no information could be returned. - ToDo: will not work when connection is or previously was inactive - """ - ip = "0.0.0.0" - device = nm.Client.new(None).get_device_by_iface(devicename) +def get_status_from_active_connection(connection): + """Return the current status of an active connection.""" + status = collections.defaultdict(dict) + + status['state'] = connection.get_state().value_name + status['ip4']['default'] = connection.get_default() + status['ip6']['default'] = connection.get_default6() + + return status + + +def get_status_from_device(device): + """Return a dictionary with current status of a network device.""" + if not device: + return None + + status = collections.defaultdict(dict) + ip4_config = device.get_ip4_config() if ip4_config: addresses = ip4_config.get_addresses() - if addresses: - ip = addresses.__getitem__(0).get_address() - return ip + status['ip4']['addresses'] = [{'address': address.get_address(), + 'prefix': address.get_prefix()} + for address in addresses] + status['ip4']['gateway'] = ip4_config.get_gateway() + status['ip4']['nameservers'] = ip4_config.get_nameservers() - -def get_ip6_from_device(devicename): - """ - Get the first ip address from the network interface. - will return "0.0.0.0" if no ip address is assiged. - IP address is a optional information, will not raise a exception - if no information could be returned. - ToDo: will not work when connection is or previously was inactive - """ - ip = "0.0.0.0" - device = nm.Client.new(None).get_device_by_iface(devicename) ip6_config = device.get_ip6_config() if ip6_config: addresses = ip6_config.get_addresses() - if addresses: - ip = addresses.__getitem__(0).get_address() - return ip + status['ip6']['addresses'] = [{'address': address.get_address(), + 'prefix': address.get_prefix()} + for address in addresses] + status['ip6']['gateway'] = ip6_config.get_gateway() + status['ip6']['nameservers'] = ip6_config.get_nameservers() + + status['type'] = device.get_type_description() + status['description'] = device.get_description() + status['hw_address'] = device.get_hw_address() + status['interface_name'] = device.get_iface() + status['state'] = device.get_state().value_nick + status['state_reason'] = device.get_state_reason().value_nick + + if device.get_device_type() == nm.DeviceType.WIFI: + status['wireless']['bitrate'] = device.get_bitrate() / 1000 + status['wireless']['mode'] = device.get_mode().value_nick + + if device.get_device_type() == nm.DeviceType.ETHERNET: + status['ethernet']['speed'] = device.get_speed() + status['ethernet']['carrier'] = device.get_carrier() + + return status -def get_all_ip_from_device(devicename): +def get_status_from_wifi_access_point(device, ssid): + """Return the current status of an access point.""" + status = {} + + for access_point in device.get_access_points(): + if access_point.get_ssid().get_data() == ssid: + status['strength'] = access_point.get_strength() + frequency = access_point.get_frequency() + status['channel'] = _get_wifi_channel_from_frequency(frequency) + break + + return status + + +def _get_wifi_channel_from_frequency(frequency): + """Get the wifi channel form a particular SSID""" + # TODO: Hard coded list of wifi frequencys and their corresponding + # channel numbers. Search for a better solution! Even 5GHz is + # not included yet. Only the plain frequency will show up on 5GHz + # AP's. + channel_map = {2412: 1, 2417: 2, 2422: 3, 2427: 4, 2432: 5, 2437: 6, + 2442: 7, 2447: 8, 2452: 9, 2457: 10, 2462: 11} + try: + return channel_map[frequency] + except KeyError: + return str(frequency / 1000) + 'GHz' + + +def get_first_ip_address_from_connection(connection): + """Return the first IP address of a connection setting. + + XXX: Work around a bug in NetworkManager/Python GI. Remove after + the bug if fixed. + https://bugzilla.gnome.org/show_bug.cgi?id=756380. """ - Get all ipv4 addresses from device - IP address is a optional information, will not raise a exception - if no information could be returned. - ToDo: will not work when connection is or previously was inactive - """ - ip = [] - device = nm.Client.new(None).get_device_by_iface(devicename) - ip4_config = device.get_ip4_config() - if ip4_config: - addresses = ip4_config.get_addresses() - for address in addresses: - netmask = str(address.get_prefix()) - ip.append(str(address.get_address() + "/" + netmask)) - return ip + command = ['nmcli', '--terse', '--mode', 'tabular', '--fields', + 'ipv4.addresses', 'connection', 'show', connection.get_uuid()] - -def get_all_ip6_from_device(devicename): - """ - Get all ipv6 addresses from device - IP address is a optional information, will not raise a exception - if no information could be returned. - ToDo: will not work when connection is or previously was inactive - """ - ip = [] - device = nm.Client.new(None).get_device_by_iface(devicename) - ip6_config = device.get_ip6_config() - if ip6_config: - addresses = ip6_config.get_addresses() - for address in addresses: - netmask = str(address.get_prefix()) - ip.append(str(address.get_address() + "/" + netmask)) - return ip - - -def get_namesever_from_device(devicename): - """ - Get all nameservers which are reachable via this device. - Nameservers is a optional information, will not raise a exception - if no information could be returned. - ToDo: will not work when connection is or previously was inactive - """ - nameservers = "" - device = nm.Client.new(None).get_device_by_iface(devicename) - ip4_config = device.get_ip4_config() - if ip4_config: - nameservers = ip4_config.get_nameservers() - return nameservers - - -def get_namesever6_from_device(devicename): - """ - Get all nameservers which are reachable via this device. - Nameservers is a optional information, will not raise a exception - if no information could be returned. - ToDo: will not work when connection is or previously was inactive - """ - nameservers = "" - device = nm.Client.new(None).get_device_by_iface(devicename) - ip6_config = device.get_ip6_config() - if ip6_config: - nameservers = ip6_config.get_nameservers() - return nameservers - - -def get_gateway_from_device(devicename): - """ - Get the default Gateway for this device. - gateway is a optional information, will not raise a exception - if no information could be returned. - ToDo: will not work when connection is or previously was inactive - """ - gateway = "" - device = nm.Client.new(None).get_device_by_iface(devicename) - ip4_config = device.get_ip4_config() - if ip4_config: - gateway = device.get_ip4_config().get_gateway() - return gateway - - -def get_gateway6_from_device(devicename): - """ - Get the default Gateway for this device. - gateway is a optional information, will not raise a exception - if no information could be returned. - ToDo: will not work when connection is or previously was inactive - """ - gateway = "" - device = nm.Client.new(None).get_device_by_iface(devicename) - ip6_config = device.get_ip6_config() - if ip6_config: - gateway = device.get_ip6_config().get_gateway() - return gateway - - -def get_linkstate_from_device(devicename): - """ - Get the physical link state from this device (carrier detected) - link state is a optional information, will not raise a exception - if no information could be returned. - ToDo: will not work when connection is or previously was inactive - """ - linkstate = False - device = nm.Client.new(None).get_device_by_iface(devicename) - ip4_config = device.get_ip4_config() - if ip4_config: - linkstate = device.get_carrier() - return linkstate - - -def get_mac_from_device(devicename): - """ - Get the MAC address of the network interface. - MAC address is a optional information, will not raise a exception - if no information could be returned. - ToDo: will not work when connection is or previously was inactive - """ - mac = "00:00:00:00:00:00" - device = nm.Client.new(None).get_device_by_iface(devicename) - ip4_config = device.get_ip4_config() - if ip4_config: - mac = device.get_hw_address() - return mac - - -def connection_is_active(connection_uuid): - """ - Return True if connection is active - Return False if connection is inactive - """ - client = nm.Client.new(None) - for connection in client.get_active_connections(): - if connection.get_uuid() == connection_uuid: - return True - return False - - -def get_primary_connection(): - """return the name of the primary (aka internet) connection""" - return nm.Client.new(None).get_primary_connection() - - -def get_wifi_signal(devicename, ssid): - """Get the wifi signal strenght form a particular SSID""" - signal = 0 - device = nm.Client.new(None).get_device_by_iface(devicename) - for ap in device.get_access_points(): - if ap.get_ssid().get_data() == ssid: - signal = ap.get_strength() - return signal - - -def get_wifi_rate(devicename, ssid): - """Get the wifi bitrate form a particular SSID""" - device = nm.Client.new(None).get_device_by_iface(devicename) - rate = device.get_bitrate() / 1000 - rate = str(str(rate) + "Mbit/s") - return rate - - -def get_wifi_channel(devicename, ssid): - """Get the wifi channeƶ form a particular SSID""" - channel = 0 - device = nm.Client.new(None).get_device_by_iface(devicename) - for ap in device.get_access_points(): - if ap.get_ssid().get_data() == ssid: - frequency = ap.get_frequency() - frequency = frequency / 1000 - - """ - Hard coded list of wifi frequencys and their corresponding channel numbers. - ToDo: search for a better solution! Even 5GHz is not included yet. - Only the plain frequency will show up on 5GHz AP's. - """ - if frequency == 2.412: - channel = 1 - elif frequency == 2.417: - channel = 2 - elif frequency == 2.422: - channel = 3 - elif frequency == 2.427: - channel = 4 - elif frequency == 2.432: - channel = 5 - elif frequency == 2.437: - channel = 6 - elif frequency == 2.442: - channel = 7 - elif frequency == 2.447: - channel = 8 - elif frequency == 2.452: - channel = 9 - elif frequency == 2.457: - channel = 10 - elif frequency == 2.462: - channel = 11 - else: - channel = str(str(frequency) + "GHz") - - return channel + output = subprocess.check_output(command).decode() + return output.strip().split(', ')[0].split('/')[0] def get_connection_list(): @@ -379,6 +249,11 @@ def get_active_connection(connection_uuid): raise ConnectionNotFound(connection_uuid) +def get_device_by_interface_name(interface_name): + """Return a device by interface name.""" + return nm.Client.new(None).get_device_by_iface(interface_name) + + def _update_common_settings(connection, connection_uuid, name, type_, interface, zone): """Create/edit basic settings for network manager connections.