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:
39
README
Normal file
39
README
Normal 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
19
TODO
Normal 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
67
debian/python-ifupdown2.postinst
vendored
Normal 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
20
debian/python-ifupdown2.postrm
vendored
Normal 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
194
init.d/networking
Normal 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
1
man/ifdown.8
Normal file
@ -0,0 +1 @@
|
||||
.so ifup.8
|
1
man/ifquery.8
Normal file
1
man/ifquery.8
Normal file
@ -0,0 +1 @@
|
||||
.so ifup.8
|
232
man/ifup.8
Normal file
232
man/ifup.8
Normal 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
310
man/interfaces.5
Normal 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
0
pkg/__init__.py
Normal file
17
pkg/exceptions.py
Normal file
17
pkg/exceptions.py
Normal 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
43
pkg/graph.py
Normal 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
366
pkg/iface.py
Normal 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
665
pkg/ifupdown_main.py
Normal 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
35
pkg/ifupdownbase.py
Normal 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
28
pkg/log.py
Normal 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
210
pkg/networkinterfaces.py
Normal 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
392
pkg/scheduler.py
Normal 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
258
pkg/statemanager.py
Normal 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
184
sbin/ifupdown
Executable 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
16
setup.py
Executable 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'])]
|
||||
)
|
Reference in New Issue
Block a user