mirror of
https://github.com/CumulusNetworks/ifupdown2.git
synced 2024-05-06 15:54:50 +00:00
Add examples and script to generate sample interfaces file
Ticket: CM-2643 Reviewed By: Testing Done: Tested build/install This also pulls in python-gvgen package from wheezy sid into our upstream dir. Previously i had packaged the gvgen module directly into the ifupdown package
This commit is contained in:
150
docs/examples/generate_interfaces.py
Executable file
150
docs/examples/generate_interfaces.py
Executable file
@@ -0,0 +1,150 @@
|
||||
#!/usr/bin/python
|
||||
|
||||
import argparse
|
||||
import sys
|
||||
import subprocess
|
||||
|
||||
""" This script prints to stdout /etc/network/interfaces entries for
|
||||
requested interfaces.
|
||||
|
||||
Currently it supports generation of interfaces(5) section for all
|
||||
swp interfaces on the system. And also an interface section
|
||||
for a bridge with all swp ports.
|
||||
|
||||
Example use of this script:
|
||||
|
||||
generate the swp_defaults file:
|
||||
(bkup existing /etc/network/interfaces.d/swp_defaults file if one exists)
|
||||
|
||||
#generate_interfaces.py -s > /etc/network/interfaces.d/swp_defaults
|
||||
|
||||
User -m option if you want the new swp_defaults to be auto merged
|
||||
with the contents from the old file, use -m option
|
||||
|
||||
#generate_interfaces.py -s -m /etc/network/interfaces.d/swp_defaults > /etc/network/interfaces.d/swp_defaults.new
|
||||
|
||||
Include the swp_defaults file in /etc/network/interfaces file
|
||||
(if not already there) using the source command as shown below:
|
||||
|
||||
source /etc/network/interfaces.d/swp_defaults
|
||||
|
||||
"""
|
||||
|
||||
def get_swp_interfaces():
|
||||
porttab_path = '/var/lib/cumulus/porttab'
|
||||
ports = []
|
||||
|
||||
ptfile = open(porttab_path, 'r')
|
||||
for line in ptfile.readlines():
|
||||
line = line.strip()
|
||||
if '#' in line:
|
||||
continue
|
||||
try:
|
||||
ports.append(line.split()[0])
|
||||
except ValueError:
|
||||
continue
|
||||
return ports
|
||||
|
||||
def print_swp_defaults_header():
|
||||
print '''
|
||||
# ** This file is autogenerated by /usr/share/doc/python-ifupdown2/generate_interfaces.py **
|
||||
#
|
||||
# This is /etc/network/interfaces section for all available swp
|
||||
# ports on the system.
|
||||
#
|
||||
# To include this file in the main /etc/network/interfaces file,
|
||||
# copy this file under /etc/network/interfaces.d/ and use the
|
||||
# source line in the /etc/network/interfaces file.
|
||||
#
|
||||
# example entry in /etc/network/interfaces:
|
||||
# source /etc/network/interfaces.d/<filename>
|
||||
#
|
||||
# See manpage interfaces(5) for details.
|
||||
'''
|
||||
|
||||
def print_bridge_untagged_defaults_header():
|
||||
print '''
|
||||
# ** This file is autogenerated by /usr/share/doc/python-ifupdown2/generate_interfaces.py **
|
||||
#
|
||||
# This is /etc/network/interfaces section for a bridge device with all swp
|
||||
# ports in the system.
|
||||
#
|
||||
# To include this file in the main /etc/network/interfaces file,
|
||||
# copy this file under /etc/network/interfaces.d/ and use the
|
||||
# source line in the /etc/network/interfaces file as shown below.
|
||||
# details.
|
||||
#
|
||||
# example entry in /etc/network/interfaces:
|
||||
# source /etc/network/interfaces.d/filename
|
||||
#
|
||||
# See manpage interfaces(5) for details
|
||||
'''
|
||||
|
||||
def interfaces_print_swp_default(swp_intf):
|
||||
outbuf = None
|
||||
if args.mergefile:
|
||||
try:
|
||||
cmd = ['/sbin/ifquery', '%s' %swp_intf, '-i', '%s' %args.mergefile]
|
||||
outbuf = subprocess.check_output(cmd, stderr=subprocess.STDOUT)
|
||||
except Exception, e:
|
||||
# no interface found gen latest
|
||||
pass
|
||||
if not outbuf:
|
||||
outbuf = 'auto %s\niface %s\n\n' %(swp_intf, swp_intf)
|
||||
return outbuf
|
||||
|
||||
def interfaces_print_swp_defaults(swp_intfs):
|
||||
print_swp_defaults_header()
|
||||
outbuf = ''
|
||||
for i in swp_intfs:
|
||||
outbuf += interfaces_print_swp_default(i)
|
||||
print outbuf
|
||||
|
||||
def interfaces_print_bridge_default(swp_intfs):
|
||||
print_bridge_untagged_defaults_header()
|
||||
outbuf = 'auto bridge-untagged\n'
|
||||
outbuf += 'iface bridge-untagged\n'
|
||||
outbuf += ' bridge-ports \\\n'
|
||||
linen = 5
|
||||
ports = ''
|
||||
for i in range(0, len(swp_intfs), linen):
|
||||
if ports:
|
||||
ports += ' \\\n'
|
||||
ports += ' %s' %(' '.join(swp_intfs[i:i+linen]))
|
||||
outbuf += ports
|
||||
print outbuf
|
||||
|
||||
def populate_argparser(argparser):
|
||||
group = argparser.add_mutually_exclusive_group(required=False)
|
||||
group.add_argument('-s', '--swp-defaults', action='store_true',
|
||||
dest='swpdefaults', help='generate swp defaults file')
|
||||
group.add_argument('-b', '--bridge-default', action='store_true',
|
||||
dest='bridgedefault',
|
||||
help='generate default untagged bridge')
|
||||
argparser.add_argument('-m', '--merge', dest='mergefile', help='merge ' +
|
||||
'new generated iface content with the old one')
|
||||
|
||||
argparser = argparse.ArgumentParser(description='ifupdown interfaces file gen helper')
|
||||
populate_argparser(argparser)
|
||||
args = argparser.parse_args(sys.argv[1:])
|
||||
|
||||
if not args.swpdefaults and not args.bridgedefault:
|
||||
argparser.print_help()
|
||||
exit(1)
|
||||
|
||||
if args.bridgedefault and args.mergefile:
|
||||
print 'error: mergefile option currently only supported with -s'
|
||||
argparser.print_help()
|
||||
exit(1)
|
||||
|
||||
swp_intfs = get_swp_interfaces()
|
||||
if not swp_intfs:
|
||||
print 'error: no ports found'
|
||||
exit(1)
|
||||
|
||||
if args.swpdefaults:
|
||||
interfaces_print_swp_defaults(swp_intfs)
|
||||
elif args.bridgedefault:
|
||||
interfaces_print_bridge_default(swp_intfs)
|
||||
else:
|
||||
argparser.print_help()
|
33
docs/examples/interfaces_bridge_template_func
Normal file
33
docs/examples/interfaces_bridge_template_func
Normal file
@@ -0,0 +1,33 @@
|
||||
#
|
||||
# mako template function to create a bridge
|
||||
#
|
||||
# mako defs provide the advantage of declaring predefined functions in
|
||||
# separate files.
|
||||
#
|
||||
# Below is an example that illustrates how to define such functions and use
|
||||
# them in the /etc/network/interfaces file to create bridges
|
||||
#
|
||||
# This file defines a function makebr to create a bridge. The arguments to the
|
||||
# function are vlan and ip address of the bridge.
|
||||
#
|
||||
# The default directory for template functions is
|
||||
# /etc/network/ifupdown2/templates/. Copy this file under the template
|
||||
# dir (create the directory if does not exist)
|
||||
#
|
||||
# To use this template function in /etc/network/interfaces, add the following
|
||||
# to the /etc/network/interfaces file:
|
||||
#
|
||||
# <%namespace name="bridge" file="/bridge_template"/>
|
||||
#
|
||||
# ${bridge.makebr(1096, "10.0.23.2/24")}
|
||||
# ${bridge.makebr(1097, "10.0.23.3/24")}
|
||||
#
|
||||
#
|
||||
|
||||
<%def name="makebr(vlan, addr)">
|
||||
auto br${vlan}
|
||||
iface br${vlan} inet static
|
||||
address ${addr}
|
||||
bridge-ports swp1.${vlan} swp2.${vlan}
|
||||
bridge-stp on
|
||||
</%def>
|
26
docs/examples/interfaces_with_template
Normal file
26
docs/examples/interfaces_with_template
Normal file
@@ -0,0 +1,26 @@
|
||||
#
|
||||
# Example interfaces file using mako templates
|
||||
#
|
||||
# The below section can be copied into
|
||||
# /etc/network/interfaces file
|
||||
# or
|
||||
# to a file under /etc/network/interfaces.d/
|
||||
# and include in the interfaces file using the
|
||||
# 'source' command.
|
||||
#
|
||||
# see manpage interfaces(5) for details
|
||||
#
|
||||
#
|
||||
|
||||
%for v in range(1000,1100):
|
||||
auto vlan-${v}
|
||||
iface vlan-${v} inet static
|
||||
bridge-ports glob swp1-6.${v}
|
||||
bridge-stp on
|
||||
bridge-treeprio 32768
|
||||
bridge-ageing 200
|
||||
bridge-maxhops 10
|
||||
bridge-maxage 10
|
||||
bridge-fdelay 10
|
||||
%endfor
|
||||
|
@@ -1,4 +1,4 @@
|
||||
#!/bin/sh -e
|
||||
#!/bin/bash
|
||||
### BEGIN INIT INFO
|
||||
# Provides: networking ifupdown
|
||||
# Required-Start: mountkernfs $local_fs urandom
|
||||
@@ -28,6 +28,25 @@ verbose=
|
||||
|
||||
[ "$VERBOSE" = yes ] && verbose=-v
|
||||
|
||||
gen_examples() {
|
||||
# Generate sample interfaces file. The interfaces files are
|
||||
# generated under /usr/share/doc/python-ifupdown2/examples/
|
||||
#
|
||||
|
||||
# generate files only at boot
|
||||
[ -f /var/tmp/network/ifstatenew ] && return
|
||||
|
||||
python_ifupdown2_docdir="/usr/share/doc/python-ifupdown2"
|
||||
swpfile=${python_ifupdown2_docdir}"/examples/swp_defaults"
|
||||
bridgedefaultfile=${python_ifupdown2_docdir}"/examples/bridge_untagged_default"
|
||||
interfaces_gen_script=${python_ifupdown2_docdir}"/examples/generate_interfaces.py"
|
||||
|
||||
[ ! -e $interfaces_gen_script ] && return
|
||||
ret=$($interfaces_gen_script -s 2>&1 >$swpfile)
|
||||
ret=$($interfaces_gen_script -b 2>&1 >$bridgedefaultfile)
|
||||
return
|
||||
}
|
||||
|
||||
perf_options() {
|
||||
# At bootup lets set perfmode
|
||||
[ -f /var/tmp/network/ifstatenew ] && echo -n "" && return
|
||||
@@ -35,7 +54,6 @@ perf_options() {
|
||||
echo -n "--perfmode"
|
||||
}
|
||||
|
||||
|
||||
process_exclusions() {
|
||||
set -- $EXCLUDE_INTERFACES
|
||||
exclusions=""
|
||||
@@ -110,12 +128,8 @@ ifupdown_init() {
|
||||
|
||||
case "$1" in
|
||||
start)
|
||||
if init_is_upstart; then
|
||||
exit 1
|
||||
fi
|
||||
|
||||
gen_examples
|
||||
ifupdown_init
|
||||
|
||||
if [ "$CONFIGURE_INTERFACES" = no ]
|
||||
then
|
||||
log_action_msg "Not configuring network interfaces, see /etc/default/networking"
|
||||
@@ -134,10 +148,6 @@ start)
|
||||
;;
|
||||
|
||||
stop)
|
||||
if init_is_upstart; then
|
||||
exit 0
|
||||
fi
|
||||
|
||||
ifupdown_init
|
||||
check_network_file_systems
|
||||
check_network_swap
|
||||
@@ -177,13 +187,8 @@ force-reload)
|
||||
;;
|
||||
|
||||
restart)
|
||||
if init_is_upstart; then
|
||||
exit 1
|
||||
fi
|
||||
|
||||
ifupdown_init
|
||||
|
||||
|
||||
log_action_begin_msg "Reconfiguring network interfaces"
|
||||
ifdown -a --exclude=lo $verbose --perfmode || true
|
||||
set -f
|
||||
|
615
pkg/gvgen.py
615
pkg/gvgen.py
@@ -1,615 +0,0 @@
|
||||
#!/usr/bin/python
|
||||
# -*- coding: utf-8 -*-
|
||||
# $Id: gvgen.py 13085 2008-02-25 16:11:50Z toady $
|
||||
"""
|
||||
GvGen - Generate dot file to be processed by graphviz
|
||||
Copyright (c) 2007-2008 INL
|
||||
Written by Sebastien Tricaud <sebastien.tricaud@inl.fr>
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, version 2 of the License.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
||||
"""
|
||||
|
||||
from sys import stdout
|
||||
|
||||
gvgen_version = "0.9"
|
||||
|
||||
debug = 0
|
||||
debug_tree_unroll = 0
|
||||
|
||||
class GvGen:
|
||||
"""
|
||||
Graphviz dot language Generation Class
|
||||
For example of usage, please see the __main__ function
|
||||
"""
|
||||
|
||||
def __init__(self, legend_name=None, options="compound=true;"): # allow links between clusters
|
||||
self.max_line_width = 10
|
||||
self.max_arrow_width = 2
|
||||
self.line_factor = 1
|
||||
self.arrow_factor = 0.5
|
||||
self.initial_line_width = 1.2
|
||||
self.initial_arrow_width = 0.8
|
||||
|
||||
self.options = options
|
||||
|
||||
self.__id = 0
|
||||
self.__nodes = []
|
||||
self.__links = []
|
||||
self.__browse_level = 0 # Stupid depth level for self.browse
|
||||
self.__opened_braces = [] # We count opened clusters
|
||||
self.fd=stdout # File descriptor to output dot
|
||||
self.padding_str=" " # Left padding to make children and parent look nice
|
||||
self.__styles = {}
|
||||
self.__default_style = []
|
||||
self.smart_mode = 0 # Disabled by default
|
||||
|
||||
# The graph has a legend
|
||||
if legend_name:
|
||||
self.options = self.options + "rankdir=LR;"
|
||||
self.legend = self.newItem(legend_name)
|
||||
|
||||
def __node_new(self, name, parent=None, distinct=None):
|
||||
"""
|
||||
Create a new node in the data structure
|
||||
@name: Name of the node, that will be the graphviz label
|
||||
@parent: The node parent
|
||||
@distinct: if true, will not create and node that has the same name
|
||||
|
||||
Returns: The node created
|
||||
"""
|
||||
|
||||
# We first check for distincts
|
||||
if distinct:
|
||||
if self.__nodes:
|
||||
for e in self.__nodes:
|
||||
props = e['properties']
|
||||
if props['label'] == name:
|
||||
# We found the label name matching, we return -1
|
||||
return -1
|
||||
|
||||
# We now insert into gvgen datastructure
|
||||
self.__id += 1
|
||||
node = {'id': self.__id, # Internal ID
|
||||
'lock': 0, # When the node is written, it is locked to avoid further references
|
||||
'parent': parent, # Node parent for easy graphviz clusters
|
||||
'style':None, # Style that GvGen allow you to create
|
||||
'properties': { # Custom graphviz properties you can add, which will overide previously defined styles
|
||||
'label': name
|
||||
}
|
||||
}
|
||||
|
||||
# Parents should be sorted first
|
||||
if parent:
|
||||
self.__nodes.insert(1, node)
|
||||
else:
|
||||
self.__nodes.append(node)
|
||||
|
||||
return node
|
||||
|
||||
def __link_smart(self, link):
|
||||
"""
|
||||
Creates a smart link if smart_mode activated:
|
||||
if a -> b exists, and we now add a <- b,
|
||||
instead of doing: a -> b
|
||||
<-
|
||||
we do: a <-> b
|
||||
"""
|
||||
|
||||
linkfrom = self.__link_exists(link['from_node'], link['to_node'])
|
||||
linkto = self.__link_exists(link['to_node'], link['from_node'])
|
||||
|
||||
if self.smart_mode:
|
||||
if linkto:
|
||||
self.__links.remove(linkto)
|
||||
self.propertyAppend(link, "dir", "both")
|
||||
|
||||
pw = self.propertyGet(linkfrom, "penwidth")
|
||||
if pw:
|
||||
pw = float(pw)
|
||||
pw += self.line_factor
|
||||
if pw < self.max_line_width:
|
||||
self.propertyAppend(linkfrom, "penwidth", str(pw))
|
||||
else:
|
||||
self.propertyAppend(link, "penwidth", str(self.initial_line_width))
|
||||
|
||||
aw = self.propertyGet(linkfrom, "arrowsize")
|
||||
if aw:
|
||||
aw = float(aw)
|
||||
if aw < self.max_arrow_width:
|
||||
aw += self.arrow_factor
|
||||
self.propertyAppend(linkfrom, "arrowsize", str(aw))
|
||||
else:
|
||||
self.propertyAppend(link, "arrowsize", str(self.initial_arrow_width))
|
||||
|
||||
if not linkfrom:
|
||||
self.__links.append(link)
|
||||
|
||||
|
||||
def __link_new(self, from_node, to_node, label = None, cl_from_node=None, cl_to_node=None):
|
||||
"""
|
||||
Creates a link between two nodes
|
||||
@from_node: The node the link comes from
|
||||
@to_node: The node the link goes to
|
||||
|
||||
Returns: The link created
|
||||
"""
|
||||
|
||||
link = {'from_node': from_node,
|
||||
'to_node': to_node,
|
||||
'style':None, # Style that GvGen allow you to create
|
||||
'properties': {}, # Custom graphviz properties you can add, which will overide previously defined styles
|
||||
'cl_from_node':None, # When linking from a cluster, the link appears from this node
|
||||
'cl_to_node':None, # When linking to a cluster, the link appears to go to this node
|
||||
}
|
||||
|
||||
if label:
|
||||
link['properties']['label'] = label
|
||||
|
||||
if cl_from_node:
|
||||
link['cl_from_node'] = cl_from_node
|
||||
if cl_to_node:
|
||||
link['cl_to_node'] = cl_to_node
|
||||
|
||||
# We let smart link work for us
|
||||
self.__link_smart(link)
|
||||
|
||||
return link
|
||||
|
||||
def __link_exists(self, from_node, to_node):
|
||||
"""
|
||||
Find if a link exists
|
||||
@from_node: The node the link comes from
|
||||
@to_node: The node the link goes to
|
||||
|
||||
Returns: true if the given link already exists
|
||||
"""
|
||||
|
||||
for link in self.__links:
|
||||
if link['from_node'] == from_node and link['to_node'] == to_node:
|
||||
return link
|
||||
|
||||
return None
|
||||
|
||||
|
||||
def __has_children(self, parent):
|
||||
"""
|
||||
Find children to a given parent
|
||||
Returns the children list
|
||||
"""
|
||||
children_list = []
|
||||
for e in self.__nodes:
|
||||
if e['parent'] == parent:
|
||||
children_list.append(e)
|
||||
|
||||
return children_list
|
||||
|
||||
def newItem(self, name, parent=None, distinct=None):
|
||||
node = self.__node_new(name, parent, distinct)
|
||||
|
||||
return node
|
||||
|
||||
def newLink(self, src, dst, label=None, cl_src=None, cl_dst=None):
|
||||
"""
|
||||
Link two existing nodes with each other
|
||||
"""
|
||||
|
||||
return self.__link_new(src, dst, label, cl_src, cl_dst)
|
||||
|
||||
def debug(self):
|
||||
for e in self.__nodes:
|
||||
print "element = " + str(e['id'])
|
||||
|
||||
def collectLeaves(self, parent):
|
||||
"""
|
||||
Collect every leaf sharing the same parent
|
||||
"""
|
||||
cl = []
|
||||
for e in self.__nodes:
|
||||
if e['parent'] == parent:
|
||||
cl.append(e)
|
||||
|
||||
return cl
|
||||
|
||||
def collectUnlockedLeaves(self, parent):
|
||||
"""
|
||||
Collect every leaf sharing the same parent
|
||||
unless it is locked
|
||||
"""
|
||||
cl = []
|
||||
for e in self.__nodes:
|
||||
if e['parent'] == parent:
|
||||
if not e['lock']:
|
||||
cl.append(e)
|
||||
|
||||
return cl
|
||||
|
||||
def lockNode(self, node):
|
||||
node['lock'] = 1
|
||||
|
||||
#
|
||||
# Start: styles management
|
||||
#
|
||||
def styleAppend(self, stylename, key, val):
|
||||
if stylename not in self.__styles:
|
||||
self.__styles[stylename] = []
|
||||
|
||||
self.__styles[stylename].append([key, val])
|
||||
|
||||
def styleApply(self, stylename, node_or_link):
|
||||
node_or_link['style'] = stylename
|
||||
|
||||
def styleDefaultAppend(self, key, val):
|
||||
self.__default_style.append([key, val])
|
||||
|
||||
#
|
||||
# End: styles management
|
||||
#
|
||||
|
||||
#
|
||||
# Start: properties management
|
||||
#
|
||||
def propertiesAsStringGet(self, node, props):
|
||||
"""
|
||||
Get the properties string according to parent/children
|
||||
props is the properties dictionnary
|
||||
"""
|
||||
|
||||
allProps = {}
|
||||
|
||||
#
|
||||
# Default style come first, they can then be overriden
|
||||
#
|
||||
if self.__default_style:
|
||||
allProps.update(self.__default_style)
|
||||
|
||||
#
|
||||
# First, we build the styles
|
||||
#
|
||||
if node['style']:
|
||||
stylename = node['style']
|
||||
allProps.update(self.__styles[stylename])
|
||||
|
||||
#
|
||||
# Now we build the properties:
|
||||
# remember they override styles
|
||||
#
|
||||
allProps.update(props)
|
||||
|
||||
if self.__has_children(node):
|
||||
propStringList = ["%s=\"%s\";\n" % (k, v) for k, v in allProps.iteritems()]
|
||||
properties = ''.join(propStringList)
|
||||
else:
|
||||
if props:
|
||||
propStringList = ["%s=\"%s\"" % (k, v) for k, v in allProps.iteritems()]
|
||||
properties = '[' + ','.join(propStringList) + ']'
|
||||
else:
|
||||
properties = ''
|
||||
|
||||
return properties
|
||||
|
||||
def propertiesLinkAsStringGet(self, link):
|
||||
has_props = 0
|
||||
|
||||
props = {}
|
||||
|
||||
if link['style']:
|
||||
stylename = link['style']
|
||||
|
||||
# Build the properties string for node
|
||||
props.update(self.__styles[stylename])
|
||||
|
||||
props.update(link['properties'])
|
||||
|
||||
properties = ''
|
||||
if props:
|
||||
properties += ','.join(["%s=\"%s\"" % (str(k),str(val)) for k, val in props.iteritems()])
|
||||
return properties
|
||||
|
||||
def propertyForeachLinksAppend(self, node, key, val):
|
||||
for l in self.__links:
|
||||
if l['from_node'] == node:
|
||||
props = l['properties']
|
||||
props[key] = val
|
||||
|
||||
def propertyAppend(self, node_or_link, key, val):
|
||||
"""
|
||||
Append a property to the wanted node or link
|
||||
mynode = newItem(\"blah\")
|
||||
Ex. propertyAppend(mynode, \"color\", \"red\")
|
||||
"""
|
||||
props = node_or_link['properties']
|
||||
props[key] = val
|
||||
|
||||
def propertyGet(self, node_or_link, key):
|
||||
"""
|
||||
Get the value of a given property
|
||||
Ex. prop = propertyGet(node, \"color\")
|
||||
"""
|
||||
try:
|
||||
props = node_or_link['properties']
|
||||
return props[key]
|
||||
except:
|
||||
return None
|
||||
|
||||
def propertyRemove(self, node_or_link, key):
|
||||
"""
|
||||
Remove a property to the wanted node or link
|
||||
mynode = newItem(\"blah\")
|
||||
Ex. propertyRemove(mynode, \"color\")
|
||||
"""
|
||||
props = node_or_link['properties']
|
||||
del props[key]
|
||||
|
||||
#
|
||||
# End: Properties management
|
||||
#
|
||||
|
||||
#
|
||||
# For a good legend, the graph must have
|
||||
# rankdir=LR property set.
|
||||
#
|
||||
def legendAppend(self, legendstyle, legenddescr, labelin=None):
|
||||
if labelin:
|
||||
item = self.newItem(legenddescr, self.legend)
|
||||
self.styleApply(legendstyle, item)
|
||||
else:
|
||||
style = self.newItem("", self.legend)
|
||||
descr = self.newItem(legenddescr, self.legend)
|
||||
self.styleApply(legendstyle, style)
|
||||
link = self.newLink(style,descr)
|
||||
self.propertyAppend(link, "dir", "none")
|
||||
self.propertyAppend(link, "style", "invis")
|
||||
self.propertyAppend(descr,"shape","plaintext")
|
||||
|
||||
def tree_debug(self, level, node, children):
|
||||
if children:
|
||||
print "(level:%d) Eid:%d has children (%s)" % (level,node['id'],str(children))
|
||||
else:
|
||||
print "Eid:"+str(node['id'])+" has no children"
|
||||
|
||||
#
|
||||
# Core function that outputs the data structure tree into dot language
|
||||
#
|
||||
def tree(self, level, node, children):
|
||||
"""
|
||||
Core function to output dot which sorts out parents and children
|
||||
and do it in the right order
|
||||
"""
|
||||
if debug:
|
||||
print "/* Grabed node = %s*/" % str(node['id'])
|
||||
|
||||
if node['lock'] == 1: # The node is locked, nothing should be printed
|
||||
if debug:
|
||||
print "/* The node (%s) is locked */" % str(node['id'])
|
||||
|
||||
if self.__opened_braces:
|
||||
self.fd.write(level * self.padding_str)
|
||||
self.fd.write("}\n")
|
||||
self.__opened_braces.pop()
|
||||
return
|
||||
|
||||
props = node['properties']
|
||||
|
||||
if children:
|
||||
node['lock'] = 1
|
||||
self.fd.write(level * self.padding_str)
|
||||
self.fd.write(self.padding_str + "subgraph cluster%d {\n" % node['id'])
|
||||
properties = self.propertiesAsStringGet(node, props)
|
||||
self.fd.write(level * self.padding_str)
|
||||
self.fd.write(self.padding_str + "%s" % properties)
|
||||
self.__opened_braces.append([node,level])
|
||||
else:
|
||||
# We grab appropriate properties
|
||||
properties = self.propertiesAsStringGet(node, props)
|
||||
|
||||
# We get the latest opened elements
|
||||
if self.__opened_braces:
|
||||
last_cluster,last_level = self.__opened_braces[-1]
|
||||
else:
|
||||
last_cluster = None
|
||||
last_level = 0
|
||||
|
||||
if debug:
|
||||
if node['parent']:
|
||||
parent_str = str(node['parent']['id'])
|
||||
else:
|
||||
parent_str = 'None'
|
||||
if last_cluster:
|
||||
last_cluster_str = str(last_cluster['id'])
|
||||
else:
|
||||
last_cluster_str = 'None'
|
||||
print "/* e[parent] = %s, last_cluster = %s, last_level = %d, opened_braces: %s */" % (parent_str, last_cluster_str,last_level,str(self.__opened_braces))
|
||||
|
||||
# Write children/parent with properties
|
||||
if node['parent']:
|
||||
if node['parent'] != last_cluster:
|
||||
while node['parent'] < last_cluster:
|
||||
last_cluster,last_level = self.__opened_braces[-1]
|
||||
if node['parent'] == last_cluster:
|
||||
last_level += 1
|
||||
# We browse any property to build a string
|
||||
self.fd.write(last_level * self.padding_str)
|
||||
self.fd.write(self.padding_str + "node%d %s;\n" % (node['id'], properties))
|
||||
node['lock'] = 1
|
||||
else:
|
||||
self.fd.write(last_level * self.padding_str)
|
||||
self.fd.write(self.padding_str + "}\n")
|
||||
self.__opened_braces.pop()
|
||||
else:
|
||||
self.fd.write(level * self.padding_str)
|
||||
self.fd.write(self.padding_str + "node%d %s;\n" % (node['id'], properties) )
|
||||
node['lock'] = 1
|
||||
cl = self.collectUnlockedLeaves(node['parent'])
|
||||
for l in cl:
|
||||
props = l['properties']
|
||||
properties = self.propertiesAsStringGet(l, props)
|
||||
self.fd.write(last_level * self.padding_str)
|
||||
self.fd.write(self.padding_str + self.padding_str + "node%d %s;\n" % (l['id'], properties))
|
||||
node['lock'] = 1
|
||||
self.lockNode(l)
|
||||
|
||||
self.fd.write(level * self.padding_str + "}\n")
|
||||
self.__opened_braces.pop()
|
||||
|
||||
else:
|
||||
self.fd.write(self.padding_str + "node%d %s;\n" % (node['id'], properties))
|
||||
node['lock'] = 1
|
||||
|
||||
|
||||
def browse(self, node, cb):
|
||||
"""
|
||||
Browse nodes in a tree and calls cb providing node parameters
|
||||
"""
|
||||
children = self.__has_children(node)
|
||||
if children:
|
||||
cb(self.__browse_level, node, str(children))
|
||||
for c in children:
|
||||
self.__browse_level += 1
|
||||
self.browse(c, cb)
|
||||
|
||||
else:
|
||||
cb(self.__browse_level, node, None)
|
||||
self.__browse_level = 0
|
||||
# if debug:
|
||||
# print "This node is not a child: " + str(node)
|
||||
|
||||
def dotLinks(self, node):
|
||||
"""
|
||||
Write links between nodes
|
||||
"""
|
||||
for l in self.__links:
|
||||
if l['from_node'] == node:
|
||||
# Check if we link form a cluster
|
||||
children = self.__has_children(node)
|
||||
if children:
|
||||
if l['cl_from_node']:
|
||||
src = l['cl_from_node']['id']
|
||||
else:
|
||||
src = children[0]['id']
|
||||
cluster_src = node['id']
|
||||
else:
|
||||
src = node['id']
|
||||
cluster_src = ''
|
||||
|
||||
# Check if we link to a cluster
|
||||
children = self.__has_children(l['to_node'])
|
||||
if children:
|
||||
if l['cl_to_node']:
|
||||
dst = l['cl_to_node']['id']
|
||||
else:
|
||||
dst = children[0]['id']
|
||||
cluster_dst = l['to_node']['id']
|
||||
else:
|
||||
dst = l['to_node']['id']
|
||||
cluster_dst = ''
|
||||
|
||||
self.fd.write("node%d->node%d" % (src, dst))
|
||||
|
||||
props = self.propertiesLinkAsStringGet(l)
|
||||
|
||||
# Build new properties if we link from or to a cluster
|
||||
if cluster_src:
|
||||
if props:
|
||||
props += ','
|
||||
props += "ltail=cluster%d" % cluster_src
|
||||
if cluster_dst:
|
||||
if props:
|
||||
props += ','
|
||||
props += "lhead=cluster%d" % cluster_dst
|
||||
|
||||
if props:
|
||||
self.fd.write(" [%s]" % props)
|
||||
|
||||
self.fd.write(";\n")
|
||||
|
||||
def dot(self, name=None, fd=stdout):
|
||||
"""
|
||||
Translates the datastructure into dot
|
||||
"""
|
||||
try:
|
||||
self.fd = fd
|
||||
|
||||
self.fd.write("/* Generated by GvGen v.%s (http://software.inl.fr/trac/wiki/GvGen) */\n\n" % (gvgen_version))
|
||||
|
||||
if name is None:
|
||||
self.fd.write("digraph G {\n")
|
||||
else:
|
||||
self.fd.write("digraph %s {\n" %name)
|
||||
|
||||
if self.options:
|
||||
self.fd.write(self.options+"\n")
|
||||
|
||||
# We write parents and children in order
|
||||
for e in self.__nodes:
|
||||
if debug_tree_unroll:
|
||||
self.browse(e, self.tree_debug)
|
||||
else:
|
||||
self.browse(e, self.tree)
|
||||
|
||||
# We write the connection between nodes
|
||||
for e in self.__nodes:
|
||||
self.dotLinks(e)
|
||||
|
||||
# We put all the nodes belonging to the parent
|
||||
self.fd.write("}\n")
|
||||
finally:
|
||||
# Remove our reference to file descriptor
|
||||
self.fd = None
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
graph = GvGen()
|
||||
|
||||
graph.smart_mode = 1
|
||||
|
||||
graph.styleDefaultAppend("color","blue")
|
||||
|
||||
parents = graph.newItem("Parents")
|
||||
father = graph.newItem("Bob", parents)
|
||||
mother = graph.newItem("Alice", parents)
|
||||
children = graph.newItem("Children")
|
||||
child1 = graph.newItem("Carol", children)
|
||||
child2 = graph.newItem("Eve", children)
|
||||
child3 = graph.newItem("Isaac", children)
|
||||
postman = graph.newItem("Postman")
|
||||
graph.newLink(father,child1)
|
||||
graph.newLink(child1, father)
|
||||
graph.newLink(father, child1)
|
||||
graph.newLink(father,child2)
|
||||
graph.newLink(mother,child2)
|
||||
myl = graph.newLink(mother,child1)
|
||||
graph.newLink(mother,child3)
|
||||
graph.newLink(postman,child3,"Email is safer")
|
||||
graph.newLink(parents, postman) # Cluster link
|
||||
|
||||
graph.propertyForeachLinksAppend(parents, "color", "blue")
|
||||
|
||||
graph.propertyForeachLinksAppend(father, "label", "My big link")
|
||||
graph.propertyForeachLinksAppend(father, "color", "red")
|
||||
|
||||
graph.propertyAppend(postman, "color", "red")
|
||||
graph.propertyAppend(postman, "fontcolor", "white")
|
||||
|
||||
graph.styleAppend("link", "label", "mylink")
|
||||
graph.styleAppend("link", "color", "green")
|
||||
graph.styleApply("link", myl)
|
||||
graph.propertyAppend(myl, "arrowhead", "empty")
|
||||
|
||||
graph.styleAppend("Post", "color", "blue")
|
||||
graph.styleAppend("Post", "style", "filled")
|
||||
graph.styleAppend("Post", "shape", "rectangle")
|
||||
graph.styleApply("Post", postman)
|
||||
|
||||
|
||||
graph.dot()
|
8
setup.py
8
setup.py
@@ -9,6 +9,7 @@ setup(name='ifupdown2',
|
||||
package_dir = {'ifupdown' : 'pkg'},
|
||||
packages=['ifupdown'],
|
||||
scripts = ['sbin/ifupdown'],
|
||||
install_requires = ['python-gvgen'],
|
||||
data_files=[('share/man/man8/',
|
||||
['man/ifup.8', 'man/ifquery.8', 'man/ifreload.8']),
|
||||
('share/man/man5/',
|
||||
@@ -18,6 +19,9 @@ setup(name='ifupdown2',
|
||||
('/sbin/', ['sbin/ifupdown']),
|
||||
('/etc/network/ifupdown2/',
|
||||
['config/ifupdown2.conf']),
|
||||
('/usr/share/doc/ifupdown/examples/',
|
||||
['docs/examples/interfaces'])]
|
||||
('/usr/share/doc/python-ifupdown2/examples/',
|
||||
['docs/examples/generate_interfaces.py',
|
||||
'docs/examples/interfaces',
|
||||
'docs/examples/interfaces_bridge_template_func',
|
||||
'docs/examples/interfaces_with_template'])]
|
||||
)
|
||||
|
Reference in New Issue
Block a user