代码更新系统(一):配置文件配置
- 背景:
平常工作中,经常写更新脚本,一个会拉,一会推,一会使用ssh隧道。脚本好几个不利于管理。一直以来都想些一套更新系统。系统可以复用,可以自己选择在配置文件里更新方法或者选择在WEB上配置,WEB接受更新和展示更新状态,甚至在WEB实事执行更新操作。为了实现这些功能,一步步来,先实现从配置文件里读取配置并执行更新。以后功能都写好后再整合
- 更新工具:rsync
- 代码
1 #-*- coding: utf-8 -*- 2 ''' 3 Created on 2013-7-1 4 5 @author: Jin 6 ''' 7 import os 8 import sys 9 import ConfigParser 10 import logging 11 import subprocess 12 13 #vars 14 config_file='./config.ini' 15 homedir = './' 16 logfile = os.path.basename(sys.argv[0])+'.log' 17 logpath = homedir+logfile 18 19 #logconfig 20 logging.basicConfig(level=logging.DEBUG, 21 format='%(asctime)s %(name)-12s %(levelname)-8s %(message)s', 22 datefmt='%Y-%m-%d %H:%M', 23 filename=logpath, 24 filemode='a') 25 26 console = logging.StreamHandler() 27 console.setLevel(logging.INFO) 28 formatter = logging.Formatter('%(asctime)s %(name)-12s %(levelname)-8s %(message)s') 29 console.setFormatter(formatter) 30 logging.getLogger('').addHandler(console) 31 32 33 def check_file(filename): 34 if os.path.exists(filename) and os.path.isfile(filename): 35 return True 36 else: 37 return False 38 39 def config_from_file(config_file='./config.ini'): 40 ''' 41 Get config from config file.return dict date 42 The dict keys is:'method','action','rsync_bin','ip','user','argv','excludeconf','localdir','pwdfile','module','remotedir' 43 If pwdfile,module,excludeconf undefine,will use default set: 44 password file is '/etc/rsync/rsync_default.secrets' 45 module is 'default' 46 excludeconf is '/etc/rsync/exclude_files.conf' 47 ''' 48 49 if check_file(config_file): 50 config=ConfigParser.ConfigParser() 51 config.read(config_file) 52 53 if config.has_option('rsyncclient','method'): 54 method=config.get('rsyncclient','method') 55 else: 56 method=None 57 if config.has_option('rsyncclient','action'): 58 action=config.get('rsyncclient','action') 59 else: 60 action=None 61 if config.has_option('rsyncclient','rsync_bin'): 62 rsync_bin=config.get('rsyncclient','rsync_bin') 63 else: 64 rsync_bin='rsync' 65 if config.has_option('rsyncclient','ip'): 66 ip=config.get('rsyncclient','ip') 67 else: 68 ip=None 69 if config.has_option('rsyncclient','user'): 70 user=config.get('rsyncclient','user') 71 else: 72 user=None 73 if config.has_option('rsyncclient','argv'): 74 argv=config.get('rsyncclient','argv') 75 else: 76 argv=None 77 if config.has_option('rsyncclient','localdir'): 78 localdir=config.get('rsyncclient','localdir') 79 else: 80 localdir=None 81 if config.has_option('rsyncclient','excludeconf'): 82 excludeconf=config.get('rsyncclient','excludeconf') 83 else: 84 excludeconf='/etc/rsync/exclude_files.conf' 85 86 #method daemon options need passwordfile and module 87 if config.has_option('rsyncclient','pwdfile'): 88 pwdfile=config.get('rsyncclient','pwdfile') 89 else: 90 pwdfile='/etc/rsync/rsync_default.secrets' 91 if config.has_option('rsyncclient','module'): 92 module=config.get('rsyncclient','module') 93 else: 94 module='default' 95 96 #method ssh options need remotedir 97 if config.has_option('rsyncclient','remotedir'): 98 remotedir=config.get('rsyncclient','remotedir') 99 else: 100 remotedir=None 101 102 data = {'method':method,'action':action,'rsync_bin':rsync_bin,'ip':ip,'user':user,'argv':argv,'localdir':localdir,'excludeconf':excludeconf,'pwdfile':pwdfile,'module':module,'remotedir':remotedir} 103 #logging.debug("data:%s" % data) 104 return data 105 106 else: 107 logging.error("%s is not exist" % config_file) 108 return None 109 110 def get_cmd(configdata): 111 ''' 112 assembling command from config dict 113 ''' 114 if type(configdata)==type({}): 115 if None in configdata.values(): 116 for key in configdata: 117 if configdata[key] == None: 118 logging.error("%s is undefine!" % key) 119 return None 120 else: 121 if configdata['method']=='daemon' and configdata['action']=='push': 122 '''push local to rsync daemon server''' 123 cmd=configdata['rsync_bin']+' '+configdata['argv']+' '+'--exclude-from='+configdata['excludeconf']+' '+'--password-file='+configdata['pwdfile']+' '+configdata['localdir']+' '+configdata['user']+'@'+configdata['ip']+'::'+configdata['module'] 124 return cmd 125 elif configdata['method']=='daemon' and configdata['action']=='pull': 126 '''pull from rsync daemon server to local''' 127 cmd=configdata['rsync_bin']+' '+configdata['argv']+' '+'--exclude-from='+configdata['excludeconf']+' '+'--password-file='+configdata['pwdfile']+' '+configdata['user']+'@'+configdata['ip']+'::'+configdata['module']+' '+configdata['localdir'] 128 return cmd 129 elif configdata['method']=='ssh' and configdata['action']=='push': 130 '''push local to remote server use ssh''' 131 cmd=configdata['rsync_bin']+' '+configdata['argv']+' '+'-e '+configdata['method']+' '+'--exclude-from='+configdata['excludeconf']+' '+configdata['localdir']+' '+configdata['user']+'@'+configdata['ip']+':'+configdata['remotedir'] 132 return cmd 133 elif configdata['method']=='ssh' and configdata['action']=='pull': 134 '''pull from to remote server to local use ssh''' 135 cmd=configdata['rsync_bin']+' '+configdata['argv']+' '+'-e '+configdata['method']+' '+'--exclude-from='+configdata['excludeconf']+' '+configdata['user']+'@'+configdata['ip']+':'+configdata['remotedir']+' '+configdata['localdir'] 136 return cmd 137 else: 138 '''undefine''' 139 logging.error("method:%s or action:%s error!" % (configdata['method'],configdata['action'])) 140 return None 141 142 def check_rsyncclient_runevn(configdata): 143 '''check excludeconf,is not exists create it,check pwdfile,is not exists warning and exit''' 144 if not check_file(configdata['excludeconf']): 145 logging.info("%s is not exists,Now create it!" % configdata['excludeconf']) 146 with open(configdata['excludeconf'],'wt') as f: 147 f.write('') 148 149 if configdata['method']=='daemon': 150 if not check_file(configdata['pwdfile']): 151 logging.error("%s is not exists!" % configdata['pwdfile']) 152 return False 153 else: 154 return True 155 elif configdata['method']=='ssh': 156 return True 157 else: 158 return False 159 160 def is_running(processname): 161 '''Get processname status ''' 162 cmd='/usr/bin/pgrep '+ processname+' > /dev/null 2>&1' 163 try: 164 #pstat=subprocess.Popen(cmd,shell=True,stdin=subprocess.PIPE) 165 pstat=subprocess.Popen(cmd,shell=True) 166 except StandardError,e: 167 logging.error("Get run status failed:: %s ,exit run!" % e) 168 sys.exit(1) 169 else: 170 pstat.wait() 171 if pstat.returncode == 0: 172 return True 173 else: 174 return False 175 176 def run_command(cmd): 177 '''run command''' 178 if is_running('rsync'): 179 logging.warning("rsync is running!") 180 else: 181 try: 182 pstat=subprocess.Popen(cmd,shell=True,stderr=subprocess.PIPE,stdout=subprocess.PIPE) 183 except StandardError,e: 184 logging.error("Run script failed:: %d (%s) ,exit run!" % (e.errno, e.strerror)) 185 sys.exit(1) 186 else: 187 pstat.wait() 188 if pstat.returncode == 0: 189 logging.info("Run script Successful,exit code is %s" % pstat.returncode) 190 lines=pstat.stdout.readlines() 191 dellist=[] 192 rsync_dict={} 193 for line in lines: 194 if 'deleting' in line: 195 dellist.append(line.split()[1]) 196 delcount=len(dellist) 197 addlist=[i.rstrip('\n') for i in lines[delcount+2:-3]] 198 addcount=len(addlist) 199 speed=' '.join(lines[-2].split()[-2:]) 200 rsync_dict={} 201 rsync_dict['delcount']=delcount 202 rsync_dict['dellist']=dellist 203 rsync_dict['addcount']=delcount 204 rsync_dict['addlist']=addlist 205 rsync_dict['speed']=speed 206 print rsync_dict 207 logging.info("Result,delcount:%d,dellist:%s,addcount:%d,addlist:%s,speed:%s" % (delcount,dellist,addcount,addlist,speed)) 208 209 else: 210 logging.error("Run script End,exit code is %s,With reason <%s>" % (pstat.returncode,pstat.stderr.read().rstrip('\n'))) 211 212 213 def __main__(): 214 configdata=config_from_file(config_file) 215 if configdata: 216 cmd=get_cmd(configdata) 217 if cmd: 218 if check_rsyncclient_runevn(configdata): 219 run_command(cmd) 220 else: 221 logging.error("check rsyncclient runenv fail,detail see log!") 222 sys.exit(3) 223 else: 224 logging.error("command is None,detail see log!") 225 sys.exit(2) 226 else: 227 logging.error("configdata is None,detail see log!") 228 sys.exit(1) 229 230 231 if __name__ == '__main__': 232 __main__()
- 配置文件格式
[rsyncclient]
action = pull
method = daemon
localdir = /tmp/testdir/
user = rsync_store
ip = 127.0.0.1
argv = -vzrtopg --delete
excludeconf=./exclude_files.conf
remotedir = /tmp/remotedir/
module = store
pwdfile = ./rsync_store.passwd
- 代码简单说明:
check_file:判断文件存在且进为文件(排除文件存在为目录的情况)
config_from_file:从ini文件读取配置,吐出一个配置字典
get_cmd:将配置字典组合成一个command,根据method和
check_rsyncclient_runevn:根据配置字典检查rsync的运行环境 排除文件和密码文件,这里是我比较纠结的地方,要不要创建空的排除列表文件?
is_running:检查程序是否运行,其实不严谨,下一个版本看是否能改进
run_command():运行组合好的命令
__main__():前面的函数逻辑上组合在一起