4
\$\begingroup\$

I am trying to change static to dhcp in the /etc/network/interfaces file and vice versa using Python.

def setMode(mode,port,interfaces):
    #If the port's mode is not configured in the interface file, add a line for it, and return
    if "iface "+port+" inet " not in interfaces:
        newLine="\niface "+port+" inet "+mode
        interfaces="".join([interfaces,newLine])
        return interfaces

    #split the string by port to get to the line containing the port's ip mode
    #then split the third element of the resulting list by newline to isolate the end of that line
    split1=interfaces.partition(port+" inet ")
    split2=split1[2].partition("\n")
    #split the isolated line by space to get each word in it. the last element in the resulting list will be the current ip mode
    line=split2[0].split(" ")
    #if the current mode matches the new mode, return
    if line[len(line)-1] == mode:
        line=" ".join(line)
        split2="".join([line,split2[1],split2[2]])
        interfaces="".join([split1[0],split1[1],split2])
        return interfaces
    #otherwise set the last element to the new mode
    line[len(line)-1]=mode
    #join the line and lines back to recreate the full string
    line=" ".join(line)
    split2="".join([line,split2[1],split2[2]])
    interfaces="".join([split1[0],split1[1],split2])

    return interfaces

This is the code I am taking over with. I am using str.partition() to achieve this. mode == static or dhcp, port = eth8 or eth0, etc, interfaces = copy of interfaces file.

/etc/network/interfaces looks like this:

auto lo
iface lo inet loopback

auto eth8
iface eth8 inet static
address 10.8.100.36
netmask 255.255.0.0
gateway 10.8.1.1
\$\endgroup\$
3
  • \$\begingroup\$ Does the code work as intended? \$\endgroup\$ Sep 9 '14 at 19:55
  • \$\begingroup\$ It does not. It does create the newLine section at the end of the /etc/network/interfaces but have yet to get it to be able to change static to dhcp. I am not too confident if the partitioning is being done right. \$\endgroup\$
    – Keith
    Sep 9 '14 at 19:58
  • \$\begingroup\$ I think it works, though. \$\endgroup\$ Sep 9 '14 at 20:22
4
\$\begingroup\$

The splitting / partitioning makes this very complicated and hard to follow. Instead of splitting and then putting the pieces back together, it would be vastly simpler to replace with a regex, for example:

import re


def set_mode(mode, iface, interfaces):
    """
    Set the mode for the specified interface
    :param mode: 'dhcp' or 'static'
    :param iface: name of the interface, for example eth1, eth2, ...
    :param interfaces: content of /etc/network/interfaces
    :return: updated content, to be saved in /etc/network/interfaces
    """
    iface_prefix = 'iface {} inet '.format(iface)
    iface_entry = iface_prefix + mode
    if iface_prefix in interfaces:
        interfaces = re.sub(iface_prefix + r'.*', iface_entry, interfaces)
    else:
        interfaces += '\n' + iface_entry
    return interfaces

For your sample file, the following gives me the same output with the original method and this new one:

with open('./interfaces') as fh:
    content = fh.read()
    print set_mode('static', 'eth8', content)
    print '---'
    print set_mode('dhcp', 'eth8', content)
    print '---'
    print set_mode('dhcp', 'eth1', content)

Coding style

The original code has several issues that violate PEP8, the official coding style guide of Python:

  • Use snake_case for function and variable names (that's why I renamed setMode to set_mode
  • Put spaces around operators: a=something instead of a = something
  • A docstring is strongly recommended for the set_mode function
\$\endgroup\$
5
  • \$\begingroup\$ This makes much more sense to me then the partition code that there was. Hopefully I can take this style and expand it to the rest of the document I am working on. I have just started working in python and have already been given a challenging task and dead line. \$\endgroup\$
    – Keith
    Sep 10 '14 at 12:02
  • \$\begingroup\$ I also did notice the .format(iface). From my experiences I have seen the %s later to be followed up by %iface. Is one method better or just a preference? \$\endgroup\$
    – Keith
    Sep 10 '14 at 12:05
  • \$\begingroup\$ I don't know if .format(...) is preferred over ... % ... style, or the other way around. I see more and more experienced Python programmers (on this site) prefer .format(...), but I don't have proper evidence or authoritative source. \$\endgroup\$
    – janos
    Sep 10 '14 at 12:14
  • \$\begingroup\$ Does the r'.*' just mean anything else on the line? \$\endgroup\$
    – Keith
    Sep 10 '14 at 12:34
  • \$\begingroup\$ It means "0 or more of any character": it should match everything until the end of the line. \$\endgroup\$
    – janos
    Sep 10 '14 at 12:37
4
\$\begingroup\$

Interface

I see several problems with def setMode(mode,port,interfaces):

  • In the interfaces(5) man page, what you call the "mode" is called "method", and the "port" is called "interface". To reduce confusion, avoid inventing non-standard terminology.
  • PEP 8 says:

    Function Names

    Function names should be lowercase, with words separated by underscores as necessary to improve readability.

    mixedCase is allowed only in contexts where that's already the prevailing style (e.g. threading.py), to retain backwards compatibility.

I propose changing the function signature to def set_method(config, iface, method).

I've also chosen to reverse the parameters to make more sense to me: start with an existing configuration, find the relevant interface, and set its method.

Strategy

That's a lot of code to accomplish a somewhat simple task. It's also fragile: an extra space in, say, iface eth0 inet dhcp could throw it off.

Fundamentally, the whole problem is just a fancy string substitution: replace a string that looks like iface iface inet method, or append such a line to the end if no such interface is found. Fancy string substitutions are best done using regular expressions.

import re

def set_method(config, iface, method):
    return re.sub(r'\n?iface[ ]+%s[ ]+inet(.*)|\Z' % (re.escape(iface)),
                  '\niface %s inet %s' % (iface, method),
                  config,
                  count=1)

Basically, that says, find a string that looks like iface iface inet anything else on the line, optionally preceded by a Newline, and with words separated by at least one space. If no such line is found, then operate on the end of the string instead. Replace whatever was found with the desired line.

\$\endgroup\$
2
  • \$\begingroup\$ Thank You, what you are saying makes a lot of sense. Splitting up the string and putting it back together makes it challenging. I took over this code and am just starting to get into python. It is a fun language but a lot of tricks. To me it seems as if there are going to be a lot of problems with this code I was given. I spent most of my day yesterday attempting to figure out how to output to the terminal window when the script is ran so I can debug. I know data is being transmitted via Json but I have no idea how that works with python. I have a lot of catching up to do with no time. \$\endgroup\$
    – Keith
    Sep 10 '14 at 11:55
  • \$\begingroup\$ Sadly I was unable to figure out how to output segments of code for debugging purposes. \$\endgroup\$
    – Keith
    Sep 10 '14 at 11:56

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.