# last updated # 1/28/2015 added sql logging import sys import threading import time import queue import csv from SQLSMO import * # =============== FUNCTIONS =========================== def LogSQL(filename, CN, sql): """ Appends to given log file the sql executed with this format: ----------------------------------- --Server: SERVERNAME --Database: DATABASE script line 1 script line 2 ... ----------------------------------- """ today = str(datetime.date.today()) f = open(filename, 'a') s = "\n\n-----------------------------------------------------\n\n" s = s + '--Server: ' + CN['servername'] + "\n" s = s + '--Database: ' + CN['db'] + "\n" s = s + '--Date: ' + today + "\n\n" s = s + sql s = s + "\n\n-----------------------------------------------------\n\n" f.write(s) f.close() def ReadCSV(filename): """ Returns array with entries marked as ENABLED=Y in source file """ ary = [] with open(filename, newline='') as f: reader = csv.reader(f) try: for row in reader: if row[-1].strip() == 'Y': ary.append(row) except csv.Error as e: sys.exit('file {}, line {}: {}'.format(filename, reader.line_num, e)) return ary def ActionParsing(DBLIST2): """ Executes the actions from the source file, passed as the DBLIST2 array """ f = DBLIST2.pop() DESTSERVER = f[0] SOURCEDB = f[1] BACKUPFOLDER = f[2] DATAFOLDER = f[3] LOGFOLDER = f[4] DESTDB = f[5] ACTIONS = f[6] # open actions file, column 6 action_list = [] with open(ACTIONS, newline='') as g: action_l = g.readlines() for y in action_l: if y[0] != '#': action_list.append(y) CONNECTION = {} CONNECTION['servername'] = DESTSERVER CONNECTION['username'] = '' CONNECTION['password'] = '' CONNECTION['db'] = DESTDB msg = "\n" SQL = "\n" for m in action_list: m = m.strip() if m == 'BACKUP DATABASE FULL': # print('testing full backup') DB_BACKUP = BACKUPFOLDER + '\\' + DESTDB + '_backup_' + DatedString() + '.BAK' #database to backup is DESTDB smobackup = SQLSMO(CONNECTION, '', '', DB_BACKUP) smobackup.BackupDatabase() msg = msg + ' action:' + m + "\n" SQL = SQL + smobackup.sqlbackup + "\n\n" # msg = msg + " SQL: \n" + smobackup.sqlbackup + "\n" elif m == 'RESTORE DATABASE': try: BKFILE = GetLatestBackup(BACKUPFOLDER, '\\' + SOURCEDB + BACKUP_MASK)[-1] except IndexError: print('No backup file!') break smo3 = SQLSMO(CONNECTION, DATAFOLDER, LOGFOLDER, BKFILE) smo3.noexecute = NOEXECUTE_OPTION ok_to_restore = smo3.Ok_to_restore() if ok_to_restore == True: confirm_msg = 'Ok to restore' else: confirm_msg = 'NOT ok to restore' smo3.noexecute = 0 smo3.RestoreDatabase() msg = msg + ' action:' + m + "\n" msg = msg + confirm_msg + "\n" SQL = SQL + smo3.sqlrestore + "\n\n" # msg = msg + " SQL: \n" + smo3.sqlrestore + "\n" elif m == 'KILL CONNECTIONS': r = KillConnections(CONNECTION, DESTDB) msg = msg + ' action:' + m + "\n" msg = msg + str(r) + "\n" elif m == 'SET DBOWNER: sa': s = 'USE [' + DESTDB + '] EXEC dbo.sp_changedbowner @loginame = N' + chr(39)+ 'sa' + chr(39) + ', @map = false' try: rows, fnames = SqlExecute(CONNECTION, s) except Exception as inst: print('Error executing set dbwoner sa: ', inst) msg = msg + ' action:' + m + "\n" SQL = SQL + s + "\n\n" # msg = msg + " SQL: \n" + s + "\n" elif m == 'SYNC LOGINS': rows = SyncLogins(CONNECTION, DESTDB) msg = msg + ' action:' + m + "\n" for y in rows: msg = msg + str(y) + "\n" elif m == 'SET SIMPLE MODE': s = 'USE [master] ALTER DATABASE [' + DESTDB + '] SET RECOVERY SIMPLE WITH NO_WAIT' try: rows, fnames = SqlExecute(CONNECTION, s) except Exception as inst: print('Error executing set simple mode ', inst) msg = msg + ' action:' + m + "\n" SQL = SQL + s + "\n\n" # msg = msg + " SQL: \n" + s + "\n" elif m == 'SHRINK LOG': s = ''' declare @logfilename varchar(200) select @logfilename = name from sysfiles where groupid = 0 DBCC SHRINKFILE (@logfilename , 0, TRUNCATEONLY) ''' s = 'USE [' + DESTDB + '] ' + s try: rows, fnames = SqlExecute(CONNECTION, s) except Exception as inst: print('Error executing shrink log ', inst) msg = msg + ' action:' + m + "\n" SQL = SQL + s + "\n\n" # msg = msg + " SQL: \n" + s + "\n" with lock: print(msg) print(f) LogSQL('SqlScripts.sql', CONNECTION, SQL) return f #threading functions def do_work(item): """ do lengthy work """ start2 = time.perf_counter() # saving start time line = ActionParsing(DBLIST2) DICT_RESULTS = {} ThreadItem = threading.current_thread().name + '_' + str(item) DICT_RESULTS[ThreadItem] = {} DICT_RESULTS[ThreadItem]['line processed:'] = line time_elapsed = time.perf_counter() - start2 DICT_RESULTS[ThreadItem]['ELAPSED TIME'] = time_elapsed DICT_RESULTS2.append(DICT_RESULTS) # -----------End of do_work------------------------------ def worker(): """ The worker thread pulls an item from the queue and processes it """ while True: item = q.get() do_work(item) q.task_done() # ===============End of functions======================= # ============== Program Start========================= # some global values DICT_RESULTS2 = [] # used to store execution messages, to be displayed at the end BACKUP_MASK = '*.bak' # backup mask for files in backup folder NOEXECUTE_OPTION = 0 # 1 = no execution, 0 = yes execution THREAD_POOL = 0 # 0 means will process all entries in source file in its own thread # csv file with list of Servers,DBs and desired actions SOURCEFILE = r'C:\Users\python\PycharmProjects\codecamp\DBLIST_ACTIONS.csv' DBLIST2 = ReadCSV(SOURCEFILE) items_to_process = len(DBLIST2) if THREAD_POOL == 0: THREAD_POOL = items_to_process print('Total items to process:', items_to_process) print('Thread pool (concurrent processes): ', THREAD_POOL) if NOEXECUTE_OPTION == 0: print('Execution option is yes') else: print('No execution') # lock to serialize console output lock = threading.Lock() # Create the queue and thread pool. q = queue.Queue() for i in range(THREAD_POOL): t = threading.Thread(target=worker) t.daemon = True # thread dies when main thread (only non-daemon thread) exits. t.start() # stuff work items on the queue. start1 = time.perf_counter() # saving start time for item in range(len(DBLIST2)): # print('item:', item) q.put(item) q.join() # block until all tasks are done print('time:', time.perf_counter() - start1) for x in DICT_RESULTS2: print(x)