#!/usr/bin/env python
#coding:utf-8
import sys,os
import datetime
import random
import threading
import time
import datetime
import logging
import ConfigParser
from optparse import OptionParser
from logging.handlers import RotatingFileHandler
from time import strftime, localtime
from time import sleep
from datetime import date
from datetime import timedelta
from cos import CosClient
from cos import UploadFileRequest
from cos import CreateFolderRequest
from cos import DelFileRequest
from cos import DelFolderRequest
from cos import ListFolderRequest
from cos import threadpool
MAX_RETRY_TIMES = 3
LOG_SAVE_EVERY_NUM = 1024
ONE_TASK_DEL_FILE_NUMS = 50
log_level = 1
log_file_name = "del_file.log"
dir_thread_num = 2
file_thread_num = 5
log_out_to_screen = 1
delete_folder_fail_exist = 0
CONFIGFILE = "%s/.coscredentials" % os.path.expanduser('~')
CONFIGSECTION = 'COSCredentials'
HAS_FORK = hasattr(os, 'fork')
HELP = \
'''coscmd:
config --appid=[appid] --id=[secret_id] --key=[secret_key] --region=[region] --bucket=[bucket]
ls cosdir
mkdir dirname
put localfile cosdir
rm(delete,del) object
'''
CMD_LIST = {}
def cmd_configure(args, options):
if options.appid is None or options.secret_id is None or options.secret_key is None or options.region is None or options.bucket is None:
print("%s miss parameters, use --appid=[appid] --id=[secret_id] --key=[secret_key] --region=[region] --bucket=[bucket] to specify appid/id/key/region/bucket pair" % args[0])
sys.exit(-1)
config = ConfigParser.RawConfigParser()
config.add_section(CONFIGSECTION)
config.set(CONFIGSECTION, 'appid', options.appid)
config.set(CONFIGSECTION, 'secret_id', options.secret_id)
config.set(CONFIGSECTION, 'secret_key', options.secret_key)
if options.region in ['sh','gz','tj','sgp']:
config.set(CONFIGSECTION, 'region', options.region)
else:
print("input region error, setup use : --region={sh,gz,tj,sgp}")
sys.exit(-1)
config.set(CONFIGSECTION, 'bucket', options.bucket)
cfgfile = open(CONFIGFILE, 'w+')
config.write(cfgfile)
print("Your configuration is saved into %s ." % CONFIGFILE)
cfgfile.close()
import stat
os.chmod(CONFIGFILE, stat.S_IREAD | stat.S_IWRITE)
def cmd_loadconfigure():
config = ConfigParser.ConfigParser()
config.read(CONFIGFILE)
global appid
global secret_id
global secret_key
global region
global bucket
appid = int(config.get(CONFIGSECTION, 'appid'))
secret_id = config.get(CONFIGSECTION, 'secret_id').decode('utf-8')
secret_key = config.get(CONFIGSECTION, 'secret_key').decode('utf-8')
region = config.get(CONFIGSECTION, 'region')
bucket = config.get(CONFIGSECTION, 'bucket').decode('utf-8')
if len(secret_id) == 0 or len(secret_key) == 0 or len(region) == 0 or len(bucket) == 0:
print("can't get appid/secret_id/secret_key/region/bucket, setup use : config --appid=[appid] --id=[secret_id] --key=[secret_key] --region=[region] --bucket=[bucket]")
sys.exit(1)
def cmd_lsdir(COSDIR):
cosdir = COSDIR.decode('utf-8')
request = ListFolderRequest(bucket, cosdir)
list_folder_ret = cos_client.list_folder(request)
if list_folder_ret[u'code'] == 0:
print(True)
else:
print("%s, appid/secret_id/secret_key/region/bucket invalid"% list_folder_ret[u'message'])
def cmd_mkdir(COSDIR):
cosdir = COSDIR.decode('utf-8')
request = CreateFolderRequest(bucket, cosdir)
create_folder_ret = cos_client.create_folder(request)
if create_folder_ret[u'code'] == 0:
print("mkdir cos://%s%s OK" % (bucket,COSDIR))
else:
print(create_folder_ret[u'message'])
def cmd_put(LOCALFILE,COSFILE):
localfile = LOCALFILE.decode('utf-8')
cosfile = COSFILE.decode('utf-8')
request = UploadFileRequest(bucket, cosfile, localfile)
request.set_insert_only(0)
upload_file_ret = cos_client.upload_file(request)
if upload_file_ret[u'code'] == 0:
print("put cos://%s%s OK" % (bucket,COSFILE))
else:
print(upload_file_ret[u'message'])
def loginit():
global config
if (log_file_name == ""):
return
log_level = logging.ERROR
if log_level == 0:
log_level = logging.DEBUG
if log_level == 1:
log_level = logging.INFO
if log_level == 2:
log_level = logging.WARNING
#定义一个RotatingFileHandler,最多备份5个日志文件,每个日志文件最大20M
logger = logging.getLogger("")
Rthandler = RotatingFileHandler(log_file_name, maxBytes= 20*1024*1024,backupCount=5)
Rthandler.setLevel(log_level)
formatter = logging.Formatter('%(asctime)s %(levelname)s %(message)s')
Rthandler.setFormatter(formatter)
logger.addHandler(Rthandler)
#输出日志到屏幕
console = logging.StreamHandler()
console.setFormatter(formatter)
if (log_out_to_screen == 1):
logger.addHandler(console)
logger.setLevel(log_level)
return logger
#日期相关操作
class Dateop():
@staticmethod
def isValidDate(str):
try:
time.strptime(str, "%Y""%m""%d")
return True
except:
return False
@staticmethod
def getdaystr(n=0):
dt = date.today()-timedelta(days=n)
tt = dt.timetuple()
daystr = strftime("%Y""%m""%d",tt)
return daystr
@staticmethod
def cmpDateAgo(t1,t2):
if (Dateop.isValidDate(t1)==False or Dateop.isValidDate(t2)==False):
return False
if (int(t1) <= int (t2)):
return True
return False
@staticmethod
def isNeedDeleteDir(dirname, n=0):
if (len(dirname) != 8):
return False
if Dateop.isValidDate(dirname) == False:
return False
d2 = Dateop.getdaystr(n);
if Dateop.cmpDateAgo(dirname, d2):
return True
return False
#删除文件统计
class FileStat():
global cos_log
def __init__(self):
self.delfilesuccnum = 0
self.deldirsuccnum = 0
self.delfilefailnum = 0
self.deldirfailnum = 0
self.lock = threading.Lock()
def addDelFileFailNum(self,num=1):
self.lock.acquire(1)
self.delfilefailnum += num
self.lock.release()
def addDelDirFailNum(self,num=1):
self.lock.acquire(1)
self.deldirfailnum += num
self.lock.release()
def addDelDirSuccNum(self, num=1):
self.lock.acquire(1)
self.deldirsuccnum += num
self.lock.release()
def addDelFileSuccNum(self, num=1):
self.lock.acquire(1)
self.delfilesuccnum += num
self.lock.release()
def printStat(self):
msg ="".join(["delfilesuccnum=",str(self.delfilesuccnum),
",delfilefailnum=",str(self.delfilefailnum),
",deldirsuccnum=",str(self.deldirsuccnum),
",deldirfailnum=",str(self.deldirfailnum)])
print(msg)
def logStat(self):
curtime = time.strftime("%Y-%m-%d %H:%M:%S", time.localtime())
log = ''.join(["delfilenum=",str(self.delfilesuccnum),
",deldirnum=",str(self.deldirsuccnum),",delfilefailnum=",
str(self.delfilefailnum),",deldirfailnum=",str(self.deldirfailnum)])
cos_log.info(log)
#执行时间统计
class TimeStat(object):
global cos_log
def __init__(self):
self.start()
def start(self):
self.start = datetime.datetime.now()
self.t1 = time.time()
msg = "delete task started ..........."
cos_log.info(msg)
def end(self):
self.end = datetime.datetime.now()
self.t2 = time.time()
msg = "delete task ended\n\nrm task finished,\ntimecost:"+str(self.t2-self.t1) + " (s)"
cos_log.info(msg)
#删除文件列表中的文件
def delfiles(cos_client, bucket, filelist):
for f in filelist:
delfile(cos_client, bucket, f)
def delfolders(cos_client, bucket, folderlist):
for f in folderlist:
delfolder(cos_client, bucket, f)
#文件夹删除
def delfolder(cos_client, bucket, folder):
global stat
global cos_log
if not folder:
return 0
delfolderreq = DelFolderRequest(bucket, folder)
retry = 0
while (retry < MAX_RETRY_TIMES):
ret = cos_client.del_folder(delfolderreq)
msg = "delfolder fail, bucket="+bucket+",folder="+folder+ret['message']
if (ret['code'] == 0):
break
elif (ret['code'] == -166):
cos_log.warning(msg)
break
#操作太频繁,频控
elif (ret['code'] == -71):
sleep(random.randint(1,5))
cos_log.warning(msg)
retry += 1
continue
#文件夹非空
elif (ret['code'] == -173):
break
else:
cos_log.warning(msg)
retry += 1
if (ret['code'] != 0 and ret['code'] != -166):
stat.addDelDirFailNum()
cos_log.error("delfolder fail, bucket="+bucket+",folder="+folder+ret['message'])
return ret['code']
if (ret['code'] == 0):
stat.addDelDirSuccNum()
msg = "delfolder success, bucket="+bucket+",folder="+folder
cos_log.info(msg)
return 0
#文件删除
def delfile(cos_client, bucket, filepath):
global stat
global cos_log
delfilereq = DelFileRequest(bucket, filepath)
retry = 0
while (retry < MAX_RETRY_TIMES):
ret = cos_client.del_file(delfilereq)
msg = "delfile fail bucket="+bucket+",file="+filepath+ret['message']
if (ret['code'] == 0):
break
#文件不存在
elif (ret['code'] == -166):
cos_log.warning(msg)
break
#单目录写操作过快
elif (ret['code'] == -143):
sleep(random.randint(1,5))
cos_log.warning(msg)
retry += 1
continue
#操作太频繁,频控
elif (ret['code'] == -71):
sleep(random.randint(1,5))
cos_log.warning(msg)
retry += 1
continue
else:
cos_log.warning(msg)
retry += 1
continue
if (ret['code'] != 0 and ret['code'] != -166):
stat.addDelFileFailNum()
cos_log.error("delfile fail, bucket="+bucket+",file="+filepath+ret['message'])
return ret['code']
if (ret['code'] == 0):
stat.addDelFileSuccNum()
msg = "delfile success, bucket="+bucket+",file="+filepath
cos_log.info(msg)
return 0
#递归文件夹进行文件删除
def delete_r(cos_client, bucket, path, thread_pool_file):
global stat
global config
global cos_log
cos_log.debug("delete_r bucket:"+bucket+",path:"+path)
context = u""
#递归文件夹
while True:
listfolderreq = ListFolderRequest(bucket, path, 1000, u'', context)
retry = 0
while (retry < MAX_RETRY_TIMES):
listret = cos_client.list_folder(listfolderreq)
if listret['code'] != 0 :
retry += 1
sleep(random.randint(1,3))
continue
else:
break
if (listret['code'] != 0):
cos_log.error("delete_r: list folder fail:"+path +",return msg:"+ listret['message'])
return listret['code']
if (len(listret['data']['infos']) == 0):
break;
filelist = []
dirlist = []
for info in listret['data']['infos']:
fullname = path + info['name']
#list出来的文件列表中文件夹和文件本身是混杂一起的
if info.has_key('filesize'):
filelist.append(fullname)
if (len(filelist) >= ONE_TASK_DEL_FILE_NUMS):
args = [cos_client, bucket, filelist]
args_tuple = (args,None)
args_list = [args_tuple]
requests = threadpool.makeRequests(delfiles, args_list)
for req in requests:
thread_pool_file.putRequest(req)
filelist = []
continue
else:
pass
else:
dirlist.append(fullname)
if (len(dirlist) >= ONE_TASK_DEL_FILE_NUMS):
args = [cos_client, bucket, dirlist]
args_tuple = (args,None)
args_list = [args_tuple]
requests = threadpool.makeRequests(delfolders, args_list)
for req in requests:
thread_pool_file.putRequest(req)
dirlist = []
continue
else:
pass
pass
if (len(filelist) > 0):
args = [cos_client, bucket, filelist]
args_tuple = (args,None)
args_list = [args_tuple]
requests = threadpool.makeRequests(delfiles, args_list)
for req in requests:
thread_pool_file.putRequest(req)
filelist = []
else:
pass
if (len(dirlist) > 0):
args = [cos_client, bucket, dirlist]
args_tuple = (args,None)
args_list = [args_tuple]
requests = threadpool.makeRequests(delfolders, args_list)
for req in requests:
thread_pool_file.putRequest(req)
filelist = []
else:
pass
cos_log.debug("delete_r thread pool file waiting\n")
thread_pool_file.wait()
cos_log.debug("delete_r thread pool file waiting end\n")
if (listret['data']['listover'] == False):
context = listret['data']['context']
continue
else:
break
stat.logStat()
return 0
#支持Ctrl+C终止程序
class Watcher():
def __init__(self):
self.child = os.fork()
if self.child == 0:
return
else:
self.watch()
def watch(self):
global cos_log
try:
os.wait()
except KeyboardInterrupt:
cos_log.ERROR("ctrl+c terminated rm_recursive.py, exiting...")
self.kill()
sys.exit()
def kill(self):
try:
os.kill(self.child, signal.SIGKILL)
except OSError:
pass
def cmd_rm(COSDIR):
global thread_pool
global cos_log
global stat
cos_log = loginit()
stat = FileStat()
timestat = TimeStat()
if HAS_FORK:
Watcher()
path = COSDIR.decode('utf-8')
thread_pool_dir = threadpool.ThreadPool(dir_thread_num)
thread_pool_file = threadpool.ThreadPool(file_thread_num)
cos_log.debug("bucket:"+bucket +",path:"+path)
args = [cos_client, bucket, path, thread_pool_file]
args_tuple = (args, None)
args_list = [args_tuple]
requests = threadpool.makeRequests(delete_r, args_list)
for req in requests:
thread_pool_dir.putRequest(req)
cos_log.debug("thread_pool_dir waiting.....\n")
thread_pool_dir.wait()
thread_pool_dir.dismissWorkers(dir_thread_num, True)
cos_log.debug("thread_pool_dir wait end.....\n")
timestat.end()
stat.logStat()
if sys.argv[1] in ['config','ls','mkdir','put','rm','delete','del'] and len(sys.argv) >= 3:
if sys.argv[1] == 'config':
parser = OptionParser()
parser.add_option("-a", "--appid", dest="appid", help="specify appid")
parser.add_option("-i", "--id", dest="secret_id", help="specify secret id")
parser.add_option("-k", "--key", dest="secret_key", help="specify secret key")
parser.add_option("-r", "--region", dest="region", help="specify region")
parser.add_option("-b", "--bucket", dest="bucket", help="specify bucket")
(options, args) = parser.parse_args()
CMD_LIST['config'] = cmd_configure
CMD_LIST['config'](args, options)
if sys.argv[1] == 'ls':
cmd_loadconfigure()
cos_client = CosClient(appid, secret_id, secret_key, region)
COSDIR = sys.argv[2]
cmd_lsdir(COSDIR)
if sys.argv[1] == 'mkdir':
cmd_loadconfigure()
cos_client = CosClient(appid, secret_id, secret_key, region)
COSDIR = sys.argv[2]
cmd_mkdir(COSDIR)
if sys.argv[1] == 'put' and len(sys.argv) == 4:
cmd_loadconfigure()
cos_client = CosClient(appid, secret_id, secret_key, region)
LOCALFILE = sys.argv[2]
COSFILE = sys.argv[3]
cmd_put(LOCALFILE,COSFILE)
if sys.argv[1] in ('rm','delete','del'):
cmd_loadconfigure()
cos_client = CosClient(appid, secret_id, secret_key, region)
COSDIR = sys.argv[2]
path = COSDIR.decode('utf-8')
cmd_rm(path)
else:
print(HELP)
exit()