|
|
192ec7 |
#!/usr/bin/python -tt
|
|
|
192ec7 |
# -*- coding: utf-8 -*-
|
|
|
192ec7 |
#
|
|
|
192ec7 |
# This script will help you with migration squid-3.3 conf files to squid-3.5 conf files
|
|
|
192ec7 |
# Copyright (C) 2016 Red Hat, Inc.
|
|
|
192ec7 |
#
|
|
|
192ec7 |
# This program is free software; you can redistribute it and/or modify
|
|
|
192ec7 |
# it under the terms of the GNU General Public License as published by
|
|
|
192ec7 |
# he Free Software Foundation; either version 2 of the License, or
|
|
|
192ec7 |
# (at your option) any later version.
|
|
|
192ec7 |
#
|
|
|
192ec7 |
# This program is distributed in the hope that it will be useful,
|
|
|
192ec7 |
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
192ec7 |
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
|
192ec7 |
# GNU General Public License for more details.
|
|
|
192ec7 |
#
|
|
|
192ec7 |
# You should have received a copy of the GNU General Public License along
|
|
|
192ec7 |
# with this program; if not, write to the Free Software Foundation, Inc.,
|
|
|
192ec7 |
# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
|
|
192ec7 |
#
|
|
|
192ec7 |
# Authors: Lubos Uhliarik <luhliari@redhat.com>
|
|
|
192ec7 |
|
|
|
192ec7 |
import sys
|
|
|
192ec7 |
import os
|
|
|
192ec7 |
import re
|
|
|
192ec7 |
import shutil
|
|
|
192ec7 |
import traceback
|
|
|
192ec7 |
import argparse
|
|
|
192ec7 |
import glob
|
|
|
192ec7 |
|
|
|
192ec7 |
class ConfMigration:
|
|
|
192ec7 |
RE_LOG_ACCESS="log_access\s+(\w+)\s+"
|
|
|
192ec7 |
RE_LOG_ACCESS_DENY_REP="access_log none "
|
|
|
192ec7 |
RE_LOG_ACCESS_ALLOW_REP="access_log daemon:/var/log/squid/access.log squid "
|
|
|
192ec7 |
RE_LOG_ACCESS_TEXT="log_access"
|
|
|
192ec7 |
|
|
|
192ec7 |
RE_LOG_ICAP="log_icap\s+"
|
|
|
192ec7 |
RE_LOG_ICAP_REP="icap_log daemon:/var/log/squid/icap.log "
|
|
|
192ec7 |
RE_LOG_ICAP_TEXT="log_icap"
|
|
|
192ec7 |
|
|
|
192ec7 |
RE_HIER_STOPLIST="hierarchy_stoplist\s+(.*)"
|
|
|
192ec7 |
RE_HIER_STOPLIST_REP="acl %s url_regex %s\nalways_direct allow %s"
|
|
|
192ec7 |
RE_HIER_STOPLIST_TEXT="hierarchy_stoplist"
|
|
|
192ec7 |
|
|
|
192ec7 |
HIER_ACL_NAME="migrated_hs_%d_%d"
|
|
|
192ec7 |
|
|
|
192ec7 |
RE_INCLUDE_CHECK="\s*include\s+(.*)"
|
|
|
192ec7 |
|
|
|
192ec7 |
COMMENT_FMT="# migrated automatically by squid-migrate-conf, the original configuration was: %s\n%s"
|
|
|
192ec7 |
|
|
|
192ec7 |
DEFAULT_SQUID_CONF="/etc/squid/squid.conf"
|
|
|
192ec7 |
DEFAULT_BACKUP_EXT=".bak"
|
|
|
192ec7 |
DEFAULT_LEVEL_INDENT=3
|
|
|
192ec7 |
|
|
|
192ec7 |
MAX_NESTED_INCLUDES=16
|
|
|
192ec7 |
|
|
|
192ec7 |
def __init__(self, args, level=0, squid_conf='', conf_seq=0):
|
|
|
192ec7 |
self.args = args
|
|
|
192ec7 |
|
|
|
192ec7 |
if squid_conf:
|
|
|
192ec7 |
self.squid_conf = squid_conf
|
|
|
192ec7 |
else:
|
|
|
192ec7 |
self.squid_conf = args.squid_conf
|
|
|
192ec7 |
self.write_changes = args.write_changes
|
|
|
192ec7 |
self.debug = args.debug
|
|
|
192ec7 |
|
|
|
192ec7 |
self.conf_seq = conf_seq
|
|
|
192ec7 |
self.acl_seq = 0
|
|
|
192ec7 |
|
|
|
192ec7 |
self.line_num = 0
|
|
|
192ec7 |
self.level = level
|
|
|
192ec7 |
if (not os.path.isfile(self.squid_conf)):
|
|
|
192ec7 |
sys.stderr.write("%sError: the config file %s does not exist\n" % (self.get_prefix_str(), self.squid_conf))
|
|
|
192ec7 |
sys.exit(1)
|
|
|
192ec7 |
|
|
|
192ec7 |
self.squid_bak_conf = self.get_backup_name()
|
|
|
192ec7 |
|
|
|
192ec7 |
self.migrated_squid_conf_data = []
|
|
|
192ec7 |
self.squid_conf_data = None
|
|
|
192ec7 |
|
|
|
192ec7 |
|
|
|
192ec7 |
print ("Migrating: " + self.squid_conf)
|
|
|
192ec7 |
|
|
|
192ec7 |
def print_info(self, text=''):
|
|
|
192ec7 |
if (self.debug):
|
|
|
192ec7 |
print "%s%s" % (self.get_prefix_str(), text)
|
|
|
192ec7 |
|
|
|
192ec7 |
def get_backup_name(self):
|
|
|
192ec7 |
file_idx = 1
|
|
|
192ec7 |
tmp_fn = self.squid_conf + self.DEFAULT_BACKUP_EXT
|
|
|
192ec7 |
|
|
|
192ec7 |
while (os.path.isfile(tmp_fn)):
|
|
|
192ec7 |
tmp_fn = self.squid_conf + self.DEFAULT_BACKUP_EXT + str(file_idx)
|
|
|
192ec7 |
file_idx = file_idx + 1
|
|
|
192ec7 |
|
|
|
192ec7 |
return tmp_fn
|
|
|
192ec7 |
|
|
|
192ec7 |
#
|
|
|
192ec7 |
# From squid config documentation:
|
|
|
192ec7 |
#
|
|
|
192ec7 |
# Configuration options can be included using the "include" directive.
|
|
|
192ec7 |
# Include takes a list of files to include. Quoting and wildcards are
|
|
|
192ec7 |
# supported.
|
|
|
192ec7 |
#
|
|
|
192ec7 |
# For example,
|
|
|
192ec7 |
#
|
|
|
192ec7 |
# include /path/to/included/file/squid.acl.config
|
|
|
192ec7 |
#
|
|
|
192ec7 |
# Includes can be nested up to a hard-coded depth of 16 levels.
|
|
|
192ec7 |
# This arbitrary restriction is to prevent recursive include references
|
|
|
192ec7 |
# from causing Squid entering an infinite loop whilst trying to load
|
|
|
192ec7 |
# configuration files.
|
|
|
192ec7 |
#
|
|
|
192ec7 |
def check_include(self, line=''):
|
|
|
192ec7 |
m = re.match(self.RE_INCLUDE_CHECK, line)
|
|
|
192ec7 |
include_list = ""
|
|
|
192ec7 |
if not (m is None):
|
|
|
192ec7 |
include_list = re.split('\s+', m.group(1))
|
|
|
192ec7 |
for include_file_re in include_list:
|
|
|
192ec7 |
# included file can be written in regexp syntax
|
|
|
192ec7 |
for include_file in glob.glob(include_file_re):
|
|
|
192ec7 |
self.print_info("A config file %s was found and it will be included" % (include_file))
|
|
|
192ec7 |
if os.path.isfile(include_file):
|
|
|
192ec7 |
self.print_info("Migrating the included config file %s" % (include_file))
|
|
|
192ec7 |
conf = ConfMigration(self.args, self.level+1, include_file, self.conf_seq+1)
|
|
|
192ec7 |
conf.migrate()
|
|
|
192ec7 |
|
|
|
192ec7 |
# check, if included file exists
|
|
|
192ec7 |
if (len(glob.glob(include_file_re)) == 0 and not (os.path.isfile(include_file_re))):
|
|
|
192ec7 |
self.print_info("The config file %s does not exist." % (include_file_re))
|
|
|
192ec7 |
|
|
|
192ec7 |
def print_sub_text(self, text, new_str):
|
|
|
192ec7 |
if self.write_changes:
|
|
|
192ec7 |
print "File: '%s', line: %d - the directive %s was replaced by %s" % (self.squid_conf, self.line_num, text, new_str)
|
|
|
192ec7 |
else:
|
|
|
192ec7 |
print "File: '%s', line: %d - the directive %s could be replaced by %s" % (self.squid_conf, self.line_num, text, new_str)
|
|
|
192ec7 |
|
|
|
192ec7 |
def add_conf_comment(self, old_line, line):
|
|
|
192ec7 |
return self.COMMENT_FMT % (old_line, line)
|
|
|
192ec7 |
|
|
|
192ec7 |
def sub_line_ad(self, line, line_re, allow_sub, deny_sub, text):
|
|
|
192ec7 |
new_line = line
|
|
|
192ec7 |
m = re.match(line_re, line)
|
|
|
192ec7 |
if not (m is None):
|
|
|
192ec7 |
# check, if allow or deny was used and select coresponding sub
|
|
|
192ec7 |
sub_text = allow_sub
|
|
|
192ec7 |
if (re.match('allow', m.group(1), re.IGNORECASE)):
|
|
|
192ec7 |
new_line = re.sub(line_re, sub_text, line)
|
|
|
192ec7 |
elif (re.match('deny', m.group(1), re.IGNORECASE)):
|
|
|
192ec7 |
sub_text = deny_sub
|
|
|
192ec7 |
new_line = re.sub(line_re, sub_text, line)
|
|
|
192ec7 |
|
|
|
192ec7 |
# print out, if there was any change and add comment to conf line, if so
|
|
|
192ec7 |
if not (new_line is line):
|
|
|
192ec7 |
self.print_sub_text(text + " " + m.group(1), sub_text)
|
|
|
192ec7 |
new_line = self.add_conf_comment(line, new_line)
|
|
|
192ec7 |
|
|
|
192ec7 |
return new_line
|
|
|
192ec7 |
|
|
|
192ec7 |
def sub_line(self, line, line_re, sub, text):
|
|
|
192ec7 |
new_line = line
|
|
|
192ec7 |
m = re.match(line_re, line)
|
|
|
192ec7 |
if not (m is None):
|
|
|
192ec7 |
new_line = re.sub(line_re, sub, line)
|
|
|
192ec7 |
|
|
|
192ec7 |
# print out, if there was any change and add comment to conf line, if so
|
|
|
192ec7 |
if not (new_line is line):
|
|
|
192ec7 |
self.print_sub_text(text, sub)
|
|
|
192ec7 |
new_line = self.add_conf_comment(line, new_line)
|
|
|
192ec7 |
|
|
|
192ec7 |
return new_line
|
|
|
192ec7 |
|
|
|
192ec7 |
def rep_hier_stoplist(self, line, sub, words):
|
|
|
192ec7 |
wordlist = words.split(' ')
|
|
|
192ec7 |
|
|
|
192ec7 |
esc_wordlist = []
|
|
|
192ec7 |
for w in wordlist:
|
|
|
192ec7 |
esc_wordlist.append(re.escape(w))
|
|
|
192ec7 |
|
|
|
192ec7 |
# unique acl name for hierarchy_stoplist acl
|
|
|
192ec7 |
acl_name = self.HIER_ACL_NAME % (self.conf_seq, self.acl_seq)
|
|
|
192ec7 |
return sub % (acl_name, ' '.join(esc_wordlist), acl_name)
|
|
|
192ec7 |
|
|
|
192ec7 |
def sub_hier_stoplist(self, line, line_re, sub, text):
|
|
|
192ec7 |
new_line = line
|
|
|
192ec7 |
m = re.match(line_re, line)
|
|
|
192ec7 |
if (not (m is None)):
|
|
|
192ec7 |
new_line = self.rep_hier_stoplist(line, sub, m.group(1))
|
|
|
192ec7 |
|
|
|
192ec7 |
# print out, if there was any change and add comment to conf line, if so
|
|
|
192ec7 |
if not (new_line is line):
|
|
|
192ec7 |
self.print_sub_text(text, sub)
|
|
|
192ec7 |
new_line = self.add_conf_comment(line, new_line)
|
|
|
192ec7 |
|
|
|
192ec7 |
return new_line
|
|
|
192ec7 |
|
|
|
192ec7 |
def process_conf_lines(self):
|
|
|
192ec7 |
for line in self.squid_conf_data.split(os.linesep):
|
|
|
192ec7 |
|
|
|
192ec7 |
# do not migrate comments
|
|
|
192ec7 |
if not line.strip().startswith('#'):
|
|
|
192ec7 |
self.check_include(line)
|
|
|
192ec7 |
line = self.sub_line_ad(line, self.RE_LOG_ACCESS, self.RE_LOG_ACCESS_ALLOW_REP, self.RE_LOG_ACCESS_DENY_REP, self.RE_LOG_ACCESS_TEXT)
|
|
|
192ec7 |
line = self.sub_line(line, self.RE_LOG_ICAP, self.RE_LOG_ICAP_REP, self.RE_LOG_ICAP_TEXT)
|
|
|
192ec7 |
line = self.sub_hier_stoplist(line, self.RE_HIER_STOPLIST, self.RE_HIER_STOPLIST_REP, self.RE_HIER_STOPLIST_TEXT)
|
|
|
192ec7 |
|
|
|
192ec7 |
self.migrated_squid_conf_data.append(line)
|
|
|
192ec7 |
|
|
|
192ec7 |
self.line_num = self.line_num + 1
|
|
|
192ec7 |
|
|
|
192ec7 |
def migrate(self):
|
|
|
192ec7 |
# prevent infinite loop
|
|
|
192ec7 |
if (self.level > ConfMigration.MAX_NESTED_INCLUDES):
|
|
|
192ec7 |
sys.stderr.write("WARNING: the maximum number of nested includes was reached\n")
|
|
|
192ec7 |
return
|
|
|
192ec7 |
|
|
|
192ec7 |
self.read_conf()
|
|
|
192ec7 |
self.process_conf_lines()
|
|
|
192ec7 |
if self.write_changes:
|
|
|
192ec7 |
if (not (set(self.migrated_squid_conf_data) == set(self.squid_conf_data.split(os.linesep)))):
|
|
|
192ec7 |
self.write_conf()
|
|
|
192ec7 |
|
|
|
192ec7 |
self.print_info("The migration finished successfully")
|
|
|
192ec7 |
|
|
|
192ec7 |
def get_prefix_str(self):
|
|
|
192ec7 |
return ((" " * int(self.level)) + "["+ self.squid_conf + "@%d]: " % (self.line_num))
|
|
|
192ec7 |
|
|
|
192ec7 |
def read_conf(self):
|
|
|
192ec7 |
self.print_info("Reading squid conf: " + self.squid_conf)
|
|
|
192ec7 |
try:
|
|
|
192ec7 |
self.in_file = open(self.squid_conf, 'r')
|
|
|
192ec7 |
self.squid_conf_data = self.in_file.read()
|
|
|
192ec7 |
self.in_file.close()
|
|
|
192ec7 |
except Exception as e:
|
|
|
192ec7 |
sys.stderr.write("%sError: %s\n" % (self.get_prefix_str(), e))
|
|
|
192ec7 |
sys.exit(1)
|
|
|
192ec7 |
|
|
|
192ec7 |
def write_conf(self):
|
|
|
192ec7 |
self.print_info("Creating backup conf: %s" % (self.squid_bak_conf))
|
|
|
192ec7 |
self.print_info("Writing changes to: %s" % (self.squid_conf))
|
|
|
192ec7 |
try:
|
|
|
192ec7 |
shutil.copyfile(self.squid_conf, self.squid_bak_conf)
|
|
|
192ec7 |
self.out_file = open(self.squid_conf, "w")
|
|
|
192ec7 |
self.out_file.write(os.linesep.join(self.migrated_squid_conf_data))
|
|
|
192ec7 |
self.out_file.close()
|
|
|
192ec7 |
except Exception as e:
|
|
|
192ec7 |
sys.stderr.write("%s Error: %s\n" % (self.get_prefix_str(), e))
|
|
|
192ec7 |
sys.exit(1)
|
|
|
192ec7 |
|
|
|
192ec7 |
def parse_args():
|
|
|
192ec7 |
parser = argparse.ArgumentParser(description='The script migrates the squid 3.3 configuration files to configuration files which are compatible with squid 3.5.')
|
|
|
192ec7 |
parser.add_argument('--conf', dest='squid_conf', action='store',
|
|
|
192ec7 |
default=ConfMigration.DEFAULT_SQUID_CONF,
|
|
|
192ec7 |
help='specify filename of squid configuration (default: %s)' % (ConfMigration.DEFAULT_SQUID_CONF))
|
|
|
192ec7 |
parser.add_argument('--write-changes', dest='write_changes', action='store_true',
|
|
|
192ec7 |
default=False,
|
|
|
192ec7 |
help='The changes are written to corresponding configuration files')
|
|
|
192ec7 |
parser.add_argument('--debug', dest="debug", action='store_true', default=False, help='print debug messages to stderr')
|
|
|
192ec7 |
return parser.parse_args()
|
|
|
192ec7 |
|
|
|
192ec7 |
if __name__ == '__main__':
|
|
|
192ec7 |
# parse args from command line
|
|
|
192ec7 |
args = parse_args()
|
|
|
192ec7 |
|
|
|
192ec7 |
# check if config file exists
|
|
|
192ec7 |
if (not os.path.exists(args.squid_conf)):
|
|
|
192ec7 |
sys.stderr.write("Error: the file %s does not exist\n" % (args.squid_conf))
|
|
|
192ec7 |
sys.exit(1)
|
|
|
192ec7 |
|
|
|
192ec7 |
# change working directory
|
|
|
192ec7 |
script_dir = os.getcwd()
|
|
|
192ec7 |
if (os.path.dirname(args.squid_conf)):
|
|
|
192ec7 |
os.chdir(os.path.dirname(args.squid_conf))
|
|
|
192ec7 |
|
|
|
192ec7 |
# start migration
|
|
|
192ec7 |
try:
|
|
|
192ec7 |
conf = ConfMigration(args, 0)
|
|
|
192ec7 |
conf.migrate()
|
|
|
192ec7 |
finally:
|
|
|
192ec7 |
print ""
|
|
|
192ec7 |
|
|
|
192ec7 |
if not args.write_changes:
|
|
|
192ec7 |
print "The changes have NOT been written to config files.\nUse the --write-changes option to write the changes"
|
|
|
192ec7 |
else:
|
|
|
192ec7 |
print "The changes have been written to config files!"
|
|
|
192ec7 |
|
|
|
192ec7 |
os.chdir(script_dir)
|