<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0">
    <channel>
        <title>نوشته های Hootan Alghaspour</title>
        <link>https://virgool.io/feed/@h.alghaspour</link>
        <description>هوتن القاس پور</description>
        <language>fa</language>
        <pubDate>2026-06-25 00:00:28</pubDate>
        <image>
            <url>https://files.virgool.io/upload/users/8015/avatar/avatar.png?height=120&amp;width=120</url>
            <title>Hootan Alghaspour</title>
            <link>https://virgool.io/@h.alghaspour</link>
        </image>

                    <item>
                <title>ساخت sitemap.xml با لاراول</title>
                <link>https://virgool.io/@h.alghaspour/%D8%B3%D8%A7%D8%AE%D8%AA-sitemapxml-%D8%A8%D8%A7-%D9%84%D8%A7%D8%B1%D8%A7%D9%88%D9%84-mwm4r32igble</link>
                <description>در ابتدا spatie/laravel-sitemap را با composer نصب می کنیم.composer require spatie/laravel-sitemapسپس فایل تنظیماتش را منتشر می کنیم که البته خیلی لازم نیست، برای من تاکنون پیش نیامده تغییراتی در تنظیمات پیش فرض اش بدهم.php artisan vendor:publish --provider=&amp;quotSpatie\Sitemap\SitemapServiceProvider&amp;quot --tag=sitemap-configدستور بالا یک فایل در config/sitemap.php ایجاد می کند که می توانید تنظیمات را از داخل آن تغییر دهد. تنها مورد تنظیمات کاربردی شاید execute_javascript باشد که میتواند لینک هایی که به صورت دینامیک در صفحه با جاوا اسکریپت ساخته می شوند را نیز کراول کند.با توجه به اینکه عملیاتی است که طول می کشد و پشت کلودفلر باشید به timeout خواهید خورد و همچنین اینکه ممکنه بخواهیم به صورت cron job استفاده کنیم به دستور artisan تبدیلش می کنیم. کلاً برای اینجور موارد تبدیل به دستور artisan و اجرا در خط فرمان بهتر است.php artisan make:command GenerateSitemapدستور بالا یک فایل در app/Console/Commands/GenerateSitemap.php میسازد که می توانید برنامه ای که می خواهید با دستور GenerateSitemap اجرا شود را در آن بنویسید.برای اینکه با crawler خودش به صورت خودکار شروع کند برای یک دامنه sitemap.xml بسازد می توان از این کد استفاده کرد. &lt;?php
namespace App\Console\Commands;
use Illuminate\Console\Command;
use Spatie\Sitemap\SitemapGenerator;
use Spatie\Sitemap\Tags\Url;
use Illuminate\Support\Facades\Http;
class GenerateSitemap extends Command{
protected $signature = &#039;sitemap:generate&#039;;
protected $description = &#039;Generate sitemap&#039;;
public function handle(){
config([&#039;app.url&#039; =&gt; &#039;http://example.com&#039;]);
$path = public_path(&#039;sitemap.xml&#039;);
SitemapGenerator::create(&#039;http://example.com&#039;)-&gt;writeToFile($path);
}
}دقت بفرمایید در اینجا چون اپلیکیشن من یک سیستم مونولیت روی یک سرور و پشت کلودفلر است قبل از اجرا app.url را به http تغییر می دهم زیرا روی این دامنه گواهینامه معتبر ssl نصب نیست و روی همین سرور دستور را اجرا می کنم و dns آن برای این دامنه همین IP را بر می گرداند و ترجیحم هم این است با http سریعتر crawl کند و با https به مشکل می خورد و ... ، برای سناریو شما شاید نیازی به تغییر app.url نباشد.بعد که sitemap.xml ساخته شد داخل آن همه http ها را با https به روش find &amp; replace عوض می کنم.روی سناریوی من این روش کار کرد، کلاً برای اجر باید با dd(Http::get(config(&#x27;app.url&#x27;))-&gt;status()) پاسخ ۲۰۰ بگیرید تا درست کار کند.برای حدود ۹۰۰۰ لینک حدود یک ساعت طول کشید تا sitemap.xml ساخته شود. cpu زیادی هم اشغال می کند و بهتر است اگر روی همان سرور اجرا می کنید در زمان خلوتی اجرا بفرمایید.می توانید برای هر Model یا Url های دلخواه هم sitemap بسازید و اینطوری خودش crawl نکند و خودکار بسازد.و در نهایت هم برای ساخت sitemap.xml دستور زیر را وارد می کنیم php artisan sitemap:generateمن دیگر شرح بیشتری لازم نیست اینجا بدهم و برای جزییات بیشتر به مقالات دیگر ارجاع می دهم که بهتر و کامل تر هم توضیح داده اند.Automatically generate a sitemap in LaravelMastering Laravel Sitemap: A Comprehensive TutorialGenerate sitemaps with ease</description>
                <category>Hootan Alghaspour</category>
                <author>Hootan Alghaspour</author>
                <pubDate>Tue, 18 Mar 2025 15:42:16 +0330</pubDate>
            </item>
                    <item>
                <title>انتقال mailboxها از یک سرور/اکانت به سرور/اکانت دیگر با imapsync</title>
                <link>https://virgool.io/@h.alghaspour/imapsync-zimbra-nomucz1kzbpy</link>
                <description>خلاصه داستان این است که ما تعدادی mailbox روی یک هاست داشتیم و می خواستیم همه را عیناً به zimbra خودمان منتقل کنیم.هم بخاطر امنیت و اینکه خودمان سرور خوب و سیستم بک آپ گیری و ... داریم،  و هم بخاطر اینکه هزینه آن هاست دیگر توجیه نداشت و هم بخاطر امکانت اضافی collaborationی که زیمبرا روی وب اینترفیس دارد.کل حجم داده ها حدود ۲۵ گیگابایت و تعداد mailbox ها شانزده عدد بود و مهم بود عیناً منتقل شود، یعنی بجز inbox و ضمیمه ها و sent و ... حتی read/unread ایمیل هم عیناً همان باشد.بهترین روشی که پیدا کردم و خیلی راضی کننده هم جواب داد imapsync بود. روی ubuntu 20.04 خیلی راحت بروش ذیل نصب شد :#apt-get install apt-file libcgi-pm-perl libauthen-ntlm-perl libclass-load-perl libcrypt-openssl-rsa-perl libcrypt-ssleay-perl libdata-uniqid-perl libdigest-hmac-perl libdist-checkconflicts-perl libencode-imaputf7-perl libfile-copy-recursive-perl libfile-tail-perl libio-compress-perl libio-socket-inet6-perl libio-socket-ssl-perl libio-tee-perl libjson-webtoken-perl libmail-imapclient-perl libmodule-scandeps-perl libnet-dbus-perl libnet-ssleay-perl libpar-packer-perl libproc-processtable-perl libreadonly-perl libregexp-common-perl libsys-meminfo-perl libterm-readkey-perl libtest-fatal-perl libtest-mock-guard-perl libtest-mockobject-perl libtest-pod-perl libtest-requires-perl libtest-simple-perl libunicode-string-perl liburi-perl libtest-nowarnings-perl libtest-deep-perl libtest-warn-perl make time cpanminus

#cpanm Mail::IMAPClient

#git clone https://github.com/imapsync/imapsync.git

#cd imapsync

#mkdir -p dist

#make install
روی مستندات زیمبرا یک راهنمای نسبتاً خوب برای انتقال با imapsync هست.من بعد از تست ها و ساختن اکانت ها روی زیمبرا روی ۲ ترمینال tmux بصورت موازی این دستور را اجرا کردم و حدود ۲ ساعته همه عیناً منتقل شدند.#imapsync --nosyncacls --subscribe --syncinternaldates --nofoldersizes --skipsize --host1 sourceImap --user1 sourceAccount@mailserver --password1 &amp;quotpassword&amp;quot --host2 destinationImap --user2 destinationAccount@mailserver --password2 &amp;quotpassword&amp;quotبرای راحتی و سرعت کار و وجود اطمینان از انتقال بین ۲ دیتاسنتر امن، من از ssl و tls در دستور بالا استفاده نکردم. البته هم اینها و هم خیلی گزینه های دیگر روی imapsync ممکن هست.مثلاً برای انتقال یک اکانت gmail به اکانتی دیگر (منبع و توضیحات) :#imapsync --host1 imap.gmail.com --ssl1 --user1 account1@gmail.com --password1 gmailsecret1 --host2 imap.gmail.com --ssl2 --user2 account2@gmail.com --password2 gmailsecret2 --maxbytespersecond 20_000 --maxbytesafter 1_000_000_000 --automap --maxsleep 2 --synclabels --resynclabels --exclude &amp;quot\[Gmail\]$&amp;quot \ --folderlast  &amp;quot[Gmail]/All Mail&amp;quotبا توجه به پرکاربرد بودن، مثال ها و منابع متعدد برای imapsync هست و اینجا هم بعنوان منبع توضیحات خوبی می توانید پیدا کنید.</description>
                <category>Hootan Alghaspour</category>
                <author>Hootan Alghaspour</author>
                <pubDate>Tue, 13 Feb 2024 16:33:45 +0330</pubDate>
            </item>
                    <item>
                <title>پیاده سازی Desktop/App in Browser</title>
                <link>https://virgool.io/@h.alghaspour/%D9%BE%DB%8C%D8%A7%D8%AF%D9%87-%D8%B3%D8%A7%D8%B2%DB%8C-desktopapp-in-browser-mualq7itbfqu</link>
                <description>بحث Desktop/App as Service موضوعی نیست که جدیداً داغ شده باشد و مدتهاست جزو سرویس های پر استفاده بوده است. پیاده سازی آن اکنون با ابزارهایی مثل کانتینرها (مجازی سازی سطح سیستم عامل)  بسیار راحت تر و ارزان شده است و هر کسی می تواند براحتی چنین سرویس هایی راه اندازی کند.با توجه به افزایش تعداد و تنوع نیازها و تهدیدها، در حال حاضر جزو سرویس های پرکاربرد و پرتقاضا است و ارایه آن در بستر وب و روی ابزار مرورگر وب که در انواع سیستم ها و حتی موبایل ها و تبلت ها وجود دارد یکی از بهترین روش هاست.برای مایی هم که متاسفانه در حوزه اینترنت از داخل و خارج تحریم هستیم داشتن یک دسکتاپ امن با IP تمیز غیر ایرانی روی یک دیتاسنتر معتبر در بعضی شرایط بسیار کارگشاست. این ور که یارو اصلاً نظرش این است از بیخ ببندد و همه دنیای ما خودش باشد، اونور هم اگر بفهمند ایرانی هستید و دستتون جایی بند نیست یا سرویس نمی دهند یا حقتون را می خورند.در مقاله ذیل ۲ مورد که توانا و راحت و در این زمان مشهور هستند را خدمتتان معرفی می کنم. هر دو مستندات کاملاً مکفی برای کسی که اینکاره باشد دارند.linuxserver.io (&amp; webtop)روی linuxserver.io یکسری کانتینرهای خوب داکر هست که اپلیکیشن های آماده استفاده هستند و کافیه pull کنید و استفاده کنید. یکی از معروفترین هاش webtop است که به شما یک لینوکس دسکتاپ خوب روی مرورگر وب می دهد. روی معماری های x86-64 و arm64 (مثلاً Raspberry Pi) هم می توان استفاده کرد.مهمترین مزیت webtop سبک و ساده بودنش است.نسخه های لینوکسی که در حال حاضر موجود است بشرح ذیل هستند.خیلی راحت و سریع یا با خط فرمان docker  یا با docker-compose یک کانتینر حاوی دسکتاپ انتخابی لینوکس بالا می آید و می توان از طریق مرورگر به آن دسترسی داشت و در صورت استفاده از volume میتوان دیتاهای روی آن را بصورت دائم ذخیره نمود. ابزارها و نرم افزارهای متداول روی آن هست و در صورت نیاز می توان چیزهای جدیدی نصب کرد و برای استفاده دسکتاپ روی مرورگر کاملاً مناسب است.فدورا  و xfceاوبونتو و KDEنمونه دستور docker و docker file :در مستنداتش توضیح و مثال های خوبی دارد و من دیگه اینجا دستور و فایل را نمی زارم.از KasmVNC برای ارائه سیستم روی وب استفاده می کند که رابط وب نسبتاً خوبی ارایه می دهد. البته با Kasm Workspaces که در ادامه توضیح خواهم داد متفاوت است اما Kasm Workspaces هم از همین KasmVNC برای اتصال کاربر روی مرورگر استفاده می کند و شما خودتان هم میتوانید روی یک سروری نصب و استفاده کنید. مستندات KasmVNC اینجاست. یکسری آپشن مثل اعتبارسنجی و ... هم مثل هر کانتینری بصورت environment variables می توانید استفاده بفرمایید که در مستنداتش توضیح داده شده اند. خیلی کانتینرهای دیگر هم هست که لیستش را اینجا می توانید ببینید. مستنداتش هم اینجاست و کاستوم هم میتوانید بکنید و راجع به هر کانتینر هم در مستنداتش توضیح داده است، مثلاً توضیحات و مستندات مربوط به webtop اینجاست.Kasm Workspacesیکی دیگر از راه حل های راحت و متداول و البته توانا Kasm Workspaces است که راه حل به اصطلاح چیزی که می گوییم سازمانی تری هم هست. نصبش بسیار ساده است، قابلیت نصب بصورت single server یا multi server را دارد، امکاناتش بسیار کامل هستند و کار کردن با آن برای کاربر و مدیر سیستم اصطلاحاً smooth و غیرآزار دهنده است، امکاناتی که کاربر در کار با یک دسکتاپ ریموت ممکن است نیاز داشته باشد (مثل Webcam Pass-through) را بخوبی پشتیبانی می کند، برای کوروم و فایرفاکس extension هم دارد، بجز کانتینر می توان برای workspace یک سرور یا server pool یا لینک به یک workspace دیگر را هم استفاده کرد، پنل مدیریت آن کامل و تواناست، امکان تعریف کاربر و گروه را دارد، api دارد، روی معماری arm64 (مثلاً Raspberry Pi) هم اجرا می شود و امکان کاستوم کردن و استفاده از اتوماسیون بوسیله terraform و ansible را نیز دارد و ... (خیلی موارد و امکانات دیگر)و البته سیستم نسبتاً سنگینی است و مثل webtop سبک نیست و مدیریت و نگهداری آن هم دردسر بیشتری دارد.همچنین نسخه رایگان آن حداکثر ۵ session همزمان را پشتیبانی می کند.یکی از مهمترین مزایای Kasm Workspaces داشتن مستندات خوب و اکثراً به همراه ویدیو آموزشی می باشد، البته با توجه به اینکه فراگیر شده بجز مستندات خودش مقالات و ویدیوهای آموزشی متعددی هم درباره اش توسط کاربران منتشر شده است. مستنداتش اینجاست که حتماً ارزش یکبار مرور را دارد. (اینقدر ویدیو و توتوریال دارد و مستنداتش خوب است که نیازی به توضیح بیشتر من نیست.)همانطور که قبلاً عرض کردم خود KasmVNC هم به تنهایی بعنوان یک راه حل قابل توجه برای دسترسی از طریق مرورگر وب به سیستم ارزشمند است.جمع بندی :برای جمع بندی باید عرض کنم Kasm Workspaces امکانات و قابلیت های بیشتری دارد و پس از یک آموزش کوتاه مدیریت آن و ارائه سرویس به کاربران بدلیل پنل مدیریتی خوب نیاز به متخصص مجازی سازی ندارد، البته نگه داری فنی آن نیاز به حداقل یک ادمین لینوکس متوسط دارد. اینکه نسخه رایگان آن حداکثر ۵ سشن همزمان را پشتیبانی می کند را هم در نظر داشته باشید.در مورد webtop و کلاً کانتینرهای linuxserver.io، کمی دستی تر است، سبک و ساده است و برای کار شخصی یا تیمی که خیلی گسترده نمی شود و خودتان بالای سر سرور هستید راه حل خوبی است.در هر صورت اگر نیاز به ارایه دسکتاپ/اپ روی مرورگر کاربر دارید این ۲ راه حل در دسترس و راحت و رایگان هستند و یکی را می توانید انتخاب بفرمایید.</description>
                <category>Hootan Alghaspour</category>
                <author>Hootan Alghaspour</author>
                <pubDate>Fri, 15 Sep 2023 16:54:18 +0330</pubDate>
            </item>
                    <item>
                <title>اتوماسیون مرورگر با Selenium + مثال دیجی کالا</title>
                <link>https://virgool.io/@h.alghaspour/%D8%A7%D8%AA%D9%88%D9%85%D8%A7%D8%B3%DB%8C%D9%88%D9%86-%D9%85%D8%B1%D9%88%D8%B1%DA%AF%D8%B1-%D8%A8%D8%A7-selenium-%D9%85%D8%AB%D8%A7%D9%84-%D8%AF%DB%8C%D8%AC%DB%8C-%DA%A9%D8%A7%D9%84%D8%A7-asc010nzpg5v</link>
                <description>سلنیوم (Selenium) یک مجموعه ابزار و کتابخانه برای خودکارسازی اموری که با مرورگرها انجام می شوند است و برای زبان های برنامه نویسی مختلف کتابخانه دارد و برای مرورگرهای مختلف هم درایور دارد تا با استفاده از آن یک مرورگر تحت کنترل سلنیوم باز شود و شروع به انجام مواردی که برنامه ریزی شده اند بکند. بنقل از سایت خودش :Primarily it is for automating web applications for testing purposes, but is certainly not limited to just that.برای تست های مختلف نرم افزاری و زیرساخت و اتوماسیون کار با مرورگر مثل ورود اطلاعات یا ثبت نام و موارد مشابه کاربرد دارد و ابزار تخصصی و محبوب این کار است.یعنی شما برنامه ریزی می فرمایید که فلان url را باز کن، در کد html دنبال فلان چیز بگرد، فلان چیز را تایپ کن، روی فلان المنت کلیک کن و هر کار دیگری که شما روی مرورگر انجام می دهید.مثل بسیاری دیگری از ماژول های پایتون کارکردن با آن ساده تر از چیزی است که در ابتدا بنظر می رسد.روی پایتون با یک pip install selenium براحتی نصب می شود، نصب کتابخانه سلنیوم برای محیط های مختلف اینجا توضیح داده شده است. باید درایور مربوط به مرورگر را هم نصب بفرمایید. در این بخش از مستندات نصب درایور به روش های مختلف و در زبان های مختلف توضیح داده شده است.در مورد مثال جمع زدن مجموع خرید از دیجی کالا :توجه بفرمایید که براساس &quot;شرایط و قوانین استفاده از سرویس‌ها و خدمات دیجی‌کالا&quot; کاربران مجاز به استفاده از ربات و داده کاوی و موارد مشابه نیستند.ما هم درحقیقت چنین کاری نمی کنیم، اکانت و اطلاعات هر کاربر متعلق به وی است و ما فقط در حال اتوماسیون امور مجاز مربوط به یک شخص کاربر هستیم.کد زیر :۰- نام کاربری و پسورد و تعداد کل سفارشات تحویل شده را می گیرد(بالای کد).۱- یک مرورگر کروم را تحت مدیریت و کنترل سلنیوم باز می کند.۲- آدرس /=https://www.digikala.com/users/login/?backUrl را که صفحه لاگین است در آن باز می کند.۳- نام کاربری و سپس رمز عبور را وارد می کند.۴- وارد صفحه =https://www.digikala.com/profile/orders/?activeTab=sent&amp;page که لیست سفارشات تحویل شده است می شود، مقدار page معادل شماره صفحه است که در یک حلقه صفحه به صفحه فراخوانی می شوند. مقدار page براساس تقسیم تعداد کل سفارشات به ۱۰ که تعداد در هر صفحه است و گرد کردن نتیجه آن به بالا تعیین می شود. مثلا ً برای ۱۰۴ سفارش می شود ۱۱ صفحه.۵- مبلغ سفارشات را در هر صفحه به یک لیست اضافه می کند و به سراغ صفحه بعد می رود. در اینجا اعداد فارسی مثل ۱ که درحقیقت کاراکتر هستند و عدد نیستند با تابع تعریف شده formatter به 1 که عدد است تبدیل می شوند که بتوان بسادگی جمع زد.۶- در پایان مجموع لیست را که حاوی مبالغ است جمع می زند و چاپ می کند.import time
import math
from selenium import webdriver
from selenium.webdriver.common.by import By

digiUser = &amp;quotusername&amp;quot # digikala mobile or username
digiPassword = &amp;quotpassword&amp;quot # digikala password
ordersCount = 104 # total number of orders
ordersPages = math.ceil(ordersCount/10) # number of orders pages to loop
purchaseAmounts=[] # container of all purchases prices

driver = webdriver.Chrome()
driver.maximize_window()
driver.get(&amp;quothttps://www.digikala.com/users/login/?backUrl=/&amp;quot) # digkala login page
time.sleep(5)

driver.find_element(by=By.NAME, value=&#039;username&#039;).send_keys(digiUser) # enter username
driver.find_element(by=By.TAG_NAME, value=&#039;form&#039;).submit()
time.sleep(5)

driver.find_element(by=By.NAME, value=&#039;password&#039;).send_keys(digiPassword) # enter password
driver.find_element(by=By.TAG_NAME, value=&#039;form&#039;).submit()
time.sleep(5)

def formatter(x):
    x = x.replace(&amp;quot,&amp;quot,&amp;quot&amp;quot)
    x = x.replace(&amp;quot۱&amp;quot,&amp;quot1&amp;quot)
    x = x.replace(&amp;quot۲&amp;quot,&amp;quot2&amp;quot)
    x = x.replace(&amp;quot۳&amp;quot,&amp;quot3&amp;quot)
    x = x.replace(&amp;quot۴&amp;quot,&amp;quot4&amp;quot)
    x = x.replace(&amp;quot۵&amp;quot,&amp;quot5&amp;quot)
    x = x.replace(&amp;quot۶&amp;quot,&amp;quot6&amp;quot)
    x = x.replace(&amp;quot۷&amp;quot,&amp;quot7&amp;quot)
    x = x.replace(&amp;quot۸&amp;quot,&amp;quot8&amp;quot)
    x = x.replace(&amp;quot۹&amp;quot,&amp;quot9&amp;quot)
    x = x.replace(&amp;quot۰&amp;quot,&amp;quot0&amp;quot)

    return int(x)

for i in range(1, ordersPages+1):
    url = &amp;quothttps://www.digikala.com/profile/orders/?activeTab=sent&amp;page=&amp;quot+str(i) # go through orders pagination
    driver.get(url)
    time.sleep(10)

    p = driver.find_elements(by=By.CSS_SELECTOR, value=&#039;div.text-body1-strong div.color-800 div.text-body1-strong&#039;)

    for pp in p:
        purchaseAmounts.append(formatter(pp.get_attribute(&amp;quotinnerHTML&amp;quot))) # text inside = purchase amount

print(&amp;quot============================&amp;quot)
print(&amp;quotTotal Purchases Count : &amp;quot+ str(len(purchaseAmounts)) + &amp;quot From Total &amp;quot + str(ordersCount) + &amp;quot orders&amp;quot)
print(&amp;quotTotal Purchases Amount : &amp;quot+f&amp;quot{sum(purchaseAmounts):,}&amp;quot+ &amp;quot (Toman)&amp;quot)
print(&amp;quot============================&amp;quot)نکات و توضیح کد :در خطوط ۱ تا ۴ ماژول های مورد نیاز import شده اند.در خطوط ۶ تا ۱۰ متغییر های مورد نیاز تعریف و مقداردهی شده اند.خطوط ۱۲ تا ۱۵ ، یک مرورگر کروم را تحت کنترل سلنیوم باز می کند، آن را maximize می کند، صفحه لاگین دیجی کالا را باز می کند و ۵ ثانیه اجرای کند متوقف می شود.خطوط ۱۶ تا ۱۸ در صفحه لاگین باز شده براساس نام المنت داخل username نام کاربری را وارد کرده و form را submit می کند و ۵ ثانیه اجرای کند متوقف می شود.خطوط ۱۹ تا ۲۱ در صفحه جدید باز شده password را مثل همان نام کاربری پیدا کرده وارد می کند و فرم را submit می کند و ۵ ثانیه اجرای کند متوقف می شود.خطوط ۲۳ تا ۳۶ تابع formatter است که , را حذف کرده و اعداد فارسی را به اعداد integer تبدیل می کند.تابع for در خط ۳۸ باعث می شود صفحات لیست سفارشات تحویل شده صفحه به صفحه باز شده و دستورات داخل آن برای آن ها اجرا شود. دقت بفرمایید حلقه در تابع range پایتون از مقدار شروع تا یکی کمتر مقدار پایان اجرا می شود بنابراین مقدار ordersPages بعلاوه یک شده است.خطوط ۳۹ تا ۴۱ هر صفحه سفارشات تحویل شده را باز می کند و ۱۰ثانیه اجرای کد روی آن متوقف می شود تا اطمینان پیدا کنیم صفحه کامل باز شده است.خط ۴۲ براساس CSS_SELECTOR و بوسیله find_elements همه مبلغ ها را پیدا می کند. مبلغ در صفحه سفارشات تحویل شده همیشه در درون یک div با کلاس text-body1-strong که خودش درون یک div  با کلاس color-800 است و خود آن درون یک div با کلاس text-body1-strong است قرار دارد و این ترتیب همیشه ثابت است. روش های مختلف پیدا کردن element را اینجا ببینید.در خطوط ۴۴ و ۴۵ مبالغ استخراج شده از هر صفحه پس از عبور از تابع formatter به لیست purchaseAmounts اضافه می شوند.خطوط ۴۷ تا ۵۰ هم مربوط به پرینت تعداد سفارشات و مجموع هستند. استفاده از f&quot;{sum(purchaseAmounts):,} بخاطر این است که عدد مجموع را فرمت کند و سه تا سه تا بین اعداد ، بیفتد.این کد کامل و مطلوب نیست و برای سادگی خلاصه شده است. در امور اتوماسیون و خودکارسازی مدیریت خطاها و exception handling بسیار مهم است. مثلاً در همین سناریو گاهی صفحه ای از سفارشات دیجی کالا خطای ارتباط می دهد یا باز نمی شود و کد پس از اتمام حلقه و پیدا نکردن قیمت بدون خطا به صفحه بعد می رود، بنابراین با توجه به ترافیک و شلوغی دیجی کالا ممکن است از هر ۱۰ بار که اجرا می کنید ۳-۴ بار همه صفحات و قیمت ها را جمع نزده باشد. من یک مکانیزم بسیار ساده برای این مشکل در این سناریو پیاده سازی کرده ام، کل سفارشات تحویل شده من ۱۰۴ مورد است بنابراین در لیست purchaseAmounts باید ۱۰۴ آیتم وجود داشته باشد و بالای مجموع حساب شده تعداد قیمت های منظور شده درج می شود تا چک کنیم حتماً همه صفحات باز شده و همه قیمت ها منظور شده اند.گاهی اوقات که پشت vpnهستم درایور گیر می کند و ۲-۳ دقیقه طول می کشد تا مرورگر باز شود، در بعضی از روش های نصب باید آدرس مسیر درایور را هم در کد و در هنگام فراخوانی بدهید.آخر کد باید ()driver.quit می زدم که خارج شود و مرورگر بسته شود که در کد بالا نیست.مجموعه سلنیوم یک WebDriver دارد که برای اجرای برنامه نویسی اتوماسیون از آن استفاده می کنیم، مثل مثال بالا، یک IDE (Integrated Development Environment) دارد که یک ابزار برای توسعه test cases است و می تواند مثلاً اعمال شما را record کند که بعد همان را کد کنید و ... ، و یک Selenium Grid دارد که اجازه می دهد روی ماشین های مختلف و محیط های مختلف test case خودتان را اجرا کنید. اطلاعات بیشتر را اینجا مطالعه بفرمایید.</description>
                <category>Hootan Alghaspour</category>
                <author>Hootan Alghaspour</author>
                <pubDate>Sat, 10 Dec 2022 01:08:55 +0330</pubDate>
            </item>
                    <item>
                <title>اتصال و کنترل آردوینو &lt;-&gt; کامپیوتر از طریق پورت سریال و پایتون</title>
                <link>https://virgool.io/@h.alghaspour/%D8%A7%D8%AA%D8%B5%D8%A7%D9%84-%D9%88-%DA%A9%D9%86%D8%AA%D8%B1%D9%84-%D8%A2%D8%B1%D8%AF%D9%88%DB%8C%D9%86%D9%88-%DA%A9%D8%A7%D9%85%D9%BE%DB%8C%D9%88%D8%AA%D8%B1-%D8%A7%D8%B2-%D8%B7%D8%B1%DB%8C%D9%82-%D9%BE%D9%88%D8%B1%D8%AA-%D8%B3%D8%B1%DB%8C%D8%A7%D9%84-%D9%88-%D9%BE%D8%A7%DB%8C%D8%AA%D9%88%D9%86-zojoydxwhuid</link>
                <description>موضوع ما اینجا هر مدل میکروکنترلر یا SoC (System on Chip) است که از طریق پورت سریال به کامپیوتر وصل می شود و ما هم میخواهیم بتوانیم از طریق برنامه نویسی با پایتون با این سریال ارتباط برقرار کنیم و بخوانیم و بنویسیم و برنامه ریزی و کنترل دوطرفه ای از طرف کامپیوتر به SoC یا برعکس داشته باشیم. در این مقاله مثلاً آردوینو UNO و یک دسکتاپ لینوکس یا ویندوز.در سناریوی اول من می خواهم یک عملگر مثل LED را که به میکروکنترلر وصل است با دستور ارسال شده از روی پورت سریال کامپیوتر کنترل کنم ،در سناریوی دوم مقادیر دما و رطوبت را از طریق پورت سریال از روی یک آردوینو اونو بخوانم و در یک فایل CSV ذخیره کنم و در سناریوی سوم می خواهم ماوس و کیبورد روی کامپیوتر را از طریق joystick  یا keypad یا ماژول ورودی دیگری که به آردوینو وصل است کنترل کنم.خواندن و نوشتن روی پورت سریال با پایتونراحت ترین راه برای این موارد در پایتون استفاده از ماژول PySerial برای خواندن و نوشتن روی پورت سریال است. خیلی مختصر و راحت ابتدا با pip install pyserial ماژول pyserial را نصب می کنید و سپس با تکه کدهای ذیل می توانید روی کامپیوتر با سریال آردوینو (یا هر چیز دیگری) ارتباط داشته باشید.نکات : دو برنامه همزمان نمی توانند یک پورت سریال را در اختیار داشته باشند، یعنی اگر روی Arduino IDE مانیتور پورت سریال را باز کنید، در هنگام اجرای کد پایتون خطای  Device or resource busy می گیرید.پورت سریال اتصال آردوینو اونو من به کامپیوتر (دسکتاپ فدورا) dev/ttyACM0/ است، روی ویندوز COM خواهد بود که از روی Device Manager می توانید پورت اتصال سریال را پیدا کنید.کد پایتون :import serial
import time

arduino = serial.Serial(port=&#039;/dev/ttyACM0&#039;, baudrate=115200, timeout=.1)

def write_read(x):
    arduino.write(bytes(x, &#039;utf-8&#039;))
    time.sleep(0.05)
    data = arduino.readline()
    return data

while True:
    num = input(&amp;quotEnter Something : &amp;quot) 
    value = write_read(num)
    print(value.decode())کد آردوینو :void setup() {
Serial.begin(115200);
Serial.setTimeout(1);
}
void loop() {
while (!Serial.available());
String x = Serial.readString();
Serial.print(&amp;quotArduino says I got : &amp;quot);
Serial.print(x);
}در کد بالا که در حقیقت آردوینو مقادیر دریافتی از سریال بوسیله اسکریپت پایتون را بازتاب می کند در کد پایتون ابتدا یک آبجکت بنام arduino از نوع سریال ساخته ایم و مشخصات ارتباط سریال مثل پورت و baudrate را برای سریال تعریف کرده ایم. از این پس از طریق آبجکت arduino به پورت سریال دسترسی داریم.سپس یک تابع write_read تعریف کرده ایم که یک ورودی می گیرد و آن را روی سریال write می کند و ۵صدم ثانیه صبر کرده و یک خط از روی سریال را می خواند (پاسخ آردوینو روی پورت سریال) و return می کند.بعد در یک حلقه while همیشه صحیح که مدام اجرا می شود، ورودی را از کاربر گرفته و به تابع write_read می دهیم و مقدار return شده write_read را print می کنیم. چون مقدار از نوع bytes است از متد ()decode برای نوشته رشته خالص بدون &#x27;b استفاده می کنیم.در کد آردوینو ، در setup سریال را تعریف کرده و در loop آن را می خوانیم (readString) و به متغییر x می دهیم و سپس همان را روی سریال دوباره print می کنیم.الان خواندن و نوشتن روی سریال در پایتون و آردوینو انجام شده و براحتی می توانیم هر طرف را برنامه ریزی کنیم که با دریافت پیام مشخص چه عملیاتی را انجام دهد.سناریوی اول : کنترل عملگر (LED) متصل به میکروکنترلر از روی پورت سریالیک مثال بسیار ساده کنترل built-in LED روی آردوینو است که بسادگی می توان به موارد پیشرفته تری بسط داد.در این مثال در صورت وارد کردن مقدار on در سریال توسط پایتون built-in LED آردوینو روشن و درصورت وارد کردن دستور off خاموش می شود.با توجه به اینکه در اینجا پایتون فقط در نقش خواندن و نوشتن روی سریال است کد پایتون در این مثال همان کد بالاست و کد آردوینو به اینصورت تغییر کرده است :void setup() {
  Serial.begin(115200);
  Serial.setTimeout(1);
  pinMode(LED_BUILTIN, OUTPUT);
}

void loop() {
  while (!Serial.available());
  String x = Serial.readString();
  if(x==&amp;quoton&amp;quot){
    digitalWrite(LED_BUILTIN, HIGH);
    Serial.print(&amp;quotBuiltin LED is now ON&amp;quot);
  }else if(x==&amp;quotoff&amp;quot){
    digitalWrite(LED_BUILTIN, LOW);
    Serial.print(&amp;quotBuiltin LED is now OFF&amp;quot);
  }else{
    Serial.print(&amp;quotUnknown Command!&amp;quot);
  }
}سناریوی دوم : خواندن مقادیر سنسور دما و رطوبت و ذخیره در فایل CSVمن یک سنسور دما و رطوبت هوای AHT10 را به آردوینو متصل کرده ام و می خواهم این مقادیر را از طریق پورت سریال دریافت کنم و با درج زمان در یک فایل CSV ذخیره نمایم. بدیهی است براساس مقادیر دریافتی میتوانید هر عملیات دیگری را در اسکریپت پایتون برنامه نویسی بفرمایید.اجازه بدهید کدها را مرور کنیم :کد آردوینو :#include &lt;Adafruit_AHTX0.h&gt;
Adafruit_AHTX0 aht;
void setup() {
Serial.begin(115200);
if (! aht.begin()) {
Serial.println(&amp;quotCould not find AHT? Check wiring&amp;quot);
while (1) delay(10);
}
}

void loop() {
sensors_event_t humidity, temp;
aht.getEvent(&amp;humidity, &amp;temp);
String thData = String(temp.temperature)+&amp;quot,&amp;quot+String(humidity.relative_humidity);
Serial.println(thData);
delay(1000);
}این کد هر یک ثانیه مقدار دریافتی از سنسور دما و رطوبت را با فرمت temp,humidity روی سریال چاپ می کند.کد پایتون :این کد مقادیر دریافتی از سریال را فرمت کرده و با اضافه کردن ستون زمان روی یک فایل CSV می نویسد.import serial
import csv
from datetime import datetime

arduino = serial.Serial(port=&#039;/dev/ttyACM0&#039;, baudrate=115200, timeout=.1)
now = datetime.now()
f = open(&#039;temp-humidity-record.csv&#039;, &#039;a&#039;)
writer = csv.writer(f)

while True:
    thData = arduino.readline().decode()
    if thData != &amp;quot&amp;quot:
        current_date_and_time = datetime.now()
        thData = thData.split(&amp;quot,&amp;quot)
        row = [current_date_and_time.isoformat(),thData[0].strip(),thData[1].strip()]
        writer.writerow(row)
        print(current_date_and_time,&amp;quot : Temp:&amp;quot,thData[0],&amp;quot Humidity:&amp;quot,thData[1])

f.close()سناریوی سوم : کنترل ماوس و کیبورد با جوی استیک و keypadرابطه انسان با کامپیوتر براساس یکسری ورودی از طرف انسان به سمت کامپیوتر مثل کیبورد و ماوس و ... و یکسری خروجی از طرف کامپیوتر به سمت انسان مثل مانیتور و پرینتر و ... برقرار می شود.در سناریوی سوم ما می خواهیم بتوانیم با ماژول هایی مثل keypad یا joystick یا مقاومت متغییر یا ... ورودی جدید به کامپیوتر تعریف کنیم. یک راه روش نوشتن درایور و ... است که سخت می شود و موضوع بحث ما در این مقاله نیست. یک راه اتصال سریال با یک میکروکنترلر و map کردن این ماژول های ورودی به همان کیبورد و ماوس و GUI کامپیوتر است که به مراتب ساده تر پیاده سازی می شود.بنابراین ما این ماژول ها را به آردوینو (یا مورد مشابه) وصل می کنیم و روی پورت سریال مقدار آن ها را به یک اسکریپت پایتون می دهیم تا اسکریپت پایتون این مقادیر را به دستورات کیبورد و ماوس کامپیوتر تبدیل کند.ماژول pyAutoGUI در پایتون می تواند بسادگی انواع برنامه ریزی ها و اتوماسیون ماوس و کیبورد را انجام دهد و مثل اینکه یک نفر پشت کامپیوتر نشسته باشد ماوس و کیبورد براساس دستورات برنامه ریزی شده کار می کنند. یعنی شما دستور می دهید ماوس به مختصات فلان در اسکرین برود و مثلاً راست کلیک کند یا کیبورد چیزی را تایپ کند و ... .من اینجا از یک مدل جوی استیک آنالوگ و یک مدل کلید ۵ جهته دیجیتال استفاده کرده ام. اتصال بقیه ماژول های مشابه هم همین کانسپت را دنبال می کند.ابتدا توضیح و نمونه کد مختصری درباره joystick آنالوگ و مدل دیجیتال که در حقیقت یک keypad ۵ جهته است می دهم و سپس درباره pyAutoGUI کمی توضیح خواهم داد و درنهایت روش و کد پیاده سازی را خدمتان عرض خواهم کرد.دست راست = جوی استیک آنالوگ | دست چپ keypad پنج محوره (جوی استیک دیجیتال)جوی استیک آنالوگجوی استیک آنالوگ در حقیقت ۲ پتانسیومتر در محور x و y است که با جابجایی دسته مقدار در هر محور ولتاژ بازگشتی تغییر می کند و روی Arduino که ADC ده بیتی با دارد با analogRead مقداری بین ۰ تا ۱۰۲۳ را بر می گرداند. وسط معمولاً مقدار حدود ۵۱۲ دارد.با فشار دادن دسته هم یک کلید فشاری فعال می شود که در صورت فشرده شدن ۰ و در صورت آزاد بودن ۱ برمیگرداند.پایه GND برای اتصال منفی.پایه Vcc برای تغذیه 5 ولت مدار پایه VRx خروجی آنالوگ محور X (حرکت چپ و راست)پایه VRy خروجی آنالوگ محور Y (حرکت بالا و پایین)پایه SW بعنوان کلید فشاری کلید که بصورت pull-up داخلی قرار گرفته است، در حالت عادی ۱ و با فشردن شدن صفر بر می گرداند.جوی استیک دیجیتال (keypad پنج محوره)این مدل در حقیقت ۴ کلید فشاری در چهار طرف و یکی وسط است. معمولاً دو دکمه reset و set هم روی این ماژول ها هست و شما هفت کلید فشاری دارید که در کد بصورت INPUT_PULLUP تعریف می شوند و با فشرده شدن هر کدام آن پایه با پایه COM مشترک می شود و مقدار صفر یا LOW برمی گرداند.همان keypad است، دقت بفرمایید در مدل جوی استیک آنالوگ شما مقدار گرایش دسته در محور x و y را می توانید دریافت کنید اما در این مدل فقط به شما فشرده شدن یک جهت را بصورت دیجیتال می دهد و مقدار بازگشتی صفر یا یک است. معرفی pyAutoGUIماژول pyautogui در پایتون به شما اجازه می دهد اعمال ماوس و کیبورد را کنترل کنید. یعنی برای عملکرد ماوس و کیبورد برنامه ریزی بفرمایید. هم براساس مختصات روی صفحه می توان دستور داد هم براساس موقعیت نسبی پوینتر ماوس.توصیه می کنم چند دقیقه وقت بگذارید و یکی از توتوریال های یوتیوب درباره pyAutoGUI را ببینید. راهنمای نصب روی سیستم های مختلف و توضیحات در مستنداتش هست، برای نصب آن روی لینوکس پس از نصب پایتون روی سیستم :#dnf install scrot
#dnf install python3-tk
#dnf install python3-dev
#pip install --user pyautoguiبرای اتوماسیون GUI ، تست نرم افزار ، اتوماسیون ورود اطلاعات ، کنترل نرم افزاری ماوس و کیبورد و ... بسیار استفاده می شود. ما درسناریوی اینجا مقادیر جوی استیک را از پورت سریال خوانده و از pyautogui برای اجرای آن ها روی ماوس استفاده می کنیم.لیستی از پرکاربردترین متدها و آرگومان های آن ها را در زیر ذکر کرده ام که می توانید امتحان بفرمایید و گویا هم هستند. کار کردن با pyautogui بسیار ساده است. مستندات خودش هم خوب هستند و ویدیوهای آموزشی متعددی هم درباره آن در یوتیوب هست.import pyautogui

######## Get Info ########
#pyautogui.mouseInfo()
#pyautogui.size()
#pyautogui.position()
#pyautogui.displayMousePosition()

######## Mouse Actions ########
#pyautogui.moveTo(x, y, duration)
#pyautogui.moveRel(x related to current position, y related to current position, duration)
#pyautogui.click(x=100, y=200)  # move to 100, 200, then click the left mouse button.
#pyautogui.click(x, y, clicks, interval, button, duration)
#pyautogui.click(button=&#039;right&#039;, clicks=3, interval=0.25)  # triple-click the right mouse button with a quarter second pause in between clicks
#pyautogui.leftClick()
#pyautogui.rightClick()
#pyautogui.doubleClick()
#pyautogui.tripleClick()
#pyautogui.middleClick()
#pyautogui.scroll(-100)
#pyautogui.mouseDown(x, y, button)
#pyautogui.mouseUp()
#pyautogui.dragTo(x, y, duration, button=&amp;quotleft&amp;quot)
#pyautogui.dragRel(x related to current position, y related to current position, duration, button=&amp;quotleft&amp;quot)

######## Keyboard Actions ########
#pyautogui.write(&amp;quotMessage&amp;quot, Interval)
#pyautogui.typewrite(&amp;quotMessage&amp;quot, Interval)
#pyautogui.press(&#039;left&#039;)     # press the left arrow key
#pyautogui.press(&#039;left&#039;, presses=3)
#pyautogui.press([&#039;left&#039;, &#039;left&#039;, &#039;left&#039;])
#pyautogui.keyDown(&#039;shift&#039;)  # hold down the shift key
#pyautogui.keyUp(&#039;shift&#039;)    # release the shift key
#pyautogui.hotkey(&#039;ctrl&#039;, &#039;shift&#039;, &#039;esc&#039;)
# with pyautogui.hold(&#039;shift&#039;):
#        pyautogui.press([&#039;left&#039;, &#039;left&#039;, &#039;left&#039;])
#
#…is equivalent to this code:
#
#pyautogui.keyDown(&#039;shift&#039;)  # hold down the shift key
#pyautogui.press(&#039;left&#039;)     # press the left arrow key
#pyautogui.press(&#039;left&#039;)     # press the left arrow key
#pyautogui.press(&#039;left&#039;)     # press the left arrow key
#pyautogui.keyUp(&#039;shift&#039;)    # release the shift key
#print(pyautogui.KEYBOARD_KEYS)

######## Message Box Actions ########
#pyautogui.alert&#40;text=&#039;&#039;, title=&#039;&#039;, button=&#039;OK&#039;&#41;
#pyautogui.confirm&#40;text=&#039;&#039;, title=&#039;&#039;, buttons=[&#039;OK&#039;, &#039;Cancel&#039;]&#41;
#messagePrompt = pyautogui.prompt&#40;text=&#039;&#039;, title=&#039;&#039; , default=&#039;&#039;&#41;
#print(messagePrompt)
#messagePassword = pyautogui.password(text=&#039;&#039;, title=&#039;&#039;, default=&#039;&#039;, mask=&#039;*&#039;)
#print(messagePassword)برخی نکات تکمیلی درباره pyautoguiبرای خارج شدن از اجرا و جلوگیری از ادامه اجرای کد یک مکانیزم FailSafeException دارد که بصورت پیش فرض True است. در این حالت اگر در زمان اجرای برنامه پوینتر ماوس را به منتهی الیه چپ و بالای صفحه ببرید اسکریپت متوقف می شود. pyautogui.FailSafeException = True|Falseبرای وقفه بین action ها می توانید همان بالای برنامه مقدار PAUSE را تعیین بفرمایید. مثلاًpyautogui.PAUSE = seconds between two actionsبرای اینکه مختصات مشخص ندهید و خودش مختصات چیزی را روی صفحه پیدا کند، قابلیتی بعنوان locate براساس تصویر نمونه در pyautogui وجود دارد که یک تصویر را به آن می دهید و موقعیت آن را روی صفحه پیدا می کند. مثلاً من می خواهم روی آیتم File از پنجره KCalc کلیک شود، بجای اینکه مختصات آن را بدهم می توانم یک اسکرین شات بگیرم و تکه مربوطه به File را در منو ببرم و به pyautogui بدهم تا مختصات آن تصویر در صفحه را پیدا کند.تصویر کل پنجرهتصویر بریده شده آیتم منوی Fileاگر تصویر بریده شده آیتم منوی File را با نام menu_file.png ذخیره کرده باشم این تکه کد مختصات آن را روی صفحه پیدا می کند و روی آن کلیک خواهد کرد.با اجرای ()pyautogui.mouseInfo پنجره mouse info که امکان لاگ کردن هم دارد باز می شود و می توان برای برنامه ریزی اعمال ماوس و تعیین مختصات از آن استفاده کرد.اتصال جوی استیک دیجیتال به GUI کامپیوتر با pyAutoGUIواقعیتش من کارم با نسخه جوی استیک دیجیتال (کلید پنج محوره) راه افتاد و دیگه کد پایتون مدل آنالوگ را ننوشته ام. با مثال ها و توضیحات بالا نباید نوشتن کد آن مشکلی داشته باشد.نمونه کد اتصال جوی استیک مدل دیجیتال (کلید ۵ محوره) به GUI و ماوس در زیر ذکر شده و در ادامه نکات و توضیحات آن را عرض خواهم کرد. در این مثال می توان با جوی استیک ماوس را کنترل کرد و دکمه SET نقش راست کلیک و دکمه RST نقش کلیک چپ و دکمه وسط نقش کلیک وسط را بازی خواهند کرد.کد آردوینو :بسته به پیام جوی استیک فقط up,down,left,right,reset,set را در سریال چاپ می کند.const int buttonPin_RST = 2;     // reset button pin
const int buttonPin_SET = 3;     // set button pin
const int buttonPin_MID = 4;     // middle button pin
const int buttonPin_RHT = 5;     // right button pin
const int buttonPin_LFT = 6;     // left button pin
const int buttonPin_DWN = 7;     // down button pin
const int buttonPin_UP = 8;      // up button pin

void setup() {
Serial.begin(115200);
pinMode(buttonPin_RST, INPUT_PULLUP);
pinMode(buttonPin_SET, INPUT_PULLUP);
pinMode(buttonPin_MID, INPUT_PULLUP);
pinMode(buttonPin_RHT, INPUT_PULLUP);
pinMode(buttonPin_LFT, INPUT_PULLUP);
pinMode(buttonPin_DWN, INPUT_PULLUP);
pinMode(buttonPin_UP, INPUT_PULLUP);
}

void loop() {

if(digitalRead(buttonPin_RST) == LOW) {
delay(250);
Serial.println(&amp;quotReset&amp;quot);
}

if(digitalRead(buttonPin_SET) == LOW) {
delay(250);
Serial.println(&amp;quotSet&amp;quot);
}

if(digitalRead(buttonPin_MID) == LOW) {
delay(250);
Serial.println(&amp;quotMiddle&amp;quot);
}

if(digitalRead(buttonPin_RHT) == LOW) {
delay(250);
Serial.println(&amp;quotRight&amp;quot);
}

if(digitalRead(buttonPin_LFT) == LOW) {
delay(250);
Serial.println(&amp;quotLeft&amp;quot);
}

if(digitalRead(buttonPin_DWN) == LOW) {
delay(250);
Serial.println(&amp;quotDown&amp;quot);
}

if(digitalRead(buttonPin_UP) == LOW) {
delay(250);
Serial.println(&amp;quotUp&amp;quot);
}
}کد پایتون :import pyautogui
import serial

arduino = serial.Serial(port=&#039;/dev/ttyACM0&#039;, baudrate=115200, timeout=.1)

while True:
    mouseta = arduino.readline().decode().strip()
    mousePosition = pyautogui.position()
    if mouseta != &amp;quot&amp;quot:
        match mouseta:
            case &amp;quotUp&amp;quot:
                pyautogui.moveTo(mousePosition[0], mousePosition[1]-10)
            case &amp;quotDown&amp;quot:
                pyautogui.moveTo(mousePosition[0], mousePosition[1]+10)
            case &amp;quotRight&amp;quot:
                pyautogui.moveTo(mousePosition[0]+10, mousePosition[1])
            case &amp;quotLeft&amp;quot:
                pyautogui.moveTo(mousePosition[0]-10, mousePosition[1])
            case &amp;quotMiddle&amp;quot:
                pyautogui.middleClick()
            case &amp;quotSet&amp;quot:
                pyautogui.leftClick()
            case &amp;quotReset&amp;quot:
                pyautogui.rightClick()

        print(mouseta) # Answer from arduinoبا کدهای آردوینو و پایتون بالا می توانید ماوس روی صفحه را بوسیله یک جوی استیک دیجیتال (کلید ۵ محوره) کنترل بفرمایید. مقدار +10 و -10 در حقیقت گام حرکت ماوس است.</description>
                <category>Hootan Alghaspour</category>
                <author>Hootan Alghaspour</author>
                <pubDate>Tue, 06 Dec 2022 14:34:32 +0330</pubDate>
            </item>
                    <item>
                <title>راهنمای کنترل با ریموت های رادیویی (433 و 315 MHz)</title>
                <link>https://virgool.io/@h.alghaspour/%D8%B1%D8%A7%D9%87%D9%86%D9%85%D8%A7%DB%8C-%DA%A9%D9%86%D8%AA%D8%B1%D9%84-%D8%A8%D8%A7-%D8%B1%DB%8C%D9%85%D9%88%D8%AA-%D9%87%D8%A7%DB%8C-%D8%B1%D8%A7%D8%AF%DB%8C%D9%88%DB%8C%DB%8C-433-%D9%88-315-mhz-h5q6hprgnk27</link>
                <description>فرکانس های متداول در ریموت های رادیویی موجود در بازار ایران ۳۱۵مگاهرتز و ۴۳۳مگاهرتز هستند.فرکانس ۴۳۳.۹۲مگاهرتز برای ریموت ها در ایران و اروپا متداول تر است. این فرکانس جزو ISM Band بوده و استفاده از آن بدلیل license-free frequency بودن آزاد است. استفاده از تجهیزات این فرکانس بسیار ارزان تمام می شود، برد قابل قبولی دارد و مصرف برق بسیار کمی هم دارند که باعث می شود مدتها با یک باتری ساده کار کنند.ماژول های مخابراتی دیگری مثل GT-38 و LoRa هم می توانند روی فرکانس ۴۳۳مگاهرتز کار کند اما موضوع ما اینجا ماژول های ساده ارسال و دریافت روی فرکانس رادیویی ۴۳۳مگاهرتز است که عموماً برای ریموت ها استفاده می شوند.در این فرکانس انواع ریموت ها در بازار وجود دارند که در ایران به ریموت های بلوتوثی معروف هستند، البته نه فرکانس نه پروتکل ربطی به بلوتوث ندارد و فقط چراغ اکثرشان آبی است! در دنیا به اینها بیشتر duplicator remote می گویند.نمونه هایی از ریموت های ۴۳۳مگاهرتز موجود در بازار ایرانبراحتی کپی می شوند و معمولاً نحوه خام کردن آن ها گرفتن همزمان A و B و چندبار زدن B بعد از چشمک زن شدن چراغ است. بسته به مدل گاهی البته ممکن است نیازی به چند بار زدن B برای خام کردن نباشد یا مثلاً بجای B باید C را چندبار زد. نشانه خام شدن این است که بعد از خام شدن با نگه داشتن کلید، چراغ روی ریموت روشن نمی ماند و ممکن است فقط یک چشمک بزند.بعد از خام کردن ، ریموت خام شده را باید روبروی ریموت اصلی گرفت و کلیدهای مدنظر را روی هر دو ریموت همزمان نگه داشت تا ریموت خام شده کد را بگیرد و چند چشمک بزند و چراغ آن با نگه داشتن دکمه روشن بماند.موضوع ما اینجا البته (فقط) کپی ریموت نیست، ما می خواهیم بتوانیم روی این ماژول وایرلس انتقال پیام و دستور انجام دهیم مثلاً مقادیر یک سنسور را دریافت و دستورات عملگر را ارسال کنیم یا در استفاده بعنوان ریموت، کد منتقل شده روی این فرکانس توسط دکمه های مختلف ریموت را بگیریم و دیکود کنیم و ذخیره کنیم و مجدداً بتوانیم ارسال کنیم و همزمان براساس دکمه فشار داده شده و کد ارسال شده در فرستنده و گیرنده بتوانیم از طریق تعدادی GPIO یا ارتباط سریال یا شبکه و ... چیزهای دیگری (مثل رله) را کنترل کنیم یا به یک سیستم نرم افزاری وصل کنیم یا هر کار دیگری که می توان بوسیله میکروکنترلر و بردهای توسعه متداول در بازار انجام داد.هر کدام از دکمه های روی این مدل ریموت ها قابلیت ذخیره و ارسال یک پیام (کد) ثابت را دارند و بنابراین رمزنگاری در کار نیست. هر کس میتواند کد را دریافت و ذخیره و ارسال کند. امنیتی ندارند.من اینجا از یک جفت ماژول گیرنده فرستنده ۴۳۳مگاهرتز که با قیمت حدود ۳۰هزار تومان در بازار به وفور هست استفاده کرده ام. عین همین مطالب برای ۳۱۵مگاهرتزی ها هم صادق است ، فقط باید فرستنده و گیرنده ۳۱۵ بجای ۴۳۳ استفاده بفرمایید.مدولاسیون این ماژول ها Amplitude-shift keying (ASK) از نوع on-off keying (OOK) و بسیار ساده است، هیچ مدل رمزنگاری و اعتبارسنجی و CSMA/CA و ... در کار نیست.برای برد کنترل من از Wemos D1 R1 استفاده کرده ام زیرا یک esp8266 روی یک برد فیزیکی مشابه آردوینو اونو است که با قیمت حدود ۱۰۰هزار تومان ، براحتی بصورت Access Point یا Station می تواند با WiFi کار کند و IP بگیرد و از طریق http دیتا بگیرد و ارایه دهد و در این زمان شاید بصرفه ترین برد توسعه ای که در بازار باشد همین است، البته یک آنالوگ بیشتر ندارد.برای کتابخانه نرم افزاری ، کتابخانه RadioHead یکی از کاملترین کتابخانه ها برای کار با انواع RF است، اما چون موضوع ما اینجا ریموت و سوییچ است من از کتابخانه RC Switch که مخصوص همین کار است و کار را خیلی راحت می کند استفاده کردم.همچنین یک ماژول مموری ریدر میکرو SD - Micro SD/TF هم برای ذخیره کدهای دریافتی استفاده شده که البته الزامی نیست اما در کد برنامه نویسی پیوست شده برای ذخیره و بازیابی داده ها (کدهای ریموت) از این روش استفاده شده است.نمای کلی سخت افزارهای استفاده شده بشکل ذیل است :نحوه اتصال اجزا و pinoutها هم مشخص هستند و پین اتصال receiver و sd و transmitter روی کد مشخص است، پین سیگنال گیرنده به D4 و فرستنده به D2 متصل شده اند، ۵ولت و gnd هم از برد گرفته شده است.پین های ماژول کارت SD هم به متناظر روی برد گیرنده وصل شده اند.نمونه کد 433MHz Web Controller :یک کد نمونه ساده را برای ارسال کننده و دریافت کننده از روی اینترفیس وب اینجا گذاشته ام :https://github.com/alghaspour/433MHz-Web-Controllerهر دو کد بسیار ساده و کوتاه هستند.مجدداً یادآوری می کنم شما روی receiver و transmitter یکسری GPIO به همراه اتصال وای فای و پورت های سریال دارید که هر کدام را می توانید با تغییرات در کد به چیز دیگری وصل بفرمایید که با ارسال و دریافت کدهای مختلف ریموت کارهای دیگری روی سیستم های دیگر انجام شود.ممکن است pinout ماژول شما متفاوت باشد، قبل از اتصال vccوgnd روی برد چک بفرمایید.نکات کد 433MHz Web Controller :- آدرس ip دریافت کننده (receiver) را که بزنید صفحه کنترل وب برای شما باز می شود و می توانید ip ارسال کننده و کد ارسالی را نیز روی همین صفحه تعیین و ارسال بفرمایید. - برای ارسال دستور از روی اینترفیس میزبانی شده روی گیرنده به وب سرور فرستنده از ajax و XMLHttpRequest و جاوااسکریپت استفاده شده است (خط ۱۰ فایل html.html تابع run). روی ارسال کننده (transmitter) هدر Access-Control-Allow-Origin: * برای وب سرور تنظیم شده است که بتوان از آدرس های دیگر به آن دسترسی داشت و مشکل CORS Policy روی مرورگر پیش نیاید.- در کد فرستنده (Transmitter.ino) برای تفسیر دستور ارسال شده از روی وب از پردازش رشته استفاده شده ( خطوط ۷۲ تا ۸۱)و بدلیل اینکه تابع send آرایه کاراکتر می گیرد string با تابع toCharArray به آرایه کاراکتر  تبدیل شده است.- در خط ۷۸ کد فرستنده (Transmitter.ino) پارامتر دوم تابع setProtocol، مقدار Pulse Length است.- پایه گرفتن کد از گیرنده باید interrupt pin باشد.روی wemos d1 r1 پین D4 برای دریافت تست شده است.https://github.com/sui77/rc-switch/blob/master/RCSwitch.hhttps://github.com/sui77/rc-switch/blob/master/RCSwitch.cpp- من برای سادگی کلاً از ESP8266WiFi برای اتصال به WiFi و ایجاد web server استفاده کرده ام، برای استفاده های پیچیده تر توصیه می شود از کتابخانه های ESP8266WebServer یا AsyncWebServer برای راه اندازی وب سرور و هندل کردن درخواست ها استفاده بفرمایید که امکانات خیلی بیشتری دارند.- در اینجا هر دو برد دریافت کننده و ارسال کننده به یک Access Point وصل می شوند و station هستند، برای استفاده outdoor، موبایل را access point می کنم و روی موبایل به وب اینترفیس دریافت کننده وصل می شوم. برای بدست آوردن ip کلاینت های متصل شده و موارد دیگر روی اندروید از subnet scanner روی  PingTools Network Utilities استفاده می کنم. همچنین Network Analyzer برای دیدن اطلاعات شبکه گوشی کمک می کند.با تغییر بخش wifi می توانید یکی را access point کنید و برای بردها ip ثابت تنظیم بفرمایید.- کد html صفحات را بصورت compress شده در کد گیرنده (Receiver.ino) استفاده کرده ام، البته روی صفحه گیت هاب نمونه کمپرس نشده آن نیز هست. htmlها (با تگ script) اینجا کمپرس شده، جاوا اسکریپت ها (داخل تگ script) اینجا کمپرس شده اند. سپس در text editor همه &quot; ها با &#x27; عوض (replace &quot; with &#x27; in all) شده است.- آنتن پیش فرض گیرنده بد نیست اما اصلاً قوی نیست ، اما فرستنده بدون آنتن اضافه، با توجه به اینکه در اینجا ۵ولت به آن برق داده ایم که تا ۱۲ولت جا دارد، چند سانتی متر بیشتر کار نمی کند. محل لحیم آنتن (آنتن فنری خوب است) در تصاویر ماژول ها در بالا نمایش داده شده است.مقالات مفید دیگر :Decode and Send 433 MHz RF Signals with ArduinoHow 433MHz RF Tx-Rx Modules Work &amp;amp;amp;amp;amp;amp;amp;amp;amp; Interface with Arduino</description>
                <category>Hootan Alghaspour</category>
                <author>Hootan Alghaspour</author>
                <pubDate>Tue, 15 Nov 2022 00:27:38 +0330</pubDate>
            </item>
                    <item>
                <title>بازیابی و کپی برنامه آردوینو</title>
                <link>https://virgool.io/@h.alghaspour/%D8%A8%D8%A7%D8%B2%DB%8C%D8%A7%D8%A8%DB%8C-%D9%88-%DA%A9%D9%BE%DB%8C-%D8%A8%D8%B1%D9%86%D8%A7%D9%85%D9%87-%D8%A2%D8%B1%D8%AF%D9%88%DB%8C%D9%86%D9%88-vpg8xuyowsdu</link>
                <description>اینجا راحت و مستقیم ترین روش برای بازیابی برنامه کامپایل و آپلود شده روی میکروکنترلر avr آردوینو و کپی آن روی یک آردوینو دیگر را بدون استفاده از programmer و ابزار تخصصی یادداشت می کنم.بعبارتی برنامه روی flash و محتوی eeprom را از روی یک آردوینو می گیریم و روی یک برد آردوینو دیگر قرار می دهیم (کپی می کنیم).زیرا Arduino IDE دکمه Upload دارد اما دکمه Download ندارد که برنامه روی بک برد را روی بردهای دیگر کپی کنیم !برنامه های کامپایل شده آردوینو که دانلود و آپلود می کنیم در فرمت Intel Hex هستند.Intel hexadecimal object file format, Intel hex format or Intellec Hex is a file format that conveys binary information in ASCII text form. It is commonly used for programming microcontrollers, EPROMs, and other types of programmable logic devices and hardware emulators. In a typical application, a compiler or assembler converts a program&#x27;s source code (such as in C or assembly language) to machine code and outputs it into a HEX file.یک مرحله پیشرفته تر از این می توانیم برنامه کامپایل شده را disassemble کنیم تا به کد اسمبلی برسیم، شما می توانید براحتی به کد اسمبلی برنامه برسید اما تبدیل آن به C (دیکامپایل) خیلی سخت تر است. بنابراین بازیابی سورس اصلی ++C برنامه آردوینو سخت است و معمولا صرفه ندارد.A decompiler takes one from a binary to source code–or something similarly high-level that can easily be read by humans. A disassembler takes one from binary to assembler–which is much lower level and is more difficult to read for humans.همچنین اگر روی میکروکنترلر lock bits تنظیم شده باشد این فرآیند ممکن نیست. (البته روی آردوینو بخاطر bootloaderش تنظیم lockbits کار راحتی نیست و معمولا نمی کنند.)Lock bits are set of bits to enable or disable some special security features of a microcontroller. For example, in some cases you might want to disable the memory read functionality of the microcontroller, so that the code you have written cannot be stolen by others. The number of lock bits and their functionality is always given in the datasheet. The controller used here has three lock bits which can be set or reset depending on the feature.مرحله ۱ : اتصال آردوینو Programmer و آردوینو Targetدر اینجا ۲ برد آردوینو اونو داریم ، یک arduino uno که بعنوان پروگرامر برای خواندن و نوشتن روی میکروکنترلر arduino uno دیگری که target است استفاده می شود.روی برد programmer من دقیقا ISP example را آپلود کردم.اینجا استفاده از ISP (In-System-Programmer) آردوینو توضیح داده شده است.The Arduino ISP is an In-System-Programmer that is used to program AVR microcontrollers.و اینجا هم توضیحات کاملتر و pinout را خواهید دید. پین اوت اتصال دو برد arduino uno برای اینکه یکی پروگرامر دیگری باشد را در تصویر زیر مشاهده می فرمایید.بعد از upload روی برد programmer و اتصال برد programmer و target براساس pinout ذکر شده در بالا می توانیم روی میکروکنترلر avr برد target بخوانیم و بنویسیم.مرحله ۲ : خواندن برنامه از روی آردوینو Targetبرد programmer را (که روی آن برنامه ISP را ریختید و برد تارگت براساس pinout بالا آن وصل است) با usb به کامپیوتر وصل کنید.ابزار avrdude یک ابزار معروف برای خواندن و نوشتن روی avr است که برای انواع سیستم عامل ها نیز موجود می باشد. خودش خط فرمانی است البته.AVRDUDE - AVR Downloader Uploader - is a program for downloading and uploading the on-chip memories of Atmel’s AVR microcontrollers.software for programming Microchip (former Atmel) AVR Microcontrollers.روی ویندوز ابزار گرافیکی و رایگان AVRDUDESS را دارید که درحقیقت یک GUI برای همان AVRDUDE است.ابزارهای پیشرفته تر و بهتر هم وجود دارند اما همین همه کاری که ما میخواهیم را می کند.برای خواندن از برد target که به برد programmer متصل است ، در پنجره AVRDUDESS ابتدا از بخش programmer گزینه Arduino را انتخاب بفرمایید و سپس از port آن پورتی که روی کامپیوتر شناسایی شده است انتخاب کنید و baud rate را همین ۱۹۲۰۰ بزنید.تنظیمات مثل تصویر بالاست.سپس دکمه detect را بزنید تا نوع میکروکنترلر را شناسایی کند و از ارتباط و شناسایی صحیح اطمینان پیدا کنید و سپس دو دکمه read در بخش Fuses &amp; lock bits را بزنید. دقت بفرمایید اگر موقع نوشتن fuse و لاک بیت ها را چیز دیگری بزنید ممکن است کار نکند و حتی دیگر MCU غیرقابل استفاده شود. بنابراین این مقادیر را یادداشت کنید و اگر اطمینان ندارید هنگام نوشتن تغییر ندهید.در قسمت Flash و EEPROM با زدن دکمه ... یک فایل برای ذخیره برنامه خوانده شده انتخاب کنید، برای flash پسوند hex. و برای eeprom پسوند eep. باشد. مقدار Format هر دو را هم Intel Hex انتخاب کنید.در هر دو هم گزینه read را در هنگام خواندن انتخاب بفرمایید.اکنون اگر در قسمت Flash و EEPROM  دکمه Go را بزنید برنامه روی flash و eeprom را خوانده و در فایلی که آدرس داده اید ذخیره می کند.در آردوینو Flash برای ذخیره برنامه کامپایل شده ای که اجرا می کند استفاده می شود و EEPROM برای ذخیره مقدار کمی دیتا (در EEPROM داخلی uno معادل ۱ کیلوبایت!) مثل وضعیت و مقدار یک سنسور و عملگر و ... که با خاموش شدن برد از بین نمی رود، درباره انواع حافظه ها در آردوینو اینجا بیشتر بخوانید.مرحله ۳ : نوشتن برنامه روی میکروکنترلر یک آردوینو دیگربدون استفاده از برد programmer :یک ابزار راحت (راحت ترین) برای نوشتن برنامه کامپایل شده hex روی میکروکنترلر آردوینو بدون Arduino IDE ابزار Arduino-XLoader است.برد target را مستقیما با usb به کامپیوتر وصل کنید و خیلی راحت فایل hex فلش و نوع برد (Device) و COM Port را انتخاب کنید و دکمه Upload را بزنید. برنامه hex دانلود شده در مرحله قبل روی flash برد target ریخته می شود و نیازی به چیز دیگری نیست.با استفاده از برد programmer :طبیعتا در AVRDUDESS آپشن ها و گزینه های بیشتری دارید.برای نوشتن با AVRDUDESS روی برد target ابتدا آن را به برد programmer و برد programmer را با usb به کامپیوتر متصل کنید.همان تنظیمات است فقط،دقت کنید مقادیر Fuses &amp; lock bits ها عینا همان باشد که در ابتدا خواندید. تیک set fuses و set lock را هم بعد از اینکه از صحیح بودن مقادیر اطمینان پیدا کردید بزنید. در قسمت Flash و EEPROM گزینه write را انتخاب کنید و در بخش Options تیک Erase flash and EEPROM را بزنید و در نهایت دکمه !Program را بزنید.</description>
                <category>Hootan Alghaspour</category>
                <author>Hootan Alghaspour</author>
                <pubDate>Thu, 01 Sep 2022 22:11:12 +0430</pubDate>
            </item>
                    <item>
                <title>پیاده سازی مجازی سازی Proxmox روی یک سرور با یک کارت شبکه</title>
                <link>https://virgool.io/@h.alghaspour/%D9%BE%DB%8C%D8%A7%D8%AF%D9%87-%D8%B3%D8%A7%D8%B2%DB%8C-%D9%85%D8%AC%D8%A7%D8%B2%DB%8C-%D8%B3%D8%A7%D8%B2%DB%8C-proxmox-%D8%B1%D9%88%DB%8C-%DB%8C%DA%A9-%D8%B3%D8%B1%D9%88%D8%B1-%D8%A8%D8%A7-%DB%8C%DA%A9-%DA%A9%D8%A7%D8%B1%D8%AA-%D8%B4%D8%A8%DA%A9%D9%87-jefz3mzv9qlr</link>
                <description>خلاصه :می خواهیم روی یک سرور bare metal خوب با یک کارت شبکه فیزیکی و چند IPv4 تعدادی vps راه اندازی کنیم و تعدادی از سرورهاس dedicate فعلی را بصورت مجازی روی این زیرساخت منتقل کنیم.در خیلی از دیتاسنترهای دنیا می توانید با قیمت های خوب سرورهای عالی بگیرید و تنها مشکل همین وجود یک کارت شبکه فیزیکی است که ممکن است امکان اضافه کردن کارت شبکه جدید نباشد یا هزینه آن زیاد شود. البته مشکلی نیست و  روی یک کارت شبکه با پهنای باند خوب هم می توان چند ماشین مجازی با ترفیک متوسط را بخوبی سرویس داد.سرور فوق با ۱ ترابایت فضای بک آپ و تعدادی IP اضافه، ماهیانه حدود ۱۰۰یورو!بجز هزینه کمی کمتر، مزیت مجازی سازی و استفاده از proxmox این است که :۱- مدیریت و راهبری و بهره برداری روی زیرساخت مجازی سازی شده از مدیریت و راهبری و بهره برداری چند سرور فیزیکی مجزا راحت تر، مطئمن تر و مانورپذیرتر است.۲- امکان کلاسترسازی و HA و Migration و ... را روی زیرساخت مجازی خیلی راحت تر داریم.۳- روی زیرساخت مجازی Backup گیری راحت تر و مطئمن تری داریم و می توان از کل ماشین بک آپ گرفت.۴- یک سرور جدید با سخت افزارهای سریعتر و قابل اطمینان تر و بهتر می گیرم و بازدهی سرورهای مجازی روی این زیرساخت مجازی شده از آن سرورهای dedicate قبلی در نهایت بهتر شد.۵- اگر بعدا لازم شد و بار یک یا تعدادی از سرورها زیاد شد میتوان یک سرور دیگر گرفت و این ماشین های مجازی را براحتی روی آن منتقل کرد. بنابراین با میزان نیاز موجود هزینه جاری تنظیم می شود.نقطه ضعف این روش ها این است که :اگر کلاستر نکنیم و HA نداشته باشیم، در شرایطی که برای این سرور مشکلی پیش بیاید همه سرورها down می شوند.مقدمه :در حال حاضر Proxmox یکی از بهترین گزینه های پیاده سازی زیرساخت مجازی است که در نسخه رایگان هم امکانات خیلی خوبی ارایه می دهد و امتحان خود را نیز پس داده است.امکانات و قابلیت های خوبی برای کانتینرهای LXC و ماشین های مجازی KVM دارد. البته LXD و Virsh را پشتیبانی نمی کند اما خودش برای آن ها جایگزین دارد.همزمان کانتینرهای LXC و ماشین های مجازی KVM را دارید، بخاطر ماهیت LXC فقط از لینوکس می توان استفاده کرد و همچنین خیلی از بسته هایی که نیاز به دسترسی و دستکاری کرنل و فایل سیستم را دارند برای نصب مشکل هستند و خیلی تنظیمات دستی باید انجام داد. مثلاْ الاستیک سرچ روی lxc خیلی به سختی نصب و اجرا می شود، بنابراین راه راحت تر این است که اینگونه موارد را روی یک vm از نوع kvm بالا آورد. در عوض کانتینرهای lxc برای بسیاری از استفاده ها مثلا LAMP بسیار خوب و پربازده هستند و ریستارت و بک آپ گیری ازشون هم خیلی سریع و ساده است.وب اینترفیس خوب و کامل و راحت و ابزارهای cli کامل دارد.خیلی راحت می توان چند Proxmox نصب کرد و با هم کلاستر کرد و از HA و Migration و ... لذت برد.حتما نیاز به فایل ISO نیست و روی دبیان ۹-۱۰-۱۱ براحتی نصب می شود. درنهایت هم شما یک دبیان در دست دارید.مواردی مثل تغییرات سخت افزاری و بک آپ گیری در آن خیلی راحت است.کامیونیتی و فوروم فعالی دارد.قیمت لایسنس های آن قابل قبول و مناسب است. سناریو :یک سرور خوب که روی آن proxmox نصب کنیم و تعدادی IPv4 برای ماشین ها می گیریم.ما حداقل ۵ سرور vps نیاز داریم و ۲ مورد آخر لیست ذیل برای آزمایش ها و تست و ... هستند که با url می توان به آن ها دسترسی داشت (IP معتبر اینترنتی ندارند) و البته می توان روی آن ها هم سرویس داد :۲ سرور که اپلیکیشن های سنگین و الاستیک سرچ و redis و .. دارند. (kvm)۲ سرور که سرویس متداول وب و ایمیل و دیتابیس و ... مثل یک هاست. (lxc)۱ سرور مانیتورینگ. (kvm)۱ سرور بعنوان Reverse Proxy &amp; WAF (ازنوع lxc)یک یا تعدادی سرور هم بدون IP ولید برای تست ها که پشت سرور بالا قرار می گیرند و با url قابل دسترس هستند. (lxc-kvm)نصب :برای نصب proxmox سه روش وجود دارد :از دیتاسنتر بخواهید ISO نصب را روی سرور mount کند.یک دبیان ۹ یا ۱۰ یا ۱۱ نصب کنید یکی از راهنماهای نصب Proxmox روی دبیان را دنبال بفرمایید.روی هتزنر از خط فرمان rescue system با دستور imageinstall و گزینه others ایمیج آماده شده توسط هتزنر را نصب کنید. (من این روش را انتخاب کردم.)در هنگام نصب با روش فوق می توان تنظیمات مورد نیاز را در فایل install-ll.conf اعمال کرد. من فقط RAID و HOSTNAME و PART را تنظیم می کنم. پس از شروع عملیات نصب چند دقیقه بعد proxmox شما آماده است.تنظیم :در مورد نصب و تنظیمات و آپشن های ماشین های مجازی در این مقاله بحثی نداریم، در مورد تنظیمات شبکه :برای اتصال ماشین های مجازی به اینترنت و بلعکس ۲ روش bridge و route وجود دارد، من route را انتخاب کردم. فراموش نکنید برای استفاده از این روش باید ip_forward را فعال بفرمایید.روش routedروش Bridge تنظیمات شبکه هاست (سرور proxmox) :روی خود proxmox تنظیمات فایل etc/network/interfaces/ برای روش routed به اینصورت است :auto lo
iface lo inet loopback
auto enp7s0
iface enp7s0 inet static
        address &lt;main (proxmox) IP&gt;/26
        gateway &lt;main (proxmox) Gateway&gt;
        pointopoint &lt;main (proxmox) Gateway&gt;
auto vmbr0
iface vmbr0 inet static
        address &lt;main (proxmox) IP&gt;/32
        bridge-ports none
        bridge-stp off
        bridge-fd 0
        up ip route add &lt;additional IP&gt;/32 dev vmbr0
        up ip route add &lt;additional IP&gt;/32 dev vmbr0
        up ip route add &lt;additional IP&gt;/32 dev vmbr0
        up ip route add &lt;additional IP&gt;/32 dev vmbr0
        up ip route add &lt;additional IP&gt;/32 dev vmbr0
	up ip route add &lt;additional IP&gt;/32 dev vmbr0همچنین آپشن های کرنل روی سرور proxmox برای فورواردینگ به اینصوت هستند :net.ipv4.ip_forward=1
net.ipv6.conf.all.forwarding=1برای تنظیم این موارد یا از دستور sysctl استفاده بفرمایید یا فایل etc/sysctl.conf/ را دستی ادیت کرده و ۲خط بالا را اضافه کنید.#sysctl -w net.ipv4.ip_forward=1
#sysctl -w net.ipv6.conf.all.forwarding=1
#sysctl -pتنظیمات guest ها :برای lxcها :درهنگام ساخت کانتینر آدرس IP را آدرس IP اضافه ای که دریافت کرده اید با 32/ تعریف کنید و آدرس gateway را آدرس IP خود سرور proxmox قرار دهید. همین!برای vmهای kvm :برای روش routed باید تنظیمات SCOPE (در ردهتی ها) یا pointopoint (در دبیان و اوبونتو) یا on-link (در دبیان و ابونتو جدیدتر و سایر توزیع هایی که از netplan استفاده می کنند) را تعیین کنید. من از ubuntu server 20.04 استفاده می کنم و تنظیمات فایل etc/netplan/00-installer-config.yaml/ به اینصورت است.network:
  ethernets:
        ens18:
            addresses: [&#039;Additional IP/32&#039;]
            nameservers:
                addresses: [127.0.0.1, 8.8.8.8]
            routes:
              - to: 0.0.0.0/0
                via: ProxmoxMainIP
                on-link: true
  version: 2برای اطلاعات بیشتر و نمونه تنظیمات سایر سیستم عامل ها بخش Network را در اینجا ببینید.لازم بذکر است ما فقط از IPv4 استفاده کردیم، تنظیمات IPv6 عملا راحت تر است و در لینک بالا توضیحات و نمونه ها را پیدا می کنید.در نهایت Proxmox ما آماده است و ماشین های مجازی را نصب و تنظیم و استفاده می کنیم که تنظیمات ماشین های مهمان در حدود این مقاله نیست اما سعی می کنم در مورد proxmox بعنوان یک گزینه خوب ، ساده و سریع مجازی سازی در آینده نیز مطالب و تجربیات جدید خود را به همین صورت یادداشت کنم.</description>
                <category>Hootan Alghaspour</category>
                <author>Hootan Alghaspour</author>
                <pubDate>Fri, 13 May 2022 19:09:02 +0430</pubDate>
            </item>
                    <item>
                <title>جابجایی سرور زیمبرا</title>
                <link>https://virgool.io/@h.alghaspour/%D8%AC%D8%A7%D8%A8%D8%AC%D8%A7%DB%8C%DB%8C-%D8%B3%D8%B1%D9%88%D8%B1-%D8%B2%DB%8C%D9%85%D8%A8%D8%B1%D8%A7-zswt6ahkw1xd</link>
                <description>Migrating from Zimbra to Zimbra (ZCS to ZCS)نیاز داشتیم زیمبرا موجود را عینا با همه چیز از روی سرور قدیمی به زیرساخت جدید منتقل کنیم. براساس این راهنما عمل کردم و همه چیز (بجز بخش chat که برای ما حیاتی نبود) حتی پسورد کاربران عینا منتقل شد. فقط چند نکته :۱- اکسپورت از روی سرور قدیمی ، zip و انتقال به سرور جدید و unzip و ایمپورت روی سرور جدید زمان زیادی بسته به میزان دیتاها نیاز دارد ، از زمانی که mailbox data کاربران را اکسپورت کرده اید ، درصورت آنلاین بودن سرور قدیمی ، ایمیل ها و اطلاعات جدید منتقل نخواهند شد، بنابراین حتما باید با کاربران برای قطعی چند ساعته زیمبرا هماهنگ کنید.۲- حتما از tmux یا ترمینال مشابهی استفاده کنید که درصورت قطع شدن ارتباط اینترنت کنسول آن برقرار بماند.۳- برای انتقال باید هر دو نسخه زیمبرا مشابه باشند، با این روش ما از روی Zimbra 8.8.15 نصب شده روی Centos8 به همین نسخه روی Ubuntu Server 20.04 انتقال دادیم و فقط اطلاعات chat نیامد که برای ما حیاتی نبودند.۴- واقعیتش کاری که سالی ۲ سالی یکبار باید انجام بشه را نمی صرفه اسکریپت و ansible و ... کنم! همین جا لیست دستورات و منبع را یادداشت می کنم بعدا برای این نسخه این کار دوباره پیش آمد استفاده می کنم.مراحل Export :Step 0.# mkdir -p /migration/zimbra# chmod -R 777 /migration/zimbra# chown -R zimbra:zimbra /migration/zimbraStep 1. Export all domains# su - zimbra$ mkdir -p /migration/zimbra/domains$ cd /migration/zimbra/domains$ zmprov gad | tee -a domains.txtStep 2. Export all accounts$ mkdir -p  /migration/zimbra/accounts$ cd /migration/zimbra/accounts$ zmprov gaaa | tee -a admins.txt$ zmprov -l gaa | tee -a users.txtStep 3. Export all account details$ mkdir -p /migration/zimbra/account_details$ cd /migration/zimbra/account_details$ for user in &#x60;cat ../accounts/users.txt&#x60;; do zmprov ga $user  | grep -i Name: | tee -a $user.txt ; doneStep 4. Export all account passwords$ mkdir -p /migration/zimbra/passwords$ cd /migration/zimbra/passwords$ for user in &#x60;cat ../accounts/users.txt&#x60;; do zmprov -l ga $user userPassword | grep userPassword: | awk &#x27;{ print $2}&#x27; | tee -a $user.shadow; doneStep 5. Export all distribution lists$ mkdir -p /migration/zimbra/distribution_lists$ cd /migration/zimbra/distribution_lists$ zmprov gadl | tee -a distribution_lists.txt$ for list in &#x60;cat distribution_list.txt&#x60;; do zmprov gdlm $list &gt; $list.txt ;echo &quot;$list&quot;; doneStep 6.  Export all aliases$ mkdir -p /migration/zimbra/aliases$ cd /migration/zimbra/aliases$ for user in &#x60;cat ../accounts/users.txt&#x60;; do zmprov ga  $user | grep zimbraMailAlias | awk &#x27;{print $2}&#x27; | tee -a $user.txt ;echo $i ;done$ find /migration/zimbra/aliases -type f -empty | xargs -n1 rm -vStep 7. Exporting mailbox data$ cd /migration/zimbra$ mkdir mailbox_data$ cd mailbox_data$ for user in &#x60;cat ../accounts/users.txt&#x60;; do echo &quot;Exporting mailbox $user&quot; ; zmmailbox -z -m $user getRestURL &#x27;/?fmt=tgz&#x27; &gt; ./$user.tgz ; doneخط بالا یک خط است، $ اول مربوط به کنسول و بقیه مربوط به متغییرها هستند$ for user in &#x60;cat ../accounts/problematic_accounts.txt&#x60;; do echo &quot;Exporting mailbox $user&quot; ; zmmailbox -z -m $user getRestURL &#x27;/?fmt=tgz&#x27; &gt; ./$user.tgz ; doneStep 8. Exporting email filters for all the accounts$ mkdir /migration/zimbra/filters$ cd /migration/zimbra/filters$ vim export_filters.shاسکریپت ذیل را درون این فایل پیست کنید#!/bin/bash
mkdir tmp
set -x
clear
for user in `cat ../accounts/users.txt`;
do
    filter=`zmprov ga $user zimbraMailSieveScript &gt; ./tmp/$user`
    sed -i -e &amp;quot1d&amp;quot ./tmp/$user
    sed &#039;s/zimbraMailSieveScript: //g&#039; ./tmp/$user &gt; ./$user;
    rm ./tmp/$user
    echo &amp;quotExport filter for $user&amp;quot
done
\rm -rf tmp$ chmod 777 export_filters.sh$ ./export_filters.sh# zip -r migration.zip migration# scp migration.zip user@newserver:/migration.zipمراحل Import :Step 9. Restore all domains to the new server# su zimbra$ cd /migration/zimbra/domains$ for domain in &#x60;cat domains.txt &#x60;; do zmprov cd $domain zimbraAuthMech zimbra ;echo $domain ;doneStep 10. Restore all accounts and passwords$ mkdir -p /migration/zimbra/scripts$ cd /migration/zimbra/scripts$ vim restore_accounts.sh#!/bin/bash
PASSWDS=&amp;quot../passwords&amp;quot
ACCOUNT_DETAILS=&amp;quot../account_details&amp;quot
USERS=&amp;quot../accounts/users.txt&amp;quot
for i in `cat $USERS`
   do
givenName=$(grep givenName: $ACCOUNT_DETAILS/$i.txt | cut -d &amp;quot:&amp;quot -f2)
displayName=$(grep displayName: $ACCOUNT_DETAILS/$i.txt | cut -d &amp;quot:&amp;quot -f2)
shadowpass=$(cat $PASSWDS/$i.shadow)
zmprov ca $i &amp;quotTeMpPa55^()&amp;quot cn &amp;quot$givenName&amp;quot displayName &amp;quot$displayName&amp;quot givenName &amp;quot$givenName&amp;quot
zmprov ma $i userPassword &amp;quot$shadowpass&amp;quot
   done$ chmod 777 restore_accounts.sh$ ./restore_accounts.shStep 11. Restore distribution lists$ cd /migration/zimbra$ for lists in &#x60;cat  distribution_lists/distribution_lists.txt&#x60;; do zmprov cdl $lists ; echo &quot;$lists -- done &quot; ; done$ cd migration/zimbra/distribution_lists$ vim restore_dist_lists.sh.#!/bin/bash
for list in `cat distribution_lists.txt`
do
    for mbmr in `grep -v &#039;#&#039; ./$list.txt | grep &#039;@&#039;`
    do
        zmprov adlm $list $mbmr
        echo &amp;quot $mbmr has been added to $list&amp;quot
    done
done$ chmod 777 restore_dist_lists.sh$ ./restore_dist_lists.shStep 12. Restore all aliases$ cd /migration/zimbra/aliases$ vim restore_aliases.sh#!/bin/bash
echo &amp;quotProcessing User accounts&amp;quot
for user in `cat ../accounts/users.txt`
do
    echo $user
    if [ -f &amp;quot./$user.txt&amp;quot ]; then
        for alias in `grep &#039;@&#039; ./$user.txt`
        do
            zmprov aaa $user $alias
            echo &amp;quot$user ALIAS $alias - Restored&amp;quot
        done
     fi
done
echo &amp;quotProcessing Admin accounts&amp;quot
for user in `cat ../accounts/admins.txt`
do
    echo $user
    if [ -f &amp;quot./$user.txt&amp;quot ]; then
        for alias in `grep &#039;@&#039; ./$user.txt`
        do
            zmprov aaa $user $alias
            echo &amp;quot$user ALIAS $alias - Restored&amp;quot
        done
    fi
done$ chmod 777 restore_aliases.sh$ ./restore_aliases.shStep 13. Importing mailboxes$ cd /migration/zimbra/mailbox_data$ for mailbox in &#x60;cat ../accounts/users.txt&#x60;; do zmmailbox -z -m $mailbox postRestURL &quot;/?fmt=tgz&amp;resolve=skip&quot; ./$mailbox.tgz ; echo &quot;$mailbox - done &quot;; doneStep 14. Importing mail filters$ cd /migration/zimbra/filters$ vim import_filters.shfor file in /migration/zimbra/filters/*    do
        StrFilter=`cat &amp;quot$file&amp;quot`
        Acc=`echo $file | cut -d &amp;quot/&amp;quot -f5`
        su - zimbra -c &amp;quotzmprov ma $Acc zimbraMailSieveScript &#039;$StrFilter&#039;&amp;quot
        echo &amp;quotProcess filter $Acc&amp;quot
    done
echo &amp;quotAll filters have been imported successfully&amp;quot$ chmod 777 import_filters.sh$ ./import_filters.shStep 15. $ zmcontrol restart$zmcontrol status</description>
                <category>Hootan Alghaspour</category>
                <author>Hootan Alghaspour</author>
                <pubDate>Fri, 06 May 2022 18:03:20 +0430</pubDate>
            </item>
                    <item>
                <title>اعمال update و upgrade و patch روی Magento2</title>
                <link>https://virgool.io/@h.alghaspour/%D8%A7%D8%B9%D9%85%D8%A7%D9%84-update-%D9%88-upgrade-%D9%88-patch-%D8%B1%D9%88%DB%8C-magento2-rn4przegdrkg</link>
                <description>در اینجا به اینکه چرا مجنتو قوی ترین open-source e-commerce platform است و چرا مجموعه های حرفه ای برای ساخت فروشگاه آنلاین بجای امثال WooCommerce و virtuemart دردسری مثل Magento را تقبل می کنند کار ندارم. بصورت خلاصه شما اگر بخواهید فروشگاه آنلاینی مثلاً تقریباً در حد دیجی کالا راه اندازی کنید از ابعاد مختلف از جمله توانایی پاسخ دادن به هزاران محصول و هزاران درخواست همزمان باید از مجنتو استفاده کنید.دو آسیب پذیری خطرناک روی مجنتو منتشر شده که بهانه ای شدند برای اینکه روش اعمال پچ را بصورت ساده اینجا ثبت کنم.قبل از هر چیز بهتر است ابزار Quality Patches Tool را نصب کنید. در پوشه public_html مجنتو از دستور ذیل استفاده بفرمایید.$composer require magento/quality-patchesبرای آپدیتش هم composer update magento/quality-patchesهمچنین در حقیقت روی default mode هم می توان اینکارها را انجام داد اما بهتر است قبل از این قبیل امور مجنتو را روی maintenance mode قرار دهید.راهنمای modeها را اینجا و راهنمای Set the operation mode را اینجا ببینید.نصب patch های مجنتو :یکسری پچ های عمومی و متداول و کاربردی و دوره ای است که به اینصورت باید اعمال بفرمایید :دستور ذیل را در فولدر روت مجنتو بزنید :$./vendor/bin/magento-patches statusلیست پچ های موجود روی نسخه شما و وضعیت آنها نمایش داده می شود.برای اعمال باید از دستور ذیل استفاده بفرمایید و لیست کد patchهایی که می خواهید اعمال شوند را بزنید. متاسفانه من در خود دستور پیدا نکردم چطور میشه همه را یکجا all کرد و باید دونه دونه کد پچ ها را بزنید. کار که می کنم اینه که خروجی دستور را میفرستم روی یک فایل و با cut و sed لیست کدپچ ها را استخراج می کنم.$./vendor/bin/magento-patches apply MAGE-XXXX MAGE-YYYYو در نهایت bin/magento cache:clean/. کنید. revert هم داره ، راهنمای کاملتر را اینجا ببینید.یکسری هم ممکنه پچ های فوری و امنیتی و موردی باشند مثل این مورد اخیر، باید دستی و موردی اعمال شوند، بهتر است پچ های عمومی را قبل از اینها نصب کرده باشید :برای اینکار از git یا patch استفاده می کنیم. من از patch استفاده می کنم.ابتدا با yum install ابزارهای فوق را اگر نصب ندارید نصب کنید.سپس بسته به اینکه از composer برای نصب استفاده کرده اید یا از روی github و سورس نصب نموده اید (آدرس فایل ها فرق می کند) پچ مربوطه (مثلاً فایل MDVA-43443_EE_2.4.2-p2_COMPOSER_v1.patch) را دانلود و extract و روی public_html سایت مجنتو خود بارگزاری بنمایید.به ترتیب نصب پچ ها و راهنمای Adobe Security Bulletin توجه بفرمایید.مثلاً در مورد اخیر :در نهایت با دستور patch در فولدر روت مجنتو (home/account/public_html/)  و دادن فایل پچ به آن :$patch -p1 &lt; patch_name.composer.patchآن را اعمال کنید. اگر p1 کار نکرد جای آن از p2 استفاده کنید. برای گرفتن backup از فایل هم سوییچ b- را استفاده کنید که البته بصورت پیش فرض در نسخه نصب شده روی centos8 من فعال بود. راهنمای patch را اینجا ببینید.برای اعمال با دستور git باید از روش ذیل استفاده بفرمایید. من با patch راحت ترم و از git استفاده نمی کنم.$git apply patch_name.composer.patchپس از اعمال patch باید php bin/magento setup:upgrade را استفاده کنید.من معمولاً پس از اینجور کارها کل فرآیند ذیل را انجام می دهم :$rm -rf var/di var/generation/* var/cache/* var/page_cache/* 
$php bin/magento cache:clean
$php bin/magento cache:flush
$php bin/magento setup:upgrade
$php bin/magento setup:di:compile
$php bin/magento setup:static-content:deploy -f
$php bin/magento indexer:reindexآپدیت و آپگرید مجنتو به نسخه جدیدتربرای اعمال آپدیت و آپگرید مجنتو هم از دستورات ذیل استفاده بفرمایید، دقت کنید قضیه بروزرسانی نسخه با نصب وصله و patch متفاوت است، همچنین من برای مجنتو از composer برای نصب و بروزرسانی استفاده می کنم و سورس را از روی گیت هاب بیلد نمی کنم.اگر بعدش خطایی داد احتمالاً مشکل ownership و permission است. (قبلش یک بک آپ بگیرید و بهتر است روی maintenance mode باشید!)$composer require magento/product-community-edition [latest version] --no-update
$composer update
$rm -rf var/di var/generation/* var/cache/* var/page_cache/* 
$php bin/magento cache:clean
$php bin/magento cache:flush
$php bin/magento setup:upgrade
$php bin/magento setup:di:compile
$php bin/magento setup:static-content:deploy -f
$php bin/magento indexer:reindexروند استاندارد ارتقا نسخه ۲.۴.۴ به ۲.۴.۵ بصورت زیر است که در آن قرار دادن روی maintenance و بک آپ گرفتن از composer.json هم منظور شده است و در پایان هم vendor را کلاً پاک می کند و مجدداً نصب می کند.php bin/magento maintenance:enablecp composer.json composer.json.bakcomposer -vcomposer require-commerce magento/product-community-edition 2.4.5 --no-updatecomposer updatephp bin/magento setup:upgradephp bin/magento setup:di:compilephp bin/magento setup:static-content:deploy -fphp bin/magento cache:cleanphp bin/magento maintenance:disablerm -rf vendorComposer install</description>
                <category>Hootan Alghaspour</category>
                <author>Hootan Alghaspour</author>
                <pubDate>Sat, 19 Feb 2022 00:31:24 +0330</pubDate>
            </item>
                    <item>
                <title>پیاده سازی Web Application Firewall با ModSecurity</title>
                <link>https://virgool.io/@h.alghaspour/modsecurity-rycsflscrgix</link>
                <description>بصورت کلی وقتی می گوییم application firewall یعنی فعالیت در لایه 7 از OSI و در سطح و محیط و ترافیک اپلیکیشن و سرویس ، به عناوین application-level gateway یا proxy firewall هم ممکن است در مستندات نام برده شده باشند. برای فایروال وب اپلیکیشن ها که عملکرد IDS/IPS هم دارد از عبارت web application firewall (WAF) استفاده می شود و همچنین باید درنظر داشت بسیاری از محصولات حرفه ای و سازمانی یکپارچه این لایه های مختلف فایروال را همزمان و هماهنگ ارایه می دهند و این دسته بندی ها و تعاریف هم مرز مشخص و دقیقی ندارند.خلاصه اینکه یک WAF بصورت تخصصی در لایه اپلیکیشن از یک سامانه تحت وب حفاظت می کند. ماژول ModSecurity اورجینالی بعنوان یک ماژول برای آپاچه معرفی شد اما اکنون روی nginx و IIS هم نصب می شود. کار کردن با آن برای استفاده های متداول نسبتاً ساده و سرراست است و قدرت و بازدهی خوبی هم ارایه می دهد. البته می توان خیلی در آن دقیق شده و از امکانات و قابلیت های بسیار آن برای پیاده سازی سناریوهای پیچیده استفاده کرد. حتی بصورت appliance و as a service هم موجود است و می توان با ترکیب با بقیه راه حل های وب سرور مثل &quot;توزیع، هدایت و FailOver ترافیک با پروکسی Apache&quot; یک application gateway قابل اطمینان و امن و قابل نظارت برای یک زیرساخت پیاده سازی نمود.توانایی شناسایی انواع حملات روی وب اپلیکیشن ها را دارد و البته configی که پیاده سازی می کنید بسیار مهم است. تعدادی rule set و config آماده مثل OWASP ModSecurity Core Rule Set نیز برای mod security موجود است که کار را خیلی راحت تر می کنند.در این مقاله معرفی کوتاه و نگاهی به ModSecurity خواهیم داشت که برای آشنایی اولیه و پایه ای برای جستجوهای تکمیلی مناسب است.محیط من در اینجا یک دبیان 10 با apache2 است (TurnKey LAMP Stack)، اصول کلی در سایر لینوکس ها و nginx و IIS هم همین خواهد بود و فقط در فرآیند نصب و فعال کردن ممکن است تفاوت هایی وجود داشته باشد که البته راهنمای نصب روی محیط های مختلف در راهنمای رسمی خودش بخوبی بیان شده.ابتدا برای چک کردن ماژول های فعال روی apache2 :روی دبیان 10 و اوبونتو نام این ماژول تحت عنوان security2_module نمایش داده می شود.برای نصب روی دبیان، اوبونتو و CentOS :Debian 7-8-9 : apt install libapache2-modsecurity
Ubuntu/Debian 10 : apt install libapache2-mod-security2
CentOS : yum install mod_securityو سپس سرویس apache را ریستارت کنید.مسیر پیش فرض تنظیمات etc/modsecurity/ است و در هنگام نصب یک فایل config بعنوان نمونه با نام modsecurity.conf-recommended خودش در این مسیر قرار می دهد. این فایل را به modsecurity.conf تغییر نام می دهیم. (فایل های conf. این مسیر در اجرا اعمال می شوند.)با بازکردن این فایل اولین مورد مربوط به تنظیم SecRuleEngine است که 2 حالت دارد : DetectionOnly که فقط لاگ می کند و حالت IDS دارد و On که بصورت اکتیو در نقش IPS حملات را بلاک می کند، یک Off هم هست که mod_security را غیرفعال می کند.مسیر پیش فرض لاگ var/log/apache2/modsec_audit.log/ است، مثل خیلی از سرویس های دیگر سطح لاگ کردن قابل تنظیم است و ابزارهای متعدد بعنوان ModSecurity log parser وجود دارند. فرمت لاگ پیش فرض شامل سکشن های مختلف است که از A تا Z مشخص می شوند و هر کدام شامل بخشی از دیتای مرتبط با مورد لاگ شده هستند که جدول آن را در زیر مشاهده می فرمایید.در فایل کانفیگ هم در بخش SecAuditLogParts و بخش های دیگر مربوط به Log می توانید تعیین کنید کدام بخش ها فعال باشند و در کجا چه چیزی ذخیره شود.برای استفاده از OWASP ModSecurity Core Rule Set ابتدا آن را دریافت کنید :#git clone https://github.com/SpiderLabs/owasp-modsecurity-crs.git
#cd owasp-modsecurity-crs/
#cp crs-setup.conf.example /etc/modsecurity/crs-setup.conf
#cp -r rules/ /etc/modsecurity

#nano /etc/apache2/mods-enabled/security2.confمحتوی security2.conf در mods-enabled آپاچه برای این مثال ما باید بشرح ذیل باشد.* خط IncludeOptional /usr/share/modsecurity-crs/*.load اضافه است یادم رفت کامنت کنم!ما در اینجا بصورت کلی در تنظیمات ماژول آپاچه این دستورات را اعمال کرده ایم، می توان در سطح virtualhost و .htaccess هم با IfModule تنظیماتی اختصاصی اعمال کرد.حال که ModSecurity را نصب کردیم، ruleها و تنظیمات OWASP را هم اضافه کردیم و سرویس آپاچه را هم ریستارت کردیم اگر من سعی کنم مثلاً آدرس http://192.168.5.11/?exec=/bin/bash را باز کنم :یا سعی کنم مثلاً xss یا sql injection انجام دهم :لاگ موارد بالا هم در var/log/apache2/error.log/ و هم در var/log/apache2/modsec_audit.log/ با جزییات بیشتر دردسترس است.بجای نمایش Forbidden و دادن 403 و ...می توانید با اکشن status یا سایر اکشن های همگروه آن پیام دلخواه را نمایش دهید.برای نصب و تنظیم و تشریح دستورات مختلف ریفرنس خوبی دارد که در اینجا قابل دسترس است.ممکن است برای اپلیکیشن خود نیاز به استثناهایی داشته باشید که با مرور نمونه های EXCLUSION در پوشه rules می توانید یک فایل conf جدید بسازید و تعریف کنید.راهنما و نمونه های ruleهایی که می توانید برای mod security بنویسید بسیار زیاد است و تعداد زیادی هم ruleهای بروز آماده براش هست.یک نمونه هم rule مثلاً برای تشخیص و ممانعت لغت &quot;mybadword&quot; یا &quot;yourbadword&quot; بسازیم:#nano /etc/modsecurity/my_rules.conf
با این محتوی
SecRule FULL_REQUEST &amp;quot@rx (?i:(mybadword|yourbadword))&amp;quot &amp;quotid:&#039;500001&#039;,deny,log,msg:&#039;Bad Word Detected!&#039;&amp;quotابتدا در مسیر /etc/modsecurity/ یک فایل conf ساخته ام و درون آن خط به خط می توان rule تعریف کنم.در rule بالا ما تعریف کرده ایم که اگر هر بخش از ریکوئست (FULL_REQUEST) براساس regex (@rx) بدون درنظر گرفتن بزرگی و کوچکی حروف (i?) حاوی عبارت اول یا (|) دوم بود؛ مسدود و لاگ شود.حالا بعد از systemctl restart apache2 :و در لاگ ها براساس تنظیماتی که انجام داده اید این موارد ثبت می شوند که می توانید مثلاً برای ذخیره و پردازش دقیق تر به الاستیک سرچ یا اسپلانک بفرستید.خیلی آپشن و تنظیمات در اختیار شما هست که برای استفاده دقیق و کامل باید زمان مناسبی برای مرور مستنداتش اختصاص دهید. اما درخصوص نوشتن rule برای ModSecurity هم توضیح کوتاهی بدهم.در ساده ترین حالت مثل چیزی که ما بالا استفاده کردیم دایرکتیو SecRule قرار دارد که یک rule برای پردازش می سازد. انواع Configuration Directivesهایی که در اختیار دارید را اینجا ببینید.در سینتکسSecRule VARIABLES &amp;quotOPERATOR&amp;quot &amp;quotTRANSFORMATIONS,ACTIONS&amp;quotانواع VARIABLEها را اینجا ، انواعOPERATORها را اینجا و انواع ACTIONSها را اینجا و انواع Transformation functionsها را اینجا ببینید.بصورت کلی بنقل از مستندات OWASP ModSecurity Core Rule Set :There are ~105 variables in 6 different categories, some examples include:Request Variables - ARGS, REQUEST_HEADERS, REQUEST_COOKIESResponse Variables - RESPONSE_HEADERS, RESPONSE_BODYServer Variables - REMOTE_ADDR, AUTH_TYPETime Variables - TIME, TIME_EPOCH, TIME_HOURCollection Variables - TX, IP, SESSION, GEOMiscellaneous Variables - HIGHEST_SEVERITY, MATCHED_VARThere are ~36 operators in 4 different categories, some examples include:String Operators - rx, pm, beginsWith, contains, endsWith, streq, withinNumerical Operators - eq, ge, gt, le, ltValidation Operators - validateByteRange, validateUrlEncoding, validateSchemaMiscellaneous Operators - rbl, geoLookup, inspectFile, verifyCCThere are ~35 transformation functions in 6 different categories, some examples include:Anti-Evasion Functions - lowercase, normalisePath, removeNulls, replaceComments, compressWhitespaceDecoding Functions - base64Decode, hexDecode, jsDecode, urlDecodeUniEncoding Functions - base64Encode, hexEncodeHashing Functions - sha1, md5There are ~47 actions in 6 different categories, some examples include:Disruptive Actions - block, drop, deny, proxyFlow Actions - chain, skip, skipAfterMetadata Actions - phase, id, msg, severity, tagVariable Actions - capture, setvar, initcolLogging Actions - log, auditlog, nolog, sanitiseArgMiscellaneous Actions - ctl, multiMatch, exec, pause, append/prependهمچنین بیاد داشته باشیدEvery SecRule must have a VARIABLEEvery SecRule must have an OPERATOR, if none is listed @rx is implied.Every SecRule must have an ACTION. The only required action is id, however, several actions are implied by SecDefaultAction (default phase:2,log,auditlog,pass)Every SecRule must have an phase ACTION, this tells the rule when to fire. If no phase is included the default is phase:2.Every SecRule must have a disruptive ACTION. This is an action that describes what to do with the transaction if triggered. If no disruptive action is included the default is passTransformations are optional but should be used to prevent your rule from being bypassedدر انتهای مطلب تعدادی مثال ساده درج کرده ام که بهتر است اول این تعاریف را برای فهم بهتر آن ها مطالعه بفرمایید.اپراتورها که با @ شروع می شوند می توانند regular expression, pattern یا keywordی باشند که در variableها چک بشوند. مثل contains یا eq یا ...متغییرها یا variableها چیزی هستند که محتوی آن ها مقایسه می شود و می توانید از | (پایپ/یا) هم برای تعریف چند variable استفاده کنید یا از ! در ابتدا برای نقض و از &amp; در ابتدا برای مشخص کردن تعداد Variable استفاده کنید.در مورد اخیر (&amp;) مثلاً برای چک کردن HTTP responses هایی که Content-Type header ندارند چنین ruleی می نویسیم. یعنی تعداد این متغییر eq=0SecRule &amp;RESPONSE_HEADERS:Content-Type &amp;quot@eq 0&amp;quot &amp;quotlog,auditlog,msg:&#039;alert message&#039;&amp;quotپرکاربردترین variableها بشرح ذیل هستند :ARGS is a collection so it means all arguments including the POST Payload.ARGS_GET contains only query string parameters.ARGS_POST contains arguments from the POST body.FILES Contains a collection of original file names. Available only on inspected multipart/form-data requests.FULL_REQUEST Contains the complete request: Request line, Request headers and Request body.QUERY_STRING Contains the query string part of a request URI. The value in QUERY_STRING is always provided raw, without URL decoding taking place.REQUEST_BODY Holds the raw request body. This variable is available only if the URLENCODED request body processor was used, which will occur by default when the application/x-www-form-urlencoded content type is detected, or if the use of the URLENCODED request body parser was forced.REQUEST_HEADERS This variable can be used as either a collection of all of the request headers or can be used to inspect selected headers.REQUEST_METHOD This variable holds the request method used in the transaction.REQUEST_URI This variable holds the full request URL including the query string data (e.g., /index.php?p=X).برای اعمال تغییرات روی text قبل از چک شدن از Transformation functions استفاده می شود که در کنار اکشن ها با t:[Transformationfunctions] مشخص می شوند. مثلا t:lowercase یعنی همه text در variable بصورت حروف کوچک مقایسه شود.اکشن ها کاری هستند که باید درصورت match شدن operator با variableها انجام شود و برای تعریف چند action آن ها را با , جدا می کنیم. پنج Action Group وجود دارد که دستورات مختلف ذیل آن ها دسته بندی می شوند.اولین اکشنی که باید درج کنید id است.همچنین با اکشن chain می توانید چند rule متوالی را به هم وصل کنید و and انجام دهید.همچنین منظور از phase پنج فاز request cycle هستند. یعنی در کدام یک از فازها این rule اعمال شود.1.Request headers → REQUEST_HEADERS2.Request body → REQUEST_BODY3.Response headers → RESPONSE_HEADERS4.Response body → RESPONSE_BODY5.Logging → LOGGINGهمانطور که قبلاً عرض کردم خیلی گزینه و امکانات در اختیار دارید و از جمله مباحثی است که بهتر است یک ebook خوب و ترجیحاً جدیدتر درباره اش پیدا کنید و درکنار مستنداتش یکبار مطالعه بفرمایید تا روی مبحث تسلط پیدا کنید. یکی از بهترین کتابها ModSecurity Handbook است که هر چند در مورد نسخه 2 و کمی قدیمی است اما در کنار مستندات بسیار مفید خواهد بود.در پایان تعدادی مثال ساده و توضیح آن ها را بنقل از SANS در تصویر ببینیم:</description>
                <category>Hootan Alghaspour</category>
                <author>Hootan Alghaspour</author>
                <pubDate>Tue, 18 Jan 2022 12:50:14 +0330</pubDate>
            </item>
                    <item>
                <title>معرفی زیرساخت مجازی سازی XCP-NG و مقایسه با VMware ESXi</title>
                <link>https://virgool.io/@h.alghaspour/%D9%85%D8%B9%D8%B1%D9%81%DB%8C-%D8%B2%DB%8C%D8%B1%D8%B3%D8%A7%D8%AE%D8%AA-%D9%85%D8%AC%D8%A7%D8%B2%DB%8C-%D8%B3%D8%A7%D8%B2%DB%8C-xcp-ng-%D9%88-%D9%85%D9%82%D8%A7%DB%8C%D8%B3%D9%87-%D8%A8%D8%A7-vmware-esxi-r22g9f1i0k9e</link>
                <description>قبل از هر چیز تاریخچه XCP-ng را یک مرور کوتاه بکنیم :زن (Xen) یک هایپروایزر نوع 1 است که ابتدا توسط لابراتوار کامپیوتر دانشگاه کمبریج بصورت سورس باز و رایگان معرفی و منتشر شد و در طول سال ها بدلیل امکانات و بازدهی بالا بخصوص در حالت paravirtualization همیشه جزو هایپروایزرهای برتر بوده و برخی شرکت ها مثل citrix هم راه حل مجازی سازی حرفه ای خود را روی این پلتفرم پیاده سازی کردند یا مثلاً آمازون برای AWS تا 2018 از Xen استفاده می کرد که اکنون رفته روی نسخه اختصاصی از KVM با عنوان Nitro. در حال حاضر Xen توسط Linux Foundation توسعه و پشتیبانی می شود.سیتریکس با ارائه محصول Xen Server Free Edition که مبتنی بر Xen بود با امکانات و قابلیت های قابل قبول برای یک نسخه رایگان توانست استقبال خوبی کسب کند اما در سال 2017 ناگهان اعلام کرد امکانات و قابلیت های زیادی از نسخه رایگان حذف شده و فقط در نسخه لایسنس دار موجود خواهد بود! علاوه بر این برای نسخه های رایگان جاری که نصب بود نیز پس از آپدیت برخی قابلیت ها از کار می افتاد و ضربه جدی به پروژه Xen Server وارد شد زیرا دیگر نسخه رایگان Xen Server امکانات و قابلیت های قابل قبول برای زیرساخت مجازی سازی جدی نداشت.در مقابل Vates از فرصت استفاده کرد و XCP-NG را بعنوان جایگزین Xen Server با امکانات و قابلیت های فراوان برای راه اندازی یک زیرساخت مجازی سازی کامل و کاملاً سورس باز و رایگان معرفی کردند و در حال حاضر Xen بوسیله XCP-NG و بسته تکمیلی آن (Xen Orchestra) در حال بازگشت به سطح بالای پلتفرم های مجازی سازی پراستفاده است.خاطره Citrix اینقدر تلخ بوده که پایین سایت XCP-NG خیلی مشخص ذکر شده :Neither XCP-ng nor its products are sponsored by, endorsed by, or affiliated with Citrix or Citrix products or services in any wayراهنمای سریع راه اندازی زیرساخت مجازی با XCP-ng :راحت ترین روش نصب دریافت فایل ISO و رایت کردن آن روی یک USB یا CD/DVD و بوت کردن با آن است، فرآیند نصب بسیار ساده و مرحله به مرحله جلو می رود و نکته خاصی ندارد.برای شروع کار بهتر است این معرفی و این توتوریال را ببینید.پس از نصب روی خود سرور یک کنسول با امکانات و خط فرمان پایه می گیرید که البته روی این خط فرمان همه کار می توان کرد.اما برای مدیریت سرورها و زیرساخت مجازی سازی روی ویندوز می توانید XCP-ng Center را دانلود و نصب کنید که چیزی شبیه vCenter در VMware است.اصولاً بهتر است اول storageها را تنظیم کنید، XCP-ng پشتیبانی خوبی از انواع storageهای تحت شبکه دارد.در این زمان که این مطلب را می نویسم هنوز راه حلی برای استفاده از object storage بصورت مستقیم روی XCP-NG پیدا نکرده ام اما احتمالاً در نسخه های بعد اضافه خواهد شد. درباره storageها بنقل از مستنداتش :مثلاً برای نصب vmها بهتر است اول یک storage که فایل های iso روی آن است معرفی کنید.بعد از آن می توانید این isoها را برای نصب سیستم عامل vmها استفاده بنمایید.با Xen Orchestra علاوه بر یک اینترفیس تحت وب خوب امکانات و قابلیت های بیشتری را نیز می گیرید. البته نسخه های کامل آن نیاز به لایسنس دارند اما نسخه پایه را می توان رایگان دانلود و نصب کرد.برای نصب From the sources نسخه رایگان و پایه اینجا را ببینید.پس از نصب XCP-NG با ورود آدرس IP آن در مرورگر هم یک صفحه راهنمای ساده می بینید.از اینجا هم میتوانید برای deploy کردن XOA (Xen Orchestra virtual Appliance) اقدام کنید. در این حالت خودش یک VM از نوع Debian 11 با نام XOA می سازد و روی آن XOA Server را نصب می کند که با وارد کردن IP این ماشین در مرورگر می توانید به Xen Orchestra دسترسی داشته باشید.مقایسه XCP-NG vs VMware ESXi :در حقیقت VMware لیدر بازار است و vSphere عنوان Best Server Virtualization Software for 2021 را دارد. اما XCP-ng امکانات و قابلیت های بیشتری در نسخه کاملاً رایگان و سورس باز ارائه می دهد و میتوان با همین امکانات نیز کار را راه انداخت و در نهایت api هم دارید و یک لینوکس در دست دارید که می توان برخی امکانات و راه حل های جانبی دیگر را برای تکمیل زیرساخت مجازی سازی روی آن نصب کرد.فول ترین لایسنس Xen Orchestra سالیانه 6هزار دلار است که تمامی امکانات و قابلیت های یک زیرساخت مجازی سازی کامل را به همراه پشتیبانی دارد، تقریباً قیمتی معادل همان vSphere فول، البته نسخه رایگان هم با Basic features و نسخه های ارزان تر هست. من خیلی با سیستم و لایسنس های VMware آشنا نیستم اما ظاهراً XCP-NG در مجموع هزینه لایسنس کمتری برای قابلیت های مشابه دارد.تصاویر درباره مقایسه امکانات از این فایل در سایت خود XCP-NG گرفته شده است :در نهایت باید عرض کنم نصب و تست نسخه اخیر مجموعه XCP-NG / Xen Orchestra / xcp-ng Center حتی با لایسنس رایگان برای من بسیار راضی کننده و جالب بود و می توان بعنوان یک پلتفرم کامل مجازی سازی برای سناریوهای Mission Critical در لیست بررسی و مقایسه قرارش داد.</description>
                <category>Hootan Alghaspour</category>
                <author>Hootan Alghaspour</author>
                <pubDate>Sun, 14 Nov 2021 19:13:57 +0330</pubDate>
            </item>
                    <item>
                <title>استراتژی های برخط کردن سیستم</title>
                <link>https://virgool.io/@h.alghaspour/%D8%A7%D8%B3%D8%AA%D8%B1%D8%A7%D8%AA%DA%98%DB%8C-%D9%87%D8%A7%DB%8C-%D8%A8%D8%B1%D8%AE%D8%B7-%DA%A9%D8%B1%D8%AF%D9%86-%D8%B3%DB%8C%D8%B3%D8%AA%D9%85-jtr5pinx61gv</link>
                <description>در حقیقت عنوان ترجمه Deployment Strategies است که فکر کردم این ترجمه بهتر باشد.قضیه از این قرار است که شما یک سیستم جدید یا یک قابلیت جدید یا نسخه بروزرسانی شده از یک سیستم و/یا یک قابلیت که اکنون زیربار است را می خواهید جایگرین نسخه قبلی کنید، در این فرآیند باید هزینه و ریسک ناشی از اشتباهات و موارد حساب نشده حداقل باشد و تا حد امکان سیستم downtime نداشته باشد.در بسیاری از محیط ها و ابزارهای DevOps مثل OpenShift و کوبرنتیس و ... هم همین عناوین و ادبیات را در سرفصل Deployment مشاهده می کنید و لازم است درک صحیحی از این مفاهیم داشته باشید.شش استراتژی متداول و معروف برای اینکار وجود دارند :روش Recreate : به این روش Basic Deployment هم می گویند، به این صورت است که نسخه A را خاموش کرده و نسخه B را آنلاین می کنیم.مزایای این روش سرعت و سادگی و نو شدن کل سیستم در یک مرحله است. همچنین نیازی به منابع مضاعف برای آنلاین نگه داشتن هر دو نسخه نیست.معایب آن این است که زمان و محیطی برای تست نسخه جدید در محیط production نیست و درصورت بروز مشکل زیربار عملیات در محیط واقعی کاربران تحت تاثیر قرار می گیرند، همچنین بین خارج شدن نسخه قدیم از دسترسی تا آنلاین شدن نسخه جدید ممکن است وقفه ای وجود داشته باشد. از سوی دیگر اگر نسخه قدیم از مدار و زیرساخت خارج شده باشد بارگذاری و آنلاین کردن مجدد آن ممکن زمانبر بوده و در این فاصله درخواست ها قابل پاسخگویی نباشند.روش A/B testing :این روش عمدتاً در آزمایش و سنجش عملکرد نسخه های جدید و سنجش بازخوردهای کاربران و صحت و بازدهی سیستم استفاده می شود و به اینصورت است که بخش مشخصی از کاربران براساس ruleهای تعریف شده به نسخه های جدید هدایت شوند (مثلاً deviceهای متفاوت، موقعیت جغرافیایی متفاوت، گروه کاربری متفاوت و ...) و شرایط و پاسخ گویی نسخه های جدید نسبت به این کاربران سنجیده شود.دقت بفرمایید ممکن است یک نسخه برای 2 دسته کاربری که شرایط متفاوت دارند نیز تست شود. یعنی هم نسخه هم دسته کاربری متغییر هستند.مزایای آن این است که میتوان روی اینکه چه کاربرانی چه نسخه ای را می بینند و بازخورد آن کنترل و سنجش داشت و نسخه های مختلف را همزمان در محیط production تست کرد.معایب آن این است که این روش در حقیقت یک روش تحقیقاتی برای آزمایش و سنجش است و برای کار واقعی با تعدد نسخ و محیط ها مواجه خواهیم شد که فرآیند deployment مستر پیچیده تر و پرریسک تر می کند ، همچنین ممکن است در توزیع ترافیک کاربران و سنجش بازخورد آن ها و سیستم پیچیدگی های فنی وجود داشته باشد.روش Shadow :در این روش نسخه قدیم و جدید همزمان آنلاین هستند، یک کپی از ترافیک جاری نسخه قدیم روی نسخه جدید ارسال می شود تا صحت و بازدهی آن تست شود. دقت بفرمایید نسخه قدیم آنلاین و فعال است و معمولاً کل عملیات نیز توسط همان نسخه قدیم انجام می شود، در اینجا مقصود تست شبیه سازی شده نسخه جدید با ترافیک و شرایط production بوسیله یک کپی زنده از شرایط نسخه قدیمی جاری است.مزیت این روش امکان تست نسخه جدید در محیط و شرایط production و قابلیت پیگیری خطاهای بوجود آمده بدون اثر گذاری روی کاربران و ترافیک زنده است. همچنین پس از کسب اطمینان می توان ترافیک را بصورت مستقیم روی نسخه جدید ارسال کرد و نسخه قدیمی را خاموش نمود.عیب آن نیاز به منابع مضاعف برای آنلاین کردن 2سیستم و پیچیدگی فنی پیاده سازی برای انتقال یک کپی از ترافیک نسخه قدیم به جدید و گرفتن بازخوردی که دیگر الزاماً بازخورد یک کاربر زنده واقعی نیست است.روش Canary :در این روش ما میزان آنلاین شدن و زیربار رفتن سیستم جدید را براساس فازهای تعیین شده افزایش می دهیم. یعنی مثلاً در ابتدا 1% ترافیک و عملیات روی نسخه جدید است، اگر ok بود ، 10% ، اگر ok بود 30% و همینطور تا 100% قضیه ادامه پیدا می کند.به این روش phased rollout و incremental rollout هم می گویند.مزیت آن کاهش ریسک و downtime و تست تدریجی نسخه جدید است.عیب آن طولانی بودن زمان برخط کردن کامل سیستم جدید و پیچیدگی ها عملیاتی برای تقسیم ترافیک و سخت بودن تست های تعیین و کنترل شده بخصوص درباره بازخوردهای کاربران است.روش Dark launching :لازم بذکر است این مورد بیشتر در سرفصل تست نرم افزار و قابلیت و Continuous Delivery دسته بندی می شود و استراتژی system deployment نیست. اما برای آشنایی و با توجه به مرتبط بودن و استفاده زیاد فکر کردم ذکر آن اینجا مفید باشد. برای مطالعه بیشتر درباره Automated testing اینجا را ببینید.این روش حالتی از روش shadow و A/B Testing و canary برای برخط کردن یک قابلیت جدید روی سیستم موجود است و بیشتر برای قابلیت های جدید بکار می رود. یعنی معمولاً وقتی نسخه قبلی نبوده و یک چیز جدید می خواهد به یک سیستم اضافه شود به این روش تست می کنند. در این روش قابلیت جدید روی backend فعال می شود یا به اصطلاح predeploy می شود، سپس برای دسته تعیین شده ای از کاربران روی frontend فعال می شود تا عملکرد آن در محیط production تست شود و اگر جواب آزمایش و سنجش ها مثبت بود، قابلیت جدید برای همه کاربران فعال خواهد شد. روش Ramped :این روش به rolling-update هم معروف است و عموماً در محیط های کانتینری و ارکستراسیون ها استفاده می شود. در این روش نسخه جدید نود به نود جایگزین نسخه قدیمی می شود.ریسک پایین و سادگی نسبی فرآیند پیاده سازی و انتقال از مزایای این روش هستند.معایب آن نداشتن کنترل روی ترافیک و نداشتن فضای آزمایش و سنجش تعیین شده و امکان پیچیدگی در roll back است.روش Blue/Green :در این روش نسخه جدید بصورت کامل در کنار نسخه قدیمی آنلاین می شود و در شرایط staging قرار می گیرد. سپس روی نسخه کامل جدید تست صحت و بازدهی در محیط و زیرساخت واقعی انجام می شود و درصورت صحت،  ترافیک از روی نسخه قبلی به نسخه جدید منتقل می گردد.تفاوت آن با shadow این است که در مرحله تست بجای یک کپی از ترافیک واقعی، معمولاً با automated testها و آزمایش های شبیه سازی شده تست انجام می شود. مزیت این روش که بسیار متداول هم هست سادگی تست و انتقال ، سادگی بازگشت به شرایط قبل فقط با تغییر مسیر ترافیک و زیر بار واقعی بودن یک نسخه در آن واحد است.عیب آن نیاز به منابع مضاعف در محیط واقعی برای آنلاین بودن همزمان نسخه قدیم و جدید و امکان ایجاد مشکلاتی زیربار واقعی و کامل که در تست های روتین کشف نشده اند است.این هفت مورد مهمترین روش های اصلی برای آنلاین کردن یک سیستم کامل جدید یا قابلیت جدید هستند که البته ممکن است بسته به نیاز و شرایط با تغییرات یا بصورت ترکیبی استفاده شوند یا اساساً روشی اختصاصی برای سناریوی شما نیاز باشد که با ایده گرفتن از اینها باید طراحی کنید.</description>
                <category>Hootan Alghaspour</category>
                <author>Hootan Alghaspour</author>
                <pubDate>Wed, 10 Nov 2021 23:13:20 +0330</pubDate>
            </item>
                    <item>
                <title>شعبده بازی با Disk Image ماشین های مجازی بوسیله libguestfs</title>
                <link>https://virgool.io/@h.alghaspour/%D8%B4%D8%B9%D8%A8%D8%AF%D9%87-%D8%A8%D8%A7%D8%B2%DB%8C-%D8%A8%D8%A7-disk-image-%D9%85%D8%A7%D8%B4%DB%8C%D9%86-%D9%87%D8%A7%DB%8C-%D9%85%D8%AC%D8%A7%D8%B2%DB%8C-%D8%A8%D9%88%D8%B3%DB%8C%D9%84%D9%87-libguestfs-zjpavnyt8uvx</link>
                <description>بصورت خلاصه libguestfs مجموعه ای از ابزارها برای کار با انواع Disk Image های مورد استفاده در VM ها و یکی از ابزارهای اصلی Virt Tools است.  بنقل از وب سایت خودش :libguestfs is a set of tools for accessing and modifying virtual machine (VM) disk images. You can use this for viewing and editing files inside guests, scripting changes to VMs, monitoring disk used/free statistics, creating guests, P2V, V2V, performing backups, cloning VMs, building VMs, formatting disks, resizing disks, and much more.libguestfs can access almost any disk image imaginable. It can do it securely — without needing root and with multiple layers of defence against rogue disk images. It can access disk images on remote machines or on CDs/USB sticks. It can access proprietary systems like VMware and Hyper-V.لیستی از فرامین و ابزارهای در دسترس در ذیل ذکر شده اند :guestfs — main API
guestfish — interactive shell
guestmount — mount guest filesystem in host
guestunmount — unmount guest filesystem
virt-alignment-scan — check alignment of virtual machine partitions
virt-builder — quick image builder
virt-builder-repository — create virt-builder repositories
virt-cat — display a file
virt-copy-in — copy files and directories into a VM
virt-copy-out — copy files and directories out of a VM
virt-customize — customize virtual machines
virt-df — free space
virt-dib — safe diskimage-builder
virt-diff — differences
virt-edit — edit a file
virt-filesystems — display information about filesystems, devices, LVM
virt-format — erase and make blank disks
virt-get-kernel — get kernel from disk
virt-inspector — inspect VM images
virt-list-filesystems — list filesystems
virt-list-partitions — list partitions
virt-log — display log files
virt-ls — list files
virt-make-fs — make a filesystem
virt-p2v — convert physical machine to run on KVM
virt-p2v-make-disk — make P2V ISO
virt-p2v-make-kickstart — make P2V kickstart
virt-rescue — rescue shell
virt-resize — resize virtual machines
virt-sparsify — make virtual machines sparse (thin-provisioned)
virt-sysprep — unconfigure a virtual machine before cloning
virt-tail — follow log file
virt-tar — archive and upload files
virt-tar-in — archive and upload files
virt-tar-out — archive and download files
virt-v2v — convert guest to run on KVM
virt-win-reg — export and merge Windows Registry keys
libguestfs-test-tool — test libguestfs
libguestfs-make-fixed-appliance — make libguestfs fixed appliance
hivex — extract Windows Registry hive
hivexregedit — merge and export Registry changes from regedit-format files
hivexsh — Windows Registry hive shell
hivexml — convert Windows Registry hive to XML
hivexget — extract data from Windows Registry hive
febootstrap — tool for building supermin appliances
febootstrap-supermin-helper — febootstrap helper
supermin — tool for building supermin appliances
supermin-helper — supermin helper
guestfsd — guestfs daemonیکی از بهترین مقاصد استفاده از این ابزارها بجز موارد زیرساختی و امنیتی و rescue و ... در اتوماسیون ها و اسکریپت نویسی است. مثلاً برای برنامه ریزی فرآیندهای مختلف روی یک یا یک پشته از ماشین های مجازی بوسیله ansible خیلی خوب وکارا هستند.من چند مثال از پرکاربردترین ابزارهاش می زنم تا فرمت و قضیه را بصورت کلی بیان کرده باشم، محیط من اینجا KVM/QEMU روی یک CentOS8 است و ماشین مجازی هدف هم یک CentOS8 است. دقت بفرمایید برای مثلاً دسترسی به فایل های داخل Disk Image چند روش و راه و ابزار هست که من همه را ذکر نکرده ام و متداولترین را مثال زده ام. disk image من در vm1data/centos8.qcow2/ قرار دارد.* برای استفاده از بسیاری از ابزارها مثل virt-customize هم باید vm خاموش و disk image آزاد باشد.* اگر در هنگام اجرا خطاهای qemuی گرفتید احتمالاً با ویرایش فایل etc/libvirt/qemu.conf/ و مثلاً تعریف user و group مشکل حل می شود.نصب libguestfs#yum install libguestfs-tools
#apt-get install libguestfs-toolsگرفتن اطلاعات درباره ماشین مجازی از روی Disk Image#qemu-img info /vm1data/centos8.qcow2
و
#virt-filesystems --long -h --all -a /vm1data/centos8.qcow2
و
#virt-inspector /vm1data/centos8.qcow2این سه متداولترین ابزارها برای گرفتن اطلاعات سیستم از vm و وضعیت فایل سیستم آن از روی disk image هستند. در مورد virt-inspector باید توجه داشته باشید که کل متادیتاهای vm را با فرمت xml خروجی می دهد و بهتر است خروجی آن را به یک فایل &lt;  بدهید که بتوانید مرورگرهای xml داخل این دیتا را مرور بفرمایید.بجای a diskimage- می توانید از d domain- هم استفاده بفرمایید که اینجا منظور از domain همان KVM VM guest name است (که با virsh list --all می گیرید).  اگر در مقالات و مستندات دیگری ملاحظه فرمودید همه را با d- زده متوجه باشید بجای معرفی آدرس فایل disk image از نام آن استفاده کرده است.کپی فایل به/از داخل Disk Image#mkdir vmfiles
#virt-copy-out -a /vm1data/centos8.qcow2 /root/file1 /root/vmfiles

و برای فرو کردن فایل به ماشین مجازی
#virt-copy-in -a /vm1data/centos8.qcow2 /root/file2ONHOST /root

در اولی مبداء ابتدا نوشته می شود و فایل روی ماشین مجازی است و پوشه مقصد روی هاست که این فایل باید در آن کپی شود سپس ذکر شده

در دومی مبداء که ابتدا نوشته شده فایل روی هاست و مقصد پوشه روی ماشین مجازی استهمین کار با استفاده از virt-customize بشکل ذیل می شود :#virt-customize -a /vm1data/centos8.qcow2 --copy fileonguest:fileonhost
#virt-customize -a /vm1data/centos8.qcow2 --copy-in fileonhost:fileonguest

توضیحات :

--copy SOURCE:DEST
Copy files or directories recursively inside the guest. Wildcards cannot be used.

--copy-in LOCALPATH:REMOTEDIR
Copy local files or directories recursively into the disk image, placing them in the directory REMOTEDIR (which must exist). Wildcards cannot be used.این virt-customize یکی از قویترین و کاملترین ابزارها برای کار کردن با فایل سیستم ماشین مجازی است.virt-customize
    [ -a disk.img [ -a disk.img ... ] | -d domname ]
    [--attach ISOFILE] [--attach-format FORMAT]
    [ -c URI | --connect URI ] [ -n | --dry-run ]
    [ --format FORMAT] [ -m MB | --memsize MB ]
    [ --network | --no-network ]
    [ -q | --quiet ] [--smp N] [ -v | --verbose ] [-x]
    [--append-line FILE:LINE] [--chmod PERMISSIONS:FILE]
    [--commands-from-file FILENAME] [--copy SOURCE:DEST]
    [--copy-in LOCALPATH:REMOTEDIR] [--delete PATH] [--edit FILE:EXPR]
    [--firstboot SCRIPT] [--firstboot-command &#039;CMD+ARGS&#039;]
    [--firstboot-install PKG,PKG..] [--hostname HOSTNAME]
    [--install PKG,PKG..] [--link TARGET:LINK[:LINK..]] [--mkdir DIR]
    [--move SOURCE:DEST] [--password USER:SELECTOR]
    [--root-password SELECTOR] [--run SCRIPT]
    [--run-command &#039;CMD+ARGS&#039;] [--scrub FILE] [--sm-attach SELECTOR]
    [--sm-register] [--sm-remove] [--sm-unregister]
    [--ssh-inject USER[:SELECTOR]] [--truncate FILE]
    [--truncate-recursive PATH] [--timezone TIMEZONE] [--touch FILE]
    [--uninstall PKG,PKG..] [--update] [--upload FILE:DEST]
    [--write FILE:CONTENT] [--no-logfile]
    [--password-crypto md5|sha256|sha512] [--selinux-relabel]
    [--sm-credentials SELECTOR]تغییر پسورد user یا root#virt-customize -a /vm1data/centos8.qcow2 --root-password password:NEWPASS
یا
#virt-sysprep --root-password password:NEWPASS -a /vm1data/centos8.qcow2
برای یوزر
#virt-customize -a /vm1data/centos8.qcow2 --password USER:PASS
#virt-sysprep --password USER:PASS -a /vm1data/centos8.qcow2از virt-sysprep غالباً برای reset or unconfigure a virtual machine so that clones can be made from it استفاده می شود یعنی همه ssh keyها و mac address ها و هرچیز دیگری را که ممکن است کانفلیکت بدهد را هم ریست می کند. بنابراین برای فقط تغییر پسورد همان virt-customize بهتر است.نصب و حذف بسته ها#virt-customize -a /vm1data/centos8.qcow2 --install [PACKAGENAME]
#virt-customize -a /vm1data/centos8.qcow2 --uninstall [PACKAGENAME]دیدن و لیست کردن فایل هااینکار با ابزار virt-ls انجام می شود، مثلاً دستور ذیل فایل هایی را که در 7 روز گذشته تغییر کرده اند را نمایش می دهد :#virt-ls -lR -a /vm1data/centos8.qcow2 --time-days / | awk &#039;$6 &lt;= 7&#039;همینطور virt-tail را داریم که به معنای tail -f است و virt-cat را داریم که برای cat کردن است و ...ساخت ماشین مجازی جدید با virt-builderابزارهای libguestfs متنوع و زیاد هستند و باید براساس مستندات یک مرور کلی روی آن ها داشته باشید، یکی از معروفترین و پرکاربردترین این ابزارها virt-builder است که برای ساخت راحت یک disk image آماده برای یک ماشین مجازی جدید استفاده می شود که خروجی آن را می توانید در محیط مجازی سازی خود import کنید.برای ساخت disk image ماشین مجازی virt-builder یکسری OS templates دارد که باید یکی از اینها را انتخاب بفرمایید.برای دیدن این لیست از virt-builder --list# استفاده بفرمایید.برای دیدن توضیحات مربوط به هر کدام از virt-builder --notes OSTEMPLATE# استفاده بفرمایید،مثلاً virt-builder --notes centos-8.2 بعد از انتخاب OS Template می توانید پارامترها را تنظیم نمایید تا ساخت disk image شروع شود.مثلاً :#virt-builder -x -v centos-8.2 --format qcow2 --size 50G --arch x86_64 --root-password password:centos --hostname centos.local --install &#039;htop&#039; --firstboot-command &#039;yum update -y&#039;با توجه به حجم زیاد OS Template ها وقتی برای اولین بار با یکی یک disk image می سازید آن OS Template روی سیستم کش می شود تا دفعات بعد دوباره دانلود نشود. برای دیدن لیست OS Template های کش شده از virt-builder --print-cache# استفاده فرمایید. برای حذف کش از virt-builder --delete-cache# و برای دانلود همه OS Templateها و کش شدن آن ها از virt-builder --cache-all-templates# استفاده بفرمایید.سوییچ های x- و v- برای نمایش جزییات دیباگ و توضیحات بصورت verbose هستند.استفاده از guestfish این ابزار بصورت interactive shell یا shell scripts برای کار با فایل سیستم disk image ها استفاده می شود. در حقیقت نوعی خط فرمان libguestfs است و مثل بقیه ابزارهای از همان apiها استفاده می کند.صحبت درباره امکانات و دستورات guestfish یک مقاله مجزا می طلبد اما برای اینکه با کلیات موضوع آشنا باشید تا بتوانید نیاز خود را در مستندات آن پیدا کنید چند مثال می زنم.برای مثال mount کردن یک پارتیشن را بوسیله guestfish ببینیم، ro به معنای readonly و rw به معنای read/write است :[root@centos ~]# guestfish --ro -a /vm1data/centos8.qcow2
Welcome to guestfish, the guest filesystem shell for editing virtual machine filesystems and disk images.
Type: ‘help’ for help on commands
      ‘man’ to read the manual
      ‘quit’ to quit the shell
&gt;&lt;fs&gt; run
 100% ⟦▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒⟧ 00:00
&gt;&lt;fs&gt; list-filesystems
/dev/sda1: ext4
/dev/cl/root: xfs
/dev/cl/swap: swap
&gt;&lt;fs&gt; mount /dev/cl/root /
&gt;&lt;fs&gt; ! mkdir /tmp/myGuestMount
&gt;&lt;fs&gt; mount-local /tmp/myGuestMount readonly:true
&gt;&lt;fs&gt; mount-local-runبجز این از guestmount هم برای Mount a guest filesystem on the host می توان استفاده کرد. مثلاً :# guestmount -a /vm1data/centos8.qcow2 -m /dev/cl/root --ro /mntدر موقع mount کردن با guestmount یا guestfish باید به سیستم عامل guest و اینکه کدام پارتیشن آن باید mount شود دقت کنید.</description>
                <category>Hootan Alghaspour</category>
                <author>Hootan Alghaspour</author>
                <pubDate>Sat, 30 Oct 2021 21:48:06 +0330</pubDate>
            </item>
                    <item>
                <title>راهنمای سریع استفاده از dig</title>
                <link>https://virgool.io/@h.alghaspour/%D8%B1%D8%A7%D9%87%D9%86%D9%85%D8%A7%DB%8C-%D8%B3%D8%B1%DB%8C%D8%B9-%D8%A7%D8%B3%D8%AA%D9%81%D8%A7%D8%AF%D9%87-%D8%A7%D8%B2-dig-rykqrozivmjv</link>
                <description>برای پرس و جوی DNS یکی از متداولترین و پرکاربردترین ابزارها dig است. dig یکی از ابزارهای BIND بوده و بنابراین روی *نیکسی ها متداول تر است هر چند برای ویندوز هم نسخه نصبی دارد اما روی ویندوز معمولاً از nslookup استفاده می شود.قبل از هر چیز روی CentOS/RHEL/Fedoraها bind-utils و روی Debian/Ubuntuها dnsutils باید نصب باشد.#yum install bind-utils
#apt-get install dnsutilsکوئری DNS پیش فرض :#dig google.comبرای اینکه وقتی dig خالی می زنید بصورت پیش فرض چه پارامترهایی استفاده شود می توانید فایل digrc./~ را بسازید و پارامتر های مدنظر را در آن اضافه کنید. تعدادی پرکاربردترین این ها در ادامه ذکر خواهند شد. [root@centos ~]# echo ANY &gt; ~/.digrc
[root@centos ~]# cat .digrc
ANYبه تصویر دقت بفرمایید :تعریف رکوردهای مدنظر برای پرس و جو و فرمت پاسخاستفاده از پارامتر ANY که دربالا مثال زده شد باعث برگشت تمام رکوردهای DNS دامنه مدنظر مثل A, AAAA, TXT, MX, NS و ... می شود. کامل ترین پاسخ dig درباره یک دامنه با این پارامتر بدست می آید.همینطور می توانیم بجای ANY ، نوع رکورد DNS که پرس و جو می کنیم را هم مشخص کنیم :#dig google.com ANY
#dig google.com MXدر اسکریپت نویسی ها خیلی از dig استفاده می شود، بنابراین کنترل و مشخص و محدود کردن خروجی های آن بسیار کارگشاست.دقت بفرمایید برای گرفتن رکورد PTR باید از سوییچ x- استفاده بفرمایید.یکی از دلایل استفاده از dig در اسکریپت نویسی ها این است که dig قابلیت تعیین خروجی خوبی دارد، مثلاً یک پارامتر short+ دارد که فقط مقدار رکورد (مثلاً IP نهایی) را برمیگرداند تا براحتی بتوان از خروجی آن استفاده کرد. در تصویر دقت بفرمایید می توان از چند پارامتر برای کنترل خروجی های استفاده کرد.همانطور که short+ دارد ، noshort+ هم دارد که به معنای غیر کوتاه است. اکثر پارامترها چنین شرایطی را دارند. تعداد این پارامترها و گزینه ها زیاد و متنوع هستند، لیست همه را می توانید با dig -h بگیرید که تصویر خروجی آن را اینجا گذاشته ام.یکی از روش های کنترل خروجی dig این است که ابتدا از پارارمتر noall+ برای خالی کردن خروجی ها استفاده کرد و سپس پارامترهای مدنظر را تعریف کرد. برای مواردی مثل short+ البته کاربرد ندارد.برخی دیگر از پرکاربردترین استفاده های dig نیز در ذیل لیست شده اند :پرس و جو از DNS Server تعیین شده :برای تعریف اینکه پرس و جو از چه DNS Serverی انجام شود (مثل server در nslookup) از DNSSERVER@ استفاده می شود.#dig @8.8.8.8 google.comتعیین پورت DNS Server هدف :برای تعریف پورت از p- استفاده می کنیم.#dig -p 53 google.comاستفاده از بستر IPv4 یا IPv6:بصورت پیش فرض جستجوهای dig در بستر IPv4 انجام می شود، برای مشخص کردن استفاده از بستر IPv4 از 4- و برای IPv6 از 6- استفاده می کنیم. دقت بفرمایید برای گرفتن آدرس های IPv6 از همان رکورد AAAA استفاده می شود و این سوییچ برای تعریف استفاده از IPv4 یا IPv6 است. (گرفتن رکورد IPv4 در بستر IPv6)#dig -6 @2001:4860:4860::8888 google.com A (گرفتن رکورد IPv6 در بستر IPv4)#dig -4 @8.8.8.8 google.com AAAAگرفتن DNS Path جستجو (Trace DNS Path) :پیمودن فرآیند و مسیر پاسخ به جستجوی DNS از Root Server ها تا سرور نهایی استفاده متداول ندارد و معمولاً در مسائل زیرساخت و/یا جمع آوری اطلاعات و/یا آموزشی استفاده می شود.برای اینکار پارامتر trace+ را استفاده می کنیم.#dig facebook.com +traceدر ضمن گوگل هم یک dig آنلاین دارد که از طریق وب می توانید استفاده بفرمایید :https://toolbox.googleapps.com/apps/dig/دقت بفرمایید با توجه به زیرساخت های ابری و CDNها و WAFها و ... مقدار بازگشتی پرس و جوی DNS ممکن است برای وب سایت های بزرگ بسته به زمان و مکان انجام پرس و جو متفاوت باشد.</description>
                <category>Hootan Alghaspour</category>
                <author>Hootan Alghaspour</author>
                <pubDate>Mon, 25 Oct 2021 02:33:12 +0330</pubDate>
            </item>
                    <item>
                <title>بازکردن GUI ابزارها از طریق SSH و X11 Forwarding</title>
                <link>https://virgool.io/@h.alghaspour/%D8%A8%D8%A7%D8%B2%DA%A9%D8%B1%D8%AF%D9%86-gui-%D8%A7%D8%A8%D8%B2%D8%A7%D8%B1%D9%87%D8%A7-%D8%A7%D8%B2-%D8%B7%D8%B1%DB%8C%D9%82-ssh-%D9%88-x11-forwarding-q0y0vcpxlgmd</link>
                <description>بصورت خلاصه شما تعدادی ابزار GUI دار در لینوکس remote دارید و می خواهید از طریق ssh روی ویندوز یا لینوکسی دیگری که به آن ssh زده اید، GUI آن ابزار روی سیستم local باز شود.1- اول از همه باید یک X Server روی سیستم local فعال باشد. روی لینوکس هایی که دسکتاپ دارند که هست ، روی ویندوز هم من از mobaxterm بعنوان ترمینال استفاده می کنم که خودش X Server هم دارد، در غیر اینصورت می توانید یک X Server مثل Xming را نصب کنید.2- در مرحله دوم روی لینوکس remote باید xorg-x11-xauth را نصب کنید :#dnf install xorg-x11-xauth3- در مرحله بعد باید در فایل etc/ssh/sshd_config/ مقدار X11Forwarding  را معادل yes قرار دهید.4- سپس سرویس ssh را ریستارت کنید.#systemctl restart sshd5- و اکنون می توانید با استفاده از سوییچ X- در هنگام اتصال ssh ، رابط GUI ابزار را روی local باز بفرمایید.#ssh -X user@remotehostاگر بجای دستور ssh از putty استفاده می کنید هم باید x11 forwarding را روی آن تیک بزنید.درخصوص متداولترین خطاها و مشکلات مثل Unable to init server: Could not connect: Connection refused یا Gtk-WARNING **: 19:22:59.609: cannot open display: :x یا بازشدن برنامه روی همان سیستم ریموت بجای لوکال؛* بعضی اوقات بهتر است بجای X- از Y- استفاده بفرمایید.-Y Enables trusted X11 forwarding. Trusted X11 forwardings are not subjected to the X11 SECURITY extension controls.* بعضی اوقات ممکن است وقتی دستوری را روی سیستم لوکال می زنید روی همان سیستم ریموت باز کند، در این شرایط متداولترین مشکل قضیه DISPLAY است که به روش ذیل می توانید آن را مشخص کنید.#export DISPLAY=&#039;IP:0.0&#039;در اینجا IP آدرس IP سیستم لوکال است که به سیستم ریموت متصل می شود.* بعضی اوقات باید دستی X11UseLocalhost را در فایل sshd_config معادل no  قرار دهید.لیست و توضیحات تنظیمات sshd_config را اینجا ببینید.</description>
                <category>Hootan Alghaspour</category>
                <author>Hootan Alghaspour</author>
                <pubDate>Thu, 21 Oct 2021 16:38:56 +0330</pubDate>
            </item>
                    <item>
                <title>گرفتن Client IP واقعی ،پشت لودبالانسر و ابر</title>
                <link>https://virgool.io/@h.alghaspour/%DA%AF%D8%B1%D9%81%D8%AA%D9%86-client-ip-%D9%BE%D8%B4%D8%AA-%D8%A7%D8%A8%D8%B1-%D9%88-%D9%84%D9%88%D8%AF%D8%A8%D8%A7%D9%84%D8%A7%D9%86%D8%B3%D8%B1-cnb0zhravdpy</link>
                <description>منظور اینجا IP Leak و گرفتن IP واقعی کاربر پشت VPN نیست.اینجا ما از سمت اپلیکیشن به قضیه نگاه می کنیم، شاید تجربه اش را داشته باشید وقتی سیستم را پشت انواع لودبالانسر مثل همین سیستم های ابر و CDN و مثلاً کلودفلر می برید دیگر روش های متداول در زبان های برنامه نویسی مختلف بجای IP کاربر IP لودبالانسر را برمی گردانند.راه حل خواندن IP کاربر از روی headerهای HTTP است. مثلاً X-Forwarded-For :The X-Forwarded-For (XFF) header is a de-facto standard header for identifying the originating IP address of a client connecting to a web server through an HTTP proxy or a load balancer. When traffic is intercepted between clients and servers, server access logs contain the IP address of the proxy or load balancer only. To see the original IP address of the client, the X-Forwarded-For request header is used.تکه کد قبلی که داشتم در برخی موارد درست کار نمی کرد و بنابراین با یک جستجو در stack overflow تکه کد ذیل را پیدا کردم که بخوبی کار می کند و روی مرورگرهای مختلف و شرایط مختلف و IPv4 و IPv6 جواب می دهد.function getIp(){
    foreach (array(&#039;HTTP_CLIENT_IP&#039;, &#039;HTTP_X_FORWARDED_FOR&#039;, &#039;HTTP_X_FORWARDED&#039;, &#039;HTTP_X_CLUSTER_CLIENT_IP&#039;, &#039;HTTP_FORWARDED_FOR&#039;, &#039;HTTP_FORWARDED&#039;, &#039;REMOTE_ADDR&#039;) as $key){
        if (array_key_exists($key, $_SERVER) === true){
            foreach (explode(&#039;,&#039;, $_SERVER[$key]) as $ip){
                $ip = trim($ip); // just to be safe
                if (filter_var($ip, FILTER_VALIDATE_IP, FILTER_FLAG_NO_PRIV_RANGE | FILTER_FLAG_NO_RES_RANGE) !== false){
                    return $ip;
                }
            }
        }
    }
    return request()-&gt;ip(); // it will return server ip when no client ip found
}حالا هر جا نیاز شد تنها کافیست تابع getIp را صدا زد تا IP کاربر را برگرداند.</description>
                <category>Hootan Alghaspour</category>
                <author>Hootan Alghaspour</author>
                <pubDate>Fri, 17 Sep 2021 18:46:28 +0430</pubDate>
            </item>
                    <item>
                <title>DomCrawling با PHP</title>
                <link>https://virgool.io/@h.alghaspour/domcrawler-z9dufzls9gtt</link>
                <description>من روش خیلی سریع و ساده را اینجا خدمتتون عرض می کنم و یک مثال می زنم.از روش های پردازش متنی هم میتوان استفاده کرد اما Document Object Model (DOM) در حقیقت یک روش و api برای کار کردن با تگ دارها مثل HTML و XML است که بتوان در بین نودها و آیتم های آن ها گشت زد و عملیات انجام داد.The Document Object Model (DOM) is a programming API for HTML and XML documents. It defines the logical structure of documents and the way a document is accessed and manipulated. In the DOM specification, the term &quot;document&quot; is used in the broad sense - increasingly, XML is being used as a way of representing many different kinds of information that may be stored in diverse systems, and much of this would traditionally be seen as data rather than as documents. Nevertheless, XML presents this data as documents, and the DOM may be used to manage this data.With the Document Object Model, programmers can create and build documents, navigate their structure, and add, modify, or delete elements and content. Anything found in an HTML or XML document can be accessed, changed, deleted, or added using the Document Object Model, with a few exceptions - in particular, the DOM interfaces for the internal subset and external subset have not yet been specified.ما میخواهیم محتوی صفحات وب را بخوانیم و ویرایش کنیم و استفاده کنیم.مثلاً می خواهیم صفحه سمت چپ تصویر را به آرایه PHP سمت راست تبدیل کنیم.یا می خواهیم یک سرویس تجیمع محتوی یا اخبار یا قیمت بنویسیم که خودش از چند سایت مطالب را خوانده و بخش های مدنظر ما را سوا و پالایش کند و اطلاعات مدنظر ما را ذخیره نماید.فرض در این مقاله این است که شما برنامه نویس PHP هستید، به همین دلیل دیگر مبانی اولیه را شرح نمی دهم. محیط استفاده من در این مقاله PHP هفت و خورده ای است و composer هم روی سرور نصب شده.مرحله 1 : ساخت پروژه و ادخال کلاس ها و کتابخانه های مورد نیازدلیلی که همینطوری از DOM و برنامه نویسی functional استفاده نمی کنم این است که از این کارهای crawl زیاد دارم و تمایل دارم همه را بصورت OOP داشته باشم که در استفاده ها و توسعه های بعدی راحت باشم و همچنین در symfony و لاراول راحت می توان از این کامپوننت ها استفاده کرد.پروژه را composer init کنید که از 2 کامپوننت DomCrawler و CssSelector در Symfony استفاده کنیم.یعنی دستور composer init را در پوشه مدنظر بزنید و اطلاعات درخواستی را وارد کنید یا نکنید که برامون ساختار پوشه بندی و composer.json و autoload.php و ... را بسازد که بعد دیگه برای استفاده از بسته هایی که با composer نصب می شوند راحت باشیم.الان یک چنین ساختار و پوشه بندی در اختیار شماست :حالا طبق راهنمای  DomCrawler و CssSelector برای نصب دستورات ذیل را در همان پوشه ای که composer.json ما آنجاست وارد می کنیم :#composer require symfony/dom-crawler
#composer require symfony/css-selector
#composer dump-autoload -oمی گیره و نصب می کنه و تنظیمات composer.json و autoload.php را خودش انجام می دهد و ساختار پوشه های ما بشکل ذیل می شود.در ادامه در همین پوشه اصلی یک فایل index.php با محتوی ذیل می سازم :&lt;?php
require_once __DIR__.&#039;/vendor/autoload.php&#039;;
use Hootan\Crw\Classes\Iranjib;

$iranjibPrices=new Iranjib(&#039;https://www.iranjib.ir/showgroup/45/%D9%82%DB%8C%D9%85%D8%AA-%D8%AE%D9%88%D8%AF%D8%B1%D9%88-%D8%AA%D9%88%D9%84%DB%8C%D8%AF-%D8%AF%D8%A7%D8%AE%D9%84/&#039;);

var_dump($iranjibPrices-&gt;getPrices());مرحله 2 : گرفتن و پالایش محتوی HTMLدر اینجا مثلاً صفحه قیمت خودرو داخلی ایران جیب را مثال می زنم. برای استخراج جدول قیمت ها از این صفحه و تبدیل آن به یک آرایه قابل استفاده در برنامه نویسی و ذخیره در دیتابیس و ... مقاله را ادامه می دهیم.از ایرانجیب تر و تمیزتر و راحت تر برای گرفتن با DOM هست اما ایران جیب را مثال می زنم که در کد چند نکته را اشاره کرده باشم.در پوشه src یک پوشه Classes می سازم و درون آن فایل Iranjib.php را با محتوی ذیل خواهم ساخت:&lt;?php
namespace Hootan\Crw\Classes;
use Symfony\Component\DomCrawler\Crawler;
class Iranjib{
private $crawl_url,$dom,$crawler,$updatedate;
function __construct($url){
$this-&gt;crawl_url=$url;
$this-&gt;dom=file_get_contents&#40;$this-&gt;crawl_url&#41;;
$this-&gt;crawler=new Crawler($this-&gt;dom);
}
function toEnNumber($input) {
$replace_pairs = array(
&#039;۰&#039; =&gt; &#039;0&#039;, &#039;۱&#039; =&gt; &#039;1&#039;, &#039;۲&#039; =&gt; &#039;2&#039;, &#039;۳&#039; =&gt; &#039;3&#039;, &#039;۴&#039; =&gt; &#039;4&#039;, &#039;۵&#039; =&gt; &#039;5&#039;, &#039;۶&#039; =&gt; &#039;6&#039;, &#039;۷&#039; =&gt; &#039;7&#039;, &#039;۸&#039; =&gt; &#039;8&#039;, &#039;۹&#039; =&gt; &#039;9&#039;,
&#039;٠&#039; =&gt; &#039;0&#039;, &#039;١&#039; =&gt; &#039;1&#039;, &#039;٢&#039; =&gt; &#039;2&#039;, &#039;٣&#039; =&gt; &#039;3&#039;, &#039;٤&#039; =&gt; &#039;4&#039;, &#039;٥&#039; =&gt; &#039;5&#039;, &#039;٦&#039; =&gt; &#039;6&#039;, &#039;٧&#039; =&gt; &#039;7&#039;, &#039;٨&#039; =&gt; &#039;8&#039;, &#039;٩&#039; =&gt; &#039;9&#039;,
&#039;,&#039;=&gt;&#039;&#039;
);
return strtr( $input, $replace_pairs );
}
function getPrices(){
if($this-&gt;crawler){
$this-&gt;priceRows=[];
$this-&gt;crawler-&gt;filter(&#039;.expand&#039;)-&gt;each(function (Crawler $crawler) {
foreach ($crawler as $node) {
$node-&gt;parentNode-&gt;removeChild($node);
}
});
$this-&gt;crawler-&gt;filter(&#039;.grp-tabl&#039;)-&gt;each(function (Crawler $crawler) {
foreach ($crawler as $node) {
$node-&gt;parentNode-&gt;removeChild($node);
}
});
$this-&gt;crawler-&gt;filter(&#039;.header&#039;)-&gt;each(function (Crawler $crawler) {
foreach ($crawler as $node) {
$node-&gt;parentNode-&gt;removeChild($node);
}
});
$this-&gt;updatedate=$this-&gt;crawler-&gt;filter(&#039;.group-refreshdate-wrapper&#039;)-&gt;text();
$brand=&amp;quot&amp;quot
$i=0;
foreach($this-&gt;crawler-&gt;filter(&#039;.items_table &gt; tr&#039;) as $content) {
$element = new Crawler($content);
if($element-&gt;matches(&#039;.catsection&#039;)){
$this-&gt;brand=$element-&gt;text();
}else{
$this-&gt;priceRows[$i][&amp;quotbrand&amp;quot]=$this-&gt;brand;
$this-&gt;priceRows[$i][&amp;quotupdate&amp;quot]=$this-&gt;updatedate;
$this-&gt;priceRows[$i][&amp;quotname&amp;quot]=trim($element-&gt;filter(&#039;td&#039;)-&gt;text(),&amp;quot\xC2\xA0 &amp;quot);
if($element-&gt;filter(&#039;span.lastprice&#039;)-&gt;eq(0)-&gt;count() &gt; 0){
$this-&gt;priceRows[$i][&amp;quotprice1&amp;quot]=$this-&gt;toEnNumber($element-&gt;filter(&#039;span.lastprice&#039;)-&gt;eq(0)-&gt;text());

}
if($element-&gt;filter(&#039;span.lastprice&#039;)-&gt;eq(1)-&gt;count() &gt; 0){
$this-&gt;priceRows[$i][&amp;quotprice2&amp;quot]=$this-&gt;toEnNumber($element-&gt;filter(&#039;span.lastprice&#039;)-&gt;eq(1)-&gt;text());
}
$i++;
}
}
return $this-&gt;priceRows;
}else{return false;}
}
}اکنون ساختار پوشه بندی و فایل ها بصورت ذیل است، محتوی فایل های index.php و Iranjib.php را هم در بالا خدمتتان گذاشته ام :اکنون با باز کردن آدرس لیست قیمت استخراج شده از صفحه مدنظر را خواهیم داشت، دقت بفرمایید در فایل index.php می توان سایر صفحات قیمت ایرانجیب را هم با تعریف یک متغییر جدید از نوع کلاس Iranjib و ارسال آدرس صفحه به آن خواند.نکات : دقت بفرمایید مثلاً ۱۹۲,۰۰۰,۰۰۰ عدد نیست، کاراکتر فارسی است، برای تبدیل اینها به عدد از این استفاده کردم. عدد کردم که بعداً بتونیم روش پردازش های عددی و sort و ... انجام بدهیم و خودمون فرمت کنیم.باید از $element-&gt;filter(&#039;span.lastprice&#039;)-&gt;eq(0)-&gt;count() &gt; 0استفاده بفرمایید، وگرنه اگر تنها یکی از نودها خالی باشد خطای Got error &#039;PHP message: PHP Fatal error:  Uncaught InvalidArgumentException: The current node list is empty.خواهید گرفت، try/catch اینجا درست کار نمی کند، تنها روش چک کردن خالی نبودن نود و نگرفتن خطا همین است.همان ابتدا در خطوط 28تا44 ردیف های تبلیغات و ... را فیلتر و حذف کرده ام.به نحوه استفاده از trim در اینجا دقت بفرمایید. بدون آن انتهای نام خودرو 2 فاصله می افتد.کل پروژه را بصورت فایل zip می توانید از اینجا بگیرید و مشکلی برای اجرا روی سرور دارای PHP هفت و خورده ای نباید داشته باشد که اگر نکته ای را هم جا انداخته بودم از روی کد مرور بفرمایید.</description>
                <category>Hootan Alghaspour</category>
                <author>Hootan Alghaspour</author>
                <pubDate>Tue, 17 Aug 2021 11:28:06 +0430</pubDate>
            </item>
                    <item>
                <title>مقدمه ای بر AWK</title>
                <link>https://virgool.io/@h.alghaspour/%D9%85%D9%82%D8%AF%D9%85%D9%87-%D8%A7%DB%8C-%D8%A8%D8%B1-awk-gwgjsm92vlai</link>
                <description>لینوکس ابزارهای text processing متنوع و با بازدهی بالایی مثل awk و sed و cut و sort و انواع grep و ... دارد که حتی هنوز هم در کارهای بسیار کوچک تا بسیار بزرگ پراستفاده هستند.شما ممکنه روزانه میلیون ها خط لاگ یا اطلاعات دیگر داشته باشید که به هر دلیل ریختن آن ها در یک پایگاه داده و کوئری گرفتن از آن میسر یا بهینه نیست یا بصرفه نیست برای پیدا کردن یک اطلاعات مشخص از انبوهی از داده های موقت یک پایگاه داده بالا بیاورید و دردسرهای آن را تحمل کنید یا اصلاً ممکنه تمایل داشته باشید خروجی اطلاعات مستقیماً به دستور دیگری فرستاده شود (پایپ | بشه) یا ممکنه تمایل داشته باشید اطلاعات را با فرمت و دامنه مشخص و فیلتر شده به دیتابیس بریزید یا ... . بجز اینکه در انواع shell scripting و automation فراوان به این دستورات و ابزارها نیاز دارید.یکی از معروفترین و پرکاربردترین و البته قوی ترین این ابزارها دستور awk (آوک) است که نام آن از مخفف اول نام فامیل Alfred Aho, Peter Weinberger, Brian Kernighan که نویسندگان نسخه اولیه در 1977 گرفته شده.آوک یک ابزار اسکریپت نویسی مبتنی بر manipulating columns of data است، یعنی مثل spreadsheet با سطر و ستون در یک فایل یا مقدار ورودی text کار دارید.خلاصه قضیه به اینصورت است :محتوی تکست براساس فاصله ستون بندی می شود و براساس n\ سطر بندی.بصورت پیش فرض awk ستون ها را با فاصله از هم جدا تشخیص می دهد اما می توان این را تغییر داد، مثلاً در فایل های CSV که ستون ها با , مشخص شده می توان با سوییچ F- جداکنند ستون ها (field separator) را مشخص کرد.عبارت 1$ در awk یعنی ستون 1 و همینطور 2$ یعنی ستون 2 و ... ، ستون آخر با NF$ به معنای number of fields مشخص می شود و 0$ یعنی همش. (از یک می شمرد.)شما می توانید patterns و actions تعریف کنید و در آن ها از انواع تابع و شرط و حلقه و ... استفاده بنمایید، این مجموعه یک rule می شود. actionها را داخل {} می نویسند. می توان در یک اسکریپت چند اکشن با استفاده از چند {} داشت. کل rule نیز باید در &#x27; &#x27; باشد.مثل یک زبان برنامه نویسی بسیار مشابه C اما با ادبیات و شیوه خودش توابع و متغییرها و شرط ها و حلقه ها و آرایه و ... را دارد و بعضاً برنامه های قوی برای پردازش متن بخصوص از جنس لاگ با آوک نوشته شده است.وقتی مثلاً یک فایل text را می خوانید اسکریپت awk خط به خط اجرا و اعمال می شود، مثل یک حلقه foreach به ازای هر خط.همچنین اگر بخواهیم متنی را به محتوی پردازش شده اضافه کنیم داخل &quot; &quot; می نویسیم.ساختار کلی دستور به اینصورت است :$ awk options &#039;{program}&#039; file   /* کل برنامه داخل &#039;&#039; است و یک فایل برای پردازش به آن داده می شود*/
مثلاً
$awk  &#039;{print $1}&#039; textFile         /*پرینت ستون اول از فایل*/
یا
$date | awk &#039;{print $2,$3,$6}&#039;        /*date خروجی فقط ماه و روز و سال از دستور */راهنمای کاملش را می توانید اینجا ببینید و البته خیلی مفصل است و خیلی کارها می شه باهاش کرد. من خیلی خلاصه پرکاربردترین مبانی را برای معرفی خدمتتان عرض می کنم :به مثال تصویر ذیل دقت بفرمایید : یک فایل تکست با نام awk_text با محتوی مشخص در تصویر ساخته ام، به تصویر دقت بفرمایید : اگر به مثال بالا دقت بفرمایید تا حدود زیادی ساختار awk را بدست خواهید آورد و اکنون سراغ نکته ها و مثال های تمرینی می رویم.به یک مثال از مقداردهی و manipulating یک رشته توجه بفرمایید :یا به یک مقایسه عددی توجه بفرمایید :برای تغییر جداکننده ستون ها از پیش فرض space گفتیم می توان از سوییچ F- استفاده کرد. مثلاً:$awk -F &amp;quot,&amp;quot &#039;{print $1}&#039; csv_file.csv
یا
$awk -F &amp;quot:&amp;quot &#039;{print $1}&#039; /etc/passwd
&amp;quot &amp;quot یا بدون
$awk -F: &#039;{print $1}&#039; /etc/passwdمی توان با استفاده از چند {} چند action تعریف کرد. مثلاً :یکسری Built-in Variables دارد که چند تا مثال ازشون میزنیم ، از جمله :FIELDWIDTHS Specifies the field width.RS Specifies the record separator.FS Specifies the field separator.OFS Specifies the Output separator.ORS Specifies the Output separator.ARGC Retrieves the number of passed parameters.ARGV Retrieves the command line parameters.ENVIRON Array of the shell environment variables and corresponding values.FILENAME The file name that is processed by awk.NF Fields count of the line being processed.NR Retrieves total count of processed records.FNR The record which is processed.IGNORECASE To ignore the character case.مثلاً :$cat /etc/passwd | awk &#039;NR==1,NR==4 {print $0}&#039;یعنی خط شماره 1 تا 4 را چاپ کن. کمی ادبیات و تکنیک های غریبی داشته و استفاده از توابع و متغییرها و شرط ها در awk نیاز به تمرین و مثال دارد.دقت بفرمایید وقتی متغییر را قبل از action استفاده می کنیم در حقیقت آن را مقداردهی می کنیم و در داخل action مقدار آن را می خوانیم.یک نکته جالب هم اینه که می توانید چند فایل را برای پردازش همزمان بهش بدهید. مثلاً من اینجا 2 فایل را دادم که مجموع خطوطشان را نمایش بدهد :همچنین می توانید Preprocessing و Postprocessing تعریف کنید، یعنی قبل و بعد از اجرای اسکریپت یک دستوری اجرا شود. با BEGIN و END ، مثلاً :$awk &#039;BEGIN{print &amp;quotThe File Contents:\n--------&amp;quot} {print $0} END{print &amp;quot--------\n&amp;quot NR&amp;quot Rows Processed&amp;quot}&#039; awk_textاز BEGIN برای مقداردهی های اولیه هم استفاده می کنیم، مثلاً :$awk &#039;BEGIN{FS=&amp;quot:&amp;quot OFS=&amp;quot-&amp;quot} {print $1,$6,$7}&#039; /etc/passwdاینجا تعریف کردیم فایل را که می خوانی جدا کننده : است و وقتی چاپ می کنی جداکننده - باشد.به استفاده از ; برای جدا کردن متغییرها داخل یک action و نوشتن کل قضیه در یک &#x27; &#x27;  توجه بفرمایید.یا مثال چاپ تعداد خط در یک فایل با NR :$awk &#039;END {print &amp;quotNumber of Lines : &amp;quotNR}&#039; awk_textیک مثال دیگر با حلقه for و آرگومان :$awk &#039;BEGIN { 
   for (i = 0; i &lt; ARGC - 1; ++i) { 
      printf &amp;quotARGV[%d] = %s\n&amp;quot, i, ARGV[i] 
   } 
}&#039; one two three fourهمانطور که ملاحظه می فرمایید استفاده از شرط ها و حلقه ها را هم پشتیبانی می کند.یک مثال دیگر از حلقه for برای شمارش :$awk &#039;BEGIN {for(i=1;i&lt;=10;i++) print i&amp;quot*&amp;quoti&amp;quot=&amp;quoti*i;}&#039;یک مثال برای چاپ کاربر جاری با استفاده از کلید USER در ENVIRON (متغییرهای محیطی لینوکس) :$ awk &#039;BEGIN { print ENVIRON[&amp;quotUSER&amp;quot] }&#039;  /* print current user*/یا گرفتن لیست یک پروسس خاص از ps :$ps -ef | awk &#039;{if($NF==&amp;quotzsh&amp;quot) print $0}&#039;یعنی در مثال بالا گفته ام اگر در ps -ef آخرین ستون zsh بود آن را چاپ کن.با همان قوانین regex در ابتدای action می توانید بین / / یک regex تعریف کنید. به مثال دقت بفرمایید :$awk &#039;/section1/ {print $2 &amp;quot\t&amp;quot $4}&#039; awk_textیکم مثال قوی تر، در اینجا لیست shellهای موجود روی سیستم را تمیز گرفته ایم :$awk -F &amp;quot/&amp;quot &#039;/^\// {print $NF}&#039; /etc/shells | uniq | sortبه \ برای تعریف کاراکتر / در regex توجه بفرمایید.یک راه دیگه برای استفاده از regex استفاده از اپراتورهای ~ (داشته باشد) و !~ (نداشته باشد) است.مثلاً :در مثال بالا یعنی ردیف هایی از 0$ (همه محتوی یا آن خط در حال پردازش) 9 داشته باشد یا نداشته باشد. در ابتدا گفتیم دستور awk مثل foreach خط به خط (n\ به n\) اجرا می شود.یکسری هم توابع پیش فرض در awk برای استفاده هستند، حتی خودتان هم می توانید function تعریف کنید، awk عملاً یک زبان برنامه نویسی پردازش متنی ستونی است. مثل :Mathematical functions
atan2(y, x)
cos(expr)
exp(expr)
int(expr)
log(expr)
rand
sin(expr)
sqrt(expr)
srand([expr])

String functions
asort(arr [, d [, how] ])
asorti(arr [, d [, how] ])
gsub(regex, sub, string)
index(str, sub)
length(str)
match(str, regex)
split(str, arr, regex)
sprintf(format, expr-list)
strtonum(str)
sub(regex, sub, string)
substr(str, start, l)
tolower(str)
toupper(str)

Time functions
systime
mktime(datespec)
strftime([format [, timestamp[, utc-flag]]])
مثلاً :در مثال بالا مورد تابع length چند مثال زده ام که شیوه صحیح استفاده از شرط و تابع را مرور کنیم.یک مثال از تابع match، می خواهم ایندکس کاراکتر a در هر خط را پیدا کنم :دقت بفرمایید از 1 می شمرد و RSTART هم از همان متغییرهای پیش فرض awk و به معنای matchThe first occurrence position of function matching است.یعنی برای هر خط ابتدا match اجرا می شود و اگر کاراکتر a در آن خط بود طبق تعریف RSTART را مقداردهی می کند.زمانی که اسکریپت شما پیچیده و با چند شرط و تابع و ... باشد دیگر نمی توانید و راحت نیست در خط فرمان همه را تایپ کنید. بنابراین می توانید در یک فایل بصورت تکست بنویسید با پسوند ترجیحاً awk ذخیره کنید و با سوییچ f برای اجرا به awk بدهید.مثلاً می شه :$ awk -f command.awk awk_textحالا یک روش دیگر این هم این است که در لینوکس به یک اسکریپت اجرایی تبدیلش کنید. در مثال ذیل این اسکریپت را در فایل accountcounter.awk نوشته ام و سپس به آن با chmod +x مجوز اجرا داده ام و بعد اجرا کرده ام.#!/usr/bin/awk -f
BEGIN {
  # set the input and output field separators
  FS=&amp;quot:&amp;quot
  # zero the accounts counter
  accounts=0
}
{
  # count another account
  accounts++
}
END {
  # print the results
  print accounts &amp;quot accounts.&amp;quot
}در مثال بالا دقت بفرمایید که awk در حقیقت بصورت یک حلقه به ازای هر خط اجرا می شود.چند مثال در تصویر با همان فایل awk_text که در ابتدا ساختیم زده ام :من در این مقاله بسیار مختصر و با چند مثال کاربرد awk و ادبیات کلی آن را نشان دادم، در حقیقت معمولاً همینقدر برای کارهای من کفایت می کند و چیز اضافه تری هم نیاز باشد موردی جستجو می کنم، اما اگر خیلی با پردازش raw data تکست ستونی و سطری سر و کار دارید احتمالاً از وقتی که برای یادگیری عمیق تر awk بگذارید پشیمان نخواهید شد. اصلاً ما یک مهارت پیشرفته لینوکسی داریم Master in AWK!</description>
                <category>Hootan Alghaspour</category>
                <author>Hootan Alghaspour</author>
                <pubDate>Wed, 11 Aug 2021 11:27:36 +0430</pubDate>
            </item>
                    <item>
                <title>گرفتن گواهینامه از Let&#039;s Encrypt برای دامنه در Zimbra</title>
                <link>https://virgool.io/@h.alghaspour/%DA%AF%D8%B1%D9%81%D8%AA%D9%86-%DA%AF%D9%88%D8%A7%D9%87%DB%8C%D9%86%D8%A7%D9%85%D9%87-%D8%A7%D8%B2-lets-encrypt-%D8%A8%D8%B1%D8%A7%DB%8C-%D8%AF%D8%A7%D9%85%D9%86%D9%87-%D8%AF%D8%B1-zimbra-dmmdenfcudfk</link>
                <description>این مقاله برای قضیه Root certificate های جدید let&#x27;s encrypt بروزرسانی شده است، از اکتبر 2021 باید از روش بروزرسانی استفاده نمایید.اطلاع دادند که کاربران فلان دامنه که روی زیمبرا هستند از روی سیستم های مک برای تنظیم mail client بدلیل گواهینامه غیرمعتبر مشکل دارند و برای mail.folan.tld از Let&#x27;s Encrypt گواهینامه معتبر بگیرید و نصب کنید و ...کمی بدقلقی کرد اما در نهایت راه حل ساده اش را روی CentOS8 و زیمبرا 8.8.15 اینجا مرحله به مرحله شرح می دهم که خودم هم دفعه بعد به همین راهنما ارجاع کنم/دهم.زیمبرا یک Collaborative Suite یا Groupware است که امکانات کامل mail server و tasks و calendar و chat و Briefcase و File Share و web interface و ... دارد و به خیلی چیزهای دیگه مثل BigBlueButton هم وصل می شه و ... ، الان در دنیا خیلی مورد استقبال واقع شده و محبوب است.مقایسه زیمبرا و اکسچنج سرور مایکروسافتبرای انجام این کار باید certbot را نصب کنیم، راحت ترین راه نصب certbot روی centos8 استفاده از snapd است، بنابراین اول باید اون را نصب کنیم. مراحل کار بشرح ذیل است :1- نصب snapd2- نصب certbot3- گرفتن و ویرایش گواهینامه برای دامنه مدنظر4- نصب و verify و deploy گواهینامه روی زیمبرا و تست Automatic Renewalفرض بر این است که شما یک زیمبرا 8.8 استاندارد و معمولی نصب شده روی centos8 دارید.نکته : هرگز فراموش نکنید که پس از نصب زیمبرا پورت های 11211 TCPوUDP مربوط به memcached را روی فایروال یک سطح بالاتر (ورودی فایروال شبکه دیتاسنتر مثلاً) ببندید که شر بدجور نشود.مرحله 1 : نصب snapd#yum -y install epel-release
#yum install snapd
#systemctl enable --now snapd
#ln -s /var/lib/snapd/snap /snap
#snap install core; sudo snap refresh coreمرحله 2 : نصب certbot#snap install --classic certbot
#ln -s /snap/bin/certbot /usr/bin/certbotمرحله 3 : گرفتن و ویرایش گواهینامه برای دامنه مدنظر#certbot certonlyبروزرسانی 2013 : در نسخه 8.8.15 زیمبرا فقط RSA پشتیبانی می شود بنابراین از دستور ذیل برای certbot استفاده بفرمایید.#certbot certonly --key-type rsa --force-renewalتصحیح گواهینامه ها با همان روش ذیل *** بروزرسانی *** در پایین انجام می شود.در اجرای این اسکریپت، ابتدا گزینه 1 را انتخاب کنید، بعد ایمیل بدهید ، سپس y بزنید سپس n بزنید و در نهایت لیست دامنه ها را بصورت جدا شده با , وارد نمایید.الان اگر به پوشه etc/letsencrypt/live/mail.folan.tld/ بروید 4 فایل cert.pem و chain.pem و fullchain.pem و privkey.pem که از پوشه etc/letsencrypt/archive/ لینک شده اند را می بینید./*************  قدیمی است،  بروزرسانی شده - روش جدید چند خط پایین تر *****************/* نکته کار اینجاست که در فایل chain.pem صادر شده توسط Let&#x27;s Encrypt بخش root CA وجود ندارد و موقع verify خطا می دهد!بصورت دستی فایل etc/letsencrypt/archive/mail.folan.tld/chain1.pem/ را با vi یا nano باز کنید و محتوی این آدرس را ته آن کپی کنید : https://letsencrypt.org/certs/trustid-x3-root.pem.txtیعنی محتوی chain1.pem اینجوری می شه، تکه آخر سوم از آدرس بالا copt/paste شده است :/******************** بروزرسانی  ********************/بجای trustid-x3-root.pem.txt از روش ذیل استفاده نمایید :#wget -O /tmp/ISRG-X1.pem https://letsencrypt.org/certs/isrgrootx1.pem
#wget -O /tmp/R3.pem https://letsencrypt.org/certs/lets-encrypt-r3.pem
#cat /tmp/R3.pem &gt; /etc/letsencrypt/archive/mail.folan.tld/chain.pem
#cat /tmp/ISRG-X1.pem &gt;&gt; /etc/letsencrypt/archive/mail.folan.tld/chain.pem/******************************************************************/مرحله 4 : نصب و verify و deploy گواهینامه روی زیمبرا#mkdir /opt/zimbra/ssl/letsencrypt
#cp /etc/letsencrypt/live/mail.folan.tld/* /opt/zimbra/ssl/letsencrypt/
#chown zimbra:zimbra /opt/zimbra/ssl/letsencrypt/*
#su zimbra
$cd /opt/zimbra/ssl/letsencrypt/
$/opt/zimbra/bin/zmcertmgr verifycrt comm privkey.pem cert.pem chain.pemباید آخرش OK بدهد.حالا :$cp /opt/zimbra/ssl/letsencrypt/privkey.pem /opt/zimbra/ssl/zimbra/commercial/commercial.key
$/opt/zimbra/bin/zmcertmgr deploycrt comm cert.pem chain.pem
$zmcontrol restartالان هم درصورت دسترسی از طریق اینترفیس وب و هم mail clientها، باید certificate معتبر let&#x27;s encrypt داشته باشید و خطایی درمورد اعتبار گواهینامه نگیرید.برای Automatic Renewal، خود certbot وقتی نصب می شود این قضیه را تنظیم می کند، گواهینامه های Let&#x27;s Encrypt سه ماهه هستند و بعد باید مجدداً بروزرسانی شوند.برای تست اینکه آیا گواهینامه خودکار بروزرسانی می شود یا خیر از دستور ذیل استفاده بفرمایید :#certbot renew --dry-runدرصورت عدم خطا پیامی مشابه تصویر ذیل می گیرد و در صورت وجود خطا توضیحات آن نمایش داده می شود.</description>
                <category>Hootan Alghaspour</category>
                <author>Hootan Alghaspour</author>
                <pubDate>Mon, 26 Jul 2021 02:44:05 +0430</pubDate>
            </item>
            </channel>
</rss>