精通 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 平台都附带了这一工具,这能够进一步提升其采用率,而且随着时间的推移,有助于它成为可知足全部数据库管理需求的新的理想语言。

相关文章
相关标签/搜索