Наше приложение использует два файла для подключения к серверам баз данных:
$ 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.