from nepi.execution.attribute import Attribute, Types, Flags from nepi.execution.resource import ResourceManager, clsinit, ResourceState from nepi.resources.linux.node import LinuxNode from nepi.resources.linux.channel import LinuxChannel import collections import logging import os import random import re import tempfile import time # TODO: UP, MTU attributes! reschedule_delay = "0.5s" @clsinit class LinuxInterface(ResourceManager): _rtype = "LinuxInterface" @classmethod def _register_attributes(cls): ip4 = Attribute("ip4", "IPv4 Address", flags = Flags.ExecReadOnly) ip6 = Attribute("ip6", "IPv6 Address", flags = Flags.ExecReadOnly) mac = Attribute("mac", "MAC Address", flags = Flags.ExecReadOnly) mask4 = Attribute("mask4", "IPv4 network mask", flags = Flags.ExecReadOnly) mask6 = Attribute("mask6", "IPv6 network mask", type = Types.Integer, flags = Flags.ExecReadOnly) mtu = Attribute("mtu", "Maximum transmition unit for device", type = Types.Integer) devname = Attribute("deviceName", "Name of the network interface (e.g. eth0, wlan0, etc)", flags = Flags.ExecReadOnly) up = Attribute("up", "Link up", type = Types.Bool) tear_down = Attribute("tearDown", "Bash script to be executed before " + \ "releasing the resource", flags = Flags.ExecReadOnly) cls._register_attribute(ip4) cls._register_attribute(ip6) cls._register_attribute(mac) cls._register_attribute(mask4) cls._register_attribute(mask6) cls._register_attribute(mtu) cls._register_attribute(devname) cls._register_attribute(up) cls._register_attribute(tear_down) def __init__(self, ec, guid): super(LinuxInterface, self).__init__(ec, guid) self._configured = False self._logger = logging.getLogger("LinuxInterface") self.add_set_hooks() def log_message(self, msg): return " guid %d - host %s - %s " % (self.guid, self.node.get("hostname"), msg) @property def node(self): node = self.get_connected(LinuxNode.rtype()) if node: return node[0] return None @property def channel(self): chan = self.get_connected(LinuxChannel.rtype()) if chan: return chan[0] return None def discover(self, filters = None): devname = self.get("deviceName") ip4 = self.get("ip4") ip6 = self.get("ip4") mac = self.get("mac") mask4 = self.get("mask4") mask6 = self.get("mask6") mtu = self.get("mtu") # Get current interfaces information (out, err), proc = self.node.execute("ifconfig", sudo = True) if err and proc.poll(): msg = " Error retrieving interface information " self.error(msg, out, err) raise RuntimeError, "%s - %s - %s" % (msg, out, err) # Check if an interface is found matching the RM attributes ifaces = out.split("\n\n") for i in ifaces: m = re.findall("(\w+)\s+Link\s+encap:\w+(\s+HWaddr\s+(([0-9a-fA-F]{2}:?){6}))?(\s+inet\s+addr:((\d+\.?){4}).+Mask:(\d+\.\d+\.\d+\.\d+))?(.+inet6\s+addr:\s+([0-9a-fA-F:.]+)/(\d+))?(.+(UP))?(.+MTU:(\d+))?", i, re.DOTALL) m = m[0] dn = m[0] mc = m[2] i4 = m[5] msk4 = m[7] i6 = m[9] msk6 = m[10] up = True if m[12] else False mu = m[14] self.debug("Found interface %(devname)s with MAC %(mac)s," "IPv4 %(ipv4)s %(mask4)s IPv6 %(ipv6)s/%(mask6)s %(up)s %(mtu)s" % ({ 'devname': dn, 'mac': mc, 'ipv4': i4, 'mask4': msk4, 'ipv6': i6, 'mask6': msk6, 'up': up, 'mtu': mu }) ) # If the user didn't provide information we take the first # interface that is UP if not devname and not ip4 and not ip6 and up: self._configured = True self.load_configuration(dn, mc, i4, msk4, i6, msk6, mu, up) break # If the user provided ipv4 or ipv6 matching that of an interface # load the interface info if (ip4 and ip4 == i4) or (ip6 and ip6 == i6): self._configured = True self.load_configuration(dn, mc, i4, msk4, i6, msk6, mu, up) break # If the user provided the device name we load the associated info if devname and devname == dn: if ((ip4 and ip4 == i4) and (ipv6 and ip6 == i6)) or \ not (ip4 or ip6): self._configured = True # If the user gave a different ip than the existing, asume ip # needs to be changed i4 = ip4 or i4 i6 = ip6 or i6 mu = mtu or mu self.load_configuration(dn, mc, i4, msk4, i6, msk6, mu, up) break if not self.get("deviceName"): msg = "Unable to resolve interface " self.error(msg) raise RuntimeError, msg super(LinuxInterface, self).discover(filters = filters) def provision(self, filters = None): devname = self.get("deviceName") ip4 = self.get("ip4") ip6 = self.get("ip4") mac = self.get("mac") mask4 = self.get("mask4") mask6 = self.get("mask6") mtu = self.get("mtu") # Must configure interface if configuration is required if not self._configured: cmd = "ifconfig %s" % devname if ip4 and mask4: cmd += " %(ip4)s netmask %(mask4)s broadcast %(bcast)s up" % ({ 'ip4': ip4, 'mask4': mask4, 'bcast': bcast}) if mtu: cmd += " mtu %d " % mtu (out, err), proc = self.node.execute(cmd, sudo = True) if err and proc.poll(): msg = "Error configuring interface with command '%s'" % cmd self.error(msg, out, err) raise RuntimeError, "%s - %s - %s" % (msg, out, err) if ip6 and mask6: cmd = "ifconfig %(devname)s inet6 add %(ip6)s/%(mask6)d" % ({ 'devname': devname, 'ip6': ip6, 'mask6': mask6}) (out, err), proc = self.node.execute(cmd, sudo = True) if err and proc.poll(): msg = "Error seting ipv6 for interface using command '%s' " % cmd self.error(msg, out, err) raise RuntimeError, "%s - %s - %s" % (msg, out, err) super(LinuxInterface, self).provision(filters = filters) def deploy(self): # Wait until node is provisioned node = self.node chan = self.channel if not node or node.state < ResourceState.PROVISIONED: self.ec.schedule(reschedule_delay, self.deploy) elif not chan or chan.state < ResourceState.READY: self.ec.schedule(reschedule_delay, self.deploy) else: # Verify if the interface exists in node. If not, configue # if yes, load existing configuration try: self.discover() self.provision() except: self._state = ResourceState.FAILED raise super(LinuxInterface, self).deploy() def release(self): tear_down = self.get("tearDown") if tear_down: self.execute(tear_down) super(LinuxInterface, self).release() def valid_connection(self, guid): # TODO: Validate! return True def load_configuration(self, devname, mac, ip4, mask4, ip6, mask6, mtu, up): self.set("deviceName", devname) self.set("mac", mac) self.set("ip4", ip4) self.set("mask4", mask4) self.set("ip6", ip6) self.set("mask6", mask6) # set the following without validating or triggering hooks attr = self._attrs["up"] attr._value = up attr = self._attrs["mtu"] def add_set_hooks(self): attrup = self._attrs["up"] attrup.set_hook = self.set_hook_up attrmtu = self._attrs["mtu"] attrmtu.set_hook = self.set_hook_mtu def set_hook_up(self, oldval, newval): if oldval == newval: return oldval # configure interface up if newval == True: cmd = "ifup %s" % self.get("deviceName") elif newval == False: cmd = "ifdown %s" % self.get("deviceName") (out, err), proc = self.node.execute(cmd, sudo = True) if err and proc.poll(): msg = "Error setting interface up/down using command '%s' " % cmd self.error(msg, err, out) return oldval return newval def set_hook_mtu(self, oldval, newval): if oldval == newval: return oldval cmd = "ifconfig %s mtu %d" % (self.get("deviceName"), newval) (out, err), proc = self.node.execute(cmd, sudo = True) if err and proc.poll(): msg = "Error setting interface MTU using command '%s' " % cmd self.error(msg, err, out) return oldval return newval