1
0
mirror of https://github.com/CumulusNetworks/ifupdown2.git synced 2024-05-06 15:54:50 +00:00

python-ifupdown initial checkin

Ticket: CM-1438
Reviewed By: TBD
Testing Done:

- Will checkin build files after some more testing and performance
  numbers. It will go into the testing repo for 2.0

- All TODO items are part of the checked in TODO file
This commit is contained in:
roopa
2013-11-04 06:06:11 -08:00
commit a6f80f0e0b
22 changed files with 3099 additions and 0 deletions

39
README Normal file
View File

@ -0,0 +1,39 @@
python-ifupdown2
================
This package is a replacement for the debian ifupdown package.
It is completely re-written in python. It maintains the original ifupdown
pluggable architecture and extends it further.
The python-ifupdown2 package provides the infrastructure for
parsing /etc/network/interfaces file, loading, scheduling and state
management of interfaces.
It dynamically loads python modules from /usr/share/ifupdownmodules (provided
by the python-ifupdown2-addons package). To remain compatible with other
packages that depend on ifupdown, it also executes scripts under /etc/network/.
To make the transition smoother, a python module under
/usr/share/ifupdownmodules will override a script by the same name under
/etc/network/.
It publishes an interface object which is passed to all loadble python
modules (more details about the interface object is available in the
python docs).
pluggable python modules:
=========================
Unlike original ifupdown, all interface configuration is moved to external
python modules. That includes inet, inet6 and dhcp configurations.
It expects a few things from the pluggable modules:
- the module should implement a class by the same name
- the interface object (class iface) and the operation to be performed is
passed to the modules
- the python class should provide a few methods:
- run() : method to configure the interface.
- get_ops() : must return a list of operations it supports.
eg: 'pre-up', 'post-down'
- get_dependent_ifacenames() : must return a list of interfaces the
interface is dependent on. This is used to build the dependency list
for sorting and executing interfaces in parallel.

19
TODO Normal file
View File

@ -0,0 +1,19 @@
TODO:
====
- support for /etc/networking.defaults
- Implement the ifupdown interface mapping feature
- Fix parallel implementation
- Test all original ifupdown options for compatibility
- Test scale
- Test with ifupdown-extra, ifmetric, ifupdown-scripts-zg2
- export other environment variables to scripts:
IFACE physical name of the interface being processed
LOGICAL logical name of the interface being processed
ADDRFAM address family of the interface
METHOD method of the interface (e.g., static)
MODE start if run from ifup, stop if run from ifdown
PHASE as per MODE, but with finer granularity, distinguishing the pre-
up, post-up, pre-down and post-down phases.
VERBOSITY indicates whether --verbose was used; set to 1 if so, 0 if not.
PATH the command search path: /usr/local/sbin:/usr/local/bin:
/usr/sbin:/usr/bin:/sbin:/bin

67
debian/python-ifupdown2.postinst vendored Normal file
View File

@ -0,0 +1,67 @@
#!/bin/sh
set -e
MYNAME="${0##*/}"
report() { echo "${MYNAME}: $*" ; }
report_warn() { report "Warning: $*" >&2 ; }
report_err() { report "Error: $*" >&2 ; }
case "$1" in
configure)
# Create /etc/network/run
[ -d /run/network ] || mkdir -p /run/network
# for backward compatibility
ln -sf /run/network /etc/network/run
[ -d /etc/network/if-pre-up.d ] || mkdir -p /etc/network/if-pre-up.d
[ -d /etc/network/if-up.d ] || mkdir -p /etc/network/if-up.d
[ -d /etc/network/if-post-up.d ] || mkdir -p /etc/network/if-post-up.d
[ -d /etc/network/if-pre-down.d ] || mkdir -p /etc/network/if-pre-down.d
[ -d /etc/network/if-down.d ] || mkdir -p /etc/network/if-down.d
[ -d /etc/network/if-post-down.d ] || mkdir -p /etc/network/if-post-down.d
# Generic stuff done on all configurations
if [ -f /etc/network/interfaces ] ; then
# TODO: This should be handled with debconf and the script
# could introduce the line there directly
if ! grep -q "^[[:space:]]*iface[[:space:]]\+lo0\?[[:space:]]\+inet[[:space:]]\+loopback\>" /etc/network/interfaces ; then
report_warn "No 'iface lo' definition found in /etc/network/interfaces"
fi
if ! grep -q "^[[:space:]]*\(allow-\|\)auto[[:space:]]\+\(.*[[:space:]]\+\|\)lo0\?\([[:space:]]\+\|$\)" /etc/network/interfaces ; then
report_warn "No 'auto lo' statement found in /etc/network/interfaces"
fi
else # ! -f /etc/network/interfaces
if [ -z "$2" ]; then
echo "Creating /etc/network/interfaces."
echo "# interfaces(5) file used by ifup(8) and ifdown(8)" > /etc/network/interfaces
echo "auto lo" >> /etc/network/interfaces
echo "iface lo inet loopback" >> /etc/network/interfaces
else
report_warn "/etc/network/interfaces does not exist"
fi
fi
[ -e /sbin/ifup ] || ln -s /sbin/ifupdown /sbin/ifup
[ -e /sbin/ifdown ] || ln -s /sbin/ifupdown /sbin/ifdown
[ -e /sbin/ifquery ] || ln -s /sbin/ifupdown /sbin/ifquery
;;
purge)
# Note: We don't remove /etc/network/interfaces
rm -f /run/network/ifstate
rm -f /sbin/ifquery
rm -f /sbin/ifup
rm -f /sbin/ifdown
;;
esac
if [ -x "/etc/init.d/networking" ]; then
update-rc.d networking start 40 S . start 35 0 6 . >/dev/null || exit $?
fi
#DEBHELPER#

20
debian/python-ifupdown2.postrm vendored Normal file
View File

@ -0,0 +1,20 @@
#!/bin/sh
set -e
case "$1" in
purge)
# Note: We don't remove /etc/network/interfaces
rm -f /etc/network/run/ifstate
if [ -L /etc/network/run ] ; then
rm -f /etc/network/run
elif [ -d /etc/network/run ] ; then
rmdir --ignore-fail-on-non-empty /etc/network/run
fi
;;
esac
if [ "$1" = "purge" ] ; then
update-rc.d networking remove >/dev/null
fi

194
init.d/networking Normal file
View File

@ -0,0 +1,194 @@
#!/bin/sh -e
### BEGIN INIT INFO
# Provides: networking ifupdown
# Required-Start: mountkernfs $local_fs urandom
# Required-Stop: $local_fs
# Default-Start: S
# Default-Stop: 0 6
# Short-Description: Raise network interfaces.
# Description: Prepare /run/network directory, ifstate file and raise network interfaces, or take them down.
### END INIT INFO
PATH="/sbin:/bin"
RUN_DIR="/run/network"
IFSTATE="$RUN_DIR/ifstate"
[ -x /sbin/ifup ] || exit 0
[ -x /sbin/ifdown ] || exit 0
. /lib/lsb/init-functions
CONFIGURE_INTERFACES=yes
EXCLUDE_INTERFACES=
VERBOSE=no
[ -f /etc/default/networking ] && . /etc/default/networking
[ "$VERBOSE" = yes ] && verbose=-v
process_exclusions() {
set -- $EXCLUDE_INTERFACES
exclusions=""
for d
do
exclusions="-X $d $exclusions"
done
echo $exclusions
}
process_options() {
[ -e /etc/network/options ] || return 0
log_warning_msg "/etc/network/options still exists and it will be IGNORED! Please use /etc/sysctl.conf instead."
}
check_ifstate() {
if [ ! -d "$RUN_DIR" ] ; then
if ! mkdir -p "$RUN_DIR" ; then
log_failure_msg "can't create $RUN_DIR"
exit 1
fi
fi
if [ ! -r "$IFSTATE" ] ; then
if ! :> "$IFSTATE" ; then
log_failure_msg "can't initialise $IFSTATE"
exit 1
fi
fi
}
check_network_file_systems() {
[ -e /proc/mounts ] || return 0
if [ -e /etc/iscsi/iscsi.initramfs ]; then
log_warning_msg "not deconfiguring network interfaces: iSCSI root is mounted."
exit 0
fi
while read DEV MTPT FSTYPE REST; do
case $DEV in
/dev/nbd*|/dev/nd[a-z]*|/dev/etherd/e*)
log_warning_msg "not deconfiguring network interfaces: network devices still mounted."
exit 0
;;
esac
case $FSTYPE in
nfs|nfs4|smbfs|ncp|ncpfs|cifs|coda|ocfs2|gfs|pvfs|pvfs2|fuse.httpfs|fuse.curlftpfs)
log_warning_msg "not deconfiguring network interfaces: network file systems still mounted."
exit 0
;;
esac
done < /proc/mounts
}
check_network_swap() {
[ -e /proc/swaps ] || return 0
while read DEV MTPT FSTYPE REST; do
case $DEV in
/dev/nbd*|/dev/nd[a-z]*|/dev/etherd/e*)
log_warning_msg "not deconfiguring network interfaces: network swap still mounted."
exit 0
;;
esac
done < /proc/swaps
}
ifup_hotplug () {
if [ -d /sys/class/net ]
then
ifaces=$(for iface in $(ifquery --list --allow=hotplug)
do
link=${iface##:*}
link=${link##.*}
if [ -e "/sys/class/net/$link" ] && [ "$(cat /sys/class/net/$link/operstate)" = up ]
then
echo "$iface"
fi
done)
if [ -n "$ifaces" ]
then
ifup $ifaces "$@" || true
fi
fi
}
case "$1" in
start)
if init_is_upstart; then
exit 1
fi
process_options
check_ifstate
if [ "$CONFIGURE_INTERFACES" = no ]
then
log_action_msg "Not configuring network interfaces, see /etc/default/networking"
exit 0
fi
set -f
exclusions=$(process_exclusions)
log_action_begin_msg "Configuring network interfaces"
if ifup -a $exclusions $verbose && ifup_hotplug $exclusions $verbose
then
log_action_end_msg $?
else
log_action_end_msg $?
fi
;;
stop)
if init_is_upstart; then
exit 0
fi
check_network_file_systems
check_network_swap
log_action_begin_msg "Deconfiguring network interfaces"
if ifdown -a --exclude=lo $verbose; then
log_action_end_msg $?
else
log_action_end_msg $?
fi
;;
reload)
process_options
log_action_begin_msg "Reloading network interfaces configuration"
state=$(cat /run/network/ifstate)
ifdown -a --exclude=lo $verbose || true
if ifup --exclude=lo $state $verbose ; then
log_action_end_msg $?
else
log_action_end_msg $?
fi
;;
force-reload|restart)
if init_is_upstart; then
exit 1
fi
process_options
log_warning_msg "Running $0 $1 is deprecated because it may not re-enable some interfaces"
log_action_begin_msg "Reconfiguring network interfaces"
ifdown -a --exclude=lo $verbose || true
set -f
exclusions=$(process_exclusions)
if ifup -a --exclude=lo $exclusions $verbose && ifup_hotplug $exclusions $verbose
then
log_action_end_msg $?
else
log_action_end_msg $?
fi
;;
*)
echo "Usage: /etc/init.d/networking {start|stop|reload|restart|force-reload}"
exit 1
;;
esac
exit 0
# vim: noet ts=8

1
man/ifdown.8 Normal file
View File

@ -0,0 +1 @@
.so ifup.8

1
man/ifquery.8 Normal file
View File

@ -0,0 +1 @@
.so ifup.8

232
man/ifup.8 Normal file
View File

@ -0,0 +1,232 @@
.TH ifup 8 "22 May 2004" IFUPDOWN ""
.SH NAME
ifup \- bring a network interface up
.PP
ifdown \- take a network interface down
.PP
ifquery \- parse interface configuration
.SH SYNOPSIS
.B ifup
[\fB\-nv\fR]
[\fB\-\-no\-act\fR]
[\fB\-\-verbose\fR]
[\fB\-i\fR \fIFILE\fR|\fB\-\-interfaces=\fR\fIFILE\fR]
[\fB\-\-allow\fR \fICLASS\fR]
\fB\-a\fR|\fIIFACE\fR...
.br
.B ifup
\fB\-h\fR|\fB\-\-help\fR
.br
.B ifup
\fB\-V\fR|\fB\-\-version\fR
.PP
.B ifdown
[\fB\-nv\fR]
[\fB\-\-no\-act\fR]
[\fB\-\-verbose\fR]
[\fB\-i\fR \fIFILE\fR|\fB\-\-interfaces=\fR\fIFILE\fR]
[\fB\-\-allow\fR \fICLASS\fR]
\fB\-a\fR|\fIIFACE\fR...
.PP
.B ifquery
[\fB\-nv\fR]
[\fB\-\-no\-act\fR]
[\fB\-\-verbose\fR]
[\fB\-i\fR \fIFILE\fR|\fB\-\-interfaces=\fR\fIFILE\fR]
[\fB\-\-allow\fR \fICLASS\fR]
\fB\-a\fR|\fIIFACE\fR...
.PP
.B ifquery
\fB\-l\fR|\fB\-\-list\fR
[\fB\-nv\fR]
[\fB\-\-no\-act\fR]
[\fB\-\-verbose\fR]
[\fB\-i\fR \fIFILE\fR|\fB\-\-interfaces=\fR\fIFILE\fR]
[\fB\-\-allow\fR \fICLASS\fR]
\fB\-a\fR|\fIIFACE\fR...
.SH DESCRIPTION
The
.BR ifup " and " ifdown
commands may be used to configure (or, respectively, deconfigure) network
interfaces based on interface definitions in the file
.IR /etc/network/interfaces ". "
.BR ifquery " command may be used to parse interfaces configuration."
.SH OPTIONS
A summary of options is included below.
.TP
.BR \-a ", " \-\-all
If given to \fBifup\fP, affect all interfaces marked \fBauto\fP.
Interfaces are brought up in the order in which they are defined
in
.IR /etc/network/interfaces .
Combined with \fB-\-allow\fP, acts on all interfaces of a specified class
instead.
If given to \fBifdown\fP, affect all defined interfaces.
Interfaces are brought down in the order in which they are
currently listed in the state file. Only interfaces defined
in
.I /etc/network/interfaces
will be brought down.
.TP
.B \-\-force
Force configuration or deconfiguration of the interface.
.TP
.BR \-h ", " \-\-help
Show summary of options.
.TP
\fB\-\-allow=\fR\fICLASS\fR
Only allow interfaces listed in an
.I allow\-CLASS
line in
.IR /etc/network/interfaces " to be acted upon."
.TP
\fB\-i\fR \fIFILE\fR, \fB\-\-interfaces=\fR\fIFILE\fR
Read interface definitions from
.I FILE
instead of from
.IR /etc/network/interfaces "."
.TP
.BI \-X " PATTERN\fR, " "\-\-exclude=" PATTERN
Exclude interfaces from the list of interfaces to operate on by the \fIPATTERN\fR.
\fIPATTERN\fR uses a usual shell glob syntax. If shell wildcards are not used, it
must match the exact interface name. This option may be specified multiple times
resulting in more than one pattern being excluded.
.TP
.BI \-o " OPTION" "\fB=" VALUE
Set \fIOPTION\fR to \fIVALUE\fR as though it were in
.IR /etc/network/interfaces .
.TP
.BR \-n ", " \-\-no\-act
Don't configure any interfaces or run any "up" or "down" commands.
.TP
.B \-\-no\-mappings
Don't run any mappings. See
.BR interfaces (5)
for more information about the mapping feature.
.TP
.B \-\-no\-scripts
Don't run any scripts under /etc/network/if-*.d/
.TP
.BR \-V ", " \-\-version
Show copyright and version information.
.TP
.BR \-v ", " \-\-verbose
Show commands as they are executed.
.TP
.BR \-l ", " \-\-list
For \fBifquery\fR, list all the interfaces which match the specified class.
If no class specified, prints all the interfaces listed as \fBauto\fR.
.SH EXAMPLES
.TP
.B ifup -a
Bring up all the interfaces defined with
.I auto
in
.I /etc/network/interfaces
.TP
.B ifup eth0
Bring up interface
.B eth0
.TP
.B ifup eth0=home
Bring up interface
.B eth0
as logical interface
.B home
.TP
.B ifdown -a
Bring down all interfaces that are currently up.
.TP
.B ifquery -l
Print names of all interfaces specified with the \fBauto\fR keyword.
.TP
.B ifquery -l --allow=hotplug
Print names of all interfaces specified with the \fBallow-hotplug\fR keyword.
.TP
.B ifquery eth0
Display the interface options as specified in the \fBifupdown\fR
configuration. Each key-value pair is printed out on individual
line using "\fB: \fR" as separator.
.SH NOTES
.BR ifup ,
.BR ifdown ,
and
.BR ifquery
are actually the same program called by different names.
.P
The program does not configure network interfaces directly;
it runs low level utilities such as
.BR ip
to do its dirty work.
.P
When invoked,
.B ifdown
checks if
.B ifup
is still running. In that case,
.B SIGTERM
is sent to ifup.
.SH FILES
.TP
.I /etc/network/interfaces
definitions of network interfaces
See
.BR interfaces (5)
for more information.
.TP
.I /run/network/ifstate
current state of network interfaces
.SH KNOWN BUGS/LIMITATIONS
The program keeps records of whether network interfaces are up or down.
Under exceptional circumstances these records can become
inconsistent with the real states of the interfaces.
For example, an interface that was brought up using
.B ifup
and later deconfigured using
.B ifconfig
will still be recorded as up.
To fix this you can use the
.B \-\-force
option to force
.B ifup
or
.B ifdown
to run configuration or deconfiguration commands despite what
it considers the current state of the interface to be.
.P
The file
.I /run/network/ifstate
must be writable for
.B ifup
or
.B ifdown
to work properly.
If that location is not writable
(for example, because the root filesystem is mounted read-only
for system recovery)
then
.I /run/network/ifstate
should be made a symbolic link to a writable location.
If that is not possible then you can use the
.B \-\-force
option to run configuration or deconfiguration commands
without updating the file.
.P
Note that the program does not run automatically:
.B ifup
alone does not bring up interfaces
that appear as a result of hardware being installed and
.B ifdown
alone does not bring down interfaces
that disappear as a result of hardware being removed.
To automate the configuration of network interfaces you need to
install other packages such as
.BR udev (7)
or
.BR ifplugd (8).
.SH AUTHOR
The ifupdown suite was written by Anthony Towns <aj@azure.humbug.org.au>.
.SH SEE ALSO
.BR interfaces (5),
.BR ip (8),
.BR ifconfig (8).

310
man/interfaces.5 Normal file
View File

@ -0,0 +1,310 @@
.\" -*- nroff -*-
.\" macros
.de EX \" Begin Example
. IP
. ft CW
. nf
. ne \\$1
..
.de EE \" End Example
. ft P
. fi
. PP
..
.TH INTERFACES 5 "5 April 2004" "ifupdown" "File formats"
.SH NAME
/etc/network/interfaces \- network interface configuration for ifup and ifdown
.SH DESCRIPTION
/etc/network/interfaces contains network interface configuration
information for the
.BR ifup (8)
and
.BR ifdown (8)
commands.
This is where you configure how your system is connected to the network.
.P
Lines starting with `#' are ignored. Note that end-of-line comments are
NOT supported, comments must be on a line of their own.
.P
A line may be extended across multiple lines by making the last character
a backslash.
.P
The file consists of zero or more "iface", "mapping", "auto", "allow-" and
"source" stanzas. Here is an example.
.EX
auto lo eth0
allow-hotplug eth1
iface lo inet loopback
source interfaces.d/machine\-dependent
mapping eth0
script /usr/local/sbin/map\-scheme
map HOME eth0\-home
map WORK eth0\-work
iface eth0\-home inet static
address 192.168.1.1
netmask 255.255.255.0
up flush\-mail
iface eth0\-work inet dhcp
iface eth1 inet dhcp
.EE
Lines beginning with the word "auto" are used to identify the physical
interfaces to be brought up when
.B ifup
is run with the
.B \-a
option. (This option is used by the system boot scripts.)
Physical interface names should follow the word "auto" on the same line.
There can be multiple "auto" stanzas.
.B ifup
brings the named interfaces up in the order listed.
.P
Lines beginning with "allow-" are used to identify interfaces that should
be brought up automatically by various subsytems. This may be done using
a command such as "ifup \-\-allow=hotplug eth0 eth1", which will only bring
up eth0 or eth1 if it is listed in an "allow-hotplug" line. Note that
"allow-auto" and "auto" are synonyms.
.P
Lines beginning with "source" are used to include stanzas from other files,
so configuration can be split into many files. The word "source" is
followed by the path of file to be sourced. Shell wildcards can be
used.
(See
.BR wordexp (3)
for details.)
.P
Stanzas beginning with the word "mapping" are used to determine how a
logical interface name is chosen for a physical interface that is to be
brought up. The first line of a mapping stanza consists of the word
"mapping" followed by a pattern in shell glob syntax. Each mapping stanza
must contain a
.BR script
definition. The named script is run with the physical interface name as
its argument and with the contents of all following "map" lines
(\fBwithout\fR the leading "map") in the
stanza provided to it on its standard input. The script must print a
string on its standard output before exiting. See
.IR /usr/share/doc/ifupdown/examples
for examples of what the script must print.
.P
Mapping a name consists of searching the remaining mapping
patterns and running the script corresponding to the first match;
the script outputs the name to which the original is mapped.
.P
.B ifup
is normally given a physical interface name as its first non\-option argument.
.B ifup
also uses this name as the initial logical name for the interface unless
it is accompanied by a suffix of the form \fI=LOGICAL\fR, in which case
ifup chooses \fILOGICAL\fR as the initial logical name for the interface.
It then maps this name, possibly more than once according to successive
mapping specifications, until no further mappings are possible. If the
resulting name is the name of some defined logical interface then
.B ifup
attempts to bring up the physical interface
as that logical interface. Otherwise
.B ifup
exits with an error.
.P
Stanzas defining logical interfaces start with a line consisting of the
word "iface" followed by the name of the logical interface.
In simple configurations without mapping stanzas this name should simply
be the name of the physical interface to which it is to be applied.
(The default mapping script is, in effect, the
.B echo
command.)
The interface name is followed by the name of the address family that the
interface uses. This will be "inet" for TCP/IP networking, but there is
also some support for IPX networking ("ipx"), and IPv6 networking ("inet6").
Following that is the name of the method used to configure the interface.
.P
Additional options can be given on subsequent lines in the stanza.
Which options are available depends on the family and method,
as described below.
Additional options can be made available by other Debian packages.
For example, the wireless\-tools package makes available a number of
options prefixed with "wireless\-" which can be used to configure the
interface using
.BR iwconfig (8) .
(See
.BR wireless (7)
for details.)
.P
Options are usually indented for clarity (as in the example above)
but are not required to be.
.P
.SH VLAN AND BRIDGE INTERFACES
To ease the configuration of VLAN interfaces, interfaces having
.B .
(full stop character) in the name are configured as 802.1q tagged
virtual LAN interface. For example, interface
.B eth0.1
is a virtual interface having
.B eth0
as physical link, with VLAN ID 1.
.P
For compatibility with
.B bridge-utils
package, if
.B bridge_ports
option is specified, VLAN interface configuration is
.B not
performed.
.SH IFACE OPTIONS
The following "command" options are available for every family and method.
Each of these options can be given multiple times in a single stanza,
in which case the commands are executed in the order in which they appear
in the stanza.
(You can ensure a command never fails by suffixing "|| true".)
.TP
.BI pre\-up " command"
Run
.I command
before bringing the interface up.
If this command fails then
.B ifup
aborts,
refraining from marking the interface as configured,
prints an error message,
and exits with status 0.
This behavior may change in the future.
.TP
.BI up " command"
.TP
.BI post\-up " command"
Run
.I command
after bringing the interface up.
If this command fails then
.B ifup
aborts,
refraining from marking the interface as configured
(even though it has really been configured),
prints an error message,
and exits with status 0.
This behavior may change in the future.
.TP
.BI down " command"
.TP
.BI pre\-down " command"
Run
.I command
before taking the interface down.
If this command fails then
.B ifdown
aborts,
marks the interface as deconfigured
(even though it has not really been deconfigured),
and exits with status 0.
This behavior may change in the future.
.TP
.BI post\-down " command"
Run
.I command
after taking the interface down.
If this command fails then
.B ifdown
aborts,
marks the interface as deconfigured,
and exits with status 0.
This behavior may change in the future.
.P
There exists for each of the above mentioned options a directory
.IR /etc/network/if\-\fB<option>\fI.d/
the scripts in which are run (with no arguments) using
.BR run\-parts (8)
after the option itself has been processed. Please note that as
.BI post\-up
and
.BI pre\-down
are aliases, no files in the corresponding directories are processed.
Please use
.IR if-up.d
and
.IR if-down.d
directories instead.
.P
All of these commands have access to the following environment variables.
.TP
.B IFACE
physical name of the interface being processed
.TP
.B LOGICAL
logical name of the interface being processed
.TP
.B ADDRFAM
address family of the interface
.TP
.B METHOD
method of the interface (e.g.,
.IR static )
.TP
.B MODE
.IR start " if run from ifup, " stop " if run from ifdown"
.TP
.B PHASE
as per MODE, but with finer granularity, distinguishing the
\fIpre-up\fR, \fIpost-up\fR, \fIpre-down\fR and \fIpost-down\fR phases.
.TP
.B VERBOSITY
indicates whether \fB\-\-verbose\fR was used; set to 1 if so, 0 if not.
.TP
.B PATH
the command search path:
.I /usr/local/sbin:\%/usr/local/bin:\%/usr/sbin:\%/usr/bin:\%/sbin:\%/bin
.P
Additionally, all options given in an interface definition stanza are
exported to the environment in upper case with "IF_" prepended and with
hyphens converted to underscores and non\-alphanumeric characters discarded.
.P
When ifupdown is being called with the \fB\-\-all\fR option, before doing anything
to interfaces, if calls all the hook scripts (\fIpre-up\fR or \fIdown\fR) with
\fBIFACE\fR set to "\-\-all", \fBLOGICAL\fR set to the current value of \-\-allow
parameter (or "auto" if it's not set), \fBADDRFAM\fR="meta" and \fBMETHOD\fR="none".
After all the interfaces have been brought up or taken down, the appropriate scripts
(\fIup\fR or \fIpost-down\fR) are executed.
##ADDRESSFAM##
.SH KNOWN BUGS/LIMITATIONS
The
.B ifup
and
.B ifdown
programs work with so-called "physical" interface names.
These names are assigned to hardware by the kernel.
Unfortunately it can happen that the kernel assigns different
physical interface names to the same hardware at different
times; for example, what was called "eth0" last time you booted
is now called "eth1" and vice versa.
This creates a problem if you want to configure the interfaces
appropriately.
A way to deal with this problem is to use mapping scripts that
choose logical interface names according to the properties of
the interface hardware.
See the
.B get-mac-address.sh
script in the examples directory for an example of such a mapping
script. See also Debian bug #101728.
.SH AUTHOR
The ifupdown suite was written by Anthony Towns <aj@azure.humbug.org.au>.
This manpage was contributed by Joey Hess <joey@kitenet.net>.
.SH "SEE ALSO"
.BR ifup (8),
.BR ip (8),
.BR ifconfig (8),
.BR run\-parts (8),
.BR resolvconf (8).
.P
For advice on configuring this package read the
.B Network Configuration
chapter of the \fIDebian Reference\fR manual,
available at
\fIhttp://www.debian.org/doc/manuals/debian-reference/ch05.en.html\fR
or in the \fBdebian-reference-en\fR package.
.P
Examples of how to set up interfaces can be found in
.BR /usr/share/doc/ifupdown/examples/network-interfaces.gz .

0
pkg/__init__.py Normal file
View File

17
pkg/exceptions.py Normal file
View File

@ -0,0 +1,17 @@
#!/usr/bin/python
class Error(Exception):
"""Base class for exceptions in ifupdown"""
pass
class ifaceNotFoundError(Error):
pass
class invalidValueError(Error):
pass
class errorReadingStateError(Error):
pass

43
pkg/graph.py Normal file
View File

@ -0,0 +1,43 @@
#!/usr/bin/python
import logging
from collections import deque
class graph():
def __init__(self):
self.logger = logging.getLogger('ifupdown.' +
self.__class__.__name__)
@classmethod
def topological_sort(cls, dependency_graph, indegrees=None):
S = []
Q = deque()
for ifname,indegree in indegrees.items():
if indegree == 0:
Q.append(ifname)
while len(Q) != 0:
# initialize queue
x = Q.popleft()
# Get dependents of x
dlist = dependency_graph.get(x)
if dlist == None or len(dlist) == 0:
S.append(x)
continue
for y in dlist:
indegrees[y] = indegrees.get(y) - 1
if indegrees.get(y) == 0:
Q.append(y)
S.append(x)
# If some indegrees are non zero, we have a cycle
for ifname,indegree in indegrees.items():
if indegree != 0:
raise Exception('cycle found involving iface %s' %ifname)
return S

366
pkg/iface.py Normal file
View File

@ -0,0 +1,366 @@
#!/usr/bin/python
import logging
class ifaceFlags():
NONE = 0x1
FOLLOW_DEPENDENTS = 0x2
class ifaceStatus():
"""iface status """
UNKNOWN = 0x1
SUCCESS = 0x2
ERROR = 0x3
@classmethod
def to_str(cls, state):
if state == cls.UNKNOWN:
return 'unknown'
elif state == cls.SUCCESS:
return 'success'
elif state == cls.ERROR:
return 'error'
@classmethod
def from_str(cls, state_str):
if state_str == 'unknown':
return cls.UNKNOWN
elif state_str == 'success':
return cls.SUCCESS
elif state_str == 'error':
return cls.ERROR
class ifaceState():
""" iface states """
UNKNOWN = 0x1
NEW = 0x2
PRE_UP = 0x3
UP = 0x4
POST_UP = 0x5
PRE_DOWN = 0x6
DOWN = 0x7
POST_DOWN = 0x8
@classmethod
def to_str(cls, state):
if state == cls.UNKNOWN:
return 'unknown'
elif state == cls.NEW:
return 'new'
elif state == cls.PRE_UP:
return 'pre-up'
elif state == cls.UP:
return 'up'
elif state == cls.POST_UP:
return 'post-up'
elif state == cls.PRE_DOWN:
return 'pre-down'
elif state == cls.POST_DOWN:
return 'post-down'
@classmethod
def from_str(cls, state_str):
if state_str == 'unknown':
return cls.UNKNOWN
elif state_str == 'new':
return cls.NEW
elif state_str == 'pre-up':
return cls.PRE_UP
elif state_str == 'up':
return cls.UP
elif state_str == 'post-up':
return cls.POST_UP
elif state_str == 'pre-down':
return cls.PRE_DOWN
elif state_str == 'post-down':
return cls.POST_DOWN
class iface():
""" config flags """
AUTO = 0x1
HOT_PLUG = 0x2
def __init__(self):
self.name = None
self.addr_family = None
self.addr_method = None
self.config = {}
self.children = []
self.state = ifaceState.NEW
self.status = ifaceStatus.UNKNOWN
self.flags = 0x0
self.refcnt = 0
self.dependents = None
self.auto = False
self.classes = []
self.env = None
self.config_current = {}
self.raw_lines = []
self.linkstate = None
def inc_refcnt(self):
#print 'inside inc_refcnt = %d' %self.refcnt
self.refcnt += 1
def dec_refcnt(self):
self.refcnt -= 1
def get_refcnt(self):
return self.refcnt
def set_refcnt(self, cnt):
self.refcnt = 0
def set_name(self, name):
self.name = name
def get_name(self):
return self.name
def set_addr_family(self, family):
self.addr_family = family
def get_addr_family(self):
return self.addr_family
def set_addr_method(self, method):
self.addr_method = method
def get_addr_method(self):
return self.addr_method
def set_config(self, config_dict):
self.config = config_dict
def get_config(self):
return self.config
def set_config_current(self, config_current):
self.config_current = config_current
def get_config_current(self):
return self.config_current
def get_auto(self):
return self.auto
def set_auto(self):
self.auto = True
def reset_auto(self):
self.auto = False
def get_classes(self):
return self.classes
def set_classes(self, classname):
self.classes.append(classname)
def belongs_to_class(self, intfclass):
if intfclass in self.classes:
return True
return False
def add_child(self, child_iface_obj):
self.children.append(child_iface_obj)
def get_state(self):
return self.state
def get_state_str(self):
return ifaceState.to_str(self.state)
def set_state(self, state):
self.state = state
def get_status(self):
return self.status
def get_status_str(self):
return ifaceStatus.to_str(self.status)
def set_status(self, status):
self.status = status
def state_str_to_hex(self, state_str):
return self.state_str_map.get(state_str)
def set_flag(self, flag):
self.flags |= flag
def clear_flag(self, flag):
self.flags &= ~flag
def set_dependents(self, dlist):
self.dependents = dlist
def get_dependents(self):
return self.dependents
def set_linkstate(self, l):
self.linkstate = l
def get_linkstate(self):
return self.linkstate
def run_children(self, stage, module_list, force=0):
if (self.is_flag_on(ifaceFlags.FOLLOW_DEPENDENTS)
== False):
return
for c in self.children:
self.set_flag(
ifaceFlags.FOLLOW_DEPENDENTS)
ret = c.run(stage, module_list, force)
self.clear_flag(
ifaceFlags.FOLLOW_DEPENDENTS)
if ret != 0:
self.set_state(stage, ret)
if force == 0:
break
return ret
def run(self, stage, module_list, force=0):
ret = 0
err = 0
ret = self.run_children()
if ret != 0 and force == 0:
return ret
for m in module_list:
ret = m.run(self, stage, force)
if ret != 0:
self.set_state(stage, ret)
# If no force, return!
if force == 0:
return -1
err += 1
else:
self.set_state(stage, ret)
return ret
def get_attr_value(self, attr_name):
config = self.get_config()
return config.get(attr_name)
def get_attr_value_first(self, attr_name):
config = self.get_config()
attr_value_list = config.get(attr_name)
if attr_value_list is not None:
return attr_value_list[0]
return None
def get_attr_value_n(self, attr_name, attr_index):
config = self.get_config()
attr_value_list = config.get(attr_name)
if attr_value_list is not None:
try:
return attr_value_list[attr_index]
except:
return None
return None
def get_env(self):
if self.env is None or len(self.env) == 0:
self.generate_env()
return self.env
def set_env(self, env):
self.env = env
def generate_env(self):
env = {}
config = self.get_config()
env['IFACE'] = self.get_name()
for attr, attr_value in config.items():
attr_env_name = 'IF_%s' %attr.upper()
env[attr_env_name] = attr_value[0]
if len(env) > 0:
self.set_env(env)
def update_config(self, attr_name, attr_value, attr_status=None):
if attr_value is None:
attr_value = ''
if attr_status == None:
attr_status_str = ''
elif attr_status == 0:
attr_status_str = ' (success)'
elif attr_status != 0:
attr_status_str = ' (error)'
self.config[attr_name] = attr_value + attr_status_str
def dump_raw(self, logger):
indent = ' '
print (self.raw_lines[0])
for i in range(1, len(self.raw_lines)):
print (indent + self.raw_lines[i])
def dump_state_pretty(self, logger):
indent = ' '
logger.info('iface %s' %self.get_name())
for attr_name, attr_value in self.get_config_current().items():
print (indent + '%s' %attr_name + ' %s' %attr_value)
def dump(self, logger):
indent = '\t'
logger.info(self.get_name() + ' : {')
logger.info(indent + 'family: %s' %self.get_addr_family())
logger.info(indent + 'method: %s' %self.get_addr_method())
logger.info(indent + 'state: %s'
%ifaceState.to_str(self.get_state()))
logger.info(indent + 'status: %s'
%ifaceStatus.to_str(self.get_status()))
logger.info(indent + 'refcnt: %d' %self.get_refcnt())
d = self.get_dependents()
if d is not None:
logger.info(indent + 'dependents: %s' %str(d))
else:
logger.info(indent + 'dependents: None')
logger.info(indent + 'config: ')
config = self.get_config()
if config is not None:
logger.info(indent + indent + str(config))
logger.info('}')
def dump_pretty(self, logger):
indent = '\t'
outbuf = 'iface %s' %self.get_name()
if self.get_addr_family() is not None:
outbuf += ' %s' %self.get_addr_family()
if self.get_addr_method() is not None:
outbuf += ' %s' %self.get_addr_method()
outbuf += '\n'
config = self.get_config()
if config is not None:
for cname, cvalue in config.items():
outbuf += indent + '%s' %cname + ' %s\n' %cvalue
#outbuf += ('%s' %indent + '%s' %self.get_state_str() +
# ' %s' %self.get_status_str())
print outbuf

665
pkg/ifupdown_main.py Normal file
View File

@ -0,0 +1,665 @@
#!/usr/bin/python
import os
import re
from statemanager import *
from networkinterfaces import *
from iface import *
from scheduler import *
from collections import deque
from collections import OrderedDict
import imp
import pprint
import logging
from graph import *
import sys, traceback
class ifupdown_main():
# Flags
FORCE = False
NOWAIT = False
DRYRUN = False
NODEPENDS = False
ALL = False
STATE_CHECK = True
modules_dir='/etc/network'
builtin_modules_dir='/usr/share/ifupdownaddons'
# iface dictionary in the below format:
# { '<ifacename>' : [<ifaceobject1>, <ifaceobject2> ..] }
# eg:
# { 'swp1' : [<ifaceobject1>, <ifaceobject2> ..] }
#
# Each ifaceobject corresponds to a configuration block for
# that interface
ifaceobjdict = OrderedDict()
# iface dictionary representing the curr running state of an iface
# in the below format:
# {'<ifacename>' : <ifaceobject>}
ifaceobjcurrdict = OrderedDict()
# Dictionary representing operation, sub operation and modules
# for every sub operation
operations = { 'up' :
OrderedDict([('pre-up', OrderedDict({})),
('up' , OrderedDict({})),
('post-up' , OrderedDict({}))]),
'down' :
OrderedDict([('pre-down', OrderedDict({})),
('down' , OrderedDict({})),
('post-down' , OrderedDict({}))])}
def __init__(self):
self.logger = logging.getLogger('ifupdown')
self.ifaces = OrderedDict()
self.njobs = 1
self.pp = pprint.PrettyPrinter(indent=4)
self.load_modules_builtin(self.builtin_modules_dir)
self.load_modules(self.modules_dir)
try:
self.statemanager = stateManager()
self.statemanager.read_saved_state()
except Exception, e:
# XXX Maybe we should continue by ignoring old state
self.logger.warning('error reading state (%s)' %str(e))
raise
def get_subops(self, op):
""" Returns sub-operation list """
return self.operations.get(op).keys()
def compat_conv_op_to_mode(self, op):
""" Returns old op name to work with existing scripts """
if op == 'up':
return 'start'
elif op == 'down':
return 'stop'
else:
return op
def set_force(self, force):
""" Set force flag. """
if force == True:
self.logger.debug('setting force to true')
self.FORCE = force
def get_force(self):
""" return force flag. """
return self.FORCE
def set_dryrun(self, dryrun):
if dryrun == True:
self.logger.debug('setting dryrun to true')
self.DRYRUN = dryrun
def get_dryrun(self):
return self.DRYRUN
def set_nowait(self, nowait):
if nowait == True:
self.logger.debug('setting dryrun to true')
self.NOWAIT = nowait
def get_nowait(self):
return self.NOWAIT
def set_njobs(self, njobs):
self.logger.debug('setting njobs to %d' %njobs)
self.njobs = njobs
def get_njobs(self):
return self.njobs
def ignore_error(self, errmsg):
if (self.FORCE == True or re.search(r'exists', errmsg,
re.IGNORECASE | re.MULTILINE) is not None):
return True
return False
def get_nodepends(self):
return self.NODEPENDS
def set_nodepends(self, nodepends):
self.logger.debug('setting nodepends to true')
self.NODEPENDS = nodepends
def set_iface_state(self, ifaceobj, state, status):
ifaceobj.set_state(state)
ifaceobj.set_status(status)
self.statemanager.update_iface_state(ifaceobj)
def get_iface_objs(self, ifacename):
return self.ifaceobjdict.get(ifacename)
def get_iface_obj_first(self, ifacename):
ifaceobjs = self.get_iface_objs(ifacename)
if ifaceobjs is not None:
return ifaceobjs[0]
return None
def get_iface_obj_last(self, ifacename):
return self.ifaceobjdict.get(ifacename)[-1]
def create_ifaceobjcurr(self, ifacename):
ifaceobj = self.get_ifaceobjcurr(ifacename)
if ifaceobj is not None:
return ifaceobj
ifaceobj = iface()
ifaceobj.set_name(ifacename)
self.ifaceobjcurrdict[ifacename] = ifaceobj
return ifaceobj
def get_ifaceobjcurr(self, ifacename):
return self.ifaceobjcurrdict.get(ifacename)
def get_iface_status(self, ifacename):
ifaceobjs = self.get_iface_objs(ifacename)
for i in ifaceobjs:
if i.get_status() != ifaceStatus.SUCCESS:
return i.get_status()
return ifaceStatus.SUCCESS
def get_iface_refcnt(self, ifacename):
max = 0
ifaceobjs = self.get_iface_objs(ifacename)
for i in ifaceobjs:
if i.get_refcnt() > max:
max = i.get_refcnt()
return max
def create_fake_vlan_iface(self, vlan_ifname, op):
""" creates and returns a fake vlan iface object.
This was added to support creation of simple vlan devices without any
user specified configuration.
"""
# XXX: Ideally this should be a call-back into the vlan module.
vlan_iface_obj = iface()
vlan_iface_obj.set_name(vlan_ifname)
vlan_iface_obj.set_dependents(self.get_dependents(vlan_iface_obj, op))
return vlan_iface_obj
def is_vlan_device(self, ifacename):
""" Returns true if iface name is a vlan interface.
only supports vlan interfaces of the format <ifacename>.<vlanid>
"""
if (re.search(r'\.', ifacename, 0) is not None):
return True
return False
def preprocess_dependency_list(self, dlist, op):
del_list = []
self.logger.debug('pre-processing dependency list: %s' %list(dlist))
for d in dlist:
dilist = self.get_iface_objs(d)
if dilist == None:
if self.is_vlan_device(d) == True:
vlan_iface_obj = self.create_fake_vlan_iface(d, op)
# Add face vlan device to ifaceobjdict dict
vlan_iface_obj.inc_refcnt()
self.save_iface(vlan_iface_obj)
else:
# Remove the device from the list
del_list.append(d)
else:
for di in dilist:
di.inc_refcnt()
for d in del_list:
dlist.remove(d)
self.logger.debug('After Processing dependency list: %s'
%list(dlist))
def get_dependents(self, ifaceobj, op):
""" Gets iface dependents by calling into respective modules """
dlist = None
self.logger.debug('%s: ' %ifaceobj.get_name() + 'getting dependency')
# Get dependents for interface by querying respective modules
subopdict = self.operations.get(op)
for subop, mdict in subopdict.items():
for mname, mdata in mdict.items():
if mdata.get('ftype') == 'pmodule':
module = mdata.get('module')
if (hasattr(module,
'get_dependent_ifacenames') == False):
continue
dlist = module.get_dependent_ifacenames(ifaceobj,
self.ifaceobjdict.keys())
if dlist:
self.logger.debug('%s: ' %ifaceobj.get_name() +
'got dependency list: %s' %str(dlist))
break
return dlist
def generate_dependency_info(self, ifacenames, dependency_graph, op):
""" recursive function to generate iface dependency info """
self.logger.debug('generating dependency info for %s' %str(ifacenames))
for i in ifacenames:
# Go through all modules and find dependent ifaces
dlist = None
ifaceobj = self.get_iface_obj_first(i)
if ifaceobj is None:
continue
dlist = ifaceobj.get_dependents()
if dlist is None:
dlist = self.get_dependents(ifaceobj, op)
if dlist is not None:
self.preprocess_dependency_list(dlist, op)
ifaceobj.set_dependents(dlist)
if dependency_graph.get(i) is None:
dependency_graph[i] = dlist
if dlist is not None:
self.generate_dependency_info(dlist,
dependency_graph, op)
def is_valid_state_transition(self, ifname, to_be_state):
return self.statemanager.is_valid_state_transition(ifname,
to_be_state)
def save_iface(self, ifaceobj):
if self.ifaceobjdict.get(ifaceobj.get_name()) is None:
self.ifaceobjdict[ifaceobj.get_name()] = [ifaceobj]
else:
self.ifaceobjdict[ifaceobj.get_name()].append(ifaceobj)
def read_default_iface_config(self):
""" Reads default network interface config /etc/network/interfaces. """
nifaces = networkInterfaces()
nifaces.subscribe('iface_found', self.save_iface)
nifaces.load()
def read_iface_config(self):
return self.read_default_iface_config()
def read_old_iface_config(self):
""" Reads the saved iface config instead of default iface config. """
# Read it from the statemanager
self.ifaceobjdict = self.statemanager.get_ifaceobjdict()
def save_module(self, mkind, msubkind, mname, mftype, module):
""" saves a module into internal module dict for later use.
mtype - pre-up.d, post-up.d and so on
mftype - pmodule (python module), bashscript (bash script)
"""
mmetadata = self.operations[mkind][msubkind].get(mname)
if mmetadata is None or mmetadata.get('ftype') != 'pmodule':
mmetadata = {}
mmetadata['ftype'] = mftype
mmetadata['module'] = module
self.operations[mkind][msubkind][mname] = mmetadata
self.logger.debug('saved module %s' %mkind +
' %s' %mname + ' %s' %mftype)
else:
self.logger.warn('ignoring module %s' %mkind + ' %s' %msubkind +
' %s' %mname + ' of type %s' %mftype)
def load_modules_builtin(self, modules_dir):
""" load python modules from modules_dir
Default modules_dir is /usr/share/ifupdownmodules
"""
self.logger.info('loading builtin modules from %s' %modules_dir)
if not modules_dir in sys.path:
sys.path.append(modules_dir)
try:
module_list = os.listdir(modules_dir)
for module in module_list:
if re.search('.*\.pyc', module, 0) != None:
continue
mname, mext = os.path.splitext(module)
if mext is not None and mext == '.py':
self.logger.info('loading ' + modules_dir + '/' + module)
try:
m = __import__(mname)
mclass = getattr(m, mname)
except:
raise
minstance = mclass(force=self.get_force(),
dryrun=self.get_dryrun(),
nowait=self.get_nowait())
ops = minstance.get_ops()
for op in ops:
if re.search('up', op) is not None:
self.save_module('up', op, mname, 'pmodule',
minstance)
else:
self.save_module('down', op, mname, 'pmodule',
minstance)
except:
raise
def load_modules(self, modules_dir):
""" loading user modules from /etc/network/.
Note that previously loaded python modules override modules found
under /etc/network if any
"""
self.logger.info('loading user modules from %s' %modules_dir)
for op, subops in self.operations.items():
for subop in subops.keys():
msubdir = modules_dir + '/if-%s.d' %subop
self.logger.info('loading modules under %s ...' %msubdir)
try:
module_list = os.listdir(msubdir)
for module in module_list:
if re.search('.*\.pyc', module, 0) != None:
continue
mname, mext = os.path.splitext(module)
if mext is not None and mext == '.py':
self.logger.debug('loading ' + msubdir + '/' + module)
try:
m = imp.load_source(module,
msubdir + '/' + module)
mclass = getattr(m, mname)
except:
raise
minstance = mclass()
self.save_module(op, subop, mname, 'pmodule',
minstance)
else:
self.save_module(op, subop, mname, 'script',
msubdir + '/' + module)
except:
raise
#self.logger.debug('modules ...')
#self.pp.pprint(self.operations)
# For query, we add a special entry, basically use all 'up' modules
self.operations['query'] = self.operations.get('up')
def conv_iface_namelist_to_objlist(self, intf_list):
for intf in intf_list:
iface_obj = self.get_iface(intf)
if iface_obj == None:
raise ifupdownInvalidValue('no iface %s', intf)
iface_objs.append(iface_obj)
return iface_objs
def run_without_dependents(self, op, ifacenames):
ifaceSched = ifaceScheduler()
self.logger.debug('run_without_dependents for op %s' %op +
' for %s' %str(ifacenames))
if ifacenames == None:
raise ifupdownInvalidValue('no interfaces found')
return ifaceSched.run_iface_list(self, ifacenames, op)
def run_with_dependents(self, op, ifacenames):
dependency_graph = {}
ret = 0
self.logger.debug('run_with_dependents for op %s'
%op + ' for %s' %str(ifacenames))
ifaceSched = ifaceScheduler()
if ifacenames is None:
ifacenames = self.ifaceobjdict.keys()
# generate dependency graph of interfaces
self.generate_dependency_info(ifacenames, dependency_graph, op)
if self.logger.isEnabledFor(logging.DEBUG) == True:
self.logger.debug('dependency graph:')
self.pp.pprint(dependency_graph)
if self.njobs > 1:
ret = ifaceSched.run_iface_dependency_graph_parallel(self,
dependency_graph, op)
else:
ret = ifaceSched.run_iface_dependency_graph(self, dependency_graph,
op)
return ret
def validate_ifaces(self, ifacenames):
""" validates interface list for config existance.
returns -1 if one or more interface not found. else, returns 0
"""
err_iface = ''
for i in ifacenames:
ifaceobjs = self.get_iface_objs(i)
if ifaceobjs is None:
err_iface += ' ' + i
if len(err_iface) != 0:
self.logger.error('cound not find ifaces: %s' %err_iface)
return -1
return 0
def iface_whitelisted(self, auto, allow_classes, ifacename):
""" Checks if interface is whitelisted depending on set of parameters.
interfaces are checked against the allow_classes and auto lists.
"""
self.logger.debug('checking if iface %s' %ifacename +
' is whitelisted')
ifaceobjs = self.get_iface_objs(ifacename)
if ifaceobjs is None:
self.logger.debug('iface %s' %ifacename + ' not found')
return False
# We check classes first
if allow_classes is not None and len(allow_classes) > 0:
for i in ifaceobjs:
if (len(i.get_classes()) > 0):
common = Set(allow_classes).intersection(
Set(i.get_classes()))
if len(common) > 0:
return True
return False
if auto == True:
for i in ifaceobjs:
if i.get_auto() == True:
return True
return False
return True
def generate_running_env(self, ifaceobj, op):
""" Generates a dictionary with env variables required for an interface.
Used to support script execution for interfaces.
"""
cenv = None
iface_env = ifaceobj.get_env()
if iface_env is not None:
cenv = os.environ
if cenv is not None:
cenv.update(iface_env)
else:
cenv = iface_env
cenv['MODE'] = self.compat_conv_op_to_mode(op)
return cenv
def run(self, op, auto=False, allow_classes=None,
ifacenames=None, query_state=None):
""" main ifupdown run method """
if auto == True:
self.logger.debug('setting flag ALL')
self.ALL = True
# Only read new iface config for 'up'
# operations. For 'downs' we only rely on
# old state
if op == 'up' or op == 'query':
try:
self.read_iface_config()
except Exception, e:
raise
else:
# for down we need to look at old state
self.logger.debug('down op, looking at old state ..')
if len(self.statemanager.get_ifaceobjdict()) > 0:
self.read_old_iface_config()
elif self.FORCE == True:
# If no old state available
self.logger.info('old state not available. Force option ' +
'set. Loading new iface config file')
try:
self.read_iface_config()
except Exception, e:
raise Exception('error reading iface config (%s)' %str(e))
else:
raise Exception('old state not available...aborting.' +
' try running with --force option')
if ifacenames is not None:
# If iface list is given, always check if iface is present
if self.validate_ifaces(ifacenames) != 0:
raise Exception('all or some ifaces not found')
# if iface list not given by user, assume all from config file
if ifacenames is None: ifacenames = self.ifaceobjdict.keys()
# filter interfaces based on auto and allow classes
filtered_ifacenames = [i for i in ifacenames
if self.iface_whitelisted(auto, allow_classes, i) == True]
if len(filtered_ifacenames) == 0:
raise Exception('no ifaces found matching ' +
'given allow lists')
if op == 'query':
if query_state == None:
return self.print_ifaceobjs_pretty(filtered_ifacenames)
elif query_state == 'presumed':
return self.print_ifaceobjs_saved_state_pretty(
filtered_ifacenames)
elif query_state == 'presumeddetailed':
return self.print_ifaceobjs_saved_state_detailed_pretty(
filtered_ifacenames)
if op == 'query' or self.NODEPENDS == True:
self.run_without_dependents(op, filtered_ifacenames)
else:
self.run_with_dependents(op, filtered_ifacenames)
if op == 'query':
if query_state == 'curr':
return self.print_ifaceobjscurr_pretty(filtered_ifacenames)
return
# Update persistant iface states
try:
if self.ALL == True:
self.statemanager.flush_state(self.ifaceobjdict)
else:
self.statemanager.flush_state()
except Exception, e:
if self.logger.isEnabledFor(logging.DEBUG):
t = sys.exc_info()[2]
traceback.print_tb(t)
self.logger.warning('error saving state (%s)' %str(e))
def up(self, auto=False, allow=None, ifacenames=None):
return self.run('up', auto, allow, ifacenames)
def down(self, auto=False, allow=None, ifacenames=None):
return self.run('down', auto, allow, ifacenames);
def query(self, auto=False, allow=None, ifacenames=None,
query_state=False):
return self.run('query', auto, allow, ifacenames,
query_state=query_state);
def dump(self):
""" all state dump """
print 'ifupdown object dump'
print self.pp.pprint(self.modules)
print self.pp.pprint(self.ifaces)
self.state_manager.dump()
def print_state(self, ifacenames=None):
self.statemanager.dump(ifacenames)
def print_ifaceobjs_pretty(self, ifacenames):
for i in ifacenames:
ifaceobjs = self.get_iface_objs(i)
for io in ifaceobjs:
io.dump_raw(self.logger)
def print_ifaceobjscurr_pretty(self, ifacenames):
for i in ifacenames:
ifaceobj = self.get_ifaceobjcurr(i)
ifaceobj.dump_pretty(self.logger)
def print_ifaceobjs_saved_state_pretty(self, ifacenames):
self.statemanager.print_state_pretty(ifacenames, self.logger)
def print_ifaceobjs_saved_state_detailed_pretty(self, ifacenames):
self.statemanager.print_state_detailed_pretty(ifacenames, self.logger)

35
pkg/ifupdownbase.py Normal file
View File

@ -0,0 +1,35 @@
#!/usr/bin/python
import logging
import subprocess
import re
from ifupdown.iface import *
class ifupdownBase(object):
def __init__(self):
modulename = self.__class__.__name__
self.logger = logging.getLogger('ifupdown.' + modulename)
def exec_command(self, cmd, cmdenv=None, nowait=False):
cmd_returncode = 0
cmdout = ''
try:
self.logger.debug('Executing ' + cmd)
ch = subprocess.Popen(cmd.split(),
stdout=subprocess.PIPE,
shell=False, env=cmdenv,
stderr=subprocess.STDOUT)
cmdout = ch.communicate()[0]
cmd_returncode = ch.wait()
except OSError, e:
raise Exception('could not execute ' + cmd +
'(' + str(e) + ')')
if cmd_returncode != 0:
raise Exception('error executing cmd \'%s\'' %cmd +
'\n(' + cmdout.strip('\n ') + ')')
return cmdout

28
pkg/log.py Normal file
View File

@ -0,0 +1,28 @@
#!/usr/bin/python
import logging
class log:
@staticmethod
def log_error(obj, prefix, *args, **kwargs):
obj.get_logger().logger.log_error(''.join(args))
@staticmethod
def log_warn(obj, *args, **kwargs):
msg = ''
logger = obj.get_logger()
errmsg = obj.get_errmsg()
msg += ''.join(args)
if errmsg is not None and len(errmsg) > 0:
msg += '(%s)' %errmsg
@staticmethod
def log(obj, log_prefix, *args, **kwargs):
msg = ''
logger = obj.get_logger()
msg += ''.join(args)

210
pkg/networkinterfaces.py Normal file
View File

@ -0,0 +1,210 @@
#!/usr/bin/python
import collections
from iface import *
import logging
import glob
class networkInterfaces():
hotplugs = {}
auto_ifaces = []
callbacks = {}
ifaces_file = "/etc/network/interfaces"
def __init__(self):
self.logger = logging.getLogger('ifupdown.' +
self.__class__.__name__)
self.callbacks = {'iface_found' : None}
self.allow_classes = {}
def subscribe(self, callback_name, callback_func):
if callback_name not in self.callbacks.keys():
print 'warning: invalid callback ' + callback_name
return -1
self.callbacks[callback_name] = callback_func
def ignore_line(self, line):
l = line.strip('\n ')
if len(l) == 0 or l[0] == '#':
return 1
return 0
def process_allow(self, lines, cur_idx, lineno):
allow_line = self.lines[cur_idx]
words = allow_line.split()
if len(words) <= 1:
raise Exception('invalid allow line \'%s\'' %allow_line)
allow_class = words[0].split('-')[1]
ifacenames = words[1:]
if self.allow_classes.get(allow_class) is not None:
for i in ifacenames:
self.allow_classes[allow_class].append(i)
else:
self.allow_classes[allow_class] = ifacenames
return 0
def process_source(self, lines, cur_idx, lineno):
# Support regex
sourced_file = lines[cur_idx].split(' ', 2)[1]
if sourced_file is not None:
for f in glob.glob(sourced_file):
self.read_file(self, sourced_file)
else:
self.logger.warn('unable to read source line at %d', lineno)
return 0
def process_auto(self, lines, cur_idx, lineno):
# XXX: Need to do more
attrs = lines[cur_idx].split()
if len(attrs) != 2:
raise Exception('line%d: ' %lineno + 'incomplete \'auto\' line')
self.auto_ifaces.append(lines[cur_idx].split()[1])
return 0
def process_iface(self, lines, cur_idx, lineno):
lines_consumed = 0
ifaceobj = iface()
iface_line = lines[cur_idx].strip('\n ')
iface_attrs = iface_line.split()
ifaceobj.raw_lines.append(iface_line)
iface_config = collections.OrderedDict()
for line_idx in range(cur_idx + 1, len(lines)):
l = lines[line_idx].strip('\n\t ')
if self.ignore_line(l) == 1:
continue
ifaceobj.raw_lines.append(l)
if self.is_keyword(l.split()[0]) == True:
line_idx -= 1
break
(attr_name, attrs) = l.split(' ', 1)
if iface_config.get(attr_name) == None:
iface_config[attr_name] = [attrs.strip(' ')]
else:
iface_config[attr_name].append(attrs.strip(' '))
lines_consumed = line_idx - cur_idx
# Create iface object
ifaceobj.set_name(iface_attrs[1])
ifaceobj.set_config(iface_config)
ifaceobj.generate_env()
if len(iface_attrs) > 2:
ifaceobj.set_addr_family(iface_attrs[2])
ifaceobj.set_addr_method(iface_attrs[3])
if ifaceobj.get_name() in self.auto_ifaces:
ifaceobj.set_auto()
classes = ifaceobj.set_classes(
self.get_allow_classes_for_iface(ifaceobj.get_name()))
if classes is not None and len(classes) > 0:
for c in classes:
ifaceobj.set_class(c)
# Call iface found callback
self.logger.debug('saving interface %s' %ifaceobj.get_name())
self.callbacks.get('iface_found')(ifaceobj)
return lines_consumed # Return next index
network_elems = { 'source' : process_source,
'allow' : process_allow,
'auto' : process_auto,
'iface' : process_iface}
def is_keyword(self, str):
# The additional split here is for allow- keyword
tmp_str = str.split('-')[0]
if tmp_str in self.network_elems.keys():
return 1
return 0
def get_keyword_func(self, str):
tmp_str = str.split('-')[0]
return self.network_elems.get(tmp_str)
def get_allow_classes_for_iface(self, ifacename):
classes = []
for class_name, ifacenames in self.allow_classes.items():
if ifacename in ifacenames:
classes.append(class_name)
return classes
def read_file(self, filename=None):
lineno = 0
line_idx = 0
lines_consumed = 0
ifaces_file = filename
if ifaces_file == None:
ifaces_file=self.ifaces_file
self.logger.debug('reading ifaces_file %s' %ifaces_file)
with open(ifaces_file) as f:
lines = f.readlines()
while (line_idx < len(lines)):
lineno = lineno + 1
if self.ignore_line(lines[line_idx]):
line_idx += 1
continue
l = lines[line_idx].strip('\n ')
words = l.split()
# Check if first element is a supported keyword
if self.is_keyword(words[0]):
keyword_func = self.get_keyword_func(
words[0])
lines_consumed = keyword_func(self,
lines, line_idx, lineno)
line_idx += lines_consumed
else:
self.logger.warning('could not ' +
'process line %s' %l + ' at' +
' lineno %d' %lineno)
line_idx += 1
return 0
def load(self, filename=None):
self.logger.debug('loading ifaces file ..')
return self.read_file(filename)

392
pkg/scheduler.py Normal file
View File

@ -0,0 +1,392 @@
#!/usr/bin/python
import os
import re
from statemanager import *
from iface import *
from graph import *
from collections import deque
from collections import OrderedDict
import imp
import pprint
import logging
from graph import *
from collections import deque
from threading import *
from ifupdownbase import *
class ifaceScheduler(ifupdownBase):
""" scheduler to schedule configuration of interfaces.
supports scheduling of interfaces serially in plain interface list
or dependency graph format.
"""
def __init__(self):
self.logger = logging.getLogger('ifupdown.' +
self.__class__.__name__)
def run_iface_subop(self, ifupdownobj, ifaceobj, op, subop, mdict, cenv):
""" Runs sub operation on an interface """
self.logger.debug('%s: ' %ifaceobj.get_name() + 'op %s' %op +
' subop = %s' %subop)
for mname, mdata in mdict.items():
m = mdata.get('module')
err = 0
try:
if (mdata.get('ftype') == 'pmodule' and
hasattr(m, 'run') == True):
self.logger.debug('%s: ' %ifaceobj.get_name() +
'running module %s' %mname +
' op %s' %op + ' subop %s' %subop)
if op == 'query':
m.run(ifaceobj, subop, query=True,
query_ifaceobj=ifupdownobj.create_ifaceobjcurr(
ifaceobj.get_name()))
else:
m.run(ifaceobj, subop)
else:
self.logger.debug('%s: ' %ifaceobj.get_name() +
'running script %s' %mname +
' op %s' %op + ' subop %s' %subop)
self.exec_command(m, cmdenv=cenv)
except Exception, e:
err = 1
if ifupdownobj.ignore_error(str(e)) == True:
pass
else:
raise
finally:
if op != 'query':
if err == 1:
ifupdownobj.set_iface_state(ifaceobj,
ifaceState.from_str(subop),
ifaceStatus.ERROR)
else:
ifupdownobj.set_iface_state(ifaceobj,
ifaceState.from_str(subop),
ifaceStatus.SUCCESS)
def run_iface_subops(self, ifupdownobj, ifaceobj, op):
""" Runs all sub operations on an interface """
# For backward compatibility execute scripts with
# environent set
cenv = ifupdownobj.generate_running_env(ifaceobj, op)
# Each sub operation has a module list
subopdict = ifupdownobj.operations.get(op)
for subop, mdict in subopdict.items():
self.run_iface_subop(ifupdownobj, ifaceobj, op, subop, mdict, cenv)
def run_iface(self, ifupdownobj, ifacename, op):
""" Runs operation on an interface """
ifaceobjs = ifupdownobj.get_iface_objs(ifacename)
for i in ifaceobjs:
if (op != 'query' and ifupdownobj.STATE_CHECK == True and
ifupdownobj.is_valid_state_transition(i, op) == False and
ifupdownobj.FORCE == False):
self.logger.warning('%s' %ifacename +
' already %s' %op)
continue
self.run_iface_subops(ifupdownobj, i, op)
def run_iface_list(self, ifupdownobj, ifacenames, operation,
sorted_by_dependency=False):
""" Runs interface list serially executing all sub operations on
each interface at a time. """
self.logger.debug('run_iface_list: running interface list for ' +
'operation %s' %operation)
iface_run_queue = deque(ifacenames)
for i in range(0, len(iface_run_queue)):
if operation == 'up':
# XXX: simplify this
if sorted_by_dependency == True:
ifacename = iface_run_queue.pop()
else:
ifacename = iface_run_queue.popleft()
else:
if sorted_by_dependency == True:
ifacename = iface_run_queue.popleft()
else:
ifacename = iface_run_queue.pop()
try:
self.run_iface(ifupdownobj, ifacename, operation)
except Exception, e:
if (ifupdownobj.ignore_error(str(e)) == True):
pass
else:
raise
def run_iface_list_subop(self, ifupdownobj, ifacenames, op, subop, mdict,
sorted_by_dependency=False):
""" Runs interface list through sub operation handler. """
self.logger.debug('running sub operation %s on all given interfaces' %op)
iface_run_queue = deque(ifacenames)
for i in range(0, len(iface_run_queue)):
if op == 'up':
# XXX: simplify this
if sorted_by_dependency == True:
ifacename = iface_run_queue.pop()
else:
ifacename = iface_run_queue.popleft()
else:
if sorted_by_dependency == True:
ifacename = iface_run_queue.popleft()
else:
ifacename = iface_run_queue.pop()
try:
ifaceobjs = ifupdownobj.get_iface_objs(ifacename)
for ifaceobj in ifaceobjs:
if (op != 'query' and ifupdownobj.STATE_CHECK == True and
ifupdownobj.is_valid_state_transition(ifaceobj,
op) == False and ifupdownobj.FORCE == False):
if subop == 'post-down' or subop == 'post-up':
self.logger.warning('%s: ' %ifacename +
' already %s' %op)
continue
cenv = ifupdownobj.generate_running_env(ifaceobj, op)
self.run_iface_subop(ifupdownobj, ifaceobj, op, subop,
mdict, cenv)
except Exception, e:
if (ifupdownobj.ignore_error(str(e)) == True):
pass
else:
raise
def run_iface_list_stages(self, ifupdownobj, ifacenames, op,
sorted_by_dependency=False):
""" Runs interface list through sub operations handler
Unlike run_iface_list, this method executes a sub operation on the
entire interface list before proceeding to the next sub-operation.
ie operation 'pre-up' is run through the entire interface list before
'up'
"""
self.logger.debug('run_iface_list_stages: running interface list for %s'
%op)
# Each sub operation has a module list
subopdict = ifupdownobj.operations.get(op)
for subop, mdict in subopdict.items():
self.run_iface_list_subop(ifupdownobj, ifacenames, op, subop, mdict,
sorted_by_dependency)
def run_iface_dependency_graph(self, ifupdownobj, dependency_graph,
operation):
""" runs interface dependency graph """
indegrees = OrderedDict()
self.logger.debug('creating indegree array ...')
for ifacename in dependency_graph.keys():
indegrees[ifacename] = ifupdownobj.get_iface_refcnt(ifacename)
if self.logger.isEnabledFor(logging.DEBUG) == True:
self.logger.debug('indegree array :')
ifupdownobj.pp.pprint(indegrees)
try:
self.logger.debug('calling topological sort on the graph ...')
sorted_ifacenames = graph.topological_sort(dependency_graph,
indegrees)
except Exception, e:
raise
self.logger.debug('sorted iface list = %s' %sorted_ifacenames)
#self.run_iface_list(ifupdownobj, sorted_ifacenames, operation,
# sorted_by_dependency=True)
self.run_iface_list_stages(ifupdownobj, sorted_ifacenames, operation,
sorted_by_dependency=True)
def init_tokens(self, count):
self.token_pool = BoundedSemaphore(count)
self.logger.debug('initialized bounded semaphore with %d' %count)
def accquire_token(self, logprefix=''):
self.token_pool.acquire()
self.logger.debug('%s ' %logprefix + 'acquired token')
def release_token(self, logprefix=''):
self.token_pool.release()
self.logger.debug('%s ' %logprefix + 'release token')
def run_iface_parallel(self, ifupdownobj, ifacename, op):
""" Configures interface in parallel.
Executes all its direct dependents in parallel
"""
self.logger.debug('%s:' %ifacename + ' %s' %op)
self.accquire_token(iface)
# Each iface can have a list of objects
ifaceobjs = ifupdownobj.get_iface_objs(ifacename)
if ifaceobjs is None:
self.logger.warning('%s: ' %ifacename + 'not found')
self.release_token(ifacename)
return -1
for ifaceobj in ifaceobjs:
# Run dependents
dlist = ifaceobj.get_dependents()
if dlist is not None and len(dlist) > 0:
self.logger.debug('%s:' %ifacename +
' found dependents: %s' %str(dlist))
try:
self.release_token(ifacename)
self.run_iface_list_parallel(ifacename, ifupdownobj,
dlist, op)
self.accquire_token(ifacename)
except Exception, e:
if (ifupdownobj.ignore_error(str(e)) == True):
pass
else:
# Dont bring the iface up if children did not come up
self.logger.debug('%s:' %ifacename +
' there was an error bringing %s' %op +
' dependents (%s)', str(e))
ifupdownobj.set_iface_state(ifaceobj,
ifaceState.from_str(
ifupdownobj.get_subops(op)[0]),
ifaceStatus.ERROR)
return -1
if (op != 'query' and ifupdownobj.STATE_CHECK == True and
ifupdownobj.is_valid_state_transition(ifaceobj,
op) == False and ifupdownobj.FORCE == False):
self.logger.warning('%s:' %ifacename + ' already %s' %op)
continue
# Run all sub operations sequentially
try:
self.logger.debug('%s:' %ifacename + ' running sub-operations')
self.run_iface_subops(ifupdownobj, ifaceobj, op)
except Exception, e:
self.logger.error('%s:' %ifacename +
' error running sub operations (%s)' %str(e))
self.release_token(ifacename)
def run_iface_list_parallel(self, parent, ifupdownobj, ifacenames, op):
""" Runs interface list in parallel """
running_threads = OrderedDict()
err = 0
for ifacename in ifacenames:
try:
self.accquire_token(parent)
running_threads[ifacename] = Thread(None,
self.run_iface_parallel, ifacename,
args=(ifupdownobj, ifacename, op))
running_threads[ifacename].start()
self.release_token(parent)
except Exception, e:
self.release_token(parent)
if (ifupdownobj.ignore_error(str(e)) == True):
pass
else:
raise Exception('error starting thread for iface %s'
%ifacename)
self.logger.debug('%s' %parent + 'waiting for all the threads ...')
for ifacename, t in running_threads.items():
t.join()
if ifupdownobj.get_iface_status(ifacename) != ifaceStatus.SUCCESS:
err += 1
return err
def run_iface_graphs_parallel(self, parent, ifupdownobj, ifacenames, op):
""" Runs iface graphs in parallel """
running_threads = OrderedDict()
err = 0
for ifacename in ifacenames:
try:
self.accquire_graph_token(parent)
running_threads[ifacename] = Thread(None,
self.run_iface_parallel, ifacename,
args=(ifupdownobj, ifacename, op))
running_threads[ifacename].start()
self.release_graph_token(parent)
except Exception, e:
self.release_graph_token(parent)
if (ifupdownobj.ignore_error(str(e)) == True):
pass
else:
raise Exception('error starting thread for iface %s'
%ifacename)
self.logger.info('%s' %parent + 'waiting for all the threads ...')
for ifacename, t in running_threads.items():
t.join()
# Check status of thread
# XXX: Check all objs
if ifupdownobj.get_iface_status(ifacename) != ifaceStatus.SUCCESS:
err += 1
return err
def run_iface_dependency_graph_parallel(self, ifupdownobj, dependency_graph,
operation):
""" Runs iface dependeny graph in parallel.
arguments:
ifupdownobj -- ifupdown object (used for getting and updating iface
object state)
dependency_graph -- dependency graph with
operation -- 'up' or 'down' or 'query'
"""
self.logger.debug('running dependency graph in parallel ..')
run_queue = []
# Build a list of ifaces that dont have any dependencies
for ifacename in dependency_graph.keys():
if ifupdownobj.get_iface_refcnt(ifacename) == 0:
run_queue.append(ifacename)
self.logger.debug('graph roots (interfaces that dont have dependents):' +
' %s' %str(run_queue))
self.init_tokens(ifupdownobj.get_njobs())
return self.run_iface_list_parallel('main', ifupdownobj, run_queue,
operation)
# OR
# Run one graph at a time
#for iface in run_queue:
# self.run_iface_list_parallel('main', ifupdownobj, [iface],
# operation)

258
pkg/statemanager.py Normal file
View File

@ -0,0 +1,258 @@
#!/usr/bin/python
import cPickle
from collections import OrderedDict
from exceptions import *
import logging
import pprint
import os
from iface import *
class pickling():
@classmethod
def save(cls, filename, list_of_objects):
try:
with open(filename, 'w') as f:
for obj in list_of_objects:
cPickle.dump(obj, f)
except:
raise
@classmethod
def save_obj(cls, f, obj):
try:
cPickle.dump(obj, f)
except:
raise
@classmethod
def load(cls, filename):
with open(filename, 'r') as f:
while True:
try: yield cPickle.load(f)
except EOFError: break
except: raise
class stateManager():
state_file = '/run/network/ifstatenew'
def __init__(self):
self.ifaceobjdict = OrderedDict()
self.logger = logging.getLogger('ifupdown.' +
self.__class__.__name__)
def save_ifaceobj(self, ifaceobj):
if self.ifaceobjdict.get(ifaceobj.get_name()) is None:
self.ifaceobjdict[ifaceobj.get_name()] = [ifaceobj]
else:
self.ifaceobjdict[ifaceobj.get_name()].append(ifaceobj)
def read_saved_state(self, filename=None):
pickle_filename = filename
if pickle_filename == None:
pickle_filename = self.state_file
if not os.path.exists(pickle_filename):
return
# Read all ifaces from file
for ifaceobj in pickling.load(pickle_filename):
self.save_ifaceobj(ifaceobj)
ifaceobj.set_refcnt(0)
return 0
def get_ifaceobjdict(self):
return self.ifaceobjdict
def save_state(self, ifaceobjs, filename=None):
pickle_filename = filename
if pickle_filename == None:
pickle_filename = self.state_file
pickling.save(pickle_filename, ifaceobjs)
def compare_iface_state(ifaceobj1, ifaceobj2):
ifaceobj1_state = ifaceobj1.get_state()
ifaceobj2_state = ifaceobj2.get_state()
if ifaceobj1_state < ifaceobj2_state:
return -1
elif ifaceobj1_state > ifaceobj2_state:
return 1
elif ifaceobj1_state == ifaceobj2_state:
return 0
def compare_iface_with_old(self, ifaceobj):
old_ifaceobj = self.ifaceobjdict.get(ifaceobj.get_name())
if old_ifaceobj == None:
raise ifacenotfound(ifaceobj.get_name())
if ifaceobj.get_addr_family() != old_ifaceobj.get_addr_family():
return -1
if ifaceobj.get_method() != old_ifaceobj.get_method():
return -1
# compare config items
unmatched_item = set(ifaceobj.items()) ^ set(old_ifaceobj.items())
if len(unmatched_item) != 0:
return -1
return 0
def get_iface_state_old(self, ifaceobj):
old_ifaceobj = self.ifaceobjdict.get(ifaceobj.get_name())
if old_ifaceobj == None:
raise ifacenotfound(ifaceobj.get_name())
return old_ifaceobj.get_state()
def get_iface_status_old(self, ifaceobj):
old_ifaceobj = self.ifaceobjdict.get(ifaceobj.get_name())
if old_ifaceobj == None:
raise ifacenotfound(ifaceobj.get_name())
return old_ifaceobj.get_status()
def cmp_old_new_state(self, ifacename, operation):
""" compares current operation with old state """
state_arg = ifaceState.from_str(operation)
if state_arg == ifaceState.UP:
old_ifaceobj = self.ifaceobjdict.get(ifacename)
if old_ifaceobj != None:
# found old state for iface
# Check its state
if (old_ifaceobj.get_state() == state_arg and
old_ifaceobj.get_status() == ifaceStatus.SUCCESS):
self.statemsg = 'iface already up'
return 0
elif state_arg == ifaceState.DOWN:
old_ifaceobj = self.ifaceobjdict.get(ifname)
if old_ifaceobj != None:
# found old state for iface
# Check its state
if (old_ifaceobj.get_state() == state_arg and
old_ifaceobj.get_status() == ifaceStatus.SUCCESS):
self.statemsg = 'iface already down'
return 0
return 1
def iface_obj_compare(self, ifaceobj_a, ifaceobj_b):
if ifaceobj_a.get_name() != ifaceobj_b.get_name():
return False
if (ifaceobj_a.get_addr_family() is None and
ifaceobj_b.get_addr_family() is not None):
return False
if (ifaceobj_a.get_addr_family() is not None and
ifaceobj_b.get_addr_family() is None):
return False
if (ifaceobj_a.get_addr_family() is None and
ifaceobj_b.get_addr_family() is None):
return True
if ifaceobj_a.get_addr_family() != ifaceobj_b.get_addr_family():
return False
return True
def update_iface_state(self, ifaceobj):
old_ifaceobjs = self.ifaceobjdict.get(ifaceobj.get_name())
if old_ifaceobjs is None:
self.ifaceobjdict[ifaceobj.get_name()] = [ifaceobj]
else:
for oi in old_ifaceobjs:
if self.iface_obj_compare(ifaceobj, oi) == True:
oi.set_state(ifaceobj.get_state())
oi.set_status(ifaceobj.get_status())
return
self.ifaceobjdict[ifaceobj.get_name()].append(ifaceobj)
def flush_state(self, ifaceobjdict=None):
if ifaceobjdict is None:
ifaceobjdict = self.ifaceobjdict
try:
with open(self.state_file, 'w') as f:
for ifaceobjs in ifaceobjdict.values():
for i in ifaceobjs:
pickling.save_obj(f, i)
except:
raise
def is_valid_state_transition(self, ifaceobj, tobe_state):
if self.ifaceobjdict is None:
return True
if tobe_state == 'up':
max_tobe_state = ifaceState.POST_UP
elif tobe_state == 'down':
max_tobe_state = ifaceState.POST_DOWN
else:
return True
old_ifaceobjs = self.ifaceobjdict.get(ifaceobj.get_name())
if old_ifaceobjs is not None:
for oi in old_ifaceobjs:
if self.iface_obj_compare(ifaceobj, oi) == True:
if (oi.get_state() == max_tobe_state and
oi.get_status() == ifaceStatus.SUCCESS):
# if old state is greater than or equal to
# tobe_state
return False
else:
return True
return True
else:
return True
def print_state(self, ifaceobj, prefix, indent):
print ('%s' %indent + '%s' %prefix +
'%s' %firstifaceobj.get_state_str(),
', %s' %firstifaceobj.get_status_str())
def print_state_pretty(self, ifacenames, logger):
for ifacename in ifacenames:
old_ifaceobjs = self.ifaceobjdict.get(ifacename)
if old_ifaceobjs is not None:
firstifaceobj = old_ifaceobjs[0]
self.print_state(self, firstifaceobj,
'%s: ' %firstifaceobj.get_name(), indent)
def print_state_detailed_pretty(self, ifacenames, logger):
for ifacename in ifacenames:
old_ifaceobjs = self.ifaceobjdict.get(ifacename)
if old_ifaceobjs is not None:
for i in old_ifaceobjs:
i.dump_pretty(logger)
self.print_state(self, firstifaceobj, '', indent)
def dump(self, ifacenames=None):
print 'iface state:'
if ifacenames is not None and len(ifacenames) > 0:
for i in ifacenames:
ifaceobj = self.ifaces.get(i)
if ifaceobj is None:
raise ifaceNotFoundError('ifname %s'
%i + ' not found')
ifaceobj.dump(self.logger)
else:
for ifacename, ifaceobj in self.ifaceobjdict.items():
ifaceobj.dump(self.logger)

184
sbin/ifupdown Executable file
View File

@ -0,0 +1,184 @@
#!/usr/bin/python
import sys
import os
import re
import argparse
from ifupdown.ifupdown_main import *
import logging
lockfile="/run/network/.lock"
logger = None
def run(args, op):
logger.debug('args = %s' %str(args))
try:
logger.debug('creating ifupdown object ..')
ifupdown_handle = ifupdown_main()
if op == 'up' or op == 'down':
if args.force == True:
ifupdown_handle.set_force(args.force)
if args.jobs > 0:
ifupdown_handle.set_njobs(args.jobs)
if args.dryrun == True:
ifupdown_handle.set_dryrun(args.dryrun)
if args.nodepends == True:
ifupdown_handle.set_nodepends(args.nodepends)
iflist = args.iflist
if len(args.iflist) == 0:
iflist = None
logger.debug('calling %s' %op + ' for all interfaces ..')
if op == 'up':
ifupdown_handle.up(args.all, args.allow, iflist)
elif op == 'down':
ifupdown_handle.down(args.all, args.allow, iflist)
elif op == 'query':
if args.curstate == True:
qstate='curr'
elif args.presumedstate == True:
qstate='presumed'
elif args.presumedstatedetailed == True:
qstate='presumeddetailed'
else:
qstate=None
ifupdown_handle.query(args.all, args.allow, iflist,
query_state=qstate)
except:
raise
def init(args):
global logger
log_level = logging.WARNING
if args.verbose == True:
log_level = logging.INFO
if args.debug == True:
log_level = logging.DEBUG
try:
logging.basicConfig(level=log_level,
format='%(message)s')
logger = logging.getLogger('ifupdown')
except:
raise
def deinit():
print 'deinit called'
def update_argparser(argparser):
argparser.add_argument('iflist', metavar='IFACE',
nargs='*', help='interfaces list')
argparser.add_argument('-a', '--all', action='store_true',
help='operate on all interfaces')
argparser.add_argument('-n', '--dry-run', dest='dryrun',
action='store_true', help='dry run')
argparser.add_argument('-v', '--verbose', dest='verbose',
action='store_true', help='verbose')
argparser.add_argument('-d', '--debug', dest='debug',
action='store_true',
help='output debug info')
argparser.add_argument('-q', '--quiet', dest='quiet',
action='store_true',
help=argparse.SUPPRESS)
argparser.add_argument('--allow', dest='allow',
help='allow class')
argparser.add_argument('--nodepends', dest='nodepends',
action='store_true', help='dont follow dependents')
def update_ifupdown_argparser(argparser):
argparser.add_argument('-f', '--force', dest='force',
action='store_true',
help='force run all operations')
argparser.add_argument('-j', '--jobs', dest='jobs', type=int,
default=-1, choices=range(1,12), help=argparse.SUPPRESS)
def update_ifup_argparser(argparser):
update_ifupdown_argparser(argparser)
def update_ifdown_argparser(argparser):
update_ifupdown_argparser(argparser)
def update_ifquery_argparser(argparser):
group = argparser.add_mutually_exclusive_group(required=False)
group.add_argument('-s', '--query-state', dest='curstate',
action='store_true', help=argparse.SUPPRESS)
group.add_argument('--presumed-state', dest='presumedstate',
action='store_true', help=argparse.SUPPRESS)
group.add_argument('--presumed-state-detailed',
dest='presumedstatedetailed',
action='store_true', help=argparse.SUPPRESS)
def parse_args(argsv, op):
descr = 'interface management'
argparser = argparse.ArgumentParser(description=descr)
update_argparser(argparser)
if op == 'up':
update_ifup_argparser(argparser)
elif op == 'down':
update_ifdown_argparser(argparser)
elif op == 'query':
update_ifquery_argparser(argparser)
return argparser.parse_args(argsv)
def main(argv):
""" main function """
try:
op = None
if re.search(r'ifup', argv[0]) != None:
op = 'up'
elif re.search(r'ifdown', argv[0]) != None:
op = 'down'
elif re.search(r'ifquery', argv[0]) != None:
op = 'query'
else:
print ('Unexpected executable.' +
' Should be \'ifup\' or \'ifdown\' or \'ifquery\'')
exit(1)
# Command line arg parser
args = parse_args(argv[1:], op)
if len(args.iflist) > 0 and args.all is True:
print 'iflist and all are mutually exclusive'
exit(1)
init(args)
run(args, op)
except Exception, e:
if args.debug == True:
raise
else:
logger.error(str(e))
finally:
deinit()
if __name__ == "__main__":
if not os.geteuid() == 0:
print 'Error: Must be root to run this command'
exit(1)
"""
if not utilities.lockFile(lockfile):
print 'Another instance of this program is already running.'
exit(0)
"""
main(sys.argv)

16
setup.py Executable file
View File

@ -0,0 +1,16 @@
from distutils.core import setup
setup(name='ifupdown2',
version='0.1',
description = "ifupdown 2",
author='Roopa Prabhu',
author_email='roopa@cumulusnetworks.com',
url='cumulusnetworks.com',
package_dir = {'ifupdown' : 'pkg'},
packages=['ifupdown'],
scripts = ['sbin/ifupdown'],
data_files=[('share/man/man8/',
['man/ifup.8', 'man/ifdown.8', 'man/ifquery.8']),
('/etc/init.d/',
['init.d/networking'])]
)

2
stdeb.cfg Normal file
View File

@ -0,0 +1,2 @@
[DEFAULT]
Conflicts: ifupdown