| TESTINFRA(1) | testinfra | TESTINFRA(1) |
نام (NAME)
testinfra - مستندات testinfra
آخرین مستندات: https://testinfra.readthedocs.io/en/latest
درباره (ABOUT)
با Testinfra میتوانید آزمونهای واحد (unit tests) را در Python بنویسید تا وضعیت واقعی سرورهای خود را که توسط ابزارهای مدیریتی مانند Salt https://saltstack.com، Ansible https://www.ansible.com، Puppet https://puppetlabs.com، Chef https://www.chef.io و غیره پیکربندی شدهاند، بسنجید و آزمایش کنید.
هدف Testinfra تبدیل شدن به معادل Serverspec https://serverspec.org در پایتون است و به عنوان یک افزایه (plugin) برای موتور قدرتمند آزمون Pytest https://pytest.org نوشته شده است.
مجوز (LICENSE)
Apache License 2.0 https://github.com/pytest-dev/pytest-testinfra/blob/main/LICENSE
نشانواره تحت مجوز Creative Commons NoDerivatives 4.0 License https://creativecommons.org/licenses/by-nd/4.0 منتشر شده است. اگر کاربرد دیگری مد نظرتان است، با ما تماس بگیرید.
شروع سریع (QUICK START)
نصب testinfra با استفاده از pip:
$ pip install pytest-testinfra # or install the devel version $ pip install 'git+https://github.com/pytest-dev/pytest-testinfra@main#egg=pytest-testinfra'
نخستین فایل آزمونهای خود را در test_myinfra.py بنویسید:
def test_passwd_file(host):
passwd = host.file("/etc/passwd")
assert passwd.contains("root")
assert passwd.user == "root"
assert passwd.group == "root"
assert passwd.mode == 0o644
def test_nginx_is_installed(host):
nginx = host.package("nginx")
assert nginx.is_installed
assert nginx.version.startswith("1.2")
def test_nginx_running_and_enabled(host):
nginx = host.service("nginx")
assert nginx.is_running
assert nginx.is_enabled
و آن را اجرا کنید:
$ pytest -v test_myinfra.py ====================== test session starts ====================== platform linux -- Python 2.7.3 -- py-1.4.26 -- pytest-2.6.4 plugins: testinfra collected 3 items test_myinfra.py::test_passwd_file[local] PASSED test_myinfra.py::test_nginx_is_installed[local] PASSED test_myinfra.py::test_nginx_running_and_enabled[local] PASSED =================== 3 passed in 0.66 seconds ====================
مستندات (DOCUMENTATION)
تغییرات (Changelog)
10.2.2
- •
- [FIX] گنجاندن فایلهای لازم برای اجرای آزمونها در sdist
10.2.1
- •
- [FIX] رفع ایراد انتشار در pypi
- [جدید] پرسوجوی تمام نامهای کاربری و نامهای گروه
- [اصلاح] جلوگیری از بنبست Paramiko هنگامی که آزمون بیش از 2MB داده به stdout ارسال میکند
- [اصلاح] هماهنگی با تغییرات ماژول shell در ansible
- [اصلاح] افزودن 4 به کد خروج مورد انتظار هنگام اجرای "systemctl is-active"
- [اصلاح] رفع خطای KeyError در MountPoint.__repr__() در صورت عدم وجود mount
- [مستندات] استفاده از دستور pytest بهجای py.test
- [مستندات] گسترش مستندات backend با افزودن یک بخش عمومی برای مشخصات میزبان (host spec)
- [متفرقه] اجرای lint برای py 3.12 و 3.13 نیز
- [متفرقه] تغییر سیستم بستهبندی برای استفاده از hatchling
- [متفرقه] حذف آرگومان اضافی و استفادهنشدهی "args" در run_winrm()
- [متفرقه] استفاده از ruff format بهجای black/isort
- [متفرقه] استفاده از ruff بهجای flake8
- [متفرقه] استفاده از f-string بهجای str.format()
- [متفرقه] استفاده از dict، list و tuple توکار برای تعیین نوع (typing)
- [متفرقه] استفاده از python 3.11 در طول آزمونها
- [متفرقه] اصلاح آزمونهای salt
- [متفرقه] رفع مشکل آزمونهایی که به دلیل سپری شدن تاریخ انقضا ناموفق میشدند
- [متفرقه] حذف کتابخانه crypt از فرایند آزمون
10.1.1
- [اصلاح] افزودن بررسی برای وجود دایرکتوری ssh control path
- [اصلاح] مدیریت is_installed برای بستههای rpm در صورت خرابی پایگاهداده rpm
- [اصلاح] رفع مشکل service.exists
10.1.0
- [جدید] افزودن ویژگی Interface.link
- [اصلاح] پیروی ویژگیهای فایل از پیوندهای نمادین (symlinks)
- [اصلاح] الزام به pytest>=6 و استفاده از حاشیهنویسیهای آینده (future annotations) برای سازگاری با pytest<7
10.0.0
- [اصلاح] Ansible: رفع مشکل نبود نامهای گروه در get_variables()
- [اصلاح] testinfra/modules/blockdevice: عدم ایجاد خطا و شکست در هنگام خروجی روی stderr
- [مستندات] گسترش و نمایش مستندات CommandResult
- [اصلاح] گسترش فهرست پسوندهای معتبر برای واحدهای systemd
- [مستندات] افزودن بخش مستندات از قلمافتادهی Environment
- [متفرقه] تعیین انواع داده (types) برای plugin.py
- [اصلاح] رفع مشکل عدم وجود توزیع RHEL در ماژول package
- [جدید] افزودن پشتیبانی از brew در ماژول package
- [جدید] افزودن Service.exists
- [متفرقه] تبدیل CommandResult به یک dataclass
9.0.0
- [تغییر ناسازگار] pytest-testinfra اکنون نیازمند python >= 3.9 است
- [تغییر ناسازگار] حذف ماژول منسوخشدهی PipPackage
- [جدید] افزودن پشتیبانی از گزینه اشتراکگذاری اتصال ControlPath در SSH (#713)
- [اصلاح] تلاش مجدد اتصال SSH هنگام بروز ConnectionResetError (#708)
- [اصلاح] فهرست کردن صریح openSUSE Leap و Tumbleweed بهعنوان توزیعهای مبتنی بر rpm
- [اصلاح] اجباری کردن نام گروه در ماژول group
8.1.0
- [جدید] افزودن پشتیبانی از Windows برای ماژولهای File و Service
- [جدید] افزودن ویژگی File.is_executable
8.0.0
- [جدید] افزودن مشخصهی Group.members
- [جدید] افزودن مشخصهی File.inode
- [جدید] افزودن متد Interface.routes()
- [جدید] افزودن مشخصهی Docker.is_restarting
- [اصلاح] رفع خطای احتمالی در Interface.default()
- [اصلاح] اصلاح تشخیص busybox در ماژول Process
- [اصلاح] رفع خطای احتمالی KeyError در ماژول SysInfo
- [تغییر ناسازگار] توقف پشتیبانی از python 3.7
- •
- [اصلاح] رفع مشکل سازگاری command -v با پوسته dash
7.0.0
- [جدید] بهبود پشتیبانی از پیکربندی ssh در بکاند Paramiko
- [جدید] افزودن بکاند chroot
- [جدید] افزودن پشتیبانی از Manjaro-Linux
- [جدید] افزودن پشتیبانی از Cloudlinux
- [تغییر بنیادین] توقف پشتیبانی از python 3.6 به دلیل پایان دوره پشتیبانی (EOL)
6.8.0
- •
- [جدید] افزودن پشتیبانی از AlmaLinux و RockyLinux
6.7.0
- [جدید] پشتیبانی از نوع ansible_connection با مقدار community.docker.docker
- [جدید] افزودن گزینه ssh_extra_args
6.6.0
- [جدید] امکان بررسی انقضای گذرواژه کاربر
- [جدید] مدیریت متغیرهای محیطی ANSIBLE_SSH_COMMON_ARGS و ANSIBLE_SSH_EXTRA_ARGS برای اتصالهای ansible
- [اصلاح] رفع مشکل کدگذاری در اتصالهای salt
- [اصلاح] رفع خطای AttributeError در صورت در دسترس نبودن "command" و استفاده جایگزین از "which"
6.5.0
- استفاده جایگزین از which در صورت شکست "command -v"
- استفاده پیشفرض از realpath برای حل پیوندهای نمادین بهجای "readlink -f"
- ansible: پشتیبانی از متغیرهای محیطی
- اجبار ماژول package به ارجاع به RpmPackage در Fedora
- رفع مشکلی که در آن نسخههای جدید supervisor ممکن بود با وضعیت != 0 خارج شوند
- رمزگشایی خروجی ansible در صورت غیر ascii بودن
- استفاده از python3 یا python جهت دریافت کدگذاری راه دور
6.4.0
- پیادهسازی نامهای Interface و پیشفرض (#615)
- پیادهسازی Service.systemd_properties (#612)
6.3.0
- رفع مشکل #451 برای استفاده با pytest -p no:terminal
- افزودن client_version() و server_version() و version() به ماژول docker.
6.2.0
- رفع مشکل #590: عدم تشخیص صحیح Windows توسط Systeminfo (#592)
- نخستین پیادهسازی فضاهای نام شبکه در ماژول addr (#596)
- پشتیبانی از pip check در ماژول PipPackage (#605)
- بازسازی ساختار pip: پیادهسازی installed و version (#606)
- امکان تعیین مسیرهای supervisorctl و supervisord.conf (#536)
6.1.0
- رفع مشکل انتخاب نادرست ماژول package در CentOS هنگام نصب بودن ابزارهای dpkg (#575) #570
- حذف میزبانهای تکراری بازگرداندهشده توسط get_backends() (#572)
- استفاده از /run/systemd/system/ برای تشخیص systemd (رفع #546)
- استفاده از ssh_args از فایل ansible.cfg
- الزام به python >= 3.6
- رفع خطای ValueError در python 3.8+ هنگام استفاده از گزینه --nagios.
6.0.0
- •
- تغییر بنیادین: پروژه testinfra به سازمان https://github.com/pytest-dev منتقل شده است. نام پروژه در PyPi به pytest-testinfra تغییر یافته است. یک بسته موقت testinfra فرایند انتقال را تسهیل میکند، اما باید نام آن را در فایلهای requirements خود به pytest-testinfra تغییر دهید.
5.3.1
- •
- رفع اشکال ویژگی تازه ارائهشده is_masked در سرویس systemd https://github.com/philpep/testinfra/pull/569
5.3.0
- •
- افزودن ویژگی is_masked به سرویس systemd
5.2.2
- •
- iptables: استفاده از گزینه -w جهت انتظار برای دریافت قفل iptables هنگام اجرای موازی با pytest-xdist.
- •
- رفع مشکل ساخت مستندات
5.2.0
- امکان تعیین کانتکست kubeconfig در بکاند kubernetes
- حذف پیادهسازی file.__ne__ و الزام به نسخه python >= 3.5
5.1.0
- استفاده از remote_user و remote_port در ansible.cfg
- افزودن مشخصه arch (معماری) به ماژول system_info
5.0.0
- •
- تغییر ناسازگار (Breaking change): دستور host.file().listdir() اکنون یک متد است
4.1.0
- ارسال آرگومانهای اضافی به خط فرمان ansible از طریق host.ansible()
- متد جدید host.file.listdir() برای فهرست کردن موارد موجود در یک دایرکتوری.
4.0.0
- •
- پایان پشتیبانی از python2
3.4.0
- افزودن ماژول و بکاند podman
- هشدار: این آخرین نسخه testinfra با پشتیبانی از python2 خواهد بود، لطفاً به python3 ارتقا دهید.
3.3.0
- افزودن extras برای وابستگیهای بکاند (#454)
- بهبودهای مختلف در مستندات یکپارچهسازی با kitchen
- پشتیبانی بکاند ansible از فیلد "password" در اینونتوری ansible
- بکاند جدید "openshift"
3.2.1
- •
- رفع ایراد ماژول Process هنگام کار با رشتههای طولانی (نام کاربری و...) #505
3.2.0
- ماژول جدید "environment" برای دریافت متغیرهای محیطی راه دور
- ماژول جدید "block_device" برای نمایش اطلاعات دستگاههای بلوکی
- افزودن پرچم سراسری --force-ansible به خط فرمان
- صدور خطا در صورت عدم وجود فایل اینونتوری ansible
- رفع مشکل گریز (escape) نویسهها در آرگومانهای ssh در ansible هنگام تنظیم در اینونتوری یا فایل پیکربندی
3.1.0
- اتصالات ssh بهطور پیشفرض از اتصالات پایدار (persistent) استفاده میکنند. میتوانید با ارسال controlpersist=0 به گزینههای اتصال، این قابلیت را غیرفعال کنید.
- اتصالات ssh در ansible اکنون بهجای paramiko از بکاند ssh استفاده میکنند. اکنون گزینههای ansible_ssh_common_args و ansible_ssh_extra_args لحاظ میشوند.
- افزودن گزینه اتصال جدید ansible با نام "force_ansible"؛ هنگامی که روی True تنظیم شود، testinfra همواره ansible را برای اجرای تمام دستوراتی که نیاز دارد فراخوانی میکند.
- پوشش تمام انواع اتصالات ansible با تنظیم force_ansible=True برای اتصالاتی که معادل متناظری در testinfra ندارند (برای نمونه "network_cli").
3.0.6
- ثبت وقایع کامل دستورات تنها در سطح لاگ DEBUG جهت جلوگیری از لاگ شدن دادههای حساس در سطح لاگ INFO.
- رفع خرابی احتمالی هنگام پردازش اینونتوریهای ansible #470
- پشتیبانی از فایل kubeconfig جایگزین در اتصالات kubectl #460
- پشتیبانی از تجزیه ProxyCommand از فایل ssh_config برای اتصالات paramiko
3.0.5
- تنظیم زمان انتظار پیشفرض (timeout) به ۱۰ ثانیه در اتصالات ssh/paramiko
- افزودن پشتیبانی از پارامتر ansible_private_key_file در اینونتوری ansible
3.0.4
- •
- افزودن پشتیبانی از اتصالات lxc و lxd در ansible
3.0.3
- رفع مشکل تجزیه RequestTTY توسط paramiko از پیکربندیهای ssh
- بازگرداندن کلید "groups" از تابع ansible.get_variables() جهت حفظ سازگاری عقبرو با نسخههای 2.X از testinfra
- رفع مشکل تطبیق ansible بدون inventory با "localhost"
- رفع مشکل پشتیبانی از ansible 2.8 بدون inventory
- رفع مشکلی در ansible/paramiko که پیکربندی میزبانها را از ~/.ssh/config نمیخواند
- امکان ارسال --ssh-config و --ssh-identity-file به اتصال ansible
3.0.1
- رفع مشکل تجزیه آدرسهای ipv6 برای backendهای paramiko، ssh و ansible.
- رفع مشکل فراخوانی --connection=ansible هنگامی که هیچ میزبانی ارائه نشده است
3.0.0
- یک backend جدید برای ansible که پشتیبانی از ansible 2.8 و مشکل مجوز (license) را برطرف میکند. برای جزئیات به https://github.com/philpep/testinfra/issues/431 مراجعه کنید. این تغییر باعث میشود ansible از backendهای بومی testinfra استفاده کند و تنها برای اتصالات local، ssh یا docker کارایی دارد. اگر انواع اتصال دیگری دارید یا با مشکلی مواجه شدید، لطفاً یک گزارش باگ در https://github.com/philpep/testinfra/issues/new باز کنید
- پشتیبانی از Windows بهبود یافته است. ماژول "package" با Chocolatey مدیریت میشود و پشتیبانی از ماژول "user" نیز فراهم شده است.
2.1.0
- docker: متد کلاسی (classmethod) جدید get_containers()
- socket: رفع مشکل تجزیه آدرسهای ipv6 در نسخههای جدید ss
- service: بازگشت systemd به sysv هنگامی که "systemctl is-active" کار نمیکند
2.0.0
- افزودن ماژول addr، مورد استفاده برای آزمایش اتصال شبکه
- حذف دستور منسوخشدهٔ "testinfra"؛ باید به جای آن از "py.test" استفاده کنید
- حذف fixtureهای سطح بالای منسوخشده؛ در عوض از طریق fixture با نام "host" به آنها دسترسی پیدا کنید.
- حذف پشتیبانی از ansible <= 2.4
1.19.0
- افزودن ماژول docker
- رفع مشکل سازگاری با pytest 4
1.18.0
- امکان استفاده از کدگذاری درصدی (urlencode) برای نویسهها در مشخصات میزبان "user:pass@host" (#387)
- رفع مشکل ثبت گزارش دوگانه (double logging) هم از pytest و هم از testinfra
- حذف پشتیبانی از python 2.6
- امکان پیکربندی مهلت زمانی (timeout) برای backend نوع winrm
1.17.0
- افزودن پشتیبانی از کاربر "become" در ماژول ansible
- افزودن مشخصه failed/succeeded به خروجی run()
1.16.0
- packaging: استفاده از setuptools_scm به جای pbr
- iptables: افزودن پشتیبانی از ip6tables
- sysctl: یافتن sysctl خارج از PATH (/sbin)
1.15.0
- رفع مشکل یافتن دستورهای ss و netstat در مسیرهای "sbin" برای Centos (359)
- افزودن یک راهکار موقت (workaround) برای https://github.com/pytest-dev/pytest/issues/3542
- مدیریت وضعیت "starting" برای ماژول Service در Alpine linux
- رفع مشکل گزینههای no_ssl و no_verify_ssl برای backend نوع WinRM
1.14.1
- رفع مشکل ترتیب اجرای آزمونهای چندمیزبانی (#347)، پسرفت معرفیشده در 1.13.1
- رفع مشکل Socket در میزبانهای OpenBSD (#338)
1.14.0
- افزودن یک backend جدید برای lxc
- Socket: رفع مشکل is_listening برای سوکتهای unix
- افزودن پشتیبانی از namespace و container برای backend نوع kubernetes
- افزودن یک حافظه موقت (cache) از inventoryهای تجزیهشدهٔ ansible برای backend نوع ansible
- Service: رفع مشکل تشخیص سرویس در میزبانهای Centos 6
- File: پیادهسازی مقایسه فایل با مسیرهای متنی (string paths)
1.13.1
- package: رفع رفتار is_installed و version برای بستههای نصبنشده (#321 و #326)
- ansible: استفاده از ترتیب آزمون پیشبینیپذیر هنگام استفاده از pytest-xdist برای رفع خطاهای تصادفی در گردآوری آزمونها (#316)
- socket: رفع مشکل در شناسایی سوکتهای شنونده udp (#311)
- ssh backend: افزودن پشتیبانی از GSSAPI
1.12.0
- ansible: رفع مشکلات سازگاری با ansible 2.5
- pip: رفع مشکلات سازگاری با pip 10 (#299)
1.11.1
- •
- Socket: رفع خطا در نسخههای قدیمی ss که فاقد گزینه --no-header هستند (#293)
1.11.0
- رفع گزارش نادرست خطا هنگام استفاده از ماژول ansible بدون ansible backend (#288)
- Socket: افزودن یک پیادهسازی جدید با استفاده از ss به جای netstat (#124)
- افزودن پشتیبانی از service، process و systeminfo برای Alpine (#283)
1.10.1
- اصلاح ()get_variables برای ansible>=2.0,<2.4 (#274)
- Paramiko: استفاده از تنظیمات RequireTTY در صورت تعیین شدن در SSHConfig مشخصشده (#247)
1.10.0
- •
- ماژول جدید iptables
1.9.1
- رفع مشکل اجرای testinfra درون مجموعهای که از doctest استفاده میکند (#268)
- Service: افزودن متد ()is_valid برای systemd
- رفع مشکل ()file.linked_to برای Mac OS
1.9.0
- Interface: امکان یافتن دستور 'ip' خارج از PATH
- رفع مشکل گزینه --nagios با python 3
1.8.0
- منسوخ شدن دستور testinfra (در نسخه 2.0 حذف خواهد شد)، به جای آن از py.test استفاده کنید #135
- مدیریت گزینه --nagios هنگام استفاده از دستور py.test
1.7.1
- •
- پشتیبانی از ansible 2.4 (#249)
1.7.0
- Salt: امکان تعیین پوشه پیکربندی (#230)
- افزودن یک WinRM backend
- Socket: امکان مدیریت کلاینتهای ipv4 توسط سوکتهای ipv6 (#234)
- Service: بهبود شناسایی upstart (#243)
1.6.5
- Service: افزودن پشتیبانی از ()is_enabled برای OpenBSD
- افزودن گزینه فایل هویت ssh برای backendهای paramiko و ssh
- گسترش تیلدا (~) به پوشه خانگی کاربر برای گزینههای ssh-config، ssh-identity-file و ansible-inventory
1.6.4
- Service: امکان یافتن دستور 'service' خارج از $PATH #211
- اصلاحات مستندات
1.6.3
- •
- رفع هشدار منسوخشدگی ناخواسته هنگام اجرای آزمونها با pytest 3.1 #204
1.6.2
- •
- رفع مشکل بسته wheel برای نسخه 1.6.1
1.6.1
- •
- پشتیبانی از ansible 2.3 همراه با python 3 (#197)
1.6.0
- •
- فیکسچر جدید 'host' به عنوان جایگزینی برای تمامی فیکسچرهای دیگر. ببینید https://testinfra.readthedocs.io/en/latest/modules.html#host (سایر فیکسچرها منسوخ شدهاند و در نگارش 2.0 حذف خواهند شد).
1.5.5
- •
- backends: رفع مشکل ansible backend با ansible >= 2.3 (#195)
- backends: بازگشت به کدگذاری UTF-8 در صورت ASCII بودن کدگذاری سیستم.
- Service: رفع باگ is_running() در سیستمهای مبتنی بر Upstart.
1.5.3
- •
- Sudo: بازگردانی دستور backend در صورت بروز استثناها.
1.5.2
- •
- اعمال become_user هنگام استفاده از بکاند ansible.
1.5.1
- •
- افزودن وابستگی به importlib در python 2.6.
1.5.0
- بکاند جدید kubectl.
- Command: حذف نویسههای carriage return و خطوط جدید در check_output (#164).
- Package: بهبود در دریافت version() و release() در rpm.
- User: افزودن فیلد gecos (توضیحات) (#155).
1.4.5
- SystemInfo: تشخیص نام مستعار (codename) از VERSION_CODENAME در /etc/os-release (پشتیبان در صورت نصب نبودن lsb_release).
- Package: افزودن ویژگی release برای سیستمهای مبتنی بر rpm.
فراخوانی (Invocation)
آزمودن چندین میزبان (Test multiple hosts)
بهطور پیشفرض Testinfra آزمونها را روی ماشین محلی اجرا میکند، اما میتوانید با استفاده از paramiko https://www.paramiko.org (یک پیادهسازی ssh در پایتون)، سیستمهای راه دور را نیز بیازمایید:
$ pip install paramiko $ pytest -v --hosts=localhost,root@webserver:2222 test_myinfra.py ====================== test session starts ====================== platform linux -- Python 2.7.3 -- py-1.4.26 -- pytest-2.6.4 plugins: testinfra collected 3 items test_myinfra.py::test_passwd_file[localhost] PASSED test_myinfra.py::test_nginx_is_installed[localhost] PASSED test_myinfra.py::test_nginx_running_and_enabled[localhost] PASSED test_myinfra.py::test_passwd_file[root@webserver:2222] PASSED test_myinfra.py::test_nginx_is_installed[root@webserver:2222] PASSED test_myinfra.py::test_nginx_running_and_enabled[root@webserver:2222] PASSED =================== 6 passed in 8.49 seconds ====================
همچنین میتوانید میزبانها را به ازای هر ماژول آزمون مشخص کنید:
testinfra_hosts = ["localhost", "root@webserver:2222"]
def test_foo(host):
[....]
اجرای موازی (Parallel execution)
اگر آزمونهای زیادی دارید، میتوانید از افزونهی pytest-xdist https://pypi.org/project/pytest-xdist برای اجرای آزمونها با استفاده از چندین پردازه استفاده کنید:
$ pip install pytest-xdist # Launch tests using 3 processes $ pytest -n 3 -v --host=web1,web2,web3,web4,web5,web6 test_myinfra.py
فراخوانی پیشرفته (Advanced invocation)
# Test recursively all test files (starting with `test_`) in current directory $ pytest # Filter function/hosts with pytest -k option $ pytest --hosts=webserver,dnsserver -k webserver -k nginx
برای موارد استفاده و قابلیتهای بیشتر، مستندات Pytest https://docs.pytest.org/en/latest را ببینید.
بکاندهای اتصال (Connection backends)
ابزار Testinfra همراه با چندین بکاند اتصال برای اجرای دستورهای راه دور ارائه میشود.
هنگام نصب، باید بکاندهای مورد نیاز خود را به عنوان extras انتخاب کنید تا از برآورده شدن وابستگیهای پایتون اطمینان حاصل شود (توجه داشته باشید که ممکن است همچنان به ابزارهای بستهبندیشدهی مختلف سیستمی نیاز باشد). برای نمونه:
$ pip install pytest-testinfra[ansible,salt]
برای تمام بکاندها، دستورها میتوانند با گزینه --sudo با دسترسی مدیر سیستم (superuser) یا با گزینه --sudo-user به عنوان یک کاربر مشخص اجرا شوند..SS "مشخصات عمومی میزبان (General Host specification)"
پارامتر --hosts در Testinfra برای تعیین میزبانهای هدف جهت اجرای آزمونها استفاده میشود.
شما میتوانید با جدا کردن هر هدف با ویرگول (comma)، چندین میزبان را مشخص کنید؛ این قابلیت به شما اجازه میدهد آزمونها را با استفاده از بکاندهای مختلف در محیطها یا ماشینهای گوناگون اجرا نمایید.
فیلدهای user، password و port اختیاری هستند و بسته به الزامات احراز هویت و اتصال شما، انعطافپذیری لازم را فراهم میکنند.
لطفاً جزئیات مربوط به هر یک از بکاندها را نیز مطالعه کنید، زیرا نحوه مدیریت مشخصات میزبان در هر بکاند اندکی متفاوت است.
نحو (Syntax):
--hosts=<backend>://<user>:<password>@<host>:<port>
اجزا:
- <backend>: نوع بکاند مورد استفاده (مانند ssh، docker، paramiko، local)
- <user>: نام کاربری برای احراز هویت (اختیاری)
- <password>: گذرواژه برای احراز هویت (اختیاری)
- <host>: نام میزبان یا نشانی IP هدف
- <port>: شماره درگاه (پورت) هدف (اختیاری)
نویسههای خاص (مانند ":") در فیلدهای user و password باید طبق استاندارد RFC 3986 درصدکدگذاری (percent-encoded) شوند. این کار در Python با استفاده از urllib.parse.quote() امکانپذیر است.
برای نمونه:
import urllib.parse
user = urllib.parse.quote('user:name')
password = urllib.parse.quote('p@ssw:rd')
host = 'hostname'
port = 22
host_spec = f"ssh://{user}:{password}@{host}:{port}"
print(host_spec)
این کار تضمین میکند که هرگونه نویسه خاص به درستی کدگذاری شده و رشته اتصال معتبر باشد.
مثالها:
بکاند SSH با مشخصات کامل:
testinfra --hosts=ssh://user:password@hostname:22
بکاند Docker:
testinfra --hosts=docker://container_id
ترکیب چند بکاند:
testinfra --hosts=ssh://user:password@hostname:22,docker://container_id,local://
local
این بکاند پیشفرض در صورت عدم تعیین میزبان است (خواه از طریق --hosts یا در ماژولها). دستورها به صورت محلی در یک زیرپردازش (subprocess) تحت کاربر جاری اجرا میشوند:
$ pytest --sudo test_myinfra.py
paramiko
این بکاند پیشفرض در صورت ارائه فهرستی از میزبانها است. Paramiko https://www.paramiko.org یک پیادهسازی از پروتکل SSHv2 در Python است. Testinfra از شما گذرواژهای درخواست نخواهد کرد، بنابراین باید بتوانید بدون نیاز به گذرواژه (با استفاده از کلیدهای بدون گذرواژه یا ssh-agent) متصل شوید.
میتوانید یک ssh-config جایگزین مشخص کنید:
$ pytest --ssh-config=/path/to/ssh_config --hosts=server
docker
بکاند Docker میتواند برای آزمودن کانتینرهای در حال اجرای Docker استفاده شود. این بکاند از دستور docker exec https://docs.docker.com/reference/commandline/exec استفاده میکند:
$ pytest --hosts='docker://[user@]container_id_or_name'
همچنین مثال Test Docker images <#test-docker-images> را ببینید..SS podman
از backend مربوط به Podman میتوان برای آزمودن کانتینرهای در حال اجرای Podman استفاده کرد. این backend از دستور podman exec https://github.com/containers/libpod/blob/master/docs/source/markdown/podman.1.md استفاده میکند:
$ pytest --hosts='podman://[user@]container_id_or_name'
ssh
این یک backend خالص SSH است که از دستور ssh استفاده میکند. مثال:
$ pytest --hosts='ssh://server' $ pytest --ssh-config=/path/to/ssh_config --hosts='ssh://server' $ pytest --ssh-identity-file=/path/to/key --hosts='ssh://server' $ pytest --hosts='ssh://server?timeout=60&controlpersist=120' $ pytest --hosts='ssh://server' --ssh-extra-args='-o StrictHostKeyChecking=no'
بهطور پیشفرض مهلت زمانی (timeout) روی ۱۰ ثانیه و ControlPersist روی ۶۰ ثانیه تنظیم شده است. میتوانید با ارسال controlpersist=0 به گزینهها، اتصال پایدار را غیرفعال کنید.
salt
موتور backend مربوط به salt از رابط برنامهنویسی پایتون کلاینت salt به آدرس https://docs.saltstack.com/en/latest/ref/clients استفاده میکند و میتوان آن را از سرور salt-master به کار برد:
$ pytest --hosts='salt://*' $ pytest --hosts='salt://minion1,salt://minion2' $ pytest --hosts='salt://web*' $ pytest --hosts='salt://G@os:Debian'
ابزار Testinfra از کانال ارتباطی salt برای اجرای دستورها استفاده خواهد کرد.
میزبانها را میتوان با استفاده از glob و تطبیقدهندههای ترکیبی https://docs.saltstack.com/en/latest/topics/targeting/compound.html انتخاب کرد.
ansible
از موجودیهای (inventories) انسیبل میتوان برای توصیف اینکه Testinfra باید از چه میزبانهایی استفاده کند و نحوهٔ اتصال به آنها با استفاده از backend انسیبل در Testinfra، بهره برد.
برای استفاده از backend انسیبل، پیشوند ansible:// را به گزینهٔ --hosts اضافه کنید؛ مانند:
$ pytest --hosts='ansible://all' # tests all inventory hosts $ pytest --hosts='ansible://host1,ansible://host2' $ pytest --hosts='ansible://web*'
یک inventory را میتوان با گزینهٔ --ansible-inventory مشخص کرد، در غیر این صورت از مقدار پیشفرض (/etc/ansible/hosts) استفاده میشود.
مقدار ansible_connection در inventory شما برای تعیین اینکه کدام backend برای هر میزبان به کار رود استفاده خواهد شد: مقادیر پشتیبانیشده عبارتند از local، ssh، paramiko و docker. سایر اتصالها (یا اگر از گزینهٔ --force-ansible استفاده کنید) باعث میشوند testinfra تمام دستورها را از طریق خود Ansible اجرا کند، که به مراتب کندتر از سایر backendها است:
$ pytest --force-ansible --hosts='ansible://all' $ pytest --hosts='ansible://host?force_ansible=True'
بهطور پیشفرض، backend اتصال Ansible ابتدا برای احراز هویت تلاش میکند از ansible_ssh_private_key_file و ansible_private_key_file استفاده کند، سپس در صورت نیاز به متغیرهای ansible_user همراه با ansible_ssh_pass رجوع میکند (هر دو مورد نیاز هستند)، و در نهایت به عنوان آخرین راهکار به پیکربندی SSH میزبان خودتان متوسل میشود.
این رفتار را میتوان با مشخص کردن گزینهٔ --ssh-identity-file یا گزینهٔ --ssh-config بازنویسی کرد.
در نهایت، این متغیرهای محیطی پشتیبانی میشوند و به متغیر متناظرشان در ansible ارسال خواهند شد (مستندات Ansible را ببینید):
https://docs.ansible.com/ansible/2.3/intro_inventory.html
https://docs.ansible.com/ansible/latest/reference_appendices/config.html
- ANSIBLE_REMOTE_USER
- ANSIBLE_SSH_EXTRA_ARGS
- ANSIBLE_SSH_COMMON_ARGS
- ANSIBLE_REMOTE_PORT
- ANSIBLE_BECOME_USER
- ANSIBLE_BECOME
kubectl
از backend مربوط به kubectl میتوان برای آزمودن کانتینرهای در حال اجرا در Kubernetes استفاده کرد. این backend از دستور kubectl exec https://kubernetes.io/docs/reference/generated/kubectl/kubectl-commands#exec بهره میبرد و از اتصال به یک کانتینر مشخص درون یک pod و استفاده از یک namespace مشخص پشتیبانی میکند:
# will use the default namespace and default container $ pytest --hosts='kubectl://mypod-a1b2c3' # specify container name and namespace $ pytest --hosts='kubectl://somepod-2536ab?container=nginx&namespace=web' # specify the kubeconfig context to use $ pytest --hosts='kubectl://somepod-2536ab?context=k8s-cluster-a&container=nginx' # you can specify kubeconfig either from KUBECONFIG environment variable # or when working with multiple configuration with the "kubeconfig" option $ pytest --hosts='kubectl://somepod-123?kubeconfig=/path/kubeconfig,kubectl://otherpod-123?kubeconfig=/other/kubeconfig'
پشتانهٔ openshift میتواند برای آزمایش کانتینرهای در حال اجرا در OpenShift به کار رود. این پشتانه از دستور oc exec https://docs.openshift.com/container-platform/4.2/nodes/containers/nodes-containers-remote-commands.html استفاده کرده و از اتصال به نام یک کانتینر مشخص درون یک pod و استفاده از یک namespace معین پشتیبانی میکند:
# will use the default namespace and default container $ pytest --hosts='openshift://mypod-a1b2c3' # specify container name and namespace $ pytest --hosts='openshift://somepod-2536ab?container=nginx&namespace=web' # you can specify kubeconfig either from KUBECONFIG environment variable # or when working with multiple configuration with the "kubeconfig" option $ pytest --hosts='openshift://somepod-123?kubeconfig=/path/kubeconfig,openshift://otherpod-123?kubeconfig=/other/kubeconfig'
winrm
پشتانهٔ winrm از pywinrm https://pypi.python.org/pypi/pywinrm استفاده میکند:
$ pytest --hosts='winrm://Administrator:Password@127.0.0.1' $ pytest --hosts='winrm://vagrant@127.0.0.1:2200?no_ssl=true&no_verify_ssl=true'
مهلت زمانی پیشفرض خواندن و عملیات pywinrm را میتوان با استفاده از آرگومانهای پرسمان read_timeout_sec و operation_timeout_sec بازنویسی کرد:
$ pytest --hosts='winrm://vagrant@127.0.0.1:2200?read_timeout_sec=120&operation_timeout_sec=100'
LXC/LXD
پشتانهٔ LXC میتواند برای آزمایش کانتینرهای در حال اجرای LXC یا LXD به کار رود. این پشتانه از دستور lxc exec https://linuxcontainers.org/lxd/getting-started-cli استفاده میکند:
$ pytest --hosts='lxc://container_name'
ماژولها (Modules)
ماژولهای Testinfra از طریق فیکسچر host https://docs.pytest.org/en/latest/fixture.html#fixture ارائه میشوند؛ آن را به عنوان آرگومان تابع آزمون خود اعلان کنید تا درون آن در دسترس قرار گیرد.
def test_foo(host):
# [...]
host
- ansible
- کلاس testinfra.modules.ansible.Ansible
- addr
- کلاس testinfra.modules.addr.Addr
- blockdevice
- کلاس testinfra.modules.blockdevice.BlockDevice
- docker
- کلاس testinfra.modules.docker.Docker
- environment
- کلاس testinfra.modules.environment.Environment
- file
- کلاس testinfra.modules.file.File
- group
- کلاس testinfra.modules.group.Group
- interface
- کلاس testinfra.modules.interface.Interface
- iptables
- کلاس testinfra.modules.iptables.Iptables
- mount_point
- کلاس testinfra.modules.mountpoint.MountPoint
- package
- کلاس testinfra.modules.package.Package
- pip
- کلاس testinfra.modules.pip.Pip
- podman
- کلاس testinfra.modules.podman.Podman
- process
- کلاس testinfra.modules.process.Process
- puppet_resource
- کلاس testinfra.modules.puppet.PuppetResource
- facter
- کلاس testinfra.modules.puppet.Facter
- salt
- کلاس testinfra.modules.salt.Salt
- service
- کلاس testinfra.modules.service.Service
- socket
- کلاس testinfra.modules.socket.Socket
- sudo
- کلاس testinfra.modules.sudo.Sudo
- supervisor
- کلاس testinfra.modules.supervisor.Supervisor
- sysctl
- کلاس testinfra.modules.sysctl.Sysctl
- system_info
- کلاس testinfra.modules.systeminfo.SystemInfo
- user
- کلاس testinfra.modules.user.User
- property has_command_v: bool
- در صورتی که command -v در دسترس باشد، مقدار True را برمیگرداند
- find_command(command: str, extrapaths: Iterable[str] = ('/sbin', '/usr/sbin')) -> str
- مسیر دستور
دادهشده
را
برمیگرداند
در صورت پیدا نشدن دستور، خطای ValueError ایجاد میکند
- run(command: str, *args: str, **kwargs: Any) -> CommandResult <#testinfra.backend.base.CommandResult>
- دستور
دادهشده
را اجرا
کرده و rc
(وضعیت
خروج)، stdout و stderr
را
برمیگرداند
>>> cmd = host.run("ls -l /etc/passwd") >>> cmd.rc 0 >>> cmd.stdout '-rw-r--r-- 1 root root 1790 Feb 11 00:28 /etc/passwd\n' >>> cmd.stderr '' >>> cmd.succeeded True >>> cmd.failed Falseروش مناسب: همواره از نقلقول آرگومانهای شل (shell quoting) استفاده کنید تا از تزریق شل (shell injection) جلوگیری شود
>>> cmd = host.run("ls -l -- %s", "/;echo inject") CommandResult( rc=2, stdout='', stderr=( 'ls: cannot access /;echo inject: No such file or directory\n'), command="ls -l '/;echo inject'")
- run_expect(expected: list[int], command: str, *args: str, **kwargs: Any) -> CommandResult <#testinfra.backend.base.CommandResult>
- دستور را اجرا کرده و بررسی میکند که آیا وضعیت خروج موردانتظار را برمیگرداند یا خیر
- پارامترها
- expected -- فهرستی از وضعیتهای خروج موردانتظار
- خطاهای صادرشده
- AssertionError
- run_test(command: str, *args: str, **kwargs: Any) -> CommandResult <#testinfra.backend.base.CommandResult>
- دستور را اجرا کرده و بررسی میکند که آیا وضعیت خروج 0 یا 1 را برمیگرداند یا خیر
- خطاهای صادرشده
- AssertionError
- check_output(command: str, *args: str, **kwargs: Any) -> str
- دریافت stdout دستوری که با موفقیت اجرا شده است
- مقادیر بازگشتی
- خروجی stdout بدون نویسه خط جدید پایانی
- خطاهای صادرشده
- AssertionError
- classmethod get_host(hostspec: str, **kwargs: Any) -> Host <#testinfra.host.Host>
- یک نمونه از
Host را از hostspec
برمیگرداند
مقدار hostspec باید به این صورت باشد: <backend_type>://<name>?param1=value1¶m2=value2
پارامترها را میتوان در **kwargs نیز ارسال کرد (به عنوان مثال، get_host("local://", sudo=True) معادل get_host("local://?sudo=true") است)
مثالها:
>>> get_host("local://", sudo=True)
>>> get_host("paramiko://user@host", ssh_config="/path/my_ssh_config")
>>> get_host("ansible://all?ansible_inventory=/etc/ansible/inventory")
Ansible
- class Ansible(module_name, module_args=None, check=True)
- توابع
ماژولهای
Ansible را اجرا
میکند
این ماژول تنها با بکاند اتصال ansible <#ansible-connection-backend> در دسترس است.
حالت بررسی (Check mode) https://docs.ansible.com/ansible/playbooks_checkmode.html به طور پیشفرض فعال است، میتوانید آن را با check=False غیرفعال کنید.
قابلیت Become https://docs.ansible.com/ansible/user_guide/become.html#id1 به طور پیشفرض False است. میتوانید آن را با become=True فعال کنید.
آرگومانهای Ansible که به inventory یا ارتباط Ansible (که هر دو توسط testinfra مدیریت میشوند) مربوط نیستند نیز از طریق آرگومانهای کلیدواژهای پذیرفته میشوند:
- become_method str sudo، su، doas و غیره.
- become_user str تبدیل به این کاربر.
- diff bool: هنگام تغییر فایلها و قالبهای (کوچک)، تفاوتها را در آن فایلها نشان میدهد.
- extra_vars dict به صورت یک رشته JSON سریالایز شده و به Ansible فرستاده میشود.
- one_line bool: فشردهسازی خروجی.
- user str اتصال به عنوان این کاربر.
- verbose int سطح جزئیات خروجی (verbosity).
>>> host.ansible("apt", "name=nginx state=present")["changed"]
False
>>> host.ansible("apt", "name=nginx state=present", become=True)["changed"]
False
>>> host.ansible("command", "echo foo", check=False)["stdout"]
'foo'
>>> host.ansible("setup")["ansible_facts"]["ansible_lsb"]["codename"]
'jessie'
>>> host.ansible("file", "path=/etc/passwd")["mode"]
'0640'
>>> host.ansible(
... "command",
... "id --user --name",
... check=False,
... become=True,
... become_user="http",
... )["stdout"]
'http'
>>> host.ansible(
... "apt",
... "name={{ packages }}",
... check=False,
... extra_vars={"packages": ["neovim", "vim"]},
... )
# Installs neovim and vim.
- exception AnsibleException(result)
- استثنایی
که هنگام
وقوع خطا در
یک
فراخوانی ansible
ایجاد
میشود
نتیجهٔ حاصل از ansible از طریق مشخصهٔ result قابل دسترسی است
>>> try: ... host.ansible("command", "echo foo") ... except host.ansible.AnsibleException as exc: ... assert exc.result['failed'] is True ... assert exc.result['msg'] == 'Skipped. You might want to try check=False' # noqa
- get_variables()
- یک dict از
متغیرهای ansible
را
برمیگرداند
>>> host.ansible.get_variables() { 'inventory_hostname': 'localhost', 'group_names': ['ungrouped'], 'foo': 'bar', }
- class Addr(name)
- بررسی
نشانی
دوردست
مثال:
>>> google = host.addr("google.com") >>> google.is_resolvable True >>> '173.194.32.225' in google.ipv4_addresses True >>> google.is_reachable True >>> google.port(443).is_reachable True >>> google.port(666).is_reachable Falseهمچنین میتواند درون یک فضای نام شبکه https://man7.org/linux/man-pages/man7/namespaces.7.html استفاده شود.
>>> localhost = host.addr("localhost", "ns1") >>> localhost.is_resolvable Trueفضاهای نام شبکه تنها زمانی قابل استفاده هستند که دستور ip https://man7.org/linux/man-pages/man8/ip.8.html در دسترس باشد؛ زیرا در این صورت، ماژول از ip-netns https://man7.org/linux/man-pages/man8/ip-netns.8.html به عنوان پیشوند دستور استفاده میکند. در غیر این صورت، خطای NotImplementedError برانگیخته خواهد شد.
- property name
- بازگرداندن نام میزبان
- property namespace
- بازگرداندن فضای نام شبکه
- property namespace_exists
- بررسی اینکه آیا فضای نام شبکه وجود دارد یا خیر
- property is_resolvable
- بازگرداندن اینکه آیا نشانی قابل تحلیل و تفکیک (resolve) است یا خیر
- property is_reachable
- بازگرداندن اینکه آیا نشانی در دسترس است یا خیر
- property ip_addresses
- بازگرداندن نشانیهای IP میزبان
- property ipv4_addresses
- بازگرداندن نشانیهای IPv4 میزبان
- property ipv6_addresses
- بازگرداندن نشانیهای IPv6 میزبان
- port(port)
- بازگرداندن جفت نشانی-درگاه
BlockDevice
- class BlockDevice(name)
- اطلاعات
مربوط به
دستگاه
بلوکی.
باید با sudo یا توسط کاربر ریشه (root) استفاده شود.
اگر دستگاه یک دستگاه بلوکی نباشد، خطای RuntimeError رخ میدهد.
- property is_partition
- در صورتی که
دستگاه یک
پارتیشن
باشد True
بازمیگرداند.
>>> host.block_device("/dev/sda1").is_partition True>>> host.block_device("/dev/sda").is_partition False
- property size
- اندازه
دستگاه را
بر حسب بایت
بازمیگرداند.
>>> host.block_device("/dev/sda1").size 512110190592
- property sector_size
- اندازه
سکتور
دستگاه را
بر حسب بایت
بازمیگرداند.
>>> host.block_device("/dev/sda1").sector_size 512
- property block_size
- اندازه
بلوک
دستگاه را
بر حسب بایت
بازمیگرداند.
>>> host.block_device("/dev/sda").block_size 4096
- property start_sector
- سکتور آغازین دستگاه را بر روی دستگاه زیرین بازمیگرداند.
>>> host.block_device("/dev/sda1").start_sector
2048
>>> host.block_device("/dev/md0").start_sector
0
- property is_writable
- اگر دستگاه
قابل نوشتن
باشد (وضعیت
RO نداشته
باشد) True
بازمیگرداند.
>>> host.block_device("/dev/sda").is_writable True>>> host.block_device("/dev/loop1").is_writable False
- property ra
- مقدار Read Ahead
دستگاه را
بر حسب
سکتورهای
۵۱۲ بایتی
بازمیگرداند.
>>> host.block_device("/dev/sda").ra 256
- class Docker(name)
- بررسی و
آزمودن
کانتینرهای
docker در حال
اجرا بر روی
سیستم.
مثال:
>>> nginx = host.docker("app_nginx") >>> nginx.is_running True >>> nginx.id '7e67dc7495ca8f451d346b775890bdc0fb561ecdc97b68fb59ff2f77b509a8fe' >>> nginx.name 'app_nginx'
- classmethod client_version()
- نسخه کلاینت Docker
- classmethod server_version()
- نسخه سرور Docker
- classmethod version(format=None)
- نسخه Docker
https://docs.docker.com/engine/reference/commandline/version
با یک قالب
اختیاری (Go template).
>>> host.docker.version() Client: Docker Engine - Community ... >>> host.docker.version("{{.Client.Context}}")) default
- classmethod get_containers(**filters)
- بازگرداندن
فهرستی از
کانتینرها
بهطور پیشفرض، فهرستی از تمامی کانتینرها شامل کانتینرهای غیر در حال اجرا بازگردانده میشود.
فیلتر کردن میتواند با استفاده از کلیدهای فیلتر تعریفشده در https://docs.docker.com/engine/reference/commandline/ps/#filtering انجام شود.
اعمال چندین فیلتر برای یک کلید مشخص، با تعیین فهرستی از رشتهها بهعنوان مقدار امکانپذیر است.
>>> host.docker.get_containers() [<docker nginx>, <docker redis>, <docker app>] # Get all running containers >>> host.docker.get_containers(status="running") [<docker app>] # Get containers named "nginx" >>> host.docker.get_containers(name="nginx") [<docker nginx>] # Get containers named "nginx" or "redis" >>> host.docker.get_containers(name=["nginx", "redis"]) [<docker nginx>, <docker redis>]
Environment
- class Environment(name)
- دریافت
متغیرهای
محیطی
مثال:
>>> host.environment() { "EDITOR": "vim", "SHELL": "/bin/bash", [...] }
File
- class File(path)
- بررسی ویژگیهای گوناگون فایل
- property exists
- بررسی وجود
داشتن فایل
>>> host.file("/etc/passwd").exists True >>> host.file("/nonexistent").exists False
- property is_file
- بررسی اینکه آیا مسیر دادهشده یک فایل معمولی (regular file) است یا خیر
- property is_directory
- بررسی اینکه آیا مسیر دادهشده وجود دارد و یک دایرکتوری است یا خیر
- property is_executable
- بررسی اینکه آیا مسیر دادهشده وجود دارد و مجوز اجرا به آن اعطا شده است یا خیر
- property is_pipe
- بررسی اینکه آیا مسیر دادهشده وجود دارد و یک لوله (pipe) است یا خیر
- property is_socket
- بررسی اینکه آیا مسیر دادهشده وجود دارد و یک سوکت (socket) است یا خیر
- property is_symlink
- بررسی اینکه آیا مسیر دادهشده وجود دارد و یک پیوند نمادین (symlink) است یا خیر
- property linked_to
- حل پیوند
نمادین و
مشخص کردن
مقصد آن
>>> host.file("/var/lock").linked_to '/run/lock'
- property user
- بازگرداندن
مالک فایل
بهصورت
رشته
>>> host.file("/etc/passwd").user 'root'
- property uid
- بازگرداندن
شناسه
کاربری (UID)
فایل
بهصورت یک
عدد صحیح
>>> host.file("/etc/passwd").uid 0
- property group
- بازگرداندن نام گروه فایل بهصورت رشته
- property gid
- بازگرداندن شناسه گروه (GID) فایل بهصورت یک عدد صحیح
- property mode
- بازگرداندن
حالت
دسترسی
فایل (file mode)
بهصورت
عدد صحیح در
مبنای هشت (octal)
>>> host.file("/etc/shadow").mode 416 # Oo640 octal >>> host.file("/etc/shadow").mode == 0o640 True >>> oct(host.file("/etc/shadow").mode) == '0o640' Trueهمچنین میتوانید از مقادیر ثابت حالت دسترسی فایل در کتابخانهٔ stat https://docs.python.org/3/library/stat.html برای بررسی حالت فایل استفاده کنید.
>>> import stat >>> host.file("/etc/shadow").mode == stat.S_IRUSR | stat.S_IWUSR | stat.S_IRGRP True
- contains(pattern)
- بررسی
محتوای
فایل برای
یافتن الگو
(pattern)
این متد از grep استفاده کرده و در نتیجه از قواعد نحو عبارات منظم (regex) در grep پیروی میکند.
- property sha256sum
- محاسبه چکیده پیام SHA256 محتوای فایل
- property content
- بازگرداندن
محتوای
فایل به
صورت بایت
>>> host.file("/tmp/foo").content b'caf\xc3\xa9'
- property content_string
- بازگرداندن
محتوای
فایل به
صورت رشته
>>> host.file("/tmp/foo").content_string 'café'
- property mtime
- بازگرداندن
زمان آخرین
تغییر به
عنوان یک
شیء datetime.datetime
>>> host.file("/etc/passwd").mtime datetime.datetime(2015, 3, 15, 20, 25, 40)
- property size
- بازگرداندن اندازه فایل بر حسب بایت
- listdir()
- بازگرداندن
فهرست
موارد
موجود در
دایرکتوری
>>> host.file("/tmp").listdir() ['foo_file', 'bar_dir']
Group
- class Group(name)
- بررسی گروه یونیکس
- property exists
- بررسی وجود
داشتن گروه
>>> host.group("wheel").exists True >>> host.group("nosuchgroup").exists False
- property get_all_groups
- بازگرداندن
فهرستی از
نامهای
گروههای
محلی و
دوردست
>>> host.group("anyname").get_all_groups ["root", "wheel", "man", "tty", <...>]
- property get_local_groups
- بازگرداندن
فهرستی از
نامهای
گروههای
محلی
>>> host.group("anyname").get_local_groups ["root", "wheel", "man", "tty", <...>]
- property members
- بازگرداندن تمام کاربرانی که عضو این گروه هستند.
Interface
- class Interface(name, family=None)
- بررسی
رابطهای
شبکه
>>> host.interface("eth0").exists Trueبه صورت اختیاری، میتوان خانواده پروتکل مورد استفاده را مشخص کرد.
>>> host.interface("eth0", "inet6").addresses ['fe80::e291:f5ff:fe98:6b8c']
- property addresses
- بازگرداندن
آدرسهای ipv4
و ipv6 روی رابط
>>> host.interface("eth0").addresses ['192.168.31.254', '192.168.31.252', 'fe80::e291:f5ff:fe98:6b8c']
- property link
- بازگرداندن
ویژگیهای
پیوند (link)
مرتبط با
رابط.
>>> host.interface("lo").link {'address': '00:00:00:00:00:00', 'broadcast': '00:00:00:00:00:00', 'flags': ['LOOPBACK', 'UP', 'LOWER_UP'], 'group': 'default', 'ifindex': 1, 'ifname': 'lo', 'link_type': 'loopback', 'linkmode': 'DEFAULT', 'mtu': 65536, 'operstate': 'UNKNOWN', 'qdisc': 'noqueue', 'txqlen': 1000}
- routes(scope=None)
- بازگرداندن
مسیرهای
مرتبط با
رابط، با
امکان
پالایش
اختیاری بر
اساس دامنه
scope ("host"، "link" یا
"global").
>>> host.interface("eth0").routes() [{'dst': 'default', 'flags': [], 'gateway': '192.0.2.1', 'metric': 3003, 'prefsrc': '192.0.2.5', 'protocol': 'dhcp'}, {'dst': '192.0.2.0/24', 'flags': [], 'metric': 3003, 'prefsrc': '192.0.2.5', 'protocol': 'dhcp', 'scope': 'link'}]
- classmethod names()
- بازگرداندن
نام تمامی
رابطها.
>>> host.interface.names() ['lo', 'tunl0', 'ip6tnl0', 'eth0']
- classmethod default(family=None)
- بازگرداندن
رابط مورد
استفاده
برای مسیر
پیشفرض (default route).
>>> host.interface.default() <interface eth0>
به صورت اختیاری، میتوان خانواده پروتکل مورد استفاده را مشخص کرد.
>>> host.interface.default("inet6") None
- class Iptables
- بررسی وجود قانون در iptables
- rules(table='filter', chain=None, version=4)
- فهرستی از قوانین iptables را برمیگرداند
- به صورت اختیاری آرگومانهای زیر را میپذیرد:
- table: پیشفرض filter است
- chain: پیشفرض تمامی زنجیرهها است
- version: پیشفرض 4 (iptables)، به صورت اختیاری 6 (ip6tables)
>>> host.iptables.rules()
[
'-P INPUT ACCEPT',
'-P FORWARD ACCEPT',
'-P OUTPUT ACCEPT',
'-A INPUT -i lo -j ACCEPT',
'-A INPUT -j REJECT'
'-A FORWARD -j REJECT'
]
>>> host.iptables.rules("nat", "INPUT")
['-P PREROUTING ACCEPT']
MountPoint
- class MountPoint(path)
- بررسی نقاط اتصال (Mount Points)
- property exists
- در صورت
وجود نقطه
اتصال
مقدار True
برمیگرداند
>>> host.mount_point("/").exists True>>> host.mount_point("/not/a/mountpoint").exists False
- property filesystem
- نوع
سیستمفایل
مرتبط را
برمیگرداند
>>> host.mount_point("/").filesystem 'ext4'
- property device
- دستگاه
مرتبط را
برمیگرداند
>>> host.mount_point("/").device '/dev/sda1'
- property options
- فهرستی از
گزینههایی
که نقطه
اتصال با
آنها
ایجاد شده
است را
برمیگرداند
>>> host.mount_point("/").options ['rw', 'relatime', 'data=ordered']
- classmethod get_mountpoints()
- فهرستی از
نمونههای
MountPoint را
برمیگرداند
>>> host.mount_point.get_mountpoints() [<MountPoint(path=/proc, device=proc, filesystem=proc, options=rw,nosuid,nodev,noexec,relatime)>, <MountPoint(path=/, device=/dev/sda1, filesystem=ext4, options=rw,relatime,errors=remount-ro,data=ordered)>]
Package
- class Package(name)
- بررسی وضعیت و نسخه بستهها
- property is_installed
- بررسی
اینکه آیا
بسته نصب
شده است یا
خیر
>>> host.package("nginx").is_installed Trueسیستمهای بسته پشتیبانیشده:
- apk (Alpine)
- apt (Debian, Ubuntu, ...)
- brew (macOS)
- pacman (Arch, Manjaro )
- pkg (FreeBSD)
- pkg_info (NetBSD)
- pkg_info (OpenBSD)
- rpm (RHEL, RockyLinux, Fedora, ...)
- property release
- اطلاعات
مشخصه
انتشار (release)
را از نسخه
بسته
برمیگرداند
>>> host.package("nginx").release '1.el6'
- property version
- نسخه بسته
را آنطور
که توسط
سیستم بسته
بازگردانده
شده
برمیگرداند
>>> host.package("nginx").version '1.2.1-2.2+wheezy3'
- class Pip(name, pip_path='pip')
- آزمودن مدیر بسته و بستههای pip
- property is_installed
- بررسی نصب
بودن بسته
>>> host.package("pip").is_installed True
- property version
- بازگرداندن
نسخهی
بسته
همانطور
که توسط pip
گزارش شده
است
>>> host.package("pip").version '18.1'
- classmethod check(pip_path='pip')
- بررسی
سازگار
بودن
وابستگیهای
بستههای
نصبشده.
>>> cmd = host.pip.check() >>> cmd.rc 0 >>> cmd.stdout No broken requirements found.
تنها در صورتی قابل استفاده است که دستور pip check https://pip.pypa.io/en/stable/reference/pip_check در دسترس باشد، برای نسخههای pip >= 9.0.0 https://pip.pypa.io/en/stable/news/#id526.
- classmethod get_packages(pip_path='pip')
- دریافت
تمام
بستههای
نصبشده و
نسخههای
آنها به
شکلی که
توسط pip list
بازگردانده
میشوند:
>>> host.pip.get_packages(pip_path='~/venv/website/bin/pip') {'Django': {'version': '1.10.2'}, 'mywebsite': {'version': '1.0a3', 'path': '/srv/website'}, 'psycopg2': {'version': '2.6.2'}}
- classmethod get_outdated_packages(pip_path='pip')
- دریافت
تمام
بستههای
قدیمی به
همراه
نسخهی
فعلی و
آخرین نسخه
>>> host.pip.get_outdated_packages( ... pip_path='~/venv/website/bin/pip') {'Django': {'current': '1.10.2', 'latest': '1.10.3'}}
Podman
- class Podman(name)
- آزمودن
کانتینرهای
podman در حال
اجرا روی
سیستم.
مثال:
>>> nginx = host.podman("app_nginx") >>> nginx.is_running True >>> nginx.id '7e67dc7495ca8f451d346b775890bdc0fb561ecdc97b68fb59ff2f77b509a8fe' >>> nginx.name 'app_nginx'
- classmethod get_containers(**filters)
- بازگرداندن
فهرستی از
کانتینرها
بهطور پیشفرض فهرستی از همهی کانتینرها، شامل کانتینرهای غیر در حال اجرا را بازمیگرداند.
فیلتر کردن میتواند با استفاده از کلیدهای فیلتر تعریفشده در podman-ps(1) انجام شود.
اعمال فیلترهای چندگانه برای یک کلید مشخص، با ارائهی فهرستی از رشتهها به عنوان مقدار انجام میشود.
>>> host.podman.get_containers() [<podman nginx>, <podman redis>, <podman app>] # Get all running containers >>> host.podman.get_containers(status="running") [<podman app>] # Get containers named "nginx" >>> host.podman.get_containers(name="nginx") [<podman nginx>] # Get containers named "nginx" or "redis" >>> host.podman.get_containers(name=["nginx", "redis"]) [<podman nginx>, <podman redis>]
Process
- class Process
- آزمودن
مشخصههای
فرآیندها
فرآیندها با استفاده از filter() یا get() انتخاب میشوند؛ نام مشخصهها در صفحهی راهنمای ps(1) شرح داده شده است: http://man7.org/linux/man-pages/man1/ps.1.html#STANDARD_FORMATSPECIFIERS.
>>> master = host.process.get(user="root", comm="nginx") # Here is the master nginx process (running as root) >>> master.args 'nginx: master process /usr/sbin/nginx -g daemon on; master_process on;' # Here are the worker processes (Parent PID = master PID) >>> workers = host.process.filter(ppid=master.pid) >>> len(workers) 4 # Nginx don't eat memory >>> sum([w.pmem for w in workers]) 0.8 # But php does ! >>> sum([p.pmem for p in host.process.filter(comm="php5-fpm")]) 19.2
- filter(**filters)
- دریافت
فهرستی از
فرآیندهای
منطبق
>>> host.process.filter(user="root", comm="zsh") [<process zsh (pid=2715)>, <process zsh (pid=10502)>, ...]
- get(**filters)
- دریافت یک
فرآیند
منطبق
اگر فرآیندی یافت نشود یا چندین فرآیند با فیلترها مطابقت داشته باشند، RuntimeError ایجاد میشود.
- class PuppetResource(type, name=None)
- دریافت
منابع puppet
برای دریافت فهرستی از انواع در دسترس، دستور puppet resource --types را اجرا کنید.
>>> host.puppet_resource("user", "www-data") { 'www-data': { 'ensure': 'present', 'comment': 'www-data', 'gid': '33', 'home': '/var/www', 'shell': '/usr/sbin/nologin', 'uid': '33', }, }
Facter
- class Facter(*facts)
- دریافت
واقعیتها
(facts) با facter https://puppetlabs.com/facter
>>> host.facter() { "operatingsystem": "Debian", "kernel": "linux", [...] } >>> host.facter("kernelversion", "is_virtual") { "kernelversion": "3.16.0", "is_virtual": "false" }
Salt
- class Salt(function, args=None, local=False, config=None)
- اجرای
توابع
ماژول salt
>>> host.salt("pkg.version", "nginx") '1.6.2-5' >>> host.salt("pkg.version", ["nginx", "php5-fpm"]) {'nginx': '1.6.2-5', 'php5-fpm': '5.6.7+dfsg-1'} >>> host.salt("grains.item", ["osarch", "mem_total", "num_cpus"]) {'osarch': 'amd64', 'num_cpus': 4, 'mem_total': 15520}برای دریافت فهرست کاملی از توابع، دستور salt-call sys.doc را اجرا کنید
Service
- class Service(name)
- آزمودن
سرویسها
پیادهسازیها:
- لینوکس: شناسایی Systemd، Upstart یا OpenRC، و بازگشت به SysV در صورت عدم شناسایی
- فریبیاسدی: service(1)
- اوپنبیاسدی: /etc/rc.d/$name check برای is_running rcctl ls on برای is_enabled (تنها در OpenBSD >= 5.8)
- نتبیاسدی: /etc/rc.d/$name onestatus برای is_running (is_enabled هنوز پیادهسازی نشده است)
- property exists
- بررسی اینکه آیا سرویس وجود دارد یا خیر
- property is_running
- بررسی اینکه آیا سرویس در حال اجرا است یا خیر
- property is_enabled
- بررسی اینکه آیا سرویس فعال (enabled) است یا خیر
- property is_valid
- بررسی
اینکه آیا
سرویس
معتبر است
یا خیر
این متد تنها در پیادهسازی systemd موجود است، و در سایر پیادهسازیها خطای NotImplementedError ایجاد خواهد کرد
- property is_masked
- بررسی
اینکه آیا
سرویس ماسک
(masked) شده است
یا خیر
این متد تنها در پیادهسازی systemd موجود است، و در سایر پیادهسازیها خطای NotImplementedError ایجاد خواهد کرد
- property systemd_properties
- ویژگیهای
سرویس (unit).
ویژگیهای سرویس را به عنوان یک dict برمیگرداند، ویژگیهای خالی بازگردانده نمیشوند.
>>> ntp = host.service("ntp") >>> ntp.systemd_properties["FragmentPath"] '/lib/systemd/system/ntp.service'این متد تنها در پیادهسازی systemd موجود است، و در سایر پیادهسازیها خطای NotImplementedError ایجاد خواهد کرد
نکته: بر اساس systemctl show https://man7.org/linux/man-pages/man1/systemctl.1.html
- class Socket(socketspec)
- بررسی
سوکتهای
در حال شنود
tcp/udp و unix
socketspec باید به صورت <protocol>://<host>:<port> مشخص شود
این ماژول نیازمند وجود دستور netstat روی میزبان هدف است.
مثال:
- سوکتهای Unix: unix:///var/run/docker.sock
- تمام سوکتهای tcp پروتکلهای ipv4 و ipv6 روی پورت 22: tcp://22
- تمام سوکتهای ipv4 روی پورت 22: tcp://0.0.0.0:22
- تمام سوکتهای ipv6 روی پورت 22: tcp://:::22
- سوکت udp روی 127.0.0.1 پورت 69: udp://127.0.0.1:69
- property is_listening
- بررسی
اینکه آیا
سوکت در حال
شنود است یا
خیر
>>> host.socket("unix:///var/run/docker.sock").is_listening False >>> # This HTTP server listen on all ipv4 addresses but not on ipv6 >>> host.socket("tcp://0.0.0.0:80").is_listening True >>> host.socket("tcp://:::80").is_listening False >>> host.socket("tcp://80").is_listening Falseنکته:
- property clients: list[tuple[str, int] | None]
- فهرستی از
کلاینتهای
متصل به یک
سوکت در حال
شنود را
برمیگرداند
برای سوکتهای tcp و udp فهرستی از جفتهای (نشانی، پورت) برگردانده میشود. برای سوکتهای unix فهرستی از None برگردانده میشود (بنابراین میتوانید از ()len برای شمارش کلاینتها استفاده کنید).
>>> host.socket("tcp://22").clients [('2001:db8:0:1', 44298), ('192.168.31.254', 34866)] >>> host.socket("unix:///var/run/docker.sock") [None, None, None]
- classmethod get_listening_sockets()
- فهرستی از
تمام
سوکتهای
در حال شنود
را
برمیگرداند
>>> host.socket.get_listening_sockets() ['tcp://0.0.0.0:22', 'tcp://:::22', 'unix:///run/systemd/private', ...]
Sudo
- class Sudo(user=None)
- ماژول Sudo
اجازه
میدهد بخش
مشخصی از کد
تحت کاربری
دیگر اجرا
شود.
این ماژول به عنوان یک مدیر زمینه (context manager) استفاده میشود و میتواند به صورت تو در تو باشد.
>>> Command.check_output("whoami") 'phil' >>> with host.sudo(): ... host.check_output("whoami") ... with host.sudo("www-data"): ... host.check_output("whoami") ... 'root' 'www-data'
Supervisor
- class Supervisor(name, supervisorctl_path='supervisorctl', supervisorctl_conf=None, _attrs_cache=None)
- بررسی
سرویسهای
مدیریتشده
با supervisor
>>> gunicorn = host.supervisor("gunicorn") >>> gunicorn.status 'RUNNING' >>> gunicorn.is_running True >>> gunicorn.pid 4242مسیری که supervisorctl و فایل پیکربندی آن در آن قرار دارند میتواند مشخص شود.
>>> gunicorn = host.supervisor("gunicorn", "/usr/bin/supervisorctl", "/etc/supervisor/supervisord.conf") >>> gunicorn.status 'RUNNING'
- property is_running
- در صورتی که سرویس مدیریتشده در وضعیت RUNNING باشد، True را برمیگرداند
- property status
- وضعیت
سرویس
مدیریتشده
را
برمیگرداند
وضعیت میتواند STOPPED، STARTING، RUNNING، BACKOFF، STOPPING، EXITED، FATAL یا UNKNOWN باشد.
ببینید: http://supervisord.org/subprocess.html#process-states
- property pid
- شناسه فرایند (pid به صورت int) سرویس مدیریتشده را برمیگرداند
- classmethod get_services(supervisorctl_path='supervisorctl', supervisorctl_conf=None)
- فهرستی از
سرویسهای
در حال اجرا
تحت supervisor را
برمیگرداند
>>> host.supervisor.get_services() [<Supervisor(name="gunicorn", status="RUNNING", pid=4232)> <Supervisor(name="celery", status="FATAL", pid=None)>]
مسیری که supervisorctl و فایل پیکربندی آن در آن قرار دارند میتواند مشخص شود.
>>> host.supervisor.get_services("/usr/bin/supervisorctl", "/etc/supervisor/supervisord.conf") [<Supervisor(name="gunicorn", status="RUNNING", pid=4232)> <Supervisor(name="celery", status="FATAL", pid=None)>]
- class Sysctl(name)
- بررسی
پارامترهای
هسته
>>> host.sysctl("kernel.osrelease") "3.16.0-4-amd64" >>> host.sysctl("vm.dirty_ratio") 20
SystemInfo
- class SystemInfo
- بازگرداندن اطلاعات سیستم
- property type
- نوع
سیستمعامل
>>> host.system_info.type 'linux'
- property distribution
- نام توزیع
>>> host.system_info.distribution 'debian'
- property release
- شماره
انتشار
توزیع
>>> host.system_info.release '10.2'
- property codename
- اسم رمز
انتشار
>>> host.system_info.codename 'bullseye'
- property arch
- معماری
میزبان
>>> host.system_info.arch 'x86_64'
User
- class User(name=None)
- بررسی
کاربران
یونیکس
اگر name مشخص نشود، کاربر جاری بررسی میشود
- property name
- بازگرداندن نام کاربر
- property exists
- بررسی وجود
کاربر
>>> host.user("root").exists True >>> host.user("nosuchuser").exists False
- property uid
- بازگرداندن شناسه کاربر
- property gid
- بازگرداندن شناسه گروه مؤثر
- property group
- بازگرداندن نام گروه مؤثر
- property gids
- بازگرداندن فهرست شناسههای گروههای کاربر
- property groups
- بازگرداندن فهرست نامهای گروههای کاربر
- property home
- بازگرداندن دایرکتوری خانگی کاربر
- property shell
- بازگرداندن پوسته ورود کاربر
- property password
- بازگرداندن گذرواژه رمزگذاریشده کاربر
- property password_max_days
- بازگرداندن حداکثر تعداد روزهای بین تغییر گذرواژه
- property password_min_days
- بازگرداندن حداقل تعداد روزهای بین تغییر گذرواژه
- property gecos
- بازگرداندن فیلد توضیحات/gecos کاربر
- property expiration_date
- بازگرداندن
تاریخ
انقضای
حساب
کاربری
>>> host.user("phil").expiration_date datetime.datetime(2020, 1, 1, 0, 0) >>> host.user("root").expiration_date None
- property get_all_users
- بازگرداندن
فهرستی از
نامهای
کاربران
محلی و
راهدور
>>> host.user().get_all_users ["root", "bin", "daemon", "lp", <...>]
- property get_local_users
- بازگرداندن
فهرستی از
نامهای
کاربران
محلی
>>> host.user().get_local_users ["root", "bin", "daemon", "lp", <...>]
- class CommandResult(backend: BaseBackend, exit_status: int, command: bytes, _stdout: str | bytes, _stderr: str | bytes)
- شیئی که
تمام
جزئیات
بازگرداندهشده
از اجرای
دستور را
کپسولهسازی
میکند.
مثال:
>>> cmd = host.run("ls -l /etc/passwd") >>> cmd.rc 0 >>> cmd.stdout '-rw-r--r-- 1 root root 1790 Feb 11 00:28 /etc/passwd\n' >>> cmd.stderr '' >>> cmd.succeeded True >>> cmd.failed False
- property succeeded: bool
- مشخص
میکند که
آیا دستور
با موفقیت
اجرا شده
است یا خیر.
>>> host.run("true").succeeded True
- property failed: bool
- مشخص
میکند که
آیا اجرای
دستور
ناموفق
بوده است یا
خیر.
>>> host.run("false").failed True
- property rc: int
- کد بازگشتی
(returncode) دستور را
دریافت
میکند.
>>> host.run("true").rc 0
- property stdout: str
- جریان
خروجی
استاندارد
(stdout) دستور
اجراشده را
دریافت
میکند.
>>> host.run("mkdir -v new_directory").stdout mkdir: created directory 'new_directory'
- property stderr: str
- جریان خطای
استاندارد
(stderr) دستور
اجراشده را
دریافت
میکند.
>>> host.run("mkdir new_directory").stderr mkdir: cannot create directory 'new_directory': File exists
- property stdout_bytes: bytes
- جریان
خروجی
استاندارد
(stdout) دستور
اجراشده را
به صورت
بایت
دریافت
میکند.
>>> host.run("mkdir -v new_directory").stdout_bytes b"mkdir: created directory 'new_directory'"
- property stderr_bytes: bytes
- جریان خطای
استاندارد
(stderr) دستور
اجراشده را
به صورت
بایت
دریافت
میکند.
>>> host.run("mkdir new_directory").stderr_bytes b"mkdir: cannot create directory 'new_directory': File exists"
API
Connection API
شما میتوانید از testinfra در خارج از pytest نیز استفاده کنید. میتوانید بهصورت پویا یک نمونه از host دریافت کرده و توابع را فراخوانی کنید یا به اعضای ماژولهای مربوطه دسترسی داشته باشید:
>>> import testinfra
>>> host = testinfra.get_host("paramiko://root@server:2222", sudo=True)
>>> host.file("/etc/shadow").mode == 0o640
True
بهعنوان مثال، میتوانید آزمونی برای مقایسه دو فایل در دو سرور مختلف ایجاد کنید:
import testinfra
def test_same_passwd():
a = testinfra.get_host("ssh://a")
b = testinfra.get_host("ssh://b")
assert a.file("/etc/passwd").content == b.file("/etc/passwd").content
پارامتریکسازی آزمونهای خود
ابزار Pytest از پارامتریکسازی آزمونها پشتیبانی میکند https://pytest.org/latest/parametrize.html:
# BAD: If the test fails on nginx, python is not tested
def test_packages(host):
for name, version in (
("nginx", "1.6"),
("python", "2.7"),
):
pkg = host.package(name)
assert pkg.is_installed
assert pkg.version.startswith(version)
# GOOD: Each package is tested
# $ pytest -v test.py
# [...]
# test.py::test_package[local-nginx-1.6] PASSED
# test.py::test_package[local-python-2.7] PASSED
# [...]
import pytest
@pytest.mark.parametrize("name,version", [
("nginx", "1.6"),
("python", "2.7"),
])
def test_packages(host, name, version):
pkg = host.package(name)
assert pkg.is_installed
assert pkg.version.startswith(version)
استفاده از unittest
ابزار Testinfra میتواند به جای pytest با فریمورک استاندارد آزمون واحد پایتون یعنی unittest https://docs.python.org/3/library/unittest.html استفاده شود:
import unittest
import testinfra
class Test(unittest.TestCase):
def setUp(self):
self.host = testinfra.get_host("paramiko://root@host")
def test_nginx_config(self):
self.assertEqual(self.host.run("nginx -t").rc, 0)
def test_nginx_service(self):
service = self.host.service("nginx")
self.assertTrue(service.is_running)
self.assertTrue(service.is_enabled)
if __name__ == "__main__":
unittest.main()
$ python test.py .. ---------------------------------------------------------------- Ran 2 tests in 0.705s OK
یکپارچهسازی با Vagrant
ابزار Vagrant https://www.vagrantup.com ابزاری برای راهاندازی و تدارک محیطهای توسعه (ماشینهای مجازی) است.
هنگامی که ماشین Vagrant شما بالا آمده و در حال اجرا است، میتوانید به آسانی مجموعه آزمونهای testinfra خود را روی آن اجرا کنید:
vagrant ssh-config > .vagrant/ssh-config pytest --hosts=default --ssh-config=.vagrant/ssh-config tests.py
یکپارچهسازی با Jenkins
سرور Jenkins https://jenkins-ci.org یک سرور یکپارچهسازی مداوم متنباز و شناختهشده است.
اگر اسلیو (slave) جنکینز شما قادر به اجرای Vagrant باشد، اسکریپتهای ساخت شما میتوانند به این صورت باشند:
pip install pytest-testinfra paramiko vagrant up vagrant ssh-config > .vagrant/ssh-config pytest --hosts=default --ssh-config=.vagrant/ssh-config --junit-xml junit.xml tests.py
سپس Jenkins را پیکربندی کنید تا نتایج آزمونها را از فایل junit.xml دریافت کند.
یکپارچهسازی با Nagios
آزمونهای شما معمولاً اعتبارسنجی میکنند که سرویسهای مستقرشده بهدرستی اجرا میشوند. این نوع آزمونها به بررسیهای پایش (مانیتورینگ) نزدیک هستند، بنابراین بیایید آنها را به Nagios https://www.nagios.org ارسال کنیم!
گزینه --nagios در Testinfra رفتاری سازگار با یک افزونه nagios را فعال میکند:
$ pytest -qq --nagios --tb line test_ok.py; echo $? TESTINFRA OK - 2 passed, 0 failed, 0 skipped in 2.30 seconds .. 0 $ pytest -qq --nagios --tb line test_fail.py; echo $? TESTINFRA CRITICAL - 1 passed, 1 failed, 0 skipped in 2.24 seconds .F /usr/lib/python3/dist-packages/example/example.py:95: error: [Errno 111] error msg 2
میتوانید این آزمونها را از سرور اصلی nagios یا با استفاده از NRPE https://en.wikipedia.org/wiki/Nagios#Nagios_Remote_Plugin_Executor روی میزبان مقصد اجرا کنید..SS یکپارچهسازی با KitchenCI
ابزار KitchenCI (معروف به Test Kitchen) میتواند با استفاده از وارسیکنندهٔ (verifier) shell خود از testinfra استفاده کند. موارد زیر را به فایل .kitchen.yml خود اضافه کنید؛ این کار نیازمند نصب بستهٔ اضافی paramiko است (بر روی ماشین میزبان شما، نه درون ماشین مجازی مدیریتشده توسط kitchen).
verifier:
name: shell
command: pytest --hosts="paramiko://${KITCHEN_USERNAME}@${KITCHEN_HOSTNAME}:${KITCHEN_PORT}?ssh_identity_file=${KITCHEN_SSH_KEY}" --junit-xml "junit-${KITCHEN_INSTANCE}.xml" "test/integration/${KITCHEN_SUITE}"
آزمودن تصویرهای Docker
داکر (Docker) روشی سودمند برای آزمایش کدهای زیرساخت شماست. این دستورالعمل نحوهٔ ساخت و اجرای کانتینرهای Docker با Testinfra را از طریق بازتعریف (overloading) فیکسچر host نشان میدهد.
import pytest
import subprocess
import testinfra
# scope='session' uses the same container for all the tests;
# scope='function' uses a new container per test function.
@pytest.fixture(scope='session')
def host(request):
# build local ./Dockerfile
subprocess.check_call(['docker', 'build', '-t', 'myimage', '.'])
# run a container
docker_id = subprocess.check_output(
['docker', 'run', '-d', 'myimage']).decode().strip()
# return a testinfra connection to the container
yield testinfra.get_host("docker://" + docker_id)
# at the end of the test suite, destroy the container
subprocess.check_call(['docker', 'rm', '-f', docker_id])
def test_myimage(host):
# 'host' now binds to the container
assert host.check_output('myapp -v') == 'Myapp 1.0'
پشتیبانی
اگر سؤالی دارید یا برای کار با testinfra نیاز به کمک دارید، لطفاً یکی از گزینههای زیر را در نظر بگیرید:
سامانه پیگیری مشکلات
گزارشها و مشکلات موجود را در سامانه پیگیری پروژه بررسی کنید: https://github.com/pytest-dev/pytest-testinfra/issues
آیآرسی (IRC)
همچنین میتوانید سؤالات خود را در IRC و در کانال #pytest https://web.libera.chat/?channel=#pytest روی شبکهٔ [libera.chat](https://libera.chat) مطرح کنید.
مستندات pytest
ابزار testinfra بهعنوان یک افزونه برای pytest پیادهسازی شده است؛ بنابراین برای بهرهمندی حداکثری از آن، لطفاً مستندات pytest را مطالعه کنید: https://docs.pytest.org/en/latest
مشارکتهای جامعه
- •
- ابزار Molecule https://molecule.readthedocs.io یک چارچوب آزمایش خودکار برای نقشهای Ansible است که بهصورت بومی از Testinfra پشتیبانی میکند.
نویسندگان (AUTHORS)
Philippe Pepiot
کپیرایت و تکثیر (COPYRIGHT)
2025, Philippe Pepiot
| December 20, 2025 | 10.2.2 |