[node-patches] Change in ovirt-node[master]: Add NetworkManager support if available and usable

fabiand at fedoraproject.org fabiand at fedoraproject.org
Tue Dec 11 20:09:36 UTC 2012


Fabian Deutsch has uploaded a new change for review.

Change subject: Add NetworkManager support if available and usable
......................................................................

Add NetworkManager support if available and usable

If NetworkManager bindings are available use fast-path methods where
possible.

Change-Id: I97bd1236dafc5b416d132142689f143a1ffde16d
Signed-off-by: Fabian Deutsch <fabiand at fedoraproject.org>
---
M scripts/tui/src/ovirt/node/plugins/security_page.py
M scripts/tui/src/ovirt/node/utils/network.py
M scripts/tui/src/ovirt/node/utils/process.py
M scripts/tui/src/ovirt/node/utils/virt.py
4 files changed, 171 insertions(+), 18 deletions(-)


  git pull ssh://gerrit.ovirt.org:29418/ovirt-node refs/changes/06/9906/1

diff --git a/scripts/tui/src/ovirt/node/plugins/security_page.py b/scripts/tui/src/ovirt/node/plugins/security_page.py
index 947aba4..681e041 100644
--- a/scripts/tui/src/ovirt/node/plugins/security_page.py
+++ b/scripts/tui/src/ovirt/node/plugins/security_page.py
@@ -54,7 +54,8 @@
 
     def validators(self):
         return {
-                "stringrng.bytes_used": ovirt.node.valid.Number(min=0) | ovirt.node.valid.Empty,
+                "stringrng.bytes_used": ovirt.node.valid.Number(min=0) | \
+                                        ovirt.node.valid.Empty,
                 "passwd.admin.password": ovirt.node.valid.Text(),
                 "passwd.admin.password_confirmation": ovirt.node.valid.Text(),
             }
@@ -67,7 +68,8 @@
                 [("yes", "Yes"), ("no", "No")])),
             ("ssh._divider", ovirt.node.ui.Divider()),
 
-            ("strongrng._label", ovirt.node.ui.Header("Strong Random Number Generator")),
+            ("strongrng._label", ovirt.node.ui.Header(
+                                            "Strong Random Number Generator")),
             ("strongrng.enabled", ovirt.node.ui.Options(
                 "Enable AES-NI",
                 [("yes", "Yes"), ("no", "No")])),
@@ -76,7 +78,8 @@
 
 
             ("passwd._label", ovirt.node.ui.Label("Local Access")),
-            ("passwd.admin.password", ovirt.node.ui.PasswordEntry("Password:")),
+            ("passwd.admin.password", ovirt.node.ui.PasswordEntry(
+                                                                "Password:")),
             ("passwd.admin.password_confirmation", ovirt.node.ui.PasswordEntry(
                 "Confirm Password:")),
         ]
diff --git a/scripts/tui/src/ovirt/node/utils/network.py b/scripts/tui/src/ovirt/node/utils/network.py
index 33b6dd8..bf2ffd0 100644
--- a/scripts/tui/src/ovirt/node/utils/network.py
+++ b/scripts/tui/src/ovirt/node/utils/network.py
@@ -22,18 +22,54 @@
 Some convenience functions related to networking
 """
 
-import gudev
+
 import os.path
 import logging
+import re
 from ovirt.node.utils import AugeasWrapper as Augeas
+import ovirt.node.utils.process as process
 
 LOGGER = logging.getLogger(__name__)
 
 
-def _query_udev_nics():
+#
+# Try to use NM if available
+#
+_nm_client = None
+try:
+    from gi.repository import NetworkManager, NMClient
+    import socket
+    import struct
+    NetworkManager
+    _nm_client = NMClient.Client.new()
+except Exception as e:
+    LOGGER.warning("NetworkManager support disabled: " +
+                   "NM Client not found (%s)" % e)
+import gudev
+
+
+class UnknownNicError(Exception):
+    pass
+
+
+def _query_udev_ifaces():
     client = gudev.Client(['net'])
     devices = client.query_by_subsystem("net")
     return [d.get_property("INTERFACE") for d in devices]
+
+
+def _nm_ifaces():
+    return [d.get_iface() for d in _nm_client.get_devices()]
+
+
+def nm_managed(iface):
+    """Wether an intreface is managed by NM or not (if it's running)
+    """
+    return _nm_client and iface in _nm_ifaces()
+
+
+def all_ifaces():
+    return _query_udev_ifaces()
 
 
 def all_nics():
@@ -45,13 +81,13 @@
     Returns:
         Dict with NIC (name, info) mappings for all known NICS
     """
-    return _collect_nic_informations(_query_udev_nics())
+    return _collect_nic_informations(_query_udev_ifaces())
 
 
 def _collect_nic_informations(nics):
     """Collects all NIC relevant informations for a list of NICs
 
-    >>> infos = _collect_nic_informations(_query_udev_nics())
+    >>> infos = _collect_nic_informations(_query_udev_ifaces())
     >>> "lo" in infos
     True
     >>> "driver" in infos["lo"]
@@ -86,7 +122,7 @@
         raise Exception("Couldn't gather informations for unknown NICs: %s" %
                         unknown_nics)
 
-    for name, info in infos.items():
+    for name, info in {k: v for k, v in infos.items() if k in nics}.items():
         LOGGER.debug("Getting additional information for '%s'" % name)
 
         # Driver
@@ -166,13 +202,13 @@
             n.startswith("vnet") or \
             n.startswith("tun") or \
             n.startswith("wlan") or \
-            (("." in n) and filter_vlans) or \
-            ((p["type"] == "bridge") and filter_bridges))
+            (filter_vlans and ("." in n)) or \
+            (filter_bridges and (p["type"] == "bridge")))
 
     relevant_nics = {n: p for n, p in all_nics().items() \
                           if not is_irrelevant(n, p)}
 
-    irrelevant_names = set(all_nics().keys()) - set(relevant_nics.keys())
+    irrelevant_names = set(all_ifaces()) - set(relevant_nics.keys())
     LOGGER.debug("Irrelevant interfaces: %s" % irrelevant_names)
 
     return relevant_nics
@@ -216,6 +252,23 @@
     return node_nics
 
 
+def node_bridge():
+    """Returns the main bridge of this node
+
+    Returns:
+        Bridge of this node
+    """
+
+    all_nics = relevant_nics(filter_bridges=False, filter_vlans=False)
+
+    bridges = [nic for nic, info in all_nics.items() \
+               if info["type"] == "bridge"]
+
+    assert len(bridges) == 1, "Expected only one bridge: %s" % bridges
+
+    return bridges[0]
+
+
 def _aug_get_or_set(augpath, new_servers=None):
     """Get or set some servers
     """
@@ -237,6 +290,7 @@
 
 def nameservers(new_servers=None):
     """Get or set DNS servers
+
     >>> len(nameservers()) > 0
     True
     """
@@ -246,8 +300,94 @@
 
 def timeservers(new_servers=None):
     """Get or set TIME servers
+
     >>> len(nameservers()) > 0
     True
     """
     augpath = "/files/etc/ntp.conf/server"
     return _aug_get_or_set(augpath, new_servers)
+
+
+def nic_link_detected(iface):
+    """Determin if L1 is up on a given interface
+
+    >>> nic_link_detected("lo")
+    True
+
+    >>> iface = all_ifaces()[0]
+    >>> cmd = "ip link set dev {dev} up ; ip link show {dev}".format(dev=iface)
+    >>> has_carrier = "LOWER_UP" in process.pipe(cmd, without_retval=True)
+    >>> has_carrier == nic_link_detected(iface)
+    True
+
+    Args:
+        iface: The interface to be checked
+    Returns:
+        True if L1 (the-link-is-up) is detected (depends on driver support)
+    """
+
+    if iface not in all_ifaces():
+        raise UnknownNicError("Unknown network interface: '%s'" % iface)
+
+    if nm_managed(iface):
+        device = _nm_client.get_device_by_iface(iface)
+        if device:
+            return device.get_carrier()
+
+    # Fallback
+    has_carrier = False
+    try:
+        with open("/sys/class/net/%s/carrier" % iface) as c:
+            content = c.read()
+            has_carrier = "1" in content
+    except:
+        LOGGER.debug("Carrier down for %s" % iface)
+    return has_carrier
+
+
+def nic_ip_addresses(iface, families=["inet", "inet6"]):
+    """Get IP addresses for an iface
+
+    FIXME NM client.get_device_by_iface(iface).get_ip?_config()
+    """
+    if iface not in all_ifaces():
+        raise UnknownNicError("Unknown network interface: '%s'" % iface)
+
+    addresses = {f: None for f in families}
+
+    if nm_managed(iface):
+        device = _nm_client.get_device_by_iface(iface)
+        if device:
+            for family, addrs in [
+                ("inet", device.get_ipv4_config().get_addresses()),
+                ("inet6", device.get_ipv6_config().get_addresses())]:
+                addr = addrs[0] if len(addrs) > 0 else None
+                addresses[family] = _nm_address_to_str(addr) if addr else None
+        return addresses
+
+    # Fallback
+    cmd = "ip -o addr show {dev}".format(dev=iface)
+    for line in process.pipe(cmd, without_retval=True).split("\n"):
+        token = re.split("\s+", line)
+        if re.search("\sinet[6]\s", line):
+            addresses[token[1]] = token[3]
+
+    return addresses
+
+
+def _nm_address_to_str(ipaddr):
+    packed = struct.pack('L', ipaddr)
+    return socket.inet_ntoa(packed)
+
+
+def networking_status():
+    status = "Not connected"
+
+    bridge = node_bridge()
+    addresses = nic_ip_addresses(bridge)
+    has_address = any([a != None for a in addresses.values()])
+
+    if nic_link_detected(bridge) and has_address:
+        status = "Connected"
+
+    return (status, bridge, addresses)
diff --git a/scripts/tui/src/ovirt/node/utils/process.py b/scripts/tui/src/ovirt/node/utils/process.py
index b433ef8..546a234 100644
--- a/scripts/tui/src/ovirt/node/utils/process.py
+++ b/scripts/tui/src/ovirt/node/utils/process.py
@@ -55,12 +55,21 @@
     return popen(cmd, shell=True).wait()
 
 
-def pipe(cmd, stdin=None):
+def pipe(cmd, stdin=None, without_retval=False):
     """Run a command interactively and cath it's output.
     This functions allows to pass smoe input to a running command.
 
+    >>> r = pipe("echo -n Hi")
+    >>> type(r[1])
+    <type 'str'>
+
+    >>> r
+    (True, 'Hi')
+
     Args:
         cmd: Commandline to be run
+        stdin: Optional string passed as stdin
+        without_retval: Optional if no retval should be passed
 
     Returns:
         A tuple (success, stdout)
@@ -74,6 +83,8 @@
         LOGGER.debug("out '%s'" % stdout)
     if stderr:
         LOGGER.warning("error '%s'" % stderr)
+    if without_retval:
+        return stdout
     return (system_cmd.returncode == 0, stdout)
 
 
diff --git a/scripts/tui/src/ovirt/node/utils/virt.py b/scripts/tui/src/ovirt/node/utils/virt.py
index 433b813..e99a527 100644
--- a/scripts/tui/src/ovirt/node/utils/virt.py
+++ b/scripts/tui/src/ovirt/node/utils/virt.py
@@ -25,7 +25,6 @@
 import os.path
 import logging
 import libvirt
-import ovirt.node.utils.process as process
 
 LOGGER = logging.getLogger(__name__)
 
@@ -41,10 +40,10 @@
     has_module = False
     with open("/proc/modules") as modules:
         for line in modules:
-           has_module = (line.startswith("kvm_intel") or \
-                         line.startswith("kvm_amd"))
-           if has_module:
-               break
+            has_module = (line.startswith("kvm_intel") or \
+                          line.startswith("kvm_amd"))
+            if has_module:
+                break
 
     if has_module and os.path.exists("/dev/kvm"):
         has_virtualization = True
@@ -64,7 +63,7 @@
             for line in cpuinfo:
                 if line.startswith("flags"):
                     if "vmx" in line or "svm" in line:
-                        is_enabled= True
+                        is_enabled = True
     return is_enabled
 
 


--
To view, visit http://gerrit.ovirt.org/9906
To unsubscribe, visit http://gerrit.ovirt.org/settings

Gerrit-MessageType: newchange
Gerrit-Change-Id: I97bd1236dafc5b416d132142689f143a1ffde16d
Gerrit-PatchSet: 1
Gerrit-Project: ovirt-node
Gerrit-Branch: master
Gerrit-Owner: Fabian Deutsch <fabiand at fedoraproject.org>



More information about the node-patches mailing list