TESTINFRA(1) testinfra TESTINFRA(1)

testinfra - مستندات testinfra

آخرین مستندات: https://testinfra.readthedocs.io/en/latest

با 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 نوشته شده است.

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 منتشر شده است. اگر کاربرد دیگری مد نظرتان است، با ما تماس بگیرید.

نصب 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 ====================

•
[FIX] گنجاندن فایل‌های لازم برای اجرای آزمون‌ها در sdist

•
[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 از فرایند آزمون

  • [اصلاح] افزودن بررسی برای وجود دایرکتوری ssh control path
  • [اصلاح] مدیریت is_installed برای بسته‌های rpm در صورت خرابی پایگاه‌داده rpm
  • [اصلاح] رفع مشکل service.exists

  • [جدید] افزودن ویژگی Interface.link
  • [اصلاح] پیروی ویژگی‌های فایل از پیوندهای نمادین (symlinks)
  • [اصلاح] الزام به pytest>=6 و استفاده از حاشیه‌نویسی‌های آینده (future annotations) برای سازگاری با pytest<7

  • [اصلاح] Ansible: رفع مشکل نبود نام‌های گروه در get_variables()
  • [اصلاح] testinfra/modules/blockdevice: عدم ایجاد خطا و شکست در هنگام خروجی روی stderr
  • [مستندات] گسترش و نمایش مستندات CommandResult
  • [اصلاح] گسترش فهرست پسوندهای معتبر برای واحدهای systemd
  • [مستندات] افزودن بخش مستندات از قلم‌افتاده‌ی Environment
  • [متفرقه] تعیین انواع داده (types) برای plugin.py
  • [اصلاح] رفع مشکل عدم وجود توزیع RHEL در ماژول package
  • [جدید] افزودن پشتیبانی از brew در ماژول package
  • [جدید] افزودن Service.exists
  • [متفرقه] تبدیل CommandResult به یک dataclass

  • [تغییر ناسازگار] pytest-testinfra اکنون نیازمند python >= 3.9 است
  • [تغییر ناسازگار] حذف ماژول منسوخ‌شده‌ی PipPackage
  • [جدید] افزودن پشتیبانی از گزینه اشتراک‌گذاری اتصال ControlPath در SSH (#713)
  • [اصلاح] تلاش مجدد اتصال SSH هنگام بروز ConnectionResetError (#708)
  • [اصلاح] فهرست کردن صریح openSUSE Leap و Tumbleweed به‌عنوان توزیع‌های مبتنی بر rpm
  • [اصلاح] اجباری کردن نام گروه در ماژول group

  • [جدید] افزودن پشتیبانی از Windows برای ماژول‌های File و Service
  • [جدید] افزودن ویژگی File.is_executable

  • [جدید] افزودن مشخصه‌ی Group.members
  • [جدید] افزودن مشخصه‌ی File.inode
  • [جدید] افزودن متد Interface.routes()
  • [جدید] افزودن مشخصه‌ی Docker.is_restarting
  • [اصلاح] رفع خطای احتمالی در Interface.default()
  • [اصلاح] اصلاح تشخیص busybox در ماژول Process
  • [اصلاح] رفع خطای احتمالی KeyError در ماژول SysInfo
  • [تغییر ناسازگار] توقف پشتیبانی از python 3.7
•
[اصلاح] رفع مشکل سازگاری command -v با پوسته dash

  • [جدید] بهبود پشتیبانی از پیکربندی ssh در بک‌اند Paramiko
  • [جدید] افزودن بک‌اند chroot
  • [جدید] افزودن پشتیبانی از Manjaro-Linux
  • [جدید] افزودن پشتیبانی از Cloudlinux
  • [تغییر بنیادین] توقف پشتیبانی از python 3.6 به دلیل پایان دوره پشتیبانی (EOL)

•
[جدید] افزودن پشتیبانی از AlmaLinux و RockyLinux

  • [جدید] پشتیبانی از نوع ansible_connection با مقدار community.docker.docker
  • [جدید] افزودن گزینه ssh_extra_args

  • [جدید] امکان بررسی انقضای گذرواژه کاربر
  • [جدید] مدیریت متغیرهای محیطی ANSIBLE_SSH_COMMON_ARGS و ANSIBLE_SSH_EXTRA_ARGS برای اتصال‌های ansible
  • [اصلاح] رفع مشکل کدگذاری در اتصال‌های salt
  • [اصلاح] رفع خطای AttributeError در صورت در دسترس نبودن "command" و استفاده جایگزین از "which"

  • استفاده جایگزین از which در صورت شکست "command -v"
  • استفاده پیش‌فرض از realpath برای حل پیوندهای نمادین به‌جای "readlink -f"
  • ansible: پشتیبانی از متغیرهای محیطی
  • اجبار ماژول package به ارجاع به RpmPackage در Fedora
  • رفع مشکلی که در آن نسخه‌های جدید supervisor ممکن بود با وضعیت != 0 خارج شوند
  • رمزگشایی خروجی ansible در صورت غیر ascii بودن
  • استفاده از python3 یا python جهت دریافت کدگذاری راه دور

  • پیاده‌سازی نام‌های Interface و پیش‌فرض (#615)
  • پیاده‌سازی Service.systemd_properties (#612)

  • رفع مشکل #451 برای استفاده با pytest -p no:terminal
  • افزودن client_version() و server_version() و version() به ماژول docker.

  • رفع مشکل #590: عدم تشخیص صحیح Windows توسط Systeminfo (#592)
  • نخستین پیاده‌سازی فضاهای نام شبکه در ماژول addr (#596)
  • پشتیبانی از pip check در ماژول PipPackage (#605)
  • بازسازی ساختار pip: پیاده‌سازی installed و version (#606)
  • امکان تعیین مسیرهای supervisorctl و supervisord.conf (#536)

  • رفع مشکل انتخاب نادرست ماژول 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.

•
تغییر بنیادین: پروژه testinfra به سازمان https://github.com/pytest-dev منتقل شده است. نام پروژه در PyPi به pytest-testinfra تغییر یافته است. یک بسته موقت testinfra فرایند انتقال را تسهیل می‌کند، اما باید نام آن را در فایل‌های requirements خود به pytest-testinfra تغییر دهید.

•
رفع اشکال ویژگی تازه ارائه‌شده is_masked در سرویس systemd https://github.com/philpep/testinfra/pull/569

•
افزودن ویژگی is_masked به سرویس systemd

•
iptables: استفاده از گزینه -w جهت انتظار برای دریافت قفل iptables هنگام اجرای موازی با pytest-xdist.
•
رفع مشکل ساخت مستندات

  • امکان تعیین کانتکست kubeconfig در بک‌اند kubernetes
  • حذف پیاده‌سازی file.__ne__ و الزام به نسخه python >= 3.5

  • استفاده از remote_user و remote_port در ansible.cfg
  • افزودن مشخصه arch (معماری) به ماژول system_info

•
تغییر ناسازگار (Breaking change): دستور host.file().listdir() اکنون یک متد است

  • ارسال آرگومان‌های اضافی به خط فرمان ansible از طریق host.ansible()
  • متد جدید host.file.listdir() برای فهرست کردن موارد موجود در یک دایرکتوری.

•
پایان پشتیبانی از python2

  • افزودن ماژول و بک‌اند podman
  • هشدار: این آخرین نسخه testinfra با پشتیبانی از python2 خواهد بود، لطفاً به python3 ارتقا دهید.

  • افزودن extras برای وابستگی‌های بک‌اند (#454)
  • بهبودهای مختلف در مستندات یکپارچه‌سازی با kitchen
  • پشتیبانی بک‌اند ansible از فیلد "password" در اینونتوری ansible
  • بک‌اند جدید "openshift"

•
رفع ایراد ماژول Process هنگام کار با رشته‌های طولانی (نام کاربری و...) #505

  • ماژول جدید "environment" برای دریافت متغیرهای محیطی راه دور
  • ماژول جدید "block_device" برای نمایش اطلاعات دستگاه‌های بلوکی
  • افزودن پرچم سراسری --force-ansible به خط فرمان
  • صدور خطا در صورت عدم وجود فایل اینونتوری ansible
  • رفع مشکل گریز (escape) نویسه‌ها در آرگومان‌های ssh در ansible هنگام تنظیم در اینونتوری یا فایل پیکربندی

  • اتصالات 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").

  • ثبت وقایع کامل دستورات تنها در سطح لاگ DEBUG جهت جلوگیری از لاگ شدن داده‌های حساس در سطح لاگ INFO.
  • رفع خرابی احتمالی هنگام پردازش اینونتوری‌های ansible #470
  • پشتیبانی از فایل kubeconfig جایگزین در اتصالات kubectl #460
  • پشتیبانی از تجزیه ProxyCommand از فایل ssh_config برای اتصالات paramiko

  • تنظیم زمان انتظار پیش‌فرض (timeout) به ۱۰ ثانیه در اتصالات ssh/paramiko
  • افزودن پشتیبانی از پارامتر ansible_private_key_file در اینونتوری ansible

•
افزودن پشتیبانی از اتصالات lxc و lxd در ansible

  • رفع مشکل تجزیه 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

  • رفع مشکل تجزیه آدرس‌های ipv6 برای backendهای paramiko، ssh و ansible.
  • رفع مشکل فراخوانی --connection=ansible هنگامی که هیچ میزبانی ارائه نشده است

  • یک 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" نیز فراهم شده است.

  • docker: متد کلاسی (classmethod) جدید get_containers()
  • socket: رفع مشکل تجزیه آدرس‌های ipv6 در نسخه‌های جدید ss
  • service: بازگشت systemd به sysv هنگامی که "systemctl is-active" کار نمی‌کند

  • افزودن ماژول addr، مورد استفاده برای آزمایش اتصال شبکه
  • حذف دستور منسوخ‌شدهٔ "testinfra"؛ باید به جای آن از "py.test" استفاده کنید
  • حذف fixtureهای سطح بالای منسوخ‌شده؛ در عوض از طریق fixture با نام "host" به آن‌ها دسترسی پیدا کنید.
  • حذف پشتیبانی از ansible <= 2.4

  • افزودن ماژول docker
  • رفع مشکل سازگاری با pytest 4

  • امکان استفاده از کدگذاری درصدی (urlencode) برای نویسه‌ها در مشخصات میزبان "user:pass@host" (#387)
  • رفع مشکل ثبت گزارش دوگانه (double logging) هم از pytest و هم از testinfra
  • حذف پشتیبانی از python 2.6
  • امکان پیکربندی مهلت زمانی (timeout) برای backend نوع winrm

  • افزودن پشتیبانی از کاربر "become" در ماژول ansible
  • افزودن مشخصه failed/succeeded به خروجی run()

  • packaging: استفاده از setuptools_scm به جای pbr
  • iptables: افزودن پشتیبانی از ip6tables
  • sysctl: یافتن sysctl خارج از PATH (/sbin)

  • رفع مشکل یافتن دستورهای 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

  • رفع مشکل ترتیب اجرای آزمون‌های چندمیزبانی (#347)، پس‌رفت معرفی‌شده در 1.13.1
  • رفع مشکل Socket در میزبان‌های OpenBSD (#338)

  • افزودن یک backend جدید برای lxc
  • Socket: رفع مشکل is_listening برای سوکت‌های unix
  • افزودن پشتیبانی از namespace و container برای backend نوع kubernetes
  • افزودن یک حافظه موقت (cache) از inventoryهای تجزیه‌شدهٔ ansible برای backend نوع ansible
  • Service: رفع مشکل تشخیص سرویس در میزبان‌های Centos 6
  • File: پیاده‌سازی مقایسه فایل با مسیرهای متنی (string paths)

  • package: رفع رفتار is_installed و version برای بسته‌های نصب‌نشده (#321 و #326)
  • ansible: استفاده از ترتیب آزمون پیش‌بینی‌پذیر هنگام استفاده از pytest-xdist برای رفع خطاهای تصادفی در گردآوری آزمون‌ها (#316)
  • socket: رفع مشکل در شناسایی سوکت‌های شنونده udp (#311)
  • ssh backend: افزودن پشتیبانی از GSSAPI

  • ansible: رفع مشکلات سازگاری با ansible 2.5
  • pip: رفع مشکلات سازگاری با pip 10 (#299)

•
Socket: رفع خطا در نسخه‌های قدیمی ss که فاقد گزینه --no-header هستند (#293)

  • رفع گزارش نادرست خطا هنگام استفاده از ماژول ansible بدون ansible backend (#288)
  • Socket: افزودن یک پیاده‌سازی جدید با استفاده از ss به جای netstat (#124)
  • افزودن پشتیبانی از service، process و systeminfo برای Alpine (#283)

  • اصلاح ()get_variables برای ansible>=2.0,<2.4 (#274)
  • Paramiko: استفاده از تنظیمات RequireTTY در صورت تعیین شدن در SSHConfig مشخص‌شده (#247)

•
ماژول جدید iptables

  • رفع مشکل اجرای testinfra درون مجموعه‌ای که از doctest استفاده می‌کند (#268)
  • Service: افزودن متد ()is_valid برای systemd
  • رفع مشکل ()file.linked_to برای Mac OS

  • Interface: امکان یافتن دستور 'ip' خارج از PATH
  • رفع مشکل گزینه --nagios با python 3

  • منسوخ شدن دستور testinfra (در نسخه 2.0 حذف خواهد شد)، به جای آن از py.test استفاده کنید #135
  • مدیریت گزینه --nagios هنگام استفاده از دستور py.test

•
پشتیبانی از ansible 2.4 (#249)

  • Salt: امکان تعیین پوشه پیکربندی (#230)
  • افزودن یک WinRM backend
  • Socket: امکان مدیریت کلاینت‌های ipv4 توسط سوکت‌های ipv6 (#234)
  • Service: بهبود شناسایی upstart (#243)

  • Service: افزودن پشتیبانی از ()is_enabled برای OpenBSD
  • افزودن گزینه فایل هویت ssh برای backendهای paramiko و ssh
  • گسترش تیلدا (~) به پوشه خانگی کاربر برای گزینه‌های ssh-config، ssh-identity-file و ansible-inventory

  • Service: امکان یافتن دستور 'service' خارج از $PATH #211
  • اصلاحات مستندات

•
رفع هشدار منسوخ‌شدگی ناخواسته هنگام اجرای آزمون‌ها با pytest 3.1 #204

•
رفع مشکل بسته wheel برای نسخه 1.6.1

•
پشتیبانی از ansible 2.3 همراه با python 3 (#197)

•
فیکسچر جدید 'host' به عنوان جایگزینی برای تمامی فیکسچرهای دیگر. ببینید https://testinfra.readthedocs.io/en/latest/modules.html#host (سایر فیکسچرها منسوخ شده‌اند و در نگارش 2.0 حذف خواهند شد).

•
backends: رفع مشکل ansible backend با ansible >= 2.3 (#195)
  • backends: بازگشت به کدگذاری UTF-8 در صورت ASCII بودن کدگذاری سیستم.
  • Service: رفع باگ is_running() در سیستم‌های مبتنی بر Upstart.

•
Sudo: بازگردانی دستور backend در صورت بروز استثناها.

•
اعمال become_user هنگام استفاده از بک‌اند ansible.

•
افزودن وابستگی به importlib در python 2.6.

  • بک‌اند جدید kubectl.
  • Command: حذف نویسه‌های carriage return و خطوط جدید در check_output (#164).
  • Package: بهبود در دریافت version() و release() در rpm.
  • User: افزودن فیلد gecos (توضیحات) (#155).

  • SystemInfo: تشخیص نام مستعار (codename) از VERSION_CODENAME در /etc/os-release (پشتیبان در صورت نصب نبودن lsb_release).
  • Package: افزودن ویژگی release برای سیستم‌های مبتنی بر rpm.

به‌طور پیش‌فرض 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):
    [....]

اگر آزمون‌های زیادی دارید، می‌توانید از افزونه‌ی 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

# 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 را ببینید.

ابزار 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://

این بک‌اند پیش‌فرض در صورت عدم تعیین میزبان است (خواه از طریق --hosts یا در ماژول‌ها). دستورها به صورت محلی در یک زیرپردازش (subprocess) تحت کاربر جاری اجرا می‌شوند:

$ pytest --sudo test_myinfra.py

این بک‌اند پیش‌فرض در صورت ارائه فهرستی از میزبان‌ها است. Paramiko https://www.paramiko.org یک پیاده‌سازی از پروتکل SSHv2 در Python است. Testinfra از شما گذرواژه‌ای درخواست نخواهد کرد، بنابراین باید بتوانید بدون نیاز به گذرواژه (با استفاده از کلیدهای بدون گذرواژه یا ssh-agent) متصل شوید.

می‌توانید یک ssh-config جایگزین مشخص کنید:

$ pytest --ssh-config=/path/to/ssh_config --hosts=server

بک‌اند 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'

این یک 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 به گزینه‌ها، اتصال پایدار را غیرفعال کنید.

موتور 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 انتخاب کرد.

از موجودی‌های (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

از 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 از 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 می‌تواند برای آزمایش کانتینرهای در حال اجرای LXC یا LXD به کار رود. این پشتانه از دستور lxc exec https://linuxcontainers.org/lxd/getting-started-cli استفاده می‌کند:

$ pytest --hosts='lxc://container_name'

ماژول‌های Testinfra از طریق فیکسچر host https://docs.pytest.org/en/latest/fixture.html#fixture ارائه می‌شوند؛ آن را به عنوان آرگومان تابع آزمون خود اعلان کنید تا درون آن در دسترس قرار گیرد.

def test_foo(host):
    # [...]

کلاس testinfra.modules.ansible.Ansible
کلاس testinfra.modules.addr.Addr
کلاس testinfra.modules.blockdevice.BlockDevice
کلاس testinfra.modules.docker.Docker
کلاس testinfra.modules.environment.Environment
کلاس testinfra.modules.file.File
کلاس testinfra.modules.group.Group
کلاس testinfra.modules.interface.Interface
کلاس testinfra.modules.iptables.Iptables
کلاس testinfra.modules.mountpoint.MountPoint
کلاس testinfra.modules.package.Package
کلاس testinfra.modules.pip.Pip
کلاس testinfra.modules.podman.Podman
کلاس testinfra.modules.process.Process
کلاس testinfra.modules.puppet.PuppetResource
کلاس testinfra.modules.puppet.Facter
کلاس testinfra.modules.salt.Salt
کلاس testinfra.modules.service.Service
کلاس testinfra.modules.socket.Socket
کلاس testinfra.modules.sudo.Sudo
کلاس testinfra.modules.supervisor.Supervisor
کلاس testinfra.modules.sysctl.Sysctl
کلاس testinfra.modules.systeminfo.SystemInfo
کلاس testinfra.modules.user.User
در صورتی که command -v در دسترس باشد، مقدار True را برمی‌گرداند
exists(command: str) -> bool اگر دستور داده‌شده در $PATH وجود داشته باشد، مقدار True را برمی‌گرداند.
مسیر دستور داده‌شده را برمی‌گرداند

در صورت پیدا نشدن دستور، خطای ValueError ایجاد می‌کند

دستور داده‌شده را اجرا کرده و 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'")
دستور را اجرا کرده و بررسی می‌کند که آیا وضعیت خروج موردانتظار را برمی‌گرداند یا خیر
پارامترها
expected -- فهرستی از وضعیت‌های خروج موردانتظار
خطاهای صادرشده
AssertionError
دستور را اجرا کرده و بررسی می‌کند که آیا وضعیت خروج 0 یا 1 را برمی‌گرداند یا خیر
خطاهای صادرشده
AssertionError
دریافت stdout دستوری که با موفقیت اجرا شده است
مقادیر بازگشتی
خروجی stdout بدون نویسه خط جدید پایانی
خطاهای صادرشده
AssertionError
یک نمونه از Host را از hostspec برمی‌گرداند

مقدار hostspec باید به این صورت باشد: <backend_type>://<name>?param1=value1&param2=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 را اجرا می‌کند

این ماژول تنها با بک‌اند اتصال 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.
استثنایی که هنگام وقوع خطا در یک فراخوانی 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
یک dict از متغیرهای ansible را برمی‌گرداند
>>> host.ansible.get_variables()
{
    'inventory_hostname': 'localhost',
    'group_names': ['ungrouped'],
    'foo': 'bar',
}
بررسی نشانی دوردست

مثال:

>>> 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 برانگیخته خواهد شد.

بازگرداندن نام میزبان
بازگرداندن فضای نام شبکه
بررسی اینکه آیا فضای نام شبکه وجود دارد یا خیر
بازگرداندن اینکه آیا نشانی قابل تحلیل و تفکیک (resolve) است یا خیر
بازگرداندن اینکه آیا نشانی در دسترس است یا خیر
بازگرداندن نشانی‌های IP میزبان
بازگرداندن نشانی‌های IPv4 میزبان
بازگرداندن نشانی‌های IPv6 میزبان
بازگرداندن جفت نشانی-درگاه

اطلاعات مربوط به دستگاه بلوکی.

باید با sudo یا توسط کاربر ریشه (root) استفاده شود.

اگر دستگاه یک دستگاه بلوکی نباشد، خطای RuntimeError رخ می‌دهد.

در صورتی که دستگاه یک پارتیشن باشد True بازمی‌گرداند.
>>> host.block_device("/dev/sda1").is_partition
True
>>> host.block_device("/dev/sda").is_partition
False
اندازه دستگاه را بر حسب بایت بازمی‌گرداند.
>>> host.block_device("/dev/sda1").size
512110190592
اندازه سکتور دستگاه را بر حسب بایت بازمی‌گرداند.
>>> host.block_device("/dev/sda1").sector_size
512
اندازه بلوک دستگاه را بر حسب بایت بازمی‌گرداند.
>>> host.block_device("/dev/sda").block_size
4096
سکتور آغازین دستگاه را بر روی دستگاه زیرین بازمی‌گرداند.
معمولاً این مقدار برای دستگاه‌های کامل صفر و برای پارتیشن‌ها غیرصفر است.
>>> host.block_device("/dev/sda1").start_sector
2048
>>> host.block_device("/dev/md0").start_sector
0
اگر دستگاه قابل نوشتن باشد (وضعیت RO نداشته باشد) True بازمی‌گرداند.
>>> host.block_device("/dev/sda").is_writable
True
>>> host.block_device("/dev/loop1").is_writable
False
مقدار Read Ahead دستگاه را بر حسب سکتورهای ۵۱۲ بایتی بازمی‌گرداند.
>>> host.block_device("/dev/sda").ra
256
بررسی و آزمودن کانتینرهای docker در حال اجرا بر روی سیستم.

مثال:

>>> nginx = host.docker("app_nginx")
>>> nginx.is_running
True
>>> nginx.id
'7e67dc7495ca8f451d346b775890bdc0fb561ecdc97b68fb59ff2f77b509a8fe'
>>> nginx.name
'app_nginx'
نسخه کلاینت Docker
نسخه سرور Docker
نسخه Docker https://docs.docker.com/engine/reference/commandline/version با یک قالب اختیاری (Go template).
>>> host.docker.version()
Client: Docker Engine - Community
...
>>> host.docker.version("{{.Client.Context}}"))
default
بازگرداندن فهرستی از کانتینرها

به‌طور پیش‌فرض، فهرستی از تمامی کانتینرها شامل کانتینرهای غیر در حال اجرا بازگردانده می‌شود.

فیلتر کردن می‌تواند با استفاده از کلیدهای فیلتر تعریف‌شده در 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>]

دریافت متغیرهای محیطی

مثال:

>>> host.environment()
{
    "EDITOR": "vim",
    "SHELL": "/bin/bash",
    [...]
}

بررسی ویژگی‌های گوناگون فایل
بررسی وجود داشتن فایل
>>> host.file("/etc/passwd").exists
True
>>> host.file("/nonexistent").exists
False
بررسی اینکه آیا مسیر داده‌شده یک فایل معمولی (regular file) است یا خیر
بررسی اینکه آیا مسیر داده‌شده وجود دارد و یک دایرکتوری است یا خیر
بررسی اینکه آیا مسیر داده‌شده وجود دارد و مجوز اجرا به آن اعطا شده است یا خیر
بررسی اینکه آیا مسیر داده‌شده وجود دارد و یک لوله (pipe) است یا خیر
بررسی اینکه آیا مسیر داده‌شده وجود دارد و یک سوکت (socket) است یا خیر
بررسی اینکه آیا مسیر داده‌شده وجود دارد و یک پیوند نمادین (symlink) است یا خیر
حل پیوند نمادین و مشخص کردن مقصد آن
>>> host.file("/var/lock").linked_to
'/run/lock'
بازگرداندن مالک فایل به‌صورت رشته
>>> host.file("/etc/passwd").user
'root'
بازگرداندن شناسه کاربری (UID) فایل به‌صورت یک عدد صحیح
>>> host.file("/etc/passwd").uid
0
بازگرداندن نام گروه فایل به‌صورت رشته
بازگرداندن شناسه گروه (GID) فایل به‌صورت یک عدد صحیح
بازگرداندن حالت دسترسی فایل (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
بررسی محتوای فایل برای یافتن الگو (pattern)

این متد از grep استفاده کرده و در نتیجه از قواعد نحو عبارات منظم (regex) در grep پیروی می‌کند.

property md5sum محاسبه چکیده پیام MD5 محتوای فایل
محاسبه چکیده پیام SHA256 محتوای فایل
بازگرداندن محتوای فایل به صورت بایت
>>> host.file("/tmp/foo").content
b'caf\xc3\xa9'
بازگرداندن محتوای فایل به صورت رشته
>>> host.file("/tmp/foo").content_string
'café'
بازگرداندن زمان آخرین تغییر به عنوان یک شیء datetime.datetime
>>> host.file("/etc/passwd").mtime
datetime.datetime(2015, 3, 15, 20, 25, 40)
بازگرداندن اندازه فایل بر حسب بایت
بازگرداندن فهرست موارد موجود در دایرکتوری
>>> host.file("/tmp").listdir()
['foo_file', 'bar_dir']

بررسی گروه یونیکس
بررسی وجود داشتن گروه
>>> host.group("wheel").exists
True
>>> host.group("nosuchgroup").exists
False
بازگرداندن فهرستی از نام‌های گروه‌های محلی و دوردست
>>> host.group("anyname").get_all_groups
["root", "wheel", "man", "tty", <...>]
بازگرداندن فهرستی از نام‌های گروه‌های محلی
>>> host.group("anyname").get_local_groups
["root", "wheel", "man", "tty", <...>]
بازگرداندن تمام کاربرانی که عضو این گروه هستند.

بررسی رابط‌های شبکه
>>> host.interface("eth0").exists
True

به صورت اختیاری، می‌توان خانواده پروتکل مورد استفاده را مشخص کرد.

>>> host.interface("eth0", "inet6").addresses
['fe80::e291:f5ff:fe98:6b8c']
بازگرداندن آدرس‌های ipv4 و ipv6 روی رابط
>>> host.interface("eth0").addresses
['192.168.31.254', '192.168.31.252', 'fe80::e291:f5ff:fe98:6b8c']
بازگرداندن ویژگی‌های پیوند (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}
بازگرداندن مسیرهای مرتبط با رابط، با امکان پالایش اختیاری بر اساس دامنه 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'}]
بازگرداندن نام تمامی رابط‌ها.
>>> host.interface.names()
['lo', 'tunl0', 'ip6tnl0', 'eth0']
بازگرداندن رابط مورد استفاده برای مسیر پیش‌فرض (default route).
>>> host.interface.default()
<interface eth0>

به صورت اختیاری، می‌توان خانواده پروتکل مورد استفاده را مشخص کرد.

>>> host.interface.default("inet6")
None
بررسی وجود قانون در iptables
فهرستی از قوانین iptables را برمی‌گرداند
بر اساس خروجی دستور iptables -t TABLE -S CHAIN
به صورت اختیاری آرگومان‌های زیر را می‌پذیرد:
  • 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']

بررسی نقاط اتصال (Mount Points)
در صورت وجود نقطه اتصال مقدار True برمی‌گرداند
>>> host.mount_point("/").exists
True
>>> host.mount_point("/not/a/mountpoint").exists
False
نوع سیستم‌فایل مرتبط را برمی‌گرداند
>>> host.mount_point("/").filesystem
'ext4'
دستگاه مرتبط را برمی‌گرداند
>>> host.mount_point("/").device
'/dev/sda1'
فهرستی از گزینه‌هایی که نقطه اتصال با آن‌ها ایجاد شده است را برمی‌گرداند
>>> host.mount_point("/").options
['rw', 'relatime', 'data=ordered']
فهرستی از نمونه‌های 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)>]

بررسی وضعیت و نسخه بسته‌ها
بررسی اینکه آیا بسته نصب شده است یا خیر
>>> 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, ...)
اطلاعات مشخصه انتشار (release) را از نسخه بسته برمی‌گرداند
>>> host.package("nginx").release
'1.el6'
نسخه بسته را آن‌طور که توسط سیستم بسته بازگردانده شده برمی‌گرداند
>>> host.package("nginx").version
'1.2.1-2.2+wheezy3'
آزمودن مدیر بسته و بسته‌های pip
بررسی نصب بودن بسته
>>> host.package("pip").is_installed
True
بازگرداندن نسخه‌ی بسته همان‌طور که توسط pip گزارش شده است
>>> host.package("pip").version
'18.1'
بررسی سازگار بودن وابستگی‌های بسته‌های نصب‌شده.
>>> 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.

دریافت تمام بسته‌های نصب‌شده و نسخه‌های آن‌ها به شکلی که توسط 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'}}
دریافت تمام بسته‌های قدیمی به همراه نسخه‌ی فعلی و آخرین نسخه
>>> host.pip.get_outdated_packages(
...     pip_path='~/venv/website/bin/pip')
{'Django': {'current': '1.10.2', 'latest': '1.10.3'}}

آزمودن کانتینرهای podman در حال اجرا روی سیستم.

مثال:

>>> nginx = host.podman("app_nginx")
>>> nginx.is_running
True
>>> nginx.id
'7e67dc7495ca8f451d346b775890bdc0fb561ecdc97b68fb59ff2f77b509a8fe'
>>> nginx.name
'app_nginx'
بازگرداندن فهرستی از کانتینرها

به‌طور پیش‌فرض فهرستی از همه‌ی کانتینرها، شامل کانتینرهای غیر در حال اجرا را بازمی‌گرداند.

فیلتر کردن می‌تواند با استفاده از کلیدهای فیلتر تعریف‌شده در 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>]

آزمودن مشخصه‌های فرآیندها

فرآیندها با استفاده از 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
دریافت فهرستی از فرآیندهای منطبق
>>> host.process.filter(user="root", comm="zsh")
[<process zsh (pid=2715)>, <process zsh (pid=10502)>, ...]
دریافت یک فرآیند منطبق

اگر فرآیندی یافت نشود یا چندین فرآیند با فیلترها مطابقت داشته باشند، RuntimeError ایجاد می‌شود.

دریافت منابع 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',
    },
}

دریافت واقعیت‌ها (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
>>> 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 را اجرا کنید

آزمودن سرویس‌ها

پیاده‌سازی‌ها:

  • لینوکس: شناسایی 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 هنوز پیاده‌سازی نشده است)
بررسی اینکه آیا سرویس وجود دارد یا خیر
بررسی اینکه آیا سرویس در حال اجرا است یا خیر
بررسی اینکه آیا سرویس فعال (enabled) است یا خیر
بررسی اینکه آیا سرویس معتبر است یا خیر

این متد تنها در پیاده‌سازی systemd موجود است، و در سایر پیاده‌سازی‌ها خطای NotImplementedError ایجاد خواهد کرد

بررسی اینکه آیا سرویس ماسک (masked) شده است یا خیر

این متد تنها در پیاده‌سازی systemd موجود است، و در سایر پیاده‌سازی‌ها خطای NotImplementedError ایجاد خواهد کرد

ویژگی‌های سرویس (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

بررسی سوکت‌های در حال شنود 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
بررسی اینکه آیا سوکت در حال شنود است یا خیر
>>> 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

نکته:

اگر میزبانی برای سوکت‌های udp و tcp مشخص نکنید، سوکت در صورتی و تنها در صورتی در وضعیت شنود خواهد بود که سوکت روی هر دو نشانی‌های ipv4 و ipv6 در حال شنود باشد (یعنی 0.0.0.0 و ::)
فهرستی از کلاینت‌های متصل به یک سوکت در حال شنود را برمی‌گرداند

برای سوکت‌های 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]
فهرستی از تمام سوکت‌های در حال شنود را برمی‌گرداند
>>> host.socket.get_listening_sockets()
['tcp://0.0.0.0:22', 'tcp://:::22', 'unix:///run/systemd/private', ...]

ماژول 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
>>> 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'
در صورتی که سرویس مدیریت‌شده در وضعیت RUNNING باشد، True را برمی‌گرداند
وضعیت سرویس مدیریت‌شده را برمی‌گرداند

وضعیت می‌تواند STOPPED، STARTING، RUNNING، BACKOFF، STOPPING، EXITED، FATAL یا UNKNOWN باشد.

ببینید: http://supervisord.org/subprocess.html#process-states

شناسه فرایند (pid به صورت int) سرویس مدیریت‌شده را برمی‌گرداند
فهرستی از سرویس‌های در حال اجرا تحت 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)>]
بررسی پارامترهای هسته
>>> host.sysctl("kernel.osrelease")
"3.16.0-4-amd64"
>>> host.sysctl("vm.dirty_ratio")
20

بازگرداندن اطلاعات سیستم
نوع سیستم‌عامل
>>> host.system_info.type
'linux'
نام توزیع
>>> host.system_info.distribution
'debian'
شماره انتشار توزیع
>>> host.system_info.release
'10.2'
اسم رمز انتشار
>>> host.system_info.codename
'bullseye'
معماری میزبان
>>> host.system_info.arch
'x86_64'

بررسی کاربران یونیکس

اگر name مشخص نشود، کاربر جاری بررسی می‌شود

بازگرداندن نام کاربر
بررسی وجود کاربر
>>> host.user("root").exists
True
>>> host.user("nosuchuser").exists
False
بازگرداندن شناسه کاربر
بازگرداندن شناسه گروه مؤثر
بازگرداندن نام گروه مؤثر
بازگرداندن فهرست شناسه‌های گروه‌های کاربر
بازگرداندن فهرست نام‌های گروه‌های کاربر
بازگرداندن دایرکتوری خانگی کاربر
بازگرداندن پوسته ورود کاربر
بازگرداندن گذرواژه رمزگذاری‌شده کاربر
بازگرداندن حداکثر تعداد روزهای بین تغییر گذرواژه
بازگرداندن حداقل تعداد روزهای بین تغییر گذرواژه
بازگرداندن فیلد توضیحات/gecos کاربر
بازگرداندن تاریخ انقضای حساب کاربری
>>> host.user("phil").expiration_date
datetime.datetime(2020, 1, 1, 0, 0)
>>> host.user("root").expiration_date
None
بازگرداندن فهرستی از نام‌های کاربران محلی و راه‌دور
>>> host.user().get_all_users
["root", "bin", "daemon", "lp", <...>]
بازگرداندن فهرستی از نام‌های کاربران محلی
>>> host.user().get_local_users
["root", "bin", "daemon", "lp", <...>]
شیئی که تمام جزئیات بازگردانده‌شده از اجرای دستور را کپسوله‌سازی می‌کند.

مثال:

>>> 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
مشخص می‌کند که آیا دستور با موفقیت اجرا شده است یا خیر.
>>> host.run("true").succeeded
True
مشخص می‌کند که آیا اجرای دستور ناموفق بوده است یا خیر.
>>> host.run("false").failed
True
کد بازگشتی (returncode) دستور را دریافت می‌کند.
>>> host.run("true").rc
0
جریان خروجی استاندارد (stdout) دستور اجراشده را دریافت می‌کند.
>>> host.run("mkdir -v new_directory").stdout
mkdir: created directory 'new_directory'
جریان خطای استاندارد (stderr) دستور اجراشده را دریافت می‌کند.
>>> host.run("mkdir new_directory").stderr
mkdir: cannot create directory 'new_directory': File exists
جریان خروجی استاندارد (stdout) دستور اجراشده را به صورت بایت دریافت می‌کند.
>>> host.run("mkdir -v new_directory").stdout_bytes
b"mkdir: created directory 'new_directory'"
جریان خطای استاندارد (stderr) دستور اجراشده را به صورت بایت دریافت می‌کند.
>>> host.run("mkdir new_directory").stderr_bytes
b"mkdir: cannot create directory 'new_directory': File exists"

شما می‌توانید از 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)

ابزار 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 https://www.vagrantup.com ابزاری برای راه‌اندازی و تدارک محیط‌های توسعه (ماشین‌های مجازی) است.

هنگامی که ماشین Vagrant شما بالا آمده و در حال اجرا است، می‌توانید به آسانی مجموعه آزمون‌های testinfra خود را روی آن اجرا کنید:

vagrant ssh-config > .vagrant/ssh-config
pytest --hosts=default --ssh-config=.vagrant/ssh-config tests.py

سرور 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 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 با 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 و در کانال #pytest https://web.libera.chat/?channel=#pytest روی شبکهٔ [libera.chat](https://libera.chat) مطرح کنید.

ابزار testinfra به‌عنوان یک افزونه برای pytest پیاده‌سازی شده است؛ بنابراین برای بهره‌مندی حداکثری از آن، لطفاً مستندات pytest را مطالعه کنید: https://docs.pytest.org/en/latest

•
ابزار Molecule https://molecule.readthedocs.io یک چارچوب آزمایش خودکار برای نقش‌های Ansible است که به‌صورت بومی از Testinfra پشتیبانی می‌کند.

Philippe Pepiot

2025, Philippe Pepiot

December 20, 2025 10.2.2