summaryrefslogtreecommitdiffstats
path: root/tools
diff options
context:
space:
mode:
authorChris St. Pierre <chris.a.st.pierre@gmail.com>2013-04-23 14:50:09 -0400
committerChris St. Pierre <chris.a.st.pierre@gmail.com>2013-04-23 14:50:09 -0400
commit46a47b4120b3d892b8149a5e181e4d976ad87f99 (patch)
treef2697f233fc7f5ad5022864222a5ca87715a651b /tools
parente1f99d1d5045e0511db42debb30aa97da2018796 (diff)
parent3d06f311274d6b942ee89d8cdb13b2ecc99af1b0 (diff)
downloadbcfg2-46a47b4120b3d892b8149a5e181e4d976ad87f99.tar.gz
bcfg2-46a47b4120b3d892b8149a5e181e4d976ad87f99.tar.bz2
bcfg2-46a47b4120b3d892b8149a5e181e4d976ad87f99.zip
Merge branch '1.4.x'
Conflicts: debian/bcfg2-server.install doc/server/plugins/grouping/metadata.txt src/lib/Bcfg2/Client/Client.py src/lib/Bcfg2/Client/Tools/Portage.py src/lib/Bcfg2/Client/Tools/RcUpdate.py src/lib/Bcfg2/Client/Tools/YUM24.py src/lib/Bcfg2/Client/Tools/__init__.py src/lib/Bcfg2/Client/Tools/launchd.py src/lib/Bcfg2/Options.py src/lib/Bcfg2/Server/Core.py src/lib/Bcfg2/Server/Plugin/helpers.py src/lib/Bcfg2/Server/Plugins/Metadata.py src/lib/Bcfg2/Server/models.py src/lib/Bcfg2/Utils.py src/sbin/bcfg2-info src/sbin/bcfg2-test testsuite/Testsrc/Testlib/TestClient/TestTools/Test_init.py testsuite/Testsrc/test_code_checks.py
Diffstat (limited to 'tools')
-rw-r--r--tools/README12
-rwxr-xr-xtools/batchadd.py168
-rwxr-xr-xtools/bcfg2-import-config10
-rwxr-xr-xtools/hostbase.py79
-rwxr-xr-xtools/hostbasepush.py14
-rwxr-xr-xtools/hostinfo.py197
-rw-r--r--tools/upgrade/1.4/README6
-rwxr-xr-xtools/upgrade/1.4/migrate_decisions.py82
8 files changed, 89 insertions, 479 deletions
diff --git a/tools/README b/tools/README
index 9e7f667e3..ad6d5eac1 100644
--- a/tools/README
+++ b/tools/README
@@ -3,9 +3,6 @@ This directory contains repository maintenance tools.
basebuilder.py <image directory>
- builds v2 base.xml from bcfg1 repo
-batchadd.py <filename>
- - Add records to Hostbase
-
bcfg2-completion.bash
- Bash tab completion for bcfg2-admin
@@ -61,15 +58,6 @@ export.sh
generate-manpages.bash
- Generate man pages from the Sphinx source
-hostbasepush.py
- - Call the Hostbase.rebuildState XML-RPC method
-
-hostbase.py {-l|-c} <hostname>
- - Display or create host information for Hostbase
-
-hostinfo.py {-q <query>|--showfields}
- - Query the hostbase databse
-
pkgmgr_gen.py
- Generate Pkgmgr XML files from a list of directories that
contain RPMS
diff --git a/tools/batchadd.py b/tools/batchadd.py
deleted file mode 100755
index e8008b330..000000000
--- a/tools/batchadd.py
+++ /dev/null
@@ -1,168 +0,0 @@
-#!/usr/bin/python
-
-from datetime import date
-import os
-import sys
-
-os.environ['DJANGO_SETTINGS_MODULE'] = 'Bcfg2.Server.Hostbase.settings'
-from Bcfg2.Server.Hostbase.hostbase.models import *
-from Bcfg2.Server.Hostbase.settings import DEFAULT_MX, PRIORITY
-import Bcfg2.Server.Hostbase.regex
-
-host_attribs = ['administrator',
- 'comments',
- 'csi',
- 'expiration_date',
- 'hostname',
- 'location',
- 'netgroup',
- 'outbound_smtp',
- 'primary_user',
- 'printq',
- 'security_class',
- 'support',
- 'whatami']
-
-
-def handle_error(field):
- if '-f' in sys.argv:
- return
- print("Error: %s is already defined in hostbase" % field)
- if '-s' in sys.argv:
- sys.exit(1)
-
-
-def checkformat(values, indices):
- """Ensures file contains all necessary attributes in order """
- filelist = [pair[0] for pair in values]
-
- # lines = len(filelist)
-
- filelist = filelist[indices[0]:]
-
- for index in indices:
- if filelist[0:13] != host_attribs:
- # figure out what to do here
- return False
- else:
- # process rest of host attributes
- try:
- next = filelist[1:].index('hostname')
- remaining = filelist[13:next + 1]
- filelist = filelist[next + 1:]
- except:
- remaining = filelist[13:]
- needfields = ['mac_addr', 'hdwr_type', 'ip_addr']
- if [item for item in needfields if item not in remaining]:
- return False
- return True
-
-
-if __name__ == '__main__':
-
- # argument handling for batchadd
- try:
- fd = open(sys.argv[1], 'r')
- except (IndexError, IOError):
- print("\nUsage: batchadd.py filename\n")
- sys.exit()
-
- lines = fd.readlines()
- # splits and strips lines into (attribute, value)
- info = [[item.strip() for item in line.split("->")] for line in lines
- if line.lstrip(' ')[0] != '#' and line != '\n']
-
- if info[0][0] == 'mx' and info[1][0] == 'priority':
- mx, created = MX.objects.get_or_create(mx=info[0][1],
- priority=info[1][1])
- info = info[2:]
- else:
- mx, created = MX.objects.get_or_create(mx=DEFAULT_MX,
- priority=PRIORITY)
- if created:
- mx.save()
-
- hostindices = [num for num in range(0, len(info))
- if info[num][0] == 'hostname']
-
- if not checkformat(info, hostindices):
- print("Error: file format")
- sys.exit()
-
-#################
-
- for host in hostindices:
- try:
- host = Host.objects.get(hostname=info[host][1])
- handle_error(info[host][1])
- except:
- # do something here
- pass
-
- macindices = [num for num in range(0, len(info))
- if info[num][0] == 'mac_addr']
- for mac_addr in macindices:
- try:
- host = Interface.objects.get(mac_addr=info[mac_addr][1])
- handle_error(info[mac_addr][1])
- except:
- # do something here
- pass
-
- for host in hostindices:
- blank = Host()
- for attrib in host_attribs:
- pair = info.pop(0)
- if pair[0] == 'outbound_smtp':
- if pair[1] == 'y':
- blank.__dict__[pair[0]] = 1
- else:
- blank.__dict__[pair[0]] = 0
- elif pair[0] == 'expiration_date':
- (year, month, day) = pair[1].split("-")
- blank.expiration_date = date(int(year),
- int(month),
- int(day))
- else:
- blank.__dict__[pair[0]] = pair[1]
- blank.status = 'active'
- blank.save()
- newhostname = blank.hostname.split(".")[0]
- newdomain = blank.hostname.split(".", 1)[1]
- while info and info[0][0] != 'hostname':
- if info[0][0] == 'mac_addr':
- pair = info.pop(0)
- inter = Interface.objects.create(host=blank,
- mac_addr=pair[1],
- hdwr_type='eth')
- if not pair[1]:
- inter.dhcp = False
- inter.save()
- elif info[0][0] == 'hdwr_type':
- pair = info.pop(0)
- inter.hdwr_type = pair[1]
- inter.save()
- elif info[0][0] == 'ip_addr':
- pair = info.pop(0)
- ip = IP.objects.create(interface=inter, ip_addr=pair[1])
- hostnamenode = Name(ip=ip,
- name=blank.hostname,
- dns_view='global',
- only=False)
- hostnamenode.save()
- namenode = Name(ip=ip,
- name=".".join([newhostname + "-" + inter.hdwr_type,
- newdomain]),
- dns_view="global", only=False)
- namenode.save()
- subnetnode = Name(ip=ip, name=newhostname + "-" +
- ip.ip_addr.split(".")[2] + "." +
- newdomain, dns_view="global", only=False)
- subnetnode.save()
- hostnamenode.mxs.add(mx)
- namenode.mxs.add(mx)
- subnetnode.mxs.add(mx)
- elif info[0][0] == 'cname':
- pair = info.pop(0)
- cname = CName.objects.create(name=hostnamenode, cname=pair[1])
- cname.save()
diff --git a/tools/bcfg2-import-config b/tools/bcfg2-import-config
index d6273f0c6..fec007e7e 100755
--- a/tools/bcfg2-import-config
+++ b/tools/bcfg2-import-config
@@ -11,7 +11,6 @@
usage() {
echo "$0: tool to import files in to bcfg2-server repository"
echo " -s Copy SSH Key files"
- echo " -p Create :info files with current file permissions"
echo " -n No suffix. Generate global files"
echo " --debian Run debsums to detect changed configuration files"
echo " ** debsums is only able to detect part of changes!"
@@ -28,7 +27,6 @@ eval set -- "$TEMP"
## Start Defaults
NEEDSSH=0
-NEEDPERM=0
DEBSUMS=0
NOSUFFIX=0
# End Defaults
@@ -37,7 +35,6 @@ NOSUFFIX=0
while true ; do
case "$1" in
-s) NEEDSSH=1; shift ;;
- -p) NEEDPERM=1; shift ;;
--debian) DEBSUMS=1; shift ;;
-n) NOSUFFIX=1; shift ;;
-h|--help)
@@ -102,11 +99,6 @@ get_files() {
FILE=$(basename $i)
mkdir -p $CFGREPO/$i
cp $i $CFGREPO/$i/${FILE}${SUFFIX}
- if [ $NEEDPERM -ne 0 ]; then
- # Get permissions for the file
- echo -n "(permissions) "
- find $i -printf "owner:%u\ngroup:%g\nperms:%#m\n" > "$CFGREPO/$i/:info"
- fi
echo "OK"
else
echo "$i: Not a file"
@@ -126,7 +118,7 @@ get_debsums() {
}
## End Functions
-if [ $(($NEEDPERM + $NEEDSSH + $DEBSUMS)) -eq 0 -a -z "$FILES" ]; then usage ; exit 0; fi
+if [ $(($NEEDSSH + $DEBSUMS)) -eq 0 -a -z "$FILES" ]; then usage ; exit 0; fi
init_temp_repo
get_debsums
diff --git a/tools/hostbase.py b/tools/hostbase.py
deleted file mode 100755
index 7474e68b7..000000000
--- a/tools/hostbase.py
+++ /dev/null
@@ -1,79 +0,0 @@
-#!/usr/bin/python
-import os
-from getopt import getopt, GetoptError
-from re import split
-import sys
-
-os.environ['DJANGO_SETTINGS_MODULE'] = 'Hostbase.settings'
-from Hostbase.hostbase.models import Host
-
-attribs = ['administrator',
- 'comments',
- 'csi',
- 'dhcp',
- 'expiration_date',
- 'hostname',
- 'last',
- 'location',
- 'netgroup',
- 'outbound_smtp',
- 'primary_user',
- 'printq',
- 'security_class',
- 'support',
- 'status',
- 'whatami']
-
-already_exists = None
-#here's my attempt at making the command line idiot proof
-#you must supply and arugument and hostname for hostbase.py to run
-try:
- (opts, args) = getopt(sys.argv[1:], 'l:c:')
- sys.argv[1]
- if len(split("\.", opts[0][1])) == 1:
- hosttouse = opts[0][1] + ".mcs.anl.gov"
- else:
- hosttouse = opts[0][1]
-except (GetoptError, IndexError):
- print("\nUsage: hostbase.py -flag (hostname)\n")
- print("Flags:")
- print("\t-l look (hostname)\n")
-# print("\t-c copy (hostname)\n")
- sys.exit()
-
-try:
- host = Host.objects.get(hostname=hosttouse)
-except:
- print("Error: host %s not in hostbase" % hosttouse)
- sys.exit(1)
-interfaces = []
-for interface in host.interface_set.all():
- interfaces.append([interface, interface.ip_set.all()])
-hostinfo = "\n"
-for attrib in attribs:
- if not (opts[0][0] == '-c' and attrib in ['status', 'last']):
- if attrib == 'dhcp' or attrib == 'outbound_smtp':
- if host.__dict__[attrib]:
- hostinfo += "%-32s-> %s\n" % (attrib, 'y')
- else:
- hostinfo += "%-32s-> %s\n" % (attrib, 'n')
- else:
- hostinfo += "%-32s-> %s\n" % (attrib, host.__dict__[attrib])
-for interface in interfaces:
- hostinfo += "\n%-32s-> %s\n" % ('mac_addr', interface[0].mac_addr)
- hostinfo += "%-32s-> %s\n" % ('hdwr_type', interface[0].hdwr_type)
- for ip in interface[1]:
- hostinfo += "%-32s-> %s\n" % ('ip_addr', ip.ip_addr)
-
-if opts[0][0] == '-l':
- """Displays general host information"""
- print(hostinfo)
-
-if opts[0][0] == '-c':
- """Provides pre-filled template to copy a host record"""
- fd = open('/tmp/hostbase.%s.tmp' % host.id, 'w')
- fd.write(hostinfo)
- fd.close()
- os.system('vi + /tmp/hostbase.%s.tmp' % host.id)
- os.system('batchadd.py /tmp/hostbase.%s.tmp' % host.id)
- os.system('rm /tmp/hostbase.%s.tmp' % host.id)
diff --git a/tools/hostbasepush.py b/tools/hostbasepush.py
deleted file mode 100755
index 02b7a582f..000000000
--- a/tools/hostbasepush.py
+++ /dev/null
@@ -1,14 +0,0 @@
-#!/usr/bin/python
-
-import os
-import Bcfg2.Client.Proxy
-
-if not os.getuid() == 0:
- print("this command must be run as root")
- raise SystemExit
-
-proxy = Bcfg2.Client.Proxy.bcfg2()
-print("building files...")
-proxy.run_method('Hostbase.rebuildState', ())
-print("running bcfg...")
-os.system('bcfg2 -q -d -v')
diff --git a/tools/hostinfo.py b/tools/hostinfo.py
deleted file mode 100755
index 8ae5c4df6..000000000
--- a/tools/hostinfo.py
+++ /dev/null
@@ -1,197 +0,0 @@
-#!/usr/bin/python
-"""Hostinfo queries the hostbase database according to user-defined data"""
-
-from os import system, environ
-environ['DJANGO_SETTINGS_MODULE'] = 'Hostbase.settings'
-from getopt import gnu_getopt, GetoptError
-from django.db import connection
-import sys
-
-logic_ops = ["and", "or"]
-host_attribs = ["hostname", "whatami", "netgroup", "security_class",
- "support", "csi", "memory", "printq", "dhcp", "outbound_smtp",
- "primary_user", "administrator", "location",
- "comments", "last", "expiration_date"]
-dispatch = {'mac_addr': ' i.',
- 'hdwr_type': ' i.',
- 'ip_addr': ' p.',
- 'name': ' n.',
- 'dns_view': ' n.',
- 'cname': ' c.',
- 'mx': ' m.',
- 'priority': ' m.'}
-
-
-def pinger(hosts):
- """Function that uses fping to ping multiple hosts in parallel"""
- hostnames = ""
- for each in hosts:
- hostnames += each[0] + " "
- system("fping -r 1" + hostnames)
- sys.exit()
-
-
-def get_query(arguments):
- """Parses the command line options and returns the necessary
- data for an SQL query"""
- logic = None
- resultset = []
- querystring = ''
- while 1:
- notflag = False
- if arguments[0] == 'not':
- notflag = True
- querypos = 1
- elif arguments[0] in logic_ops:
- logic = arguments[0]
- if arguments[1] == 'not':
- notflag = True
- querypos = 2
- else:
- querypos = 1
- else:
- querypos = 0
- if len(arguments[querypos].split("==")) > 1:
- operator = "="
- if notflag:
- operator = "<>"
- querysplit = arguments[querypos].split("==")
- if querysplit[0] in host_attribs:
- querystring = " h.%s%s\'%s\'" % (querysplit[0],
- operator,
- querysplit[1])
- elif querysplit[0] in dispatch:
- querystring = dispatch[querysplit[0]]
- querystring += "%s%s\'%s\'" % (querysplit[0],
- operator,
- querysplit[1])
- elif len(arguments[querypos].split("=")) > 1:
- notstring = ''
- if notflag:
- notstring = 'NOT '
- querysplit = arguments[querypos].split("=")
- if querysplit[0] in host_attribs:
- querystring = " h.%s %sLIKE \'%%%%%s%%%%\'" % (querysplit[0],
- notstring,
- querysplit[1])
- elif querysplit[0] in dispatch:
- querystring = dispatch[querysplit[0]]
- querystring += "%s %sLIKE \'%%%%%s%%%%\'" % (querysplit[0],
- notstring,
- querysplit[1])
- else:
- print("ERROR: bad query format")
- sys.exit()
- if not querystring:
- print("ERROR: bad query format")
- sys.exit()
- resultset.append((querystring, logic))
- arguments = arguments[querypos + 1:]
- if arguments == [] or arguments[0] not in logic_ops:
- break
- return resultset
-
-try:
- (opts, args) = gnu_getopt(sys.argv[1:],
- 'q:', ['showfields', 'fields', 'ping', 'summary'])
- cursor = connection.cursor()
- if ('--showfields', '') in opts:
- print("\nhost fields:\n")
- for field in host_attribs:
- print(field)
- for field in dispatch:
- print(field)
- print("")
- sys.exit()
- if opts[0][0] == '-q':
- results = get_query(sys.argv[2:])
- queryoptions = ""
- for result in results:
- if result[1] == 'and':
- queryoptions += " AND " + result[0]
- elif result[1] == 'or':
- queryoptions += " OR " + result[0]
- else:
- queryoptions += result[0]
- if ('--summary', '') in opts:
- fields = "h.hostname, h.whatami, h.location, h.primary_user"
- query = """SELECT DISTINCT %s FROM (((((hostbase_host h
- INNER JOIN hostbase_interface i ON h.id = i.host_id)
- INNER JOIN hostbase_ip p ON i.id = p.interface_id)
- INNER JOIN hostbase_name n ON p.id = n.ip_id)
- INNER JOIN hostbase_name_mxs x ON x.name_id = n.id)
- INNER JOIN hostbase_mx m ON m.id = x.mx_id)
- LEFT JOIN hostbase_cname c ON n.id = c.name_id
- WHERE %s ORDER BY h.hostname
- """ % (fields, queryoptions)
- cursor.execute(query)
- results = cursor.fetchall()
- if not results:
- print("No matches were found for your query")
- sys.exit()
- print("\n%-32s %-10s %-10s %-10s" % ('Hostname', 'Type', 'Location', 'User'))
- print("================================ ========== ========== ==========")
- for host in results:
- print("%-32s %-10s %-10s %-10s" % (host))
- print("")
- elif ('--fields', '') in opts:
- tolook = [arg for arg in args if arg in host_attribs or arg in dispatch]
- fields = ""
- fields = ", ".join(tolook)
- if not fields:
- print("No valid fields were entered. exiting...")
- sys.exit()
- query = """SELECT DISTINCT %s FROM (((((hostbase_host h
- INNER JOIN hostbase_interface i ON h.id = i.host_id)
- INNER JOIN hostbase_ip p ON i.id = p.interface_id)
- INNER JOIN hostbase_name n ON p.id = n.ip_id)
- INNER JOIN hostbase_name_mxs x ON x.name_id = n.id)
- INNER JOIN hostbase_mx m ON m.id = x.mx_id)
- LEFT JOIN hostbase_cname c ON n.id = c.name_id
- WHERE %s ORDER BY h.hostname
- """ % (fields, queryoptions)
-
- cursor.execute(query)
- results = cursor.fetchall()
-
- last = results[0]
- for field in results[0]:
- print(repr(field) + "\t")
- for host in results:
- if not host == last:
- for field in host:
- print(repr(field) + "\t")
- last = host
- print("")
- else:
- basequery = """SELECT DISTINCT h.hostname FROM (((((hostbase_host h
- INNER JOIN hostbase_interface i ON h.id = i.host_id)
- INNER JOIN hostbase_ip p ON i.id = p.interface_id)
- INNER JOIN hostbase_name n ON p.id = n.ip_id)
- INNER JOIN hostbase_name_mxs x ON x.name_id = n.id)
- INNER JOIN hostbase_mx m ON m.id = x.mx_id)
- LEFT JOIN hostbase_cname c ON n.id = c.name_id
- WHERE
- """
- cursor.execute(basequery + queryoptions + " ORDER BY h.hostname")
- results = cursor.fetchall()
-
- if not results:
- print("No matches were found for your query")
- sys.exit()
-
- if ("--ping", '') in opts:
- pinger(results)
-
- for host in results:
- print(host[0])
-
-
-except (GetoptError, IndexError):
- print("\nUsage: hostinfo.py -q <field>=[=]<value> [and/or <field>=<value> [--long option]]")
- print(" hostinfo.py --showfields\tshows all data fields")
- print("\n long options:")
- print("\t --fields f1 f2 ...\tspecifies the fields displayed from the queried hosts")
- print("\t --summary\t\tprints out a predetermined set of fields")
- print("\t --ping\t\t\tuses fping to ping all queried hosts\n")
- sys.exit()
diff --git a/tools/upgrade/1.4/README b/tools/upgrade/1.4/README
new file mode 100644
index 000000000..b6ff8d8c8
--- /dev/null
+++ b/tools/upgrade/1.4/README
@@ -0,0 +1,6 @@
+This directory contains scripts to help with upgrading from Bcfg2 1.3
+to 1.4.
+
+migrate_decisions.py
+ - Convert old group- and host-specific whitelist and blacklist
+ files into structured XML
diff --git a/tools/upgrade/1.4/migrate_decisions.py b/tools/upgrade/1.4/migrate_decisions.py
new file mode 100755
index 000000000..f7072783a
--- /dev/null
+++ b/tools/upgrade/1.4/migrate_decisions.py
@@ -0,0 +1,82 @@
+#!/usr/bin/env python
+
+import os
+import re
+import sys
+import glob
+import lxml.etree
+import Bcfg2.Options
+from Bcfg2.Server import XMLParser
+
+
+SPECIFIC = re.compile(r'.*\/(white|black)list'
+ r'(\.(H_(?P<host>.*)|G\d+_(?P<group>.*)))?$')
+
+
+def convert(files, xdata):
+ hosts = []
+ groups = []
+ for oldfile in files:
+ spec = SPECIFIC.match(oldfile)
+ if spec and spec.group('host'):
+ hosts.append(spec.group('host'))
+ elif spec and spec.group('group'):
+ groups.append(spec.group('group'))
+
+ for oldfile in files:
+ print("Converting %s" % oldfile)
+ spec = SPECIFIC.match(oldfile)
+ if not spec:
+ print("Skipping unknown file %s" % oldfile)
+ continue
+
+ parent = xdata
+ if spec.group('host'):
+ for host in hosts:
+ if host != spec.group('host'):
+ parent = lxml.etree.SubElement(parent, "Client",
+ name=host, negate="true")
+ parent = lxml.etree.SubElement(parent, "Client",
+ name=spec.group('host'))
+ elif spec.group('group'):
+ for host in hosts:
+ parent = lxml.etree.SubElement(parent, "Client",
+ name=host, negate="true")
+ for group in groups:
+ if group != spec.group('group'):
+ parent = lxml.etree.SubElement(parent, "Group",
+ name=group, negate="true")
+ parent = lxml.etree.SubElement(parent, "Group",
+ name=spec.group('group'))
+ parent.append(lxml.etree.Comment("Converted from %s" % oldfile))
+ olddata = lxml.etree.parse(oldfile, parser=Bcfg2.Server.XMLParser)
+ for decision in olddata.xpath('//Decision'):
+ parent.append(decision)
+ return xdata
+
+
+def main():
+ opts = dict(repo=Bcfg2.Options.SERVER_REPOSITORY,
+ configfile=Bcfg2.Options.CFILE)
+ setup = Bcfg2.Options.load_option_parser(opts)
+ setup.parse(sys.argv[1:])
+
+ datadir = os.path.join(setup['repo'], 'Decisions')
+ whitelist = lxml.etree.Element("Decisions")
+ blacklist = lxml.etree.Element("Decisions")
+ if os.path.exists(datadir):
+ convert(glob.glob(os.path.join(datadir, 'whitelist*')),
+ whitelist)
+ convert(glob.glob(os.path.join(datadir, 'blacklist*')),
+ blacklist)
+
+ print("Writing %s" % os.path.join(datadir, "whitelist.xml"))
+ open(os.path.join(datadir, "whitelist.xml"),
+ 'w').write(lxml.etree.tostring(whitelist, pretty_print=True))
+ print("Writing %s" % os.path.join(datadir, "blacklist.xml"))
+ open(os.path.join(datadir, "blacklist.xml"),
+ 'w').write(lxml.etree.tostring(blacklist, pretty_print=True))
+
+
+if __name__ == '__main__':
+ sys.exit(main())