精通 Oracle+Python,第 8 部分:適合 Oracle DBA 使用的 Python

傳統上,當須要爲操做系統編寫一些腳本時,人們經常會選用 Bash 或 Perl 腳本工具。這些工具易於使用,於是它們幾乎變得無處不在,滲透到了包括 Oracle Database 在內的其餘軟件中,Oracle Database 在很大程度上依賴它們執行各類管理任務。

可是最近,這種趨勢有所轉變,轉向有利於 Python 這類較新的編程工具。Python 可爲咱們提供直觀的開發以及各類靈活的數據結構和庫。全部的現代 Unix 和 Linux 系統都附帶了 Python;例如,Oracle Linux 6.1 附帶了 Python 2.6.6。

本教程將介紹對數據庫管理員尤其有用的一些 Python 特性,不管這些管理員要實現一次性代碼段仍是徹底可重用的程序,這些特性對他們來講都十分有用。在本部分中,咱們將探究如何與操做系統和遠程資源交互,而後瞭解各類壓縮和文件系統遍歷模塊。

出於本教程的目的,咱們將在 Oracle Linux 6.1 和 Python 2.6.6 環境中使用 Oracle Database 11g 快捷版 (XE)。html


與文件系統交互

Python 用來與操做系統交互的核心庫是 os 模塊,您能夠經過此模塊處理系統進程、識別平臺、處理操做系統管道以及使用環境變量 — 以 100 多個函數和變量的形式。

檢測當前平臺如同訪問 os 模塊中的預約義字符串那樣容易。如下示例展現了 Oracle Linux 6.1 上的結果,而且還顯示了此操做系統的默認路徑分隔符。

>>> import os
>>> os.name
‘posix’
>>> os.sep
‘/’

經過 os.environ 能夠訪問全部 Oracle 環境變量的列表。如下示例利用一個內聯生成器表達式:

>>> import os
>>> oracle_vars = dict((a,b) for a,b in os.environ.items() if a.find('ORACLE')>=0)
>>> from pprint import pprint
>>> pprint(oracle_vars)
{'ORACLE_HOME': '/u01/app/oracle/product/11.2.0/xe', 'ORACLE_SID':'XE'}

這至關於

SELECT key, value 
FROM os.environ 
WHERE key LIKE ‘%ORACLE%’ 

(若用 SQL 編寫)。

當咱們進一步探索時,咱們開始查看文件系統並瞭解所處位置。下表列出了最經常使用的文件系統訪問函數及其描述。 

python

函數linux

做用sql

os.getcwd()數據庫

獲取操做系統中的當前工做目錄express

os.chdir(path)編程

將目錄更改成給定 path網絡

os.chroot(path)數據結構

將當前 Python 進程的根路徑更改成 pathoracle

os.chown(pathuidgid)

與 chmod Linux 命令相同(uid 和 gid 是編號)

os.listdir(path)

列出給定 path 下的文件和目錄

os.mkdir(pathmode)

在給定 path 下建立目錄,並將八進制權限設置爲 mode(默認爲 0777)

os.remove(path)

刪除 path 下的一個文件

os.rmdir(path)

刪除 path 下的目錄

os.rename(path,newpath)

將 path 重命名爲 newpath

os.stat(path)

使用 OS stat() 調用顯示 path 的屬性

os.walk(pathtopdown,onerrorfollowlinks)

針對 path 下的文件系統樹返回生成器返回字節組(路徑、目錄、文件)

 

掌握了支持文件系統瀏覽的基本功能以後,咱們來了解一下如何使用 Python 快速查看舊的跟蹤文件和「未輪轉」日誌的列表並顯示它們使用了多少空間。清單 1 中的程序須要兩個參數:Oracle 日誌路徑(DIAGNOSTIC_DEST 指向的目錄)和文件被視爲過期的天數。此示例基於 os.walk


清單 1. walk.py:Oracle 診斷目錄下的舊的日誌和跟蹤文件

import datetime
import os
import sys
import time
from pprint import pprint

def readable(size):
  si=('B','KB','MB','GB','TB', 'PB', 'EB', 'ZB', 'YB')
  div = [n for n, m in enumerate(si) if pow(1024, n+1)>size][0]
  return "%.1f%s"%(size/float(pow(1024, div)), si[div])

total = {"log":0, "trace":0}
for path, dirs, files in os.walk(sys.argv[1]):
  for f in files:
    filepath = path+os.sep+f
    if os.stat(filepath).st_mtime>time.time()-(3600*24*int(sys.argv[2])):
      size = readable(os.path.getsize(filepath))
      age = datetime.datetime.fromtimestamp(os.stat(filepath).st_mtime)
      if f in ("log.xml", "alert.log", "listener.log"):
        filetype = "log"
      elif f.endswith("trc") or f.endswith("trm"):
        filetype = "trace"
      else:
        filetype = None
      if filetype:
        total[filetype] += os.path.getsize(filepath)

for a, b in total.items():
  total[a] = readable(b)

pprint(total)

運行 walk.py 獲得以下輸出:

$ python walk.py /home/oracle/app 10
{'log': '132.0MB', 'trace':'0.0B'}

在 os 命名空間中,另有一個名爲 os.path 的模塊,用於解決路徑名稱操做。它包含適用於不一樣系統的平臺敏感的實現,所以導入 os.path 將始終得到正確的操做系統版本。

os.path 模塊中的經常使用函數包括:

  • basename(path),用於得到給定路徑的葉名稱
  • dirname(path),用於得到文件路徑的目錄部分;它由 split(path) 函數加以補充,後者返回包含隔開的目錄部分和文件部分的字節組
  • exists(path),用於查看路徑下是否存在文件,針對沒法解析的符號連接返回 False
  • getsize(path),用於快速查看路徑下的字節數
  • isfile(path) 和 isdir(path),用於解析路徑類型


雖然目前爲止咱們已經瞭解了一些豐富的文件系統瀏覽功能,但咱們也只是初涉皮毛,由於還有多個其餘模塊。例如,filecmp 模塊既可以比較文件又可以比較目錄,tempfile 能夠輕鬆地管理臨時文件,glob 解析符合 Unix 式模式的文件路徑(如在 ora_pmon_*.trc、log_*.xml 中,等等),很是有用的 shutil 模塊實現高級文件系統操做,如複製和刪除多個文件或整個文件樹。 

與進程通訊


os 模塊並不只限於文件管理。還能夠用來與系統進程交互和生成系統進程,以及執行系統 kill 和 nice 調用。下表列出了最有用的進程管理函數。這些函數只對 Unix 和 Linux 平臺有效,但在 Python 3.2 分支中正在進行一些工做以使這些函數可用於 Windows。 

函數

做用

os.abort()

向當前 Python 進程發送 SIGABRT

os.exec*(patharg1...argN,environ)

exec* 函數系列,用於以 path 指定的進程取代當前進程,可選擇提供命令行參數和環境變量

os.kill(pidsignal)

向給定 pid 發送 signal

os.nice(value)

更改當前進程的 nice 值

os.popen(commandmode,buffersize)

對給定 command 打開一個未命名管道,有效地實現與進程的進一步交互;mode 表示管道打開處理屬性(默認爲「r」,表示讀取)

os.spawn*(modepath,environ)

在一個新的進程中運行 path 下的程序(這些函數如今已被 subprocess 模塊棄用)

os.system(command)

此函數經過操做系統 system() 調用(該調用可用於 Unix 和 Windows)運行由 command 定義的新進程



雖然其中許多函數可能在較舊的 Python 版本中派上用場,但從版本 2.4 開始,專門建立了一個專用的 subprocess 模塊來管理進程。這個新模塊最初在 2003 年提交到 Python 加強建議索引 (PEP),如今成爲與系統進程通訊的首選方法。

Subprocess 以簡單、可用而且至關通用的接口取代 os.popen、os.spawn* 和 os.system 函數。清單 2 顯示了 ps.py 程序的代碼,此程序執行 ps aux 命令並將結果移到 Python 字典中。這裏使用了一個管道來做爲 stdout 的目標以捕獲全部信息,並阻止輸出到屏幕。

清單 2. ps.py:將系統進程映射移到 Python 字典中
import re
import subprocess

args = ['ps', 'aux']
ps = subprocess.Popen(args, stdout=subprocess.PIPE)
processes = ps.stdout.readlines()
header = re.split('\s+', processes.pop(0))[:-1]
header.remove('COMMAND')

PS = {}
for process in processes:
  columns = re.split('\s+', process)
  if columns[0]!='oracle':
       continue
  PS[int(columns[1])] = {}
  for position, column in enumerate(columns[:9]):
       PS[int(columns[1])][header[position].lower()] = column
       PS[int(columns[1])]['command'] = ' '.join(columns[10:])

from pprint import pprint
pprint(PS)


輸出以下:

...
 25892: {'%cpu': '0.0',
       '%mem': '3.9',
       'command': 'xe_w000_XE ',
       'pid': '25892',
       'rss': '23672',
       'start': '16:02',
       'stat': 'Ss',
       'tty': '?',
       'user': 'oracle',
       'vsz': '457240'},
 26142: {'%cpu': '2.0',
       '%mem': '0.9',
       'command': 'python proc.py ',
       'pid': '26142',
       'rss': '5732',
       'start': '16:36',
       'stat': 'S+',
       'tty': 'pts/2',
       'user': 'oracle',
       'vsz': '160776'},
 26143: {'%cpu': '0.0',
       '%mem': '0.1',
       'command': 'ps aux ',
       'pid': '26143',
       'rss': '1100',
       'start': '16:36',
       'stat': 'R+',
       'tty': 'pts/2',
       'user': 'oracle',
       'vsz':'108044'}}

popen 函數接受多個關鍵字參數,如 stdin/stdout/stderr 描述符、用於設置進程工做目錄的 cwd,或者設置子進程環境變量的 env。要查看命令的狀態,只需查看 returncode 屬性。進程標識符在 pid 屬性下提供。

針對已建立進程的方法包括用於查看進程是否仍在運行的 poll()、用於在程序完成時進行恢復的 wait()、用於發送特定信號的 send_signal(),以及分別用於發送 SIGTERM 或 SIGKILL 信號的 terminate() 或 kill()。最後,要與生成的子進程徹底交互,咱們使用 communicate() 函數發送 stdin 輸入。

爲了對此進行說明,咱們來建立一個基於遺留的 SYSDBA 鏈接而發展的簡單 SQL*Plus 包裝器。

清單 3. sp.py:經過 Python 與 SQL*Plus 進程通訊
import os
from subprocess import Popen, PIPE

sqlplus = Popen(["sqlplus", "-S", "/", "as", "sysdba"], stdout=PIPE, stdin=PIPE)
sqlplus.stdin.write("select sysdate from dual;"+os.linesep)
sqlplus.stdin.write("select count(*) from all_objects;"+os.linesep)
out, err = sqlplus.communicate()
print out

This return output similar to:

SYSDATE
--------------
02-DEC-11

COUNT(*)
--------------
76147

報告服務

涉及走出數據庫的一項最使人頭疼的任務是發送警報或推送從數據倉庫提取的常常性報告。好消息是,Python 不只成功實現了一個全球流行的郵件列表系統 — Mailman,並且還提供一個豐富的電子郵件處理庫,此庫支持 MIME、附件、消息編碼以及與電子郵件處理有關的各個方面。email 模塊將協議自己內容與表示層相分離以便僅專一於構建郵件消息,而交付工做經過 smtplib 模塊處理。

email.message 中的 Message 類表明用於處理電子郵件的核心類。email.mime 命名空間中的各個處理程序用於處理不一樣的附件類型。但在此示例中,咱們將使用最通用的一個處理程序:email.mime.base 中的 MIMEBase。不過咱們這方面略施小計,利用了電子表格軟件將以表格格式打開 HTML 文件(若是它們具備 .xls 擴展名)的事實。咱們還將利用 tempfile 模塊的幫助。

Oracle Linux 並未預先安裝 cx_Oracle 模塊,所以您將須要從 cx-oracle.sourceforge.net 得到此模塊。此外,爲了可以導入 cx_Oracle 並使用網絡配置文件,在啓動 Python 解釋器以前須要設置 ORACLE_HOME 和 LD_LIBRARY_PATH。 

[root@xe ~]# rpm -ivh cx_Oracle-5.1-11g-py26-1.x86_64.rpm
Preparing...########################################### [100%]
1:cx_Oracle ########################################### [100%]
[root@xe ~]# 
[root@xe ~]# su - oracle
[oracle@xe ~]$ export ORACLE_HOME=/u01/app/oracle/product/11.2.0/xe
[oracle@xe ~]$ export LD_LIBRARY_PATH=$ORACLE_HOME/lib


請參見清單 4 瞭解完整程序,此程序鏈接到 Oracle Database 11g XE,提取員工數據,並將此數據打包爲電子表格附件以便發送到電子郵件組。

清單 4. report.py:電子郵件報告服務

import cx_Oracle
import datetime
import smtplib
import tempfile
from email.message import Message
from email.encoders import encode_base64
from email.mime.base import MIMEBase
from email.mime.multipart import MIMEMultipart

today = datetime.datetime.now()

msg = MIMEMultipart()
msg['From'] = 'Reports Service <reports@company.intranet>'
msg['To'] = 'receipients@company.intranet'
msg['Subject'] = 'Monthly employee report %d/%d ' % (today.month, today.year)

db = cx_Oracle.connect('hr', 'hrpwd', 'localhost/xe')
cursor = db.cursor()
cursor.execute("select * from employees order by 1")
report = tempfile.NamedTemporaryFile()
report.write("<table>")
for row in cursor:
  report.write("<tr>")
  for field in row:
    report.write("<td>%s</td>" % field)
  report.write("</tr>")
report.write("</table>")
report.flush()
cursor.close()
db.close()

attachment = MIMEBase('application', 'vnd.ms-excel')
report.file.seek(0)
attachment.set_payload(report.file.read())
encode_base64(attachment)
attachment.add_header('Content-Disposition', 'attachment;filename=emp_report_%d_%d.xls' % (today.month, today.year))
msg.attach(attachment)

emailserver = smtplib.SMTP("localhost")
emailserver.sendmail(msg['From'], msg['To'], msg.as_string())
emailserver.quit()

若是咱們更進一步採用此示例,可使用 Python 圖形處理庫 (PIL) 獲取統計圖,附加存儲在數據庫中的 BLOB 的縮略圖,或者藉助 ReportLab 生成 PDF 報告以便發送到相關組。email 模塊的功能很是強大,足以應對每種可能的情形。

總結

Python 豐富的跨平臺模塊庫確實完善了 DBA 的技術組合,DBA 使用這些技術可以監視整個數據庫體系、加快開發速度,同時保持極低的維護開銷。Python 使用普遍,每一個現代 Linux 平臺都附帶了這一工具,這能夠進一步提升其採用率,而且隨着時間的推移,有助於它成爲可知足全部數據庫管理需求的新的理想語言。

相關文章
相關標籤/搜索