Python: скрипт редактирования файлов конфигурации

Автор: | 07/01/2015
 

PythonНаше приложение использует два файла для подключения к серверам баз данных:

$ ls -l ../secure
total 8
-rw-r----- 1 app_env_1 app 1306 Nov 27 16:12 external.properties
-rw------- 1 app_env_1 app 12 Nov 27 15:51 secret.key

Задача – написать скрипт, с помощью которого их можно редактировать, не прибегая к использованию редакторов (т.к. у товарищей из Индии это вызывает… трудности…).

UPD Это была первая версия, вторая – тут: Python: модуль ConfigParser для работы с файлами конфигураций, и скрипт редактирования файлов

У нас нет возможности установки дополнительных модулей типа pysed и argparse, поэтому – использовался обычный sed и if/elif для проверки аргументов.

В результате – получился такой скрипт:

#!/usr/bin/env python

import os
import sys
import subprocess

import getpass
import shutil
import time

_USAGE = ('nUsage: %s <--property> [env] OR <--secret>nnExample:nn%s --property main rpln' % (__file__,__file__))

if len(sys.argv) < 2:
    print(_USAGE)
    sys.exit(3)

DBS = ['main', 'rpl', 'lima', 'rf', 'tcp', 'gpc']

try:
    SEC = os.environ['APP_SECURE_LOCATION_PWD']
    PROP = os.environ['APP_EXT_PROP']
except KeyError as e:
    print('ERROR: %s' % e)
    sys.exit(2)


def backup(file):

    ''''Create backup copy for given in argument file, with name including timestamp.'''

    try:
        bak = (file + time.strftime('_%Y-%m-%d-%H_%M_bak'))
        print('nCreating backup copy of %s to %s...' % (file, bak))
        shutil.copyfile(file, bak)
        print('Done.')
    except IOError as e:
        print('ERROR: %s' % e)
        sys.exit(1)


def answer(prompt, choice='Yes or no, please!'):

    '''Return OK, if input Ok, and False if input No.
       Thus - used in other functions, can run or pass next actios.'''

    while True:
        result = raw_input(prompt)
        if result in ('y', 'Y', 'yes', 'Yes'):
            return(True)
        elif result in ('n', 'N', 'no', 'No'):
            return(False)
        else:
            print(choice)

def new_secret(file):

    '''Creates backup of <SEC> config;
       clears file;
       accepts new keyword twice and check if it is identical;
       finally - writes new line to config.'''

    backup(file)

    try:

        with open(SEC, 'w+') as sec:
            newsecret1 = getpass.getpass('nPlease, enter new secret keyword: ')
            newsecret2 = getpass.getpass('Repeat: ')

            if newsecret1 == newsecret2:
                print('nWriting new keyword...')
                sec.write(newsecret1)
                print('Done.nnPlease, note: you need to regenerate all passwords inside %s config.n' % PROP)
            else:
                print('New keyword must be entered.n')

    except KeyboardInterrupt:
        print('nnExit.n')
        sys.exit(0)


def edit_entry(oldline, file):

    '''Edit <PROP> file, replacing old line with new raw_input() line.
       Uses OS 'sed' utility.'''

    if answer('Edit? [y/n] '):

        newline = raw_input('Please,set new value: ')

        if answer('nNew value is: %snIs it correct? [y/n] ' % newline):
            subprocess.call(("sed -i -e 's/%s/%s/g' %s" % (oldline.rstrip('n'), newline.rstrip('n'), file)), shell=True)
            print('New value witen to file.')


def change_property(file, **argv):

    '''Can accept few databases names.
       If none given - uses <DBS> kist with all databases.
       For each database - selects it's HOST (url iside file), USER and it's Password.
       Offers edit each entry with edit_entry() function.'''

    list = sys.argv[2:]

    if len(list) < 2:
        list = DBS

    backup(file)

    try:
        for host in list:
            print('nDatabase: %s' % host)
            with open(PROP, 'r') as prop:
                for line in prop.readlines():
                    if host in line:
                        if 'url' in line:
                            print('nHOST: %s' % line.rstrip('n'))
                            edit_entry(line, PROP)
                        elif 'user' in line:
                            print('nUsername: %s' % line.rstrip('n'))
                            edit_entry(line, PROP)
                        elif 'password' in line:
                            print('nPassword: %s' % line.rstrip('n'))
                            edit_entry(line, PROP)

    except KeyboardInterrupt:
        print('nnExit.n')
        sys.exit(0)


if sys.argv[1] == '--property':
    change_property(PROP)
elif sys.argv[1] == '--secret':
    new_secret(SEC)
elif sys.argv[1] == '*':
    print(_USAGE)

При запуске – скрипт создаёт резервную копию:

$ ls -l ../secure
total 12
-rw-r----- 1 app_env_1 app 1306 Nov 27 16:12 external.properties
-rw-r----- 1 app_env_1 app 1306 Nov 29 12:44 external.properties_2014-11-29-12_44_bak
-rw------- 1 app_env_1 app   12 Nov 27 15:51 secret.key

Как видно из скрипта – можно выбрать, подключение к какой базе или базам редактировать.

Например – для main  и rpl:

$ ./app_secure.py --property main rpl

Creating backup copy of /home/app_env_1/secure/external.properties to /home/app_env_1/secure/external.properties_2014-11-29-12_44_bak...
Done.

Database: main

HOST: jdbc.main.url=jdbc:oracle:thin:@domain.com:1845:SID1
Edit? [y/n] n

Username: jdbc.main.username=OLD_USER
Edit? [y/n] n

Password: jdbc.main.password=ENC(iFU//78v***Onk)
Edit? [y/n] n

И сам процесс редактирования:

$ ./app_secure.py --property main

Creating backup copy of /home/app_env_1/secure/external.properties to /home/app_env_1/secure/external.properties_2014-11-29-12_45_bak...
Done.

Database: main

HOST: jdbc.main.url=jdbc:oracle:thin:@domain.com:1845:SID1
Edit? [y/n] n

Username: jdbc.main.username=OLD_USER
Edit? [y/n] y
Please,set new value: jdbc.main.username=NEW_USER_NAME

New value is: jdbc.main.username=NEW_USER_NAME
Is it correct? [y/n] y
New value witen to file.