محمد أحمد العيل

المساهمون
  • المساهمات

    224
  • تاريخ الانضمام

  • تاريخ آخر زيارة

السُّمعة بالموقع

98 Excellent

المعلومات الشخصية

  • النبذة الشخصية مهندس نظم معلومات، أكتب في مدونتي (تلميحات تقنية) عن البرامج الحرة ومفتوحة المصدر. لدي خبرة في التطوير بلغة جافا وإدارة قواعد بيانات MySQL و Oracle، إضافة إلى إدارة أنظمة لينوكس (دبيان ومشتقاتها).
  • الموقع الالكتروني

6 متابعين

آخر الزُوّار

3,363 زيارة للملف الشّخصي
  1. رفع الملفّات وإدارتها في تطبيقات Laravel

    يتوفّر إطار العمل Laravel على واجهة تطبيقات برمجيّة API موّحّدة للتعامل مع الملفات. تأتي واجهة التطبيقات هذه مجهّزة مبدئيا بتعريفات Drivers تمكّن من إدارة الملفات على نظام الملفات المحلّي، خادوم FTP أو على خدمتي Amazon S3 وRackspace السّحابيتين. سنعرض في هذا الدرس لأساسيّات إدارة الملفات: رفعها Upload، تخزينها والعثور عليها في الإصدار 5.3 من إطار العمل Laravel. أقراص التخزين في Laravel تُضبَط إعدادات تخزين الملفات ضمن الملف config/filesystems.php عن طريق ما يُسمّيه Laravel الأقراص Disks. يُمثّل كل قرص تعريفا، مسارا للتخزين وإعدادات خاصّة بالقرص. يعرّف ملفّ الإعداد مبدئيا ثلاثة أقراص public، local وs3: 'disks' => [ 'local' => [ 'driver' => 'local', 'root' => storage_path('app'), ], 'public' => [ 'driver' => 'local', 'root' => storage_path('app/public'), 'visibility' => 'public', ], 's3' => [ 'driver' => 's3', 'key' => 'your-key', 'secret' => 'your-secret', 'region' => 'your-region', 'bucket' => 'your-bucket', ], ], يستخدم القرصُ local في المثال المبدئي أعلاه التعريفَ local (نظام الملفات المحلّي) ومسار التخزين storage/app. بالنسبة للقرص s3 فهو يستخدم التعريف s3 (خدمة Amazon S3) ويتطلّب قيما ضرورية للولوج إلى الخدمة. يشبه القرص public القرصَ local؛ إلا أنّ بينهما فرقًا جوهريًّا: هذا القرص مهيّأ للملفات التي نريد إتاحتها للعموم. تُخزَّن ملفات هذا القرص على المسار storage/app/public. يمكن ملاحظة أن المسار storage/app/public يوجد خارج المجلّد public الذي يحوي ملفّات المشروع المتاحة للعموم. نستخدم أمر Artisan التالي لجعل ملفات القرص public متاحة على الوِب: php artisan storage:link ينشئ الأمر وصلة رمزيّة على المسار public/storage ويجعلها تحيل إلىstorage/app/public الذي هو مسار تخزين القرص public. سنرى بعد قليل كيف نصل إلى الملفات الموجودة في هذا القرص. ملحوظة: يتطلّب استخدام التعريفيْن s3 وrackspace تثبيت الحزمتييْن التاليّتيْن على التوالي (عن طريق composer): league/flysystem-aws-s3-v3 ~1.0 league/flysystem-rackspace ~1.0 رفع ملفات وعرض روابطها في Laravel 5.3 سنهيّئ في بقيّة الدرس مشروع Laravel 5.3 للعمل عليه. سيكون هدفنا رفع صورة في المتصفّح ثم عرض هذه الصورة في صفحة الوِب. نضيف مسارين إلى ملف مسارات الوِب routes/web.php: Route::get('image-upload','ImageController@imageUpload'); Route::post('image-upload','ImageController@imageUploadPost'); يتلقّى الإجراء get طلبات عرض الصفحة، في ما نستخدم الإجراء post لتخزين الصّورة المحمَّلة في الصفحة التي سننشئها بعد قليل. الخطوة التاليّة هي إنشاء المتحكّم ImageController وكتابة الدالتين imageUpload وimageUploadPost: php artisan make:controller ImageController كلّ ما تفعله الدالة imageUpload هو استدعاء العرض image-upload: public function imageUpload() { return view('image-upload'); } بالنسبة للدالة imageUploadPost فستستقبل الصورة المحمّلة من المتصفّح، تخزّنها ثم ترسلها إلى image-upload الذي يعرضها: public function imageUploadPost(Request $request) { // TODO: return view('image-upload') ->with('message', "Image uploaded successfully") ->with('path', $imagePath); } الدالة غير مكتملة لحد الساعة، فكل ما يظهر منها هو استدعاء القالب وتمرير رسالة إليه تفيد بنجاح رفع الصورة، إضافة إلى متغيّر يمثّل رابط الصورة. استقبال الصورة، تخزينها والحصول على رابط تخزينها سيكون محلّ التعليق TODO. ننشئ القالب image-upload قبل العودة إلى الدالة imageUploadPost. ننشئ الملف image-upload.blade.php على المسار resources/views ونضع فيه المحتوى التالي: <!DOCTYPE html> <html> <head> <title>Laravel 5.3 Image Upload example</title> </head> <body> <div> @if (isset($path)) <p>{{ $message }}</p> <img src="{{ url($path) }}"> @endif <form action="{{ url('image-upload') }}" enctype="multipart/form-data" method="POST"> {{ csrf_field() }} <div> <div> <input type="file" name="image" /> </div> <div> <button type="submit">Upload</button> </div> </div> </form> </div> </body> </html> نتحقّق أولا، عن طريق الدالة isset، من وجود متغيّر باسم path في المعطيات الممرّرة إلى القالب. يمثّل المتغيّر path مسار الصورة التي نعرضها في حال وجود المتغيّر path. ثم يأتي دور استمارة الرّفع التي ترسل طلبا بإجراء POST إلى المسار image-upload. نحدّد نوع المُدخَل input الذي نريد استقبال الصورة عن طريقه بالنوع file ونحدّد اسمه بـimage. نستطيع الآن العودة إلى الدالة imageUploadPost لإكمالها: public function imageUploadPost(Request $request) { // TODO: return view('image-upload') ->with('message', "Image uploaded successfully") ->with('path', $imagePath); } نستقبل الطلب في المعطى request؛ حيث يمكننا تطبيق الدالة file للحصول على الصورة التي حمّلها المتصفّح بتحديد اسم المُدخَل input الذي استقبلها في صفحة الوٍب: $request->file('image'); نطبّق على الملف الدالة store لتخزينه: $image = $request->file('image')->store('images', 'public'); تأخذ الدالة store معطَيَيْن، الأول منهما هو اسم المجلّد حيثُ نريد تخزين الملفّ والثاني اسم القرص الذي نريد استخدامه. إن لم نحدّد اسمَ القرص فسيُستخدَم القرص المبدئي المعرّف بالتعليمة default ضمن ملف الإعداد config/filesystems (تأخذ التعليمة مبدئيا القيمة local). نريد أن تكون الصورة متاحة للعموم ضمن مجلد خاصّ بالصور اسمه images، لذا نمرّر القيمتيْن images وpublic للمعطييْن الأول والثاني على التوالي. خزّنا الآن الصورة على القرص المتاح للعموم. الخطوة التاليّة هي الحصول على مسارها من أجل إرساله إلى القالب image-upload لعرضه. نستخدم الدالة url ضمن الصنف Storage والتي يمكن تطبيقها على أقراص تستخدم أحد التعريفيْن local أو s3: $imagePath = Storage::url($image); لا ننسى استيراد الصّنف Storage: use Illuminate\Support\Facades\Storage; أصبحت دالة المتحكّم مكتملة على النحو التالي: public function imageUploadPost(Request $request) { $image = $request->file('image')->store('images','public'); $imagePath = Storage::url($image); return view('image-upload') ->with('message', "Image uploaded successfully") ->with('path', $imagePath); } لا ننسى إنشاء الوصلة الرمزية: php artisan storage:link نشغّل خادوم التطوير المضمَّن في Laravel: php artisan serve ثم نفتح المتصفّح لزيارة المسار http://localhost:8000/image-upload. نختار صورة لرفعها ثم نضغط على الزّر Upload. يخزّن Laravel الصّورة في المجلّد storage/app/public، ثم يطلُب عرض القالب image-upload مع تمرير رابط الصّورة في المتغيّر path، إضافة إلى رسالة في المتغيّر message تفيد بنجاح الرّفع. ملحوظة: يختار Laravel عند استخدام الدالة store اسما مميّزا للملف بتطبيق دالة تجزئة Hash عليه. إن أردت اختيار الاسم الذي يُخزَّن به الملف فيمكنك ذلك بالدالة storeAs: تخزين الملف في القرص المبدئي: $image = $request->file('image')->storeAs('images','fileName'); تخزين الملف مع تحديد القرص: $image = $request->file('image')->storeAs('images','fileName','public'); العمليّات على الملفات المرفوعة يوفّر Laravel الصّنفَ Storage للتعامل مع الملفات والأقراص. إضافة ملفات إلى التخزين تُستخدَم الدالة put في الصّنف Storage لتخزين ملفات على النحو التالي: Storage::put('images', $fileContents); يُمثّل المعطى الأوّل الممرَّر إلى الدالة اسمَ المجلّد الذي تريد حفظ الملفّ فيه والثاني محتوى الملفّ. تستخدم الدالة أعلاه القرص المبدئي (التعليمة default في ملف الإعداد). إن أردت استخدام قرص مغاير فيمكنك الاستعانة بالدالة disk: Storage::disk('public')->put('images', $fileContents); يُحدَّد المجلّد المُمرَّر إلى الدالة put (المعطَى الأول) اعتمادا على المسار الجذر للقرص المستخدَم؛ أي أن المقصود بالمجلّد images في المثال السابق هو المجلد storage/app/public/images؛ نظرا لكون storage/app/public هو المسار الجذر للقرص public. يُعيَّن المسار الجذر بالتعليمة root أثناء إعداد الأقراص في ملف الإعداد config/filesystems. استرجاع ملف من التخزين يتيح الصّنف Storage الدالة get لاسترجاع محتوى ملف مخزّن في القرص المبدئي: $contents = Storage::get('images/file.jpg'); يمكن تحديد القرص المستهدَف باستدعاء الدالة disk قبل تطبيق get. يتوفّر الصّنف Storage على الدالة exists التي تتيح التأكد من وجود الملفّ: $exists = Storage::disk('s3')->exists('file.jpg'); الحصول على بيانات ملفّ استخدم الدالة size على النحو التالي لمعرفة حجم ملفّ موجود في القرص المبدئي: $size = Storage::size('file1.jpg'); أو بتحديد القرص المستهدَف: $size = Storage::disk('s3')->size('file1.jpg'); يمكن على نفس المنوال معرفة تاريخ آخر تعديل على الملف على صيغة ختم زمني Timestamp: $time = Storage::lastModified('file1.jpg');
  2. مقدمة إلى نظام الإشعارات Notifications في Laravel

    يضيف الإصدار 5.3 من Laravel نظاما للإشعارات Notifications يمكن من خلاله إخطار المستخدم بحدوث شيء ما. تُستخدَم الإشعارات، التي تكون عادة على هيئة بلاغات قصيرة، لإعلام مستخدم التطبيق بوقوع أمر ما؛ على سبيل المثال “صدور فاتورة”، “تسجيل مستخدم”، “إعادة تعيين كلمة السّر” أو “نشر درس”؛ حسب طبيعة التطبيق الذي تعمل عليه. يتيح نظام الإشعارات في Laravel 5.3 وسائط عدّة (تُسمّى قنوات Channels) لتوصيل الإشعار: البريد الإلكتروني، الرسائل النصية القصيرة SMS (بالاعتماد على خدمة Nexmo) وخدمة Slack. يمكن أيضا تخزين الإشعارات في قاعدة البيانات من أجل عرضها في واجهة الوِب. سنرى في هذا الدرس كيفية عمل الإشعارات، بأخذ مثال لإشعار يُرسَل إلى بريد المستخدم عند إضافة منشور جديد. تهيئة المشروع نبدأ أولا بتهيئة المشروع الذي سنعمل عليه. أنشئ مشروع Laravel 5.3 جديدا، إن لم يكن لديك واحد جاهز واضبط إعدادات قاعدة البيانات. نستخدم نفس الإعدادات المذكورة في فقرة تهيئة بيئة التطوير من درس أساسيات التخبئة في Laravel؛ مع التعديل عليها قليلا. سنضيف صنفا لبذر المستخدمين: php artisan make:seeder UsersTableSeeder ونعدّل صنف البذر على النحو التالي لإضافة مستخدمَيْن إلى قاعدة البيانات: public function run() { $user1 = [ 'name' => 'Med Ahmed Eyil', 'email' => 'medeyil@laravel.com', 'password' => Hash::make('1234'), ]; $user2 = [ 'name' => 'Fatima Benziane', 'email' => 'fbenziane@laravel.com', 'password' => Hash::make('4567'), ]; User::create($user1); User::create($user2); } ثم ننفّذ البذر: php artisan db:seed يصبح لدينا الآن نموذجان، واحد للمستخدمين وآخر للمنشورات. ننتقل إلى ملف المسارات routes/web.php ونضيف المسار التالي: Route::get('/notify/{user_id}/{post_id}', 'PostsController@notify'); يعني هذا أنه عند زيارة الرابط notify/user_id/post_id/ (حيث user_id وpost_id معرّفا المستخدم والمنشور على التوالي) سيطلُب Laravel تنفيذ الدالة notify في المتحكم PostsController. نعدّل المتحكّم PostsController لإضافة هذه الدالة: public function notify($user_id,$post_id) { $user = User::find($user_id); $post = Post::find($post_id); // todo } سنعود لاحقا لإكمال الدالة notify التي تكتفي لحد الساعة بالعثور على المستخدم والمنشور الممررَيْن إليها في المعطيات. الإشعارات هدفنا هو التالي: يطلُب المتصفح الرابط notify/1/2/ (مثلا) فيُرسَل إشعار عبر البريد إلى المستخدم ذي المعرف 1 يخبره أن المنشور ذا المعرف 2 قد أُضيف إلى الموقع. سنحتاج لبلوغ هذا الهدف لإشعار “إضافة منشور”. ننفذ الأمر التالي لإنشاء هذا الإشعار: php artisan make:notification NewPost ستلاحظ بعد تنفيذ الأمر أعلاه ظهور مجلد جديد باسم Notifications (إن لم يكن موجودا سلفا) متفرع عن المجلد app وبداخله صنف باسم NewPost.php (نزعنا التعليقات للاختصار): <?php namespace App\Notifications; use Illuminate\Bus\Queueable; use Illuminate\Notifications\Notification; use Illuminate\Contracts\Queue\ShouldQueue; use Illuminate\Notifications\Messages\MailMessage; class NewPost extends Notification { use Queueable; public function __construct() { } public function via($notifiable) { return ['mail']; } public function toMail($notifiable) { return (new MailMessage) ->line('The introduction to the notification.') ->action('Notification Action', 'https://laravel.com') ->line('Thank you for using our application!'); } public function toArray($notifiable) { return [ ]; } } يمدّد الصنف NewPost الصنفَ Notification الذي يحوي التعليمات الضرورية للتعامل مع الإشعارات. تظهر الدالة المشيّدة Constructor وثلاث دوال أخرى: via: تعرّف القنوات التي نريد إرسال الإشعارات عبرها. توجد قناة البريد mail مبدئيا. تمكن إضافة قناة أخرى من تلك المتوفرة. يستخدم Laravel جميع القنوات المذكورة في المصفوفة المرجَعة، بمعنى؛ إن حددت مثلا القناتين mail وdatabase في المصفوفة فإن الإشعار سيُرسَل عبر البريد ويُخزَّن أيضا في قاعدة البيانات. toMail: هذه الدالة خاصّة بالتعامل مع قناة البريد الإلكتروني. تنشئ الدالة toMail كائنا من صنف MailMessage لتخصيص بريد إلكتروني. سنعود لهذا الصنف بعد قليل. toArray: إن حدّدت قناة للإشعارات في الدالة via دون أن تنشئ دالة خاصّة للتعامل معها فسيستدعي Laravel الدالة toArray. تجدر الإشارة إلى أن الدوال المخصّصة للتعامل مع قنوات الإشعارات تبدأ بـto متبوعة باسم القناة. مثلا toMail للبريد الإلكتروني (تأتي مبدئيا) وtoDatabase لتخصيص الإشعار عبر قناة قاعدة البيانات. نعدّل الصنف NewPost قليلا بحيث نمرّر للمشيّد معطى من نوع Post (نزعنا أجزاء الصنف التي لم يطرأ عليها تعديل): <?php // استدعاء الأصناف، أضفنا إليه الصنف Post use App\Post; class NewPost extends Notification { // أضفنا خاصيّة post إلى الصنف protected $post; public function __construct(Post $post) { $this->post = $post; } // بقية شفرة الصّنف } نعود إلى الدالة notify في المتحكم ونعدّلها لتصبح على النحو التالي: public function notify($user_id,$post_id) { $user = User::find($user_id); $post = Post::find($post_id); $user->notify(new NewPost($post)); } لا تنس استدعاء الصنف NewPost في المتحكم: use App\Notifications\NewPost; استدعينا الدالة notify من الكائن user$ ومررنا إليها صنفا جديدا من الإشعار NewPost. سنحتاج قبل تجربة إرسال الإشعار إلى ضبط البريد الإلكتروني في ملف النظام env.. ستجد في الملف عند فتحه الإعدادات التالية: MAIL_DRIVER=smtp MAIL_HOST=mailtrap.io MAIL_PORT=2525 MAIL_USERNAME=null MAIL_PASSWORD=null MAIL_ENCRYPTION=null يقترح Laravel مبدئيا موقع https://mailtrap.io الذي يوفر خدمة لمحاكاة إرسال بريد إلكتروني وعرضه. نحصُل بالتسجيل في الموقع على صندوق بريد للاختبار. ندخل إلى صندوق البريد (Demo inbox). ننسخ قيم Username وPassword ونضيفها إلى الإعدادات في ملف env. على النحو التالي: MAIL_DRIVER=smtp MAIL_HOST=mailtrap.io MAIL_PORT=2525 MAIL_USERNAME="be7c7d3972da27" MAIL_PASSWORD="5f445b7bdca027" MAIL_ENCRYPTION=null يمكننا الآن اختبار الإشعار بتشغيل خادوم التطبيق والذهاب إلى المسار http://localhost:8000/notify/1/2. إن نظرت في صندوق البريد على موقع mailtrap فستجد رسالة جديدة: لاحظ العنوان البريدي الذي يُوجّه إليه الإشعار medeyil@laravel.com. إن عدلت المسار الذي تطلُبه إلى التالي http://localhost:8000/notify/2/2 ونظرت في الإشعار الذي يصل إلى البريد فستجد أن المرسَل إليه To تبدّل إلى fbenziane@laravel.com الذي هو بريد المستخدم ذي المعرف 2. هل تساءلت كيف يعرف نظام الإشعارات البريد الإلكتروني الذي يجب إرسال الإشعار إليه، أي البريد الإلكتروني الخاصّ بالمستخدِم؟ تخصيص الإشعار عبر البريد الإلكتروني يمكن تطبيق الإشعارات على أي نموذج Eloquent في النظام، بشرط أن يستخدم النموذجُ السّمةَ Trait المسمّاة Notifiable. إن نظرنا إلى النموذج User الذي يأتي مبدئيا مع المشروع فسنلاحظ التالي: use Illuminate\Notifications\Notifiable; و use Notifiable; يتيح استخدام هذه السمة في النموذج إمكانية استدعاء الدالة notify: $user->notify(new NewPost($post)); التي تأخذ معطى عبارة عن كائن من صنف Notification. بالنسبة لكيفية معرفة نظام الإشعارات ببريد المستخدم فالإجابة هي أن كل قناة إشعار تتطلّب وجود خاصيّات Properties معيّنة في النموذج الذي يستدعي الدالة notify. في حالة قناة البريد الإلكتروني فإن الخاصيّة المنتظرة هي - منطقيا - عنوان البريد email. بالعودة إلى الصنف NewPost لاحظ دالة التعامل مع البريد الإلكتروني: public function toMail($notifiable) { return (new MailMessage) ->line('The introduction to the notification.') ->action('Notification Action', 'https://laravel.com') ->line('Thank you for using our application!'); } ترجع هذه الدالة كائنا من صنف MailMessage. إن تنبهت جيدا فستدرك أن العبارات الواردة في الشفرة أعلاه هي نفسها الموجودة في البريد المرسَل إلى المستخدم: الدالة line تدرج فقرة في بريد الإشعار. الدالة action تدرج زرًّا وتحدّد النص الذي يظهر على الزرّ ورابط الصفحة التي تزورها عند النقر على هذا الزرّ. توجد دوال أخرى لتخصيص الإشعار أكثر: subject لتخصيص موضوع البريد الإلكتروني. from لتحديد بريد المرسِل. attach لإضافة ملف مرفَق. priority لتحديد الأولوية (1 تعني أولوية قصوى). نخصّص في ما يلي البريد السّابق: public function toMail($notifiable) { return (new MailMessage) ->subject('New post: '. $this->post->title) ->line('Don\'t miss our new post ' .$this->post->title) ->action('View the post', 'https://laravel.com') ->line('Thank you for your visit!') ->from('hsoub@academy.com'); } نحصل بزيارة الرابط http://localhost:8000/notify/1/2 على النتيجة أدناه. بالنسبة لاسم التطبيق الذي يظهر في الإشعار فيمكن تعديله عن طريق التعليمة name في ملف الإعداد config/app.php. تأخذ التعليمة مبدئيا القيمة Laravel. إن أردت تخصيصا أكبر للبريد ومظهره فيمكنك ذلك بنشر قالب Blade الخاص بالبريد الإلكتروني للإشعار ومن ثم تخصيصه. نفذ الأمر التالي لنشر القالب: php artisan vendor:publish --tag=laravel-notifications ستجد القالب على المسار resources/views/vendor/notifications/email.blade.php؛ حيث يمكنك التعديل عليه بما تراه مناسبا.
  3. يأتي Laravel بإطار العمل Eloquent مضمّنا مبدئيا؛ وهو ما يتيح بناء نماذج تُعالّج فيها البيانات وتُطبّق عليها القواعد التي تحكُم سير التطبيق. يُيسّر Eloquent كثيرا من الأمور على المطوِّر؛ إلا أنّه يمكن أن يكون أحيانا غير مناسب لتعقيد المشروع الذي تعمل عليه. يوفّر Laravel آلية للتعامل مع قواعد البيانات مباشرة دون الحاجة لربط كائنات التطبيق بجداول قاعدة البيانات. تُسمى هذه الآلية منشئ الاستعلامات Query builder. كانت الاستعلامات المعدّة بمنشئ الاستعلامات ترجع النتائج، في الإصدارات السابقة (قبل الإصدار 5.3) من Laravel، على هيئة مصفوفة Array؛ إلا أن الأمر تغيّر في الإصدار 5.3 وأصبحت أغلب دوالّ بناء الاستعلامات ترجع نتيجة من نوع Collection (مجموعة). سنرى في هذا المقال أمثلة تفصيلية حول هذا التغيير، ولكن قبل ذلك سنعرّج على مقارنة بين Eloquent ومنشئ الاستعلامات ومتى يكون استخدام أحدهما أنسب. أيهما أستخدم.. Eloquent أم منشئ الاستعلامات؟ يخضع تطوير البرمجيّات لإكراهات كثيرة، وهو ما يعني أن عليك أحيانا كتابة استعلامات SQL للتخاطب مع قاعدة البيانات مباشرة. يتيح Eloquent آلية أنيقة للتعامل مع قاعدة البيانات بربطها بأصناف التطبيق ممّا يقلّل كثيرا من الحاجة لتدخّل المطوّر في تفاصيل تنفيذ العمليّات. تشمل التسهيلات التي يتيحها Eloquent: التحقق المُستَتِر Dirty check: تحديث الحقول التي تغيّرت قيمتها فقط دون غيرها، عند تنفيذ استعلامات UPDATE. ربط النماذج بأحداث Events؛ مثلا لإشعار المستخدمين أو تحديث إحصائيات عند إنشاء مستخدم جديد. إدارة الحذف اللطيف Soft delete: تعليم تسجيلة على أنها محذوفة بدلا من حذفها فعلا. تنفيذ آليات التحميل الحثيث Lazy loading (طلب البيانات عند الحاجة إليها). يمكن - بالطبع - تطبيق هذه الآليات بنفسك دون الاعتماد على Eloquent؛ إلا أن ذلك سيأخذ وقتا. من الجيد أن يكون لديك سبب مقنع للوقت الذي ستمضيه في هذه المهمة بدلا من التركيز على أمور *قد* تكون أهم. سيكون مثاليّا جدا الحصولُ على كلّ هذه التسجيلات بدون دفع ثمن، إلا أن الأمور في الواقع ليست مثالية. الثمن الذي تدفعه مقابل التسهيلات التي يتضمّنها Eloquent - وأطر عمل ORM الأخرى، بغضّ النظر عن لغة البرمجة - هو سرعة تنفيذ الاستعلامات. على الجهة الأخرى، تمكّن كتابة الاستعلامات من الوصول إلى أقصى سرعة ممكنة في التنفيذ، مقابل التضحية بالجهد والوقت أثناء التطوير. لا يوجد مانع من مزج جميع الوسائل المتاحة أمامك؛ بل إن الأمر محبّذ. مثلا؛ استخدم Eloquent عندما تتعامل مع كيانات منفردة (استمارة تسجيل على موقع مثلا)، الجأ لمنشئ الاستعلامات في المهامّ التي تتطلب وقتا للتنفيذ (كيانات بالجملة Batch processing أو إعداد تقارير على سبيل المثال) واكتب استعلامات SQL مباشرة عندما تكون أمام استعلام شديد التعقيد. الجديد في Laravel 5.3 نستخدم منشئ الاستعلامات في المثال التالي للحصول على كافة المستخدمين (جدول users في قاعدة البيانات): DB::table('users')->get(); تكافئ التعليمة السابقة في نتيجتها تنفيذ الاستعلام التالي: SELECT * FROM users; كانت النتيجة، في الإصدارات السابقة من Laravel، من نوع Array (مصفوفة)؛ إلا أن هذا الأمر تغيّر في الإصدار 5.3 لتصبح النتيجة من نوع Collection (مجموعة). يعني هذا أنه للوصول إلى العنصر الأول في مجموعة المستخدمين السابقة فإننا يمكن أن نكتب: DB::table('users')->get()->first(); بدلا من: DB::table('users')->get()[0]; يجعل هذا التغيير من منشئ الاستعلامات متناسقا في نوع النتيجة مع Eloquent الذي يرجع مجموعة كائنات؛ كما أنه يمكّن من استخدام الدوال الموجودة مبدئيا للتعامل مع المجموعات؛ ومن بينها الدالة all التي ترجع مصفوفة مكافئة للمجموعة؛ مثلا ترجع التعليمة التالية مصفوفة بالمستخدمين: DB::table('users')->get()->all(); إن كان لديك تطبيق مبني على اعتبار أن منشئ الاستعلامات يُرجع النتائج على هيئة مصفوفات، فأسهل طريقة لتعديل الشفرة المصدرية للتوافق مع تغيير نوع النتيجة إلى مجموعة بدلا من مصفوفة؛ هي استخدام الدالة all. ملحوظة 1: لا ينطبق التغيير المذكور أعلاه (إرجاع مجموعة بدلا من مصفوفة) على دوال الصنف DB التي تتعامل مع استعلامات SQL مباشرة. مثلا؛ ترجع التعليمة التالية مصفوفة: DB::select('SELECT name FROM users WHERE id = ?', [1]); ملحوظة 2: انتبه عند استخدام استعلامات SQL مباشرة من ثغرات الحَقن SQL injection. استخدم ربط المتغيّرات Binding (كما في الملحوظة 1) بدلا من تمرير المتغيّر مباشرة كما في الاستعلام التالي: DB::select("SELECT name FROM users WHERE id = '$id' "]); قد يمثل الاستعلام الأخير خطرا إن لم يُتحقّق من المتغيّرid$ قبل استخدامه في الاستعلام. يتحقّق Laravel تلقائيا من المُدخلات عند ربط المتغيّرات كما في استعلام الملحوظة 1. راجع مقال استخدام Eloquent ORM للتعامل مع قاعدة البيانات في Laravel 5.
  4. تخصيص التّصفيح Pagination في Laravel

    يعدّ التصفيح Pagination (إعداد الصفحات) إحدى المهامّ الشائعة في مواقع الوِب، وهو ما دعا Laravel لتضمين هذه الوظيفة مبدئيا في إطار العمل. كانت تخصيص التصفيح في الإصدارات الأولى من Laravel بسهولة إعداد قالب Blade؛ إلا أن الأمر تغيّر في الإصدار 5.0، إذ عمل إطار العمل على تسهيل نظام التصفيح الذي يستخدمه لتمكن إعادة استخدامه في مشاريع لا تعتمد على إطار العمل Laravel. صاحب هذه العمليّة تعقيد في إمكانيّة تخصيص التصفيح. يأتي الإصدار 5.3 لتصحيح هذه الوضعية وإعادة الأمور إلى نصابها. تهيئة المشروع سنهيّئ مشروع Laravel 5.3 للعمل عليه. نبدأ بالتثبيت: composer create-project --prefer-dist laravel/laravel laravel53pagination "5.3.*" يأتي المشروع مبدئيا بنموذج للمستخدم User، سنعمل من أجل الشرح على هذا النموذج. نبدأ بإعداد مسار للمستخدمين في ملف المسارات الخاصّ بالوِب routes/web.php: Route::get('users', function () { return view('users.index') ->with('users', User::paginate(5)); }); يطلُب المسار users/ عند زيارته عرضَ القالب index.blade.php الموجود في المجلّد users ضمن مجلد القوالب resources/views. نمرّر للقالب المتغيّر users الذي يتلقّى نتيجة استدعاء الدالة paginate للحصول على المستخدمين المسجلين في قاعدة البيانات. ما يميّز هذه الدالة عن بقيّة الدوال الأخرى التي يمكن استدعاؤها في النموذج هي أنها توفّر إمكانية تطبيق الدالة links في قالب Blade من أجل إنشاء الصفحات، كما سنرى الآن. ليس لدينا لحد الساعة المجلد users ولا الملف index.blade.php، ننشئ المجلد والملف بحيث يصبح مسار ملف القالب resources/views/users/index.blade.php ثم نفتحه ونضيف إليه المحتوى التالي: <!DOCTYPE html> <html> <head> <meta charset="utf-8" > <title> Document </title> <link rel="stylesheet" href="/css/app.css"> <style type="text/css"> body { padding: 10em;}</style> </head> <body> @foreach ($users as $user) <li>{{ $user->name }}</li> @endforeach {{ $users->links() }} </body> </html> لدينا في أعلى الملف بضعة تعليمات للتنسيق، ثم تأتي في جسم المستند body التعليمة التكرارية foreach التي تمرّ على المستخدمين وتعرض أسماءهم، وفي الأخير الدالة links التي تحدثنا عنها أعلاه. يمكننا استدعاء هذه الدالة على المتغيّر users نظرا لأننا استخدمنا الدالة paginate في ملف المسارات للحصول على المستخدمين. تتكفّل الدالة links بإنشاء الصفحات عبر تقسيم المستخدمين على مجموعة من خمسة عناصر (مرّرنا المعطى 5 للدالة paginate في ملف المسارات). بقي لنا بذر جدول المستخدمين حتى يمكننا تجربة ما أنجزناه لحد الآن: php artisan make:seeder UsersTableSeeder نستعين بمكتبة Faker لتوليد مستخدمين وهميين وبذر الجدول: public function run() { User::unguard(); // نستخدم Faker لتوليد البيانات $faker = Faker\Factory::create(); // إنشاء 20 مستخدما foreach(range(1, 20) as $index) { User::create([ 'name' => $faker->name, 'email' => $faker->safeEmail, 'password' => $faker->password, ]); } User::reguard(); } لا تنس استيراد النموذج: use App\User; ثم نعدّل DatabaseSeeder لإضافة ملف البذر الذي أنشأناه للتو: $this->call(UsersTableSeeder::class); يمكننا الآن تنفيذ التهجيرات (تهجير المستخدم يأتي مبدئيا مع المشروع): php artisan migrate ثم بذر قاعدة البيانات: php artisan db:seed ثم تشغيل خادوم التطوير: php artisan serve تظهر عند زيارة الرابط http://localhost:8000/users قائمة بالمستخدمين مقسّمة على صفحات من خمسة مستخدمين. عند النقر على رقم الصّفحة ينقلك إلى مجموعة جديدة من المستخدمين. تخصيص التصفيح في Laravel يوجد القالب المبدئي لروابط الصفحات على المسار vendor/laravel/framework/src/Illuminate/Pagination/resources/views إن أردت تخصيصه فسيتوجّب عليك نشره بالأمر التالي: php artisan vendor:publish --tag=laravel-pagination وستجد بضعة قوالب على المسار ressources/views/vendor/pagination. يستخدم اثنان من هذه القوالب (bootstrap-4.blade.php وsimple-boostrap-4.blade.php) الإصدار 4 من Bootstrap والذي ما زال قيد التطوير؛ أما الآخران فأحدهما هو القالب المبدئي default.blade.php والآخر (simple-default.blade.php) نسخة مبسّطة منه لا تحوي سوى أزرار سابق ولاحق للتنقل بين الصفحات. أدناه الشفرة الخاصّة بالقالب المبدئي: @if ($paginator->hasPages()) <ul class="pagination"> {{-- Previous Page Link --}} @if ($paginator->onFirstPage()) <li class="disabled"><span>«</span></li> @else <li><a href="{{ $paginator->previousPageUrl() }}" rel="prev">«</a></li> @endif {{-- Pagination Elements --}} @foreach ($elements as $element) {{-- "Three Dots" Separator --}} @if (is_string($element)) <li class="disabled"><span>{{ $element }}</span></li> @endif {{-- Array Of Links --}} @if (is_array($element)) @foreach ($element as $page => $url) @if ($page == $paginator->currentPage()) <li class="active"><span>{{ $page }}</span></li> @else <li><a href="{{ $url }}">{{ $page }}</a></li> @endif @endforeach @endif @endforeach {{-- Next Page Link --}} @if ($paginator->hasMorePages()) <li><a href="{{ $paginator->nextPageUrl() }}" rel="next">»</a></li> @else <li class="disabled"><span>»</span></li> @endif </ul> @endif تتيح الدالة hasPages التحقق من وجود صفحات لعرضها. نتحقّق بالدالة onFirstPage من أننا على الصفحة الأولى. يمكن بالدالة hasMorePages معرفة ما إذا كان لدينا مزيد من الصفحات لعرضها. تطبع الدالتان previousPageUrl وnextPageUrlعلى التوالي رابطي الصفحة السابقة واللاحقة للصفحة الحالية التي نحصل عليها بالدالة currentPage. سنعدّل الآن القالب بحيث نضع كلمة Previous (السابق) مكان الرمز »، على ألا تظهر عندما نكون على الصفحة الأولى. نضع بنفس الطريقة كلمة Next (التالي) مكان «، على ألا تظهر عندما نكون على الصفحة الأخيرة. نحصُل على الشفرة التاليّة: @if ($paginator->hasPages()) <ul class="pagination"> {{-- Previous Page Link --}} @if (!$paginator->onFirstPage()) <li><a href="{{ $paginator->previousPageUrl() }}" rel="prev">Previous</a></li> @endif {{-- Pagination Elements --}} @foreach ($elements as $element) {{-- "Three Dots" Separator --}} @if (is_string($element)) <li class="disabled"><span>{{ $element }}</span></li> @endif {{-- Array Of Links --}} @if (is_array($element)) @foreach ($element as $page => $url) @if ($page == $paginator->currentPage()) <li class="active"><span>{{ $page }}</span></li> @else <li><a href="{{ $url }}">{{ $page }}</a></li> @endif @endforeach @endif @endforeach {{-- Next Page Link --}} @if ($paginator->hasMorePages()) <li><a href="{{ $paginator->nextPageUrl() }}" rel="next">Next</a></li> @endif </ul> @endif احفظ التعديلات ثم أعد تنزيل الصفحة للحصول على نتيجة مشابهة لتلك الظاهرة في الصورتين أعلاه.
  5. أساسيات التخبئة Cache في Laravel

    يوفّر Laravel واجهة تطبيقات برمجية موّحدة للتعامل مع أنظمة تخبئة Cache مختلفة مثل Memcached وRedis. سنعرض في هذا المقال لكيفية إعداد التخبئة في Laravel ومبدأ عملها ثم نتطرق للدالة المساعدة cache التي أضيفت إلى إطار العمل بداية من الإصدار 5.3. تهيئة بيئة التطوير يُحدَّد اسم النظام المستخدَم في التعليمة CACHE_DRIVER ضمن ملف env.. تأخذ التعليمة CACHE_DRIVER مبدئيا القيمة file التي تعني أن الكائنات Objects المخبَّأة ستخزن ضمن ملف ضمن نظام التشغيل. تُضبَط إعدادات نظام التخبئة المستخدَم في الملف config/cache.php، بعد تعيينه بالتعليمة السابقة. نفترض أنك لديك مشروع Laravel بالإصدار 5.3 للتطبيق عليه. ننفذ الخطوات التالية لتهيئة مثال نعتمد عليه في ما بعد للشرح. إنشاء نموذج والتهجير المصاحب له وبذره نستخدم artisan لإنشاء نموذج Eloquent مع التهجير المصاحب له على النحو التالي: php artisan make:model Post -m نفتح ملف التهجير لضبط الحقول في جدول البيانات بإضافة حقل جديد يُسمّىtitle (العنوان): public function up() { Schema::create('posts', function (Blueprint $table) { $table->increments('id'); $table->string("title"); $table->timestamps(); }); } يمكننا الآن تنفيذ التهجير: php artisan migrate الخطوة التالية هي بذر الجدول الحصول على بيانات للتطبيق عليها. ننشئ ملف البذر: php artisan make:seeder PostsTableSeeder ثم نعدل عليه: class PostsTableSeeder extends Seeder { public function run() { Model::unguard(); // نستورد faker لبذر الجدول $faker = Faker\Factory::create(); // ننشئ 10 تسجيلات في الجدول foreach(range(1, 10) as $index) { Post::create([ 'title' => $faker->sentence(5), ]); } Model::reguard(); } } نعدّل ملف بذر قاعدة البيانات لإضافة ملف بذر الجدول posts إليه: public function run() { $this->call(PostsTableSeeder::class); } ثم ننفذ البذر: php artisan db:seed --class=PostsTableSeeder لدينا الآن جدول بيانات بـ10 تسجيلات. أكملنا إعداد النموذج الذي نريد العمل عليه، الخطوة الموالية هي إعداد المتحكم والمسارات. المتحكم والمسارات نستعين بأداة artisan لإنشاء متحكم على النحو التالي: php artisan make:controller PostsController ننشئ في المتحكم PostsController دالة باسم index نستخدمها للحصول على جميع المنشورات الموجودة في جدول البيانات posts بصيغة JSON: public function index() { $posts = Post::all(); return response()->json($posts); } لا تنس استيراد النموذج Post: use App\Post; ثم ننتقل إلى إعداد المسارات. سنضيف مسارا باسم posts/ إلى ملف مسارات واجهةالوب routes/web.php: Route::get('/posts', 'PostsController@index'); يمكننا الآن تنفيذ الأمر php artisan serve من مجلد المشروع لتشغيل الخادوم الخاص ببيئة التطوير ثم الذهاب إلى الرابط http://localhost:8000/posts لرؤية جميع المنشورات المحفوظة في الجدول. إعداد التخبئة واستخدامها تُضبَط التخبئة - كما أسلفنا - في الملفين env. وconfig/cache.php. يحوي الصنف Illuminate\Support\Facades\Cache الكثير من الدوال الثابتة Static التي تتعامل مع التخبئة. إضافة قيمة إلى التخبئة تُستخدَم الدالة Cache::put لإضافة قيمة إلى التخبئة. تأخذ الدالة ثلاثة معطيات: Cache::put('key', 'value', 10); مفتاح key يمثل اسم المتغير. لا يمكن أن تحمل قيمتان في النظام نفس المفتاح. قيمة المفتاح value. مدة زمنية معبَّر عنها بالدقائق (10 في المثال أعلاه)، وتمثل المدة التي سيحتفظ النظام فيها بقيمة المفتاح ضمن التخبئة. تمكن أتمتة إضافة المفاتيح إلى التخبئة بالدالة remember. تبحث هذه الدالة أولا عن قيمة المفتاح في التخبئة، وترجعها إن وجدته؛ فإن لم تجده تنشئ المفتاح وتعطيه القيمة التي ترجعها دالة الإغلاق Closure الممررة في المعطى الثالث. سنستخدم الدالة remember في دالة index التي أضفناها سابقا إلى المتحكم. نعدّل الدالة index على النحو التالي: public function index() { $posts = Cache::remember('posts', 5, function() { return Post::all(); }); return response()->json($posts); } لا ننسى استدعاء الصنف Cache: use Illuminate\Support\Facades\Cache; تعني الشفرة أعلاه أننا عند طلب الدالة index فإنها ستبحث عن المنشورات الموجودة في التخبئة، فإن وجدتها ترجعها وإلا فإنها تنشئها باستدعاء الدالة all من النموذج Post؛ وتحتفظ بها في التخبئة لمدة خمس دقائق؛ ثم ترجع النتيجة. بهذا تكون جميع الطلبات التي ترد إلى المسار posts/ تمر عبر التخبئة؛ سنضيف لأغراض التوضيح مسارا جديدا (ضمن routes/web.php) لنعدل عن طريقه على إحدى التسجيلات الموجودة في جدول المنشورات: Route::get('/edit/post/{id}', 'PostsController@edit'); ونعدّل المتحكم بإضافة الدالة edit التالية: public function edit($id) { $post = Post::find($id); $post->title = "New title for ".$id; $post->save(); $posts = Post::all(); return response()->json($posts); } ما يحدث هو التالي: تظهر عند زيارة الرابط posts/ المنشورات مع المرور على نظام التخبئة؛ أما عند زيارة المسار edit/post/id/ فسنعدّل على المنشور ذي المعرف id، ثم نعرض المنشورات المخزنة في الجدول مباشرة، دون البحث عنها في التخبئة. يظهر في الصورة المتحركة التالية الفرق بين استخدام التخبئة وطلب المنشورات مباشرة من الجدول: تكون التخبئة فارغة عند زيارة المسار posts/ لأول مرة. في هذه الحالة يطلب Laravel المنشورات مباشرة من جدول البيانات، يعرضها ثم يضيفها إلى التخبئة. نطلب الرابط edit/post/1/ الذي يغيّر عنوان المنشور ذي المعرف 1 ليصبح New title for 1؛ ثم يعرض المنشورات بطلبها مباشرة من جدول قاعدة البيانات. لاحظ تغير عنوان المنشور. نعود للرابط posts/ قبل انقضاء المدة المحددة في دالة التخبئة (5 دقائق)، نلاحظ أن التعديل الذي أجريناه لا يظهر في المنشورات المعروضة. ملحوظة: يجب التأكد في التطبيقات الموجهة لبيئة الإنتاج من تناسق عمليات التعديل على قاعدة البيانات والتخبئة. بمعنى أنه يجب أن ينعكس التعديل على قيمة في جدول البيانات مباشرة على القيمة المحفوظة في التخبئة. العمليات على التخبئة الحصول على قيمة من التخبئة: Cache::get('key'); يمكن إرجاع قيمة مبدئية في حال عدم وجود المفتاح في التخبئة: Cache::get('key', 'default'); تبحث الدالة أعلاه عن قيمة المفتاح key في التخبئة، فإن لم تجدها ترجع القيمة المبدئية default. التحقق من وجود مفتاح في التخبئة: if (Cache::has('key')){ Cache::get('key'); } else { Cache::put('key', $values, 10); } نتحقق في الشفرة أعلاه من وجود قيمة للمفتاح key ونرجعها إن وجدت؛ وإلا نضيف قيمة جديدة للمفتاح في التخبئة. نزع مفتاح من التخبئة: Cache::forget('key'); كما يمكن استخدام الدالة pull التي تبحث عن قيمة في التخبئة، ثم تحذفها بعد الحصول عليها: Cache::pull('key'); يشبه الأمر تنفيذ الدالتين get وforget بالتتالي. توجد طريقة أخرى لحذف محتوى التخبئة قبل انتهاء المدة المحددة له بتنفيذ أمر artisan التالي: php artisan cache:clear الدالة المساعدة cache يحتوي إطار العمل Laravel على الكثير من دوال PHP المساعدة؛ بعضها مستخدَم في وظائف إطار العمل نفسه. تعمل الدوال المساعدة Helper functions على الرفع من الإنتاجية والابتعاد عن تكرار الشفرات البرمجية بتجميع وظائف اعتيادية وإتاحة الوصول إليها حيث دعت الحاجة. يضيف الإصدار 5.3 دالة مساعدة جديدة باسم cache يمكن استخدامها في أي جزء من المشروع من أجل تسهيل التخاطب مع نظام التخبئة، بدلا من الصّنف Illuminate\Support\Facades\Cache. نحصل على قيمة المتغير key (المفتاح) باستخدام الدالة المساعدة cache على النحو التالي: cache('key'); تكافئ التعليمة أعلاه استخدام الصنف Cache على النحو التالي: Cache::get('key'); يمكن أيضا تمرير مصفوفة بالمفاتيح وقيمها إلى الدالة cache؛ ونحدّد المدة الزمنية التي نريد أن تستغرقها التخبئة: cache(['title' => 'Hsoub Academy'], 5); إن طلبنا في الخمس دقائق القادمة قيمة المفتاح title في الدالة cache فسنجد القيمة Hsoub Academy. نستخدم الدوال الموجودة في الصنف Cache في الدالة المساعدة cache بنفس الطريقة: إضافة مفتاح إلى التخبئة: cache()->put('key', 'value', 10); أو cache(['key' => 'value'],10); البحث عن قيمة المفتاح في التخبئة، وإضافته إليها إن لم يوجد بها: cache()->remember('key', 5, function() { // }); الحصول على قيمة مفتاح من التخبئة ثم حذفه منها بعد ذلك: cache()->pull('key'); حذف مفتاح من التخبئة: cache()->forget('key'); نعيد كتابة الدالة index في المتحكم PostsController بالدالة المساعدة cache بدلا من الصنف Cache: public function index() { $posts = cache()->remember('posts', 5, function() { return Post::all(); }); return response()->json($posts); } يمكن ملاحظة أنك لا تحتاج لاستيرد صنف جديد؛ فالدالة المساعدة متوفرة في جميع المشروع.
  6. إدارة بيئة KVM بأدوات سطر الأوامر

    سنعرض في هذا الدرس، الرابع من سلسلة دروس الحوسبة الافتراضية باستخدام KVM، كيفية إدارة الآلات الافتراضية عن طريق أدوات تعمل على سطر الأوامر؛ بدلا من واجهة virt-manager الرسومية. تفيد أدوات سطر الأوامر خصوصا في كتابة السكربتات لأتمتة Automating المهام. سنرى الأدوات التاليّة: virt-install: لإنشاء الآلات الافتراضية وضبطها. virsh: لإنشاء مجامع التخزين وإعدادها. qemu-img: لإدارة تجزئات التخزين الافتراضية. إعداد مجمع تخزين تُعد أداة virsh على سطر الأوامر واجهةً لإدارة الآلات الافتراضية وإعدادها. سنستخدم هذه الأداة في الفقرات المقبلة لإنشاء مجمع تخزين لبيئة KVM. ننفذ الأمر pool-define-as مع virsh لتعريف مجمع تخزين جديد. نمرّر للأمر اسم المجمع، نوعه ومعطيات النوع. يبلغ عدد معطيات نوع المجمع خمسة، وتختلف معطيات نوع المجمع التي يجب تمريرها إلى الأمر من نوع إلى أخر. إن أردنا - مثلا - إنشاء مجمع تخزين مماثل للمجمع SPool1 الذي أنشأناه في الدرس الماضي ، والذي هو من نوع dir فسنحتاج لذكر المعطى target (الأخير من المعطيات الخمسة، ويشير إلى مسار مجلّد على النظام المضيف) فقط ونعوّض المعطيات الأربعة المتبقية بعلامة -: $ sudo virsh pool-define-as SPool1CL dir - - - - "/mnt/spools/Spool1New" Pool SPool1CL defined تظهر بعد تنفيذ الأمر رسالة توضّح أن مجمع التخزين قد عُرِّف. ملحوظة: أسمينا مجمع التخزين SPool1CL لتمييزه عن مجمع التخزين الذي أنشأناه في الدرس السابق. يمكننا سرد جميع مجامع التخزين المتوفّرة على النظام بتمرير المعطى all-- إلى الأمر virsh pool-list: $ sudo virsh pool-list --all Name State Autostart ------------------------------------------- default active yes SPool1 active yes SPool1CL inactive no SPool2 active yes ستلاحظ ظهور مجمع التخزين SPool1CL إلى جانب مجامع التخزين التي أنشأناها في الدرس الماضي؛ لكنّه يختلف عنها في الحالة State والتفعيل التلقائي (Autostart). في الواقع، الأمر السابق يعرّف مجمع تخزين، ولكنه لا ينشئه فعليا. ننفّذ الأمر virsh pool-build لإنشاء مجمع التخزين SPool1CL المعرَّف أعلاه: $ sudo virsh pool-build SPool1CL Pool SPool1CL built تظهر بعد تنفيذ الأمر رسالة تخبرنا بإنشاء المجمع SPool1CL. يمكننا الآن تفعيل SPool1CL (تغيير الحالة من خامل inactive إلى نشِط active): $ sudo virsh pool-start SPool1CL Pool SPool1CL started إن أعدنا سرد جميع مجامع التخزين في النظام فسنلاحظ تغيّر حالة SPool1CL من inactive إلى active: $ sudo virsh pool-list --all Name State Autostart ------------------------------------------- default active yes SPool1 active yes SPool1CL active no SPool2 active yes بقي لنا تفعيل التفعيل التلقائي لـSPool1CL بتنفيذ الأمر التالي: $ sudo virsh pool-autostart SPool1CL Pool SPool1CL marked as autostarted فلنعرض معلومات مفصّلة عن مجمع التخزين الذي أكملنا إنشاءه للتو: $ sudo virsh pool-info SPool1CL Name: SPool1CL UUID: 120247f3-669c-4868-8c1e-bbd134077f55 State: running Persistent: yes Autostart: yes Capacity: 184.33 GiB Allocation: 17.90 GiB Available: 166.43 GiB مجمع التخزين SPool1CL جاهز الآن للاستخدام. يُضبَط مجمع تخزين جديد إذن على خطوات: التعريف pool-define-as، الإنشاء pool-build، التفعيل pool-start والتفعيل التلقائي pool-autostart. إعداد تجزئات تخزين افتراضية حان الآن دور التجزئات الافتراضية التي نستخدم الأداة qemu-img لإعدادها. سننشئ بهذه الأداة تجزئة افتراضية جديدة على مجمع التخزين SPool1CL. نحدّد للأداة الأمر الذي نريد تنفيذه (create بالنسبة للإنشاء)، صيغة تجزئة التخزين، مسار الملفّ الخاصّ بالتجزئة وحجمها: $ sudo qemu-img create -f raw /mnt/spools/Spool1New/SVol1.img 10G Formatting '/mnt/spools/Spool1New/SVol1.img', fmt=raw size=10737418240 لاحظ أن مسار الملفّ الخاصّ بالتجزئة mnt/spools/Spool1New/SVol1.img/ يوجد ضمن المجلّد mnt/spools/Spool1New/ الخاصّ بمجمع التخزين الذي أنشأناه أعلاه (تذكّر أننا حدّدنا النوع dir، الذي يحفظ التجزئات الافتراضية ضمن مجلد يوجد في نظام التشغيل). يمكن التحقّق من خاصيّات التجزئة بتنفيذ الأمر qemu-img info: $ sudo qemu-img info /mnt/spools/Spool1New/SVol1.img image: /mnt/spools/Spool1New/SVol1.img file format: raw virtual size: 10G (10737418240 bytes) disk size: 0 تحذير: تجنّب استخدام الأمر qemu-img للتعديل على تجزئات تستخدمها آلالات افتراضية نشطة، أو أي عمليّة Process أخرى؛ فقد يؤدي ذلك إلى إفساد التجزئة. إنشاء آلات افتراضية الخطوة الأخيرة هي إنشاء آلة افتراضية وتثبيت نظام تشغيل عليها؛ نستخدم الأداة virt-install لهذا الغرض. يتطلّب إنشاء آلة افتراضية جديدة تمرير المعطيات التاليّة إلى الأمر virt-install: اسم الآلة الافتراضية name--. مسار التجزئة التي نريد استخدامها مع الآلة الافتراضية disk path--. كيفية الاتصال بالآلة الافتراضية graphics--. تكون هذه القيمة عادة spice (اسم برمجية للتخاطب مع أسطح المكتب الافتراضية، موجودة في حزمة libvirt-bin). عدد المعالجات الافتراضية vcpu--. حجم الذاكرة العشوائية بالميغابايت ram--. مسار ملفّ ISO المستخدَم لتثبيت نظام التشغيل الضيف على الآلة الافتراضية cdrom--. واجهة الشبكة الافتراضية network--. $ sudo virt-install --name=win7 --disk path=/mnt/spools/Spool1New/SVol1.img --graphics spice --vcpu=2 --ram=4096 --cdrom=/home/zeine77/Documents/ISOs/GRMCULFRER_FR_DVD.iso --network bridge=virbr0 يبدأ بعد تنفيذ الأمر إنشاءُ الآلة الافتراضية. ثم تظهر نافذة منبثقة جديدة لبدء تثبيت نظام التشغيل الضيف. ترجمة - بتصرّف - لمقال How to Manage KVM Virtual Environment using Commandline Tools in Linux لصاحبه Mohammad Dosoukey.
  7. رأينا في الدرسين السابقين من هذه السلسلة ( تثبيت KVM وإنشاء آلات افتراضية Virtual machines باستخدامه على أوبونتو و إنشاء آلات افتراضية في بيئة KVM بوسائط تثبيت شبكية) كيفية استخدام virt-manager لإنشاء آلات افتراضيّة وتثبيت أنظمة تشغيل (ضيفة) عليها، انطلاقا من وسيط تخزين محلي أو عبر الشبكة. سنتابع في هذا الدرس مع virt-manager ونرى كيفية إدارة وسائط تخزين افتراضيّة Virtual storage devices. تستخدَم أنظمة التشغيل يوميا وسائط تخزين بنظم ملفات File systems مختلفة، بعضها محلّي Local وآخر يعمل عبر الشبكة. لا توجد عموما اختلافات كبيرة بين مبادئ عمل وسائط التخزين الافتراضية وتلك الملموسة Physical. تُنشأ الأقراص الخاصّة بالآلات الافتراضية في بيئة KVM اعتمادا على أقراص التخزين الملموسة التي يتخاطب معها النظام المُضيف. يتعامل نظام التشغيل مع أقراص التخزين حسب تجزئات Partitions (أو Volumes)، ويتكوّن كل قرص صلب من تجزئة أو أكثر. بالنسبة لبيئة KVM فإنها تتعامل مع التجزئات الافتراضية ضمن مجموعات تُسمّى مجامع (أو أحواض) التخزين Storage pools. يجب أولا إنشاء مَجمع تخزين ثم ننشئ به تجزئة جديدة لقرص افتراضي. إنشاء مجامع تخزين Storage pools في بيئة KVM نبدأ أولا بعرض مجامع التخزين المتوفّرة بالنقر على قائمة Edit (تعديل) ضمن برنامج virt-manager، ثم Connection Details (تفاصيل الاتصال) ونختار تبويب Storage (تخزين). يوجد مبدئيًّا مجمع تخزين باسم default يحفظ التجزئات الافتراضية على المسار var/lib/libvirt/images/. ستلاحظ وجودَ التجزئة الخاصة بنظام التشغيل الضيف الذي أنشأناه في الدرس الأول (اخترنا عند إعداد التخزين في الخطوة 4 مساحة تخزين افتراضية موجودة سلفا). سننشئ في الخطوات التاليّة مجمع تخزين جديدا، بالنقر على زر + في الجانب السفلي الأيسر من النافذة. تظهر نافذة جديدة لإعطاء اسم لمجمع التخزين واختيار نوعه. يدعم KVM ثمانية أنواع لمجامع التخزين: dir: يحفظ التجزئات الافتراضية ضمن مجلد يوجد في نظام الملفات الذي يستخدمه نظام التشغيل . disk: يستخدم أقراصا صلبة ملموسة لحفظ التجزئات الافتراضية بداخلها. fs: يستخدم تجزئات تخزين مهيّأة سلفا لتخزين التجزئات الافتراضية. netfs: يستخدم وسائط تخزين متشاركة عبر الشبكة، مثل NFS، من أجل حفظ التجزئات الافتراضية. gluster: يحتفظ بالتجزئات الافتراضية ضمن وسيط تخزين ملموس يعمل بنظام الملفات GlusterFS. iscsi: يحفظ التجزئات الافتراضية في وسائط تخزين متوفّرة على الشبكة ببروتوكول ISCSI. scsi: يستخدم وسيط تخزين محلي يعمل بنظام SCSI. lvm: يعتمد على مدير التجزئات المنطقية لتخزين التجزئات الافتراضية. سنختار في هذه الخطوة استخدام مجمع تخزين من نوع dir (مجلد في نظام التشغيل المُضيف)؛ فهو النوع الأكثر شيوعا ولا يحتاج للتعديل على بنية نظام الملفات المحلية. لا توجد تقييدات على مكان وجود المجلد الخاص بمجمع التخزين، ولكن من المستحسَن أن يكون على تجزئة منفصلة؛ مع التأكد من الأذون والملكية الخاصة بالمجلّد حتى يمكن ل KVM التعامل معه. تأكد من أن التجزئة التي تريد إنشاء المجمع عليها فارغة، ثم ركّبها على النحو التالي: sudo mount -t ext4 /dev/sdb4 /mnt/spools/SPool1 تحذير: يمكن أن يؤدي استخدام تجزئة غير فارغة إلى مسح محتوياتها. حيث dev/sdb4/ التجزئة الملموسة وmnt/spools/SPool1/ مجلد التركيب. استخدم الأمر lsblk لعرض التجزئات المتوفرة لديك (أو الموجودة على قرص خارجي)؛ كما يمكنك استخدام الأداة الرسومية GParted لنفس الغرض. يظهر في النافذة التالية المسار الذي تريد حفظ تجزئة التخزين الافتراضية فيه. إن كنت تستخدم تجزئة منفصلة فتأكد من ذكر مسار مجلد التركيب (مثلا mnt/spools/SPool1/) هنا. يظهر مجمع التخزين SPool1 بعد إنشائه في القائمة ضمن تبويب Storage السابق. أنشأنا مجمع تخزين ويمكننا الآن إنشاء تجزئة تخزين افتراضية عليه؛ ولكن قبل ذلك سنرى نوعا آخر من أنواع مجامع التخزين، وهو fs. يشير النوع fs إلى أننا نريد استخدام تجزئة قرص ملموسة كاملة، بدلا من مجلد فقط. ستحتاج لإعداد تجزئة قرص ملموس جديدة. تأكد من اسم التجزئة الجديدة (وأنها فارغة) ثم أعدّها لاستخدام نظام الملفات ext4 (على سبيل المثال) بالأمر التالي أو ببرنامج إدارة الأقراص المفضّل لديك: sudo mkfs.ext4 /dev/sdb6 أنشئ أيضا مجلدا جديدا (مثلا SPool2) في النظام المُضيف لتركيب القرص عليه. ملحوظة: اكتف بتهيئة التجزئة وإنشاء المجلد دون تركيبها عليه. سيتولى KVM هذه المهمة. يمكننا الآن إنشاء مجمع تخزين جديد وتسميته مع تحديد نوع المجمع بـfs. حدد في الخطوة التالية من إنشاء المجمع مسار مجلد التركيب (مثلا mnt/spools/SPool2/) في الخانة Target Path (المسار المستهدَف) ومسار التجزئة التي أعددناها سابقا (dev/sdb6/ في المثال) في الخانة Source Path (المسار المصدَر). يظهر مجمع التخزين الجديد في قائمة وسائط التخزين سنناقش في الدرس الموالي إنشاء نوع آخر من مجامع التخزين (عن طريق سطر الأوامر). ننتقل الآن لإنشاء تجزئة جديدة. إضافة تجزئة تخزين افتراضية إلى مجمع تخزين في بيئة KVM توجد صيغ عدّة لتجزئات التخزين. تمكّن هذه الصّيغ من استخدام التجزئة مع مراقبين Hypervisors مختلفين: QEMU، VMware، VirtualBox أو Hyper-V. حدّد مجمع التخزين الذي تريد إضافة التجزئة إليه (SPool2 مثلا) ثم انقر على زر + الموجود تحت بيانات مجمع التخزين أمام عبارة Volumes (التجزئات) لتظهر نافذة إضافة تجزئة. حدّد اسم التجزئة الجديدة والصيغة Format التي تريد استخدامها لإنشاء التجزئة ومساحتها ثم انقر على زر Finish (إنهاء). إن كنت تنوي استخدام التجزئة على مراقب آخر غير KVM في ما بعد فتأكد من اختيار صيغة يدعمها ذلك المراقب. تظهر التجزئة الجديدة ضمن قائمة التجزئات الموجودة في مجمع التخزين SPool2. ترجمة - بتصرّف - لمقال How to Manage KVM Storage Volumes and Pools for Virtual Machines – Part 3 لصاحبه.
  8. يتناول هذا المقال، الثاني من سلسلة من أربعة أجزاء، كيفيّة توزيع آلات افتراضيّة عبر وسائط شبكة في بيئة تستخدم KVM. تأكد - قبل تنفيذ الخطوات المشروحة هنا - من تثبيت المتطلّبات المذكورة في الجزء الأول من السلسلة. تثبيت نظام تشغيل عن طريق خادوم FTP في بيئة KVM سنثبّت لأغراض هذا الدرس خادوم FTP على النظام المُضيف؛ وذلك باتباع الخطوات التالية (راجع درس كيفية تنصيب وإعداد خادوم FTP على أوبنتو لتفاصيل أكثر عن إعداد خادوم FTP). سنكتفي هنا بالأوامر الأساسية للحصول على الخادوم وتثبيت نظام ضيف على آلة افتراضية عن طريقه. للتثبيت: $ sudo apt install vsftpd ثم نعدّل ملف الإعداد etc/vsftpd.conf/ للسماح بالوصول المجهول لخادوم FTP: $ sudo nano /etc/vsftpd.conf نغيّر قيمة التعليمة anonymous_enable من NO إلى YES؛ ثم نعيد تشغيل الخادوم: $ sudo systemctl restart vsftpd.service إن أردنا مشاركة ملفات عن طريق خادوم FTP فيمكننا وضعها على المسار ‎/srv/ftp. جهّزنا خادوم FTP لمشاركة الملفات؛ حان الآن وقت اختيار ملف ISO الخاص بنظام التشغيل الذي نريد تثبيته على الآلة الافتراضية (النظام الضّيف)؛ ونحدّد نقطة تركيب Mount point ليُركَّب عليها هذا الملف: $ sudo mount -t iso9660 -o ro ISO_FILE MOUNT_POINT تشير ISO_FILE إلى المسار الذي يوجد عليه ملف ISO وMOUNT_POINT إلى مسار مجلد التركيب. ملحوظة: تمثّل ملفات ISO نسخا خامة من أقراص صلبة ويجب تركيبها على نظام الملفات File system حتى يمكن لنظام التشغيل التعامل معها. يتعامل نظام التشغيل مع المجلَّد الذي رُكِّب عليه المحتوى كما لو كان يقرأ البيانات من قرص مًدمَج (CD أو DVD). نحدّد أثناء التركيب نظام الملفات المناسب للتعامل مع ملف ISO وهو iso9660. لا يمكن لحدّ الساعة الوصول إلى محتويات الملفّ أعلاه عن طريق FTP؛ إذ أنها لا توجد في المسار الذي يمكن لخادوم تشارك الملفات القراءة منه. ننشئ مجلدا جديدا على المسار ‎/srv/ftp: $ sudo mkdir /srv/ftp/ubuntu-14.04 ثم ننقل إليه محتويات ملفّ ISO انطلاقا من نقطة التركيب: $ sudo cp -r /mnt/ubuntu-14.04/* /srv/ftp/ubuntu-14.04/ حيثُ mnt/ubuntu-14.04/ هي نقطة التركيب (MOUNT_POINT) السابقة. نتأكد من أن مجموعة المستخدمين ftp هي المالكة للمجلد ubuntu-14.04 الذي أنشأناه للتو حتى يمكن لخادوم FTP قراءة محتوياته: $ sudo chown -R :ftp /srv/ftp/ubuntu-14.04/ يمكننا الآن استخدام أداة virt-manager لإنشاء آلات افتراضية تستخدم وسيط التثبيت الموجود على خادوم تشارك الملفات. تظهر عند طلب إنشاء آلة افتراضية جديدة النافذة التالية. نحدّد خيار التثبيت عبر الشبكة Network Install وننتقل للخطوة الموالية. نحدّد في هذه الخطوة المسار URL الذي يوجد عليه وسيط التثبيت. بما أننا نريد الوصول إلى خادوم FTP فالمسار سيكون حسب الصيغة التالية: ftp://ip_or_host/folder حيثُ ip_or_host عنوان IP الخاص بخادوم تشارك الملفات أو اسم المضيف Host الخاص به؛ وfolder المجلّد حيث توجد ملفات نظام التشغيل الذي نريد تثبيته على الآلة الافتراضية (مجلد ubuntu-14.04 الموجود على خادوم تشارك الملفات). تذكّر أننا في الدرس السابق ثبّتنا حزمة bridge-utils وذكرنا أنها تنشئ جسرا يعمل بين الآلات الافتراضية (النظام الضّيف) والنظام المُضيف. ستلاحظ بعد تثبيت الحزمة ظهور واجهة شبكة جديدة لديك على النظام المُضيف (حيثُ ثبتتنا KVM وأدواته) باسم virbr0. يمكن للأجهزة الافتراضية التواصل مع النظام المضيف عن طريق هذه الواجهة؛ لذا سنحصُل على عنوانها بتنفيذ الأمر التالي: $ ip -4 a show 1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default qlen 1 inet 127.0.0.1/8 scope host lo valid_lft forever preferred_lft forever 4: virbr0: <NO-CARRIER,BROADCAST,MULTICAST,UP> mtu 1500 qdisc noqueue state DOWN group default qlen 1000 inet 192.168.122.1/24 brd 192.168.122.255 scope global virbr0 valid_lft forever preferred_lft forever يظهر من نتيجة الأمر أعلاه أن الواجهة vibr0 لديها العنوان 192.168.122.1 (قد يختلف العنوان لديك). يصبح عنوان المجلّد حيث توجد ملفات التثبيت على النحو التالي: ftp://192.168.122.1/ubuntu-14.04 وهو العنوان الذي أعطيناه للأداة virt-manager كما في الصورة أعلاه. تشبه الخطوات الموالية (من 3 إلى 5) الخطوات المذكورة في الجزء الأول من هذه السلسلة. تثبيت نظام تشغيل عن طريق خادوم وِب في بيئة KVM لا تقتصر إمكانيات التثبيت عبر الشبكة على الوسائط الموجودة على خواديم تشارك ملفات، بل يمكن استخدام خواديم أخرى مثل خواديم الوِب (Apache أو Nginx على سبيل المثال). سنأخذ في هذه الفقرة مثالا لإنشاء آلة افتراضية عبر الأداة virt-manager وتثبيت نظام تشغيل ضيف عن طريق وسيط تثبيت موجود على خادوم وِب. راجع درس كيف تثبت حزم MySQL ،Apache ،Linux :LAMP و PHP على أوبنتو 14.04 للمزيد عن تفاصيل تثبيت خادوم وِب Apache؛ أو مقال كيف تثبت حزم MySQL ،nginx ،Linux :LEMP وPHP على أوبنتو 14.04 إن كنت تفضّل خادوم Nginx. يمكن الاكتفاء - لأغراض هذا الدرس - بالجزء الأول من أحد المقالين المذكوريْن سابقا (تثبيت Apache أو Nginx دون بقية الحزم). لا يختلف تثبيت النظام الضّيف انطلاقا من وسيط تثبيت على خادوم وِب عن التثبيت عبر خادوم تشارك ملفات سوى في الخطوة الثانية عند تحديد العنوان. في حالة خادوم الوِب يُحدّد العنوان على النحو التالي: http://192.168.122.1/ubuntu-14.04 رأينا في هذا الدرس والدرس السابق كيفية استخدام الأداة الرسومية virt-manager لإنشاء آلات افتراضيّة في بيئة تستخدم KVM للحوسبة الافتراضية؛ سنرى في الدرس الموالي كيفية إدارة الأقراص الافتراضية في هذه البيئة. ترجمة - بتصرّف لمقال How to Deploy Multiple Virtual Machines using Network Install (HTTP, FTP and NFS) under KVM Environment – Part 2 لصاحبه Mohammad Dosoukey.
  9. تقدّم تقنية KVM (اختصار Kernel-based Virtual Machine؛ الأجهزة الافتراضيّة المعتمدة على نواة لينكس) آلية لتشغيل الحوسبة الافتراضيّة (التخيّليّة أو الوهميّة) Virtualization على الأنظمة العاملة بلينكس. تمكّن هذه الآلية - التي تتوفّر على هيئة وحدة Module في نواة لينكس منذ الإصدار 2.6.20 - من استخدام أنظمة تشغيل مستقلّة ومنفصلة في وقت واحد على نفس العتاد Hardware. يتولّى نظامٌ يُعرَف بالمضيف Host تشغيل العتاد Hardware عن طريق برنامج يُعرَف بالمراقِب Hypervisor. يوفّر المراقب موارد (معالج، ذاكرة عشوائية، أجهزة طرفية وغيرها) للآلات الافتراضيّة. يُسمّى النظام الذي يُشغّل الآلة الافتراضيّة بالنظام الضيف Guest. يقدّم KVM الميزات التاليّة: التعهّد الزائد Over-commiting: يعني تخصيص موارد افتراضيّة (المعالجات والذاكرة العشوائية) أكثر من تلك التي يوفّرها العتاد. التموين السريع Thin provisionning: يسمح بالتوزيع المرن للمساحة التخزينية المتوفّرة والبحث عن أمثل طريقة لمشاركتها بين الآلات الافتراضية. تحجيم الإدخال/الإخراج من القرص Disk I/O throttling: يوفّر إمكانية تعريف حد لطلبات الإدخال والإخراج بالنسبة لكل آلة افتراضيّة. إمكانيّة زيادة قدرات المعالجة الخاصّة بالآلات الافتراضية حسب الطّلب دون الحاجة لإيقافها عن العمل. يتطرّق هذا المقال، الأوّل في سلسلة من أربعة أجزاء، لكيفية استخدام KVM والأدوات التي يوفّرها على توزيعة Ubuntu 16.04. تشترك توزيعات لينكس عموما في الخطوط العريضة، وكثير من الخطوات الواردة في هذه السلسلة. المتطلّبات يجب أن تكون لدى العتاد الذي يعمل عليه النظام المُضيف القدرةُ على تنفيذ طلبات المُراقب عن طريق ما يُعرَف بامتدادات العتاد الخاصّة بالحوسبة الافتراضية Hardware virtualization extensions؛ وهو ما توفّره أغلب الحواسيب الحديثة. يمكّن الأمر kvm-ok من التأكد من جاهزيّة جهازك لاستخدام KVM: $ kvm-ok INFO: /dev/kvm exists KVM acceleration can be used يظهر من نتيجة الأمر أعلاه أن الجهاز قادر على استخدام التقنيّة. تأكّد، إن كانت النتيجة مخالفة، من أن امتدادات العتاد موجودة ومفعّلة في محمّل الإقلاع BIOS (أو UEFI) الخاص بجهازك. تثبيت أدوات KVM ونشر الآلات الافتراضية سنحتاج لبضعة حزم قبل أن يمكننا البدء في إنشاء آلات افتراضية باستخدام KVM. يُثبّت الأمر التالي هذه الحزم: $ sudo apt install qemu-kvm libvirt-bin bridge-utils virt-manager تعمل حزمة أدوات kvm-qemu على تشغيل وحدة KVM في النواة لتنفيذ الشفرة البرمجية الخاصّة بالآلة الافتراضية. كما أن هذه الأدوات تحجز الذاكرة العشوائية والموارد التي تحتاجها الآلات الافتراضيّة للعمل؛ زيادة على كونها تعمل على محاكاة عمل الأجهزة الطرفية Peripherals الملموسة التي يتوفّر عليها النظام المُضيف من أجل إتاحة أجهزة طرفية افتراضية للنظام الضيف. تتيح الحزمة libvirt-bin مجموعة من الأدوات للتخاطب مع قدرات الحوسبة الافتراضية التي يتوفّر عليها الجهاز المضيف. نذكر مثلا الأداة virsh التي يمكن من خلالها إنشاء آلات افتراضية، تعليقها (توقيف مؤقّت) أو إيقافها عن العمل. تحوي الحزمة bridge-utils الأدوات الضرورية لإنشاء أجهزة طرفية تعمل جسورا Bridge بين واجهات شبكة عدّة، ولإدارة هذه الأجهزة. من المهمّ توفّر هذه الميزة حتى يمكننا ضبط شبكة حواسيب تشمل الآلات الافتراضية. توفّر حزمة virt-manage واجهة رسومية لإدارة الآلات الافتراضية. الخطوة التاليّة هي إضافة المستخدم الحالي إلى مجموعة المستخدمين libvirtd حتى يمكنه استخدام الآلات الافتراضيّة التي ننشئها عن طريق KVM؛ إذ لا تُتاح هذه الوظيفة لغير أعضاء هذه المجموعة والحساب الجذر: $ sudo adduser zeine77 libvirtd نطلُب، من أجل التأكد من أن كل شيء على ما يُرام، سردَ الآلات الافتراضية المتوفّرة؛ وذلك باستخدام الأمر virsh على النحو التالي: $ virsh -c qemu:///system list Id Name State ---------------------------------------------------- لا توجد آلات افتراضية لحد الساعة، لذا لن تظهر معلومات عن هذه الآلات؛ وسيكتفي الأمر بعرض أسماء أعمدة (المعرّف Id، الاسم Name والحالة State) تصف معلومات تتعلّق بالآلات الافتراضية. تشير هذه النتيجة إلى أننا مستعدون الآن للبدء في استخدام KVM. إنشاء آلات افتراضية باستخدام KVM سنستخدم في هذا الجزء من المقال الأداة virt-manager للتخاطب مع KVM من أجل إنشاء آلات افتراضية. يمكن تشغيل virt-manager من قوائم سطح المكتب الذي تستخدمه (Gnome، KDE أو غيرهما) أو بتنفيذ الأمر virt-manager في سطر الأوامر. تظهر النافذة التاليّة بعد تشغيل الأداة. يظهر في واجهة البرنامج أنه متّصل بمضيف محلي يستخدم المُراقب QEMU/KVM. يمكن من هذه الواجهة الاتّصال بمضيفات Hosts بعيدة (توجد على أجهزة مغايرة للجهاز الذي تعمل عليه الأداة virt-manager)، وذلك عن طريق القائمة …File -> Add Connection ثم تحديد الخانة Connect to remote host وذكر البيانات المطلوبة. ننقر على زر Create a new virtual machine في واجهة virt-manager لإنشاء آلة افتراضية جديدة. تظهر نافذة جديدة تطلُب تحديد طريقة لتثبيت نظام التشغيل الضّيف على الآلة الافتراضية. توجد أكثر من طريقة كما يظهر في لقطة الشاشة أعلاه. سنحدّد خيار Local install media (وسيط تثبيت محليّ). تظهر النافذة التاليّة. يمكن عبر الواجهة الظاهرة في الصورة أعلاه الاختيار بين قرص مُدمَج (CD أو DVD) أو ملفّ ISO. سنحدّد الخيار الثاني (ملف ISO) وننقر على الزّر Browse. تظهر النافذة التالية. نستطيع عبر هذه النافذة إنشاء مساحة تخزين Storage volume افتراضية لاستخدامها مع الآلة؛ إلا أننا لن نفعل ذلك الآن وسننقر على الزّر Browse Local لتحديد المسار Path الذي يوجد عليه ملفّ ISO. يعود البرنامج بعد تحديد المسار إلى واجهة الاختيار الخاصة بوسيط التثبيت. نلاحظ أن البرنامج تعرّف تلقائيا على نوعية نظام التشغيل وإصداره اعتمادا على اسم وسيط التثبيت. ثم يطلب منا البرنامج ضبط إعدادات المعالج والذاكرة العشوائية الخاصّين بالآلة الافتراضيّة. نختار القيم المناسبة اعتمادا على العتاد المتوفّر على المُضيف ثم ننتقل لإعدادات التخزين. يمكن تحديد أحد خيارين؛ إما إنشاء مساحة تخزين افتراضية جديدة أو اختيار مساحة تخزين افتراضية موجودة سلفا. بما أننا لم نضبُط مساحة تخزين قبلًا فسنحدّد الخيار الأول. ثم ننتقل للخطوة الأخيرة وهي تحديد اسم للآلة الافتراضية. إن أردت تخصيص عمليّة التثبيت فبإمكانك تحديد الخيار Customize configuration before install (تخصيص الإعدادات قبل التثبيت). ثم ننقر على زر Finish. تبدأ الآلة الافتراضية بالعمل مباشرة وتظهر في واجهة برنامج virt-manager شاشةُ التثبيت الخاصّة بنظام التشغيل الضيف على الآلة الافتراضية. توجد في واجهة virt-manager أزرار للتحكّم في الآلة الافتراضية: إعادة التشغيل Reboot، الإيقاف Shut Down، فرض إعادة التعيين Force reset، فرض الإيقاف Force off (يشبه فصل حاسوب عن الطاقة دون إيقاف نظام التشغيل) وحفظ حالة الآلة Save. كما يمكن عن طريق زرّ Open فتح نافذة خاصّة للآلة الافتراضية يمكن عبرها أخذ لقطات سريعة Snapshots. تخزّن اللقطات السريعة جميع البيانات الموجودة على الآلة ويمكن الرجوع إلى هذه اللقطات في حالات مثل حدوث مشكل مع الآلة الافتراضية، توقفها عن العمل أو لإنشاء آلة افتراضية مطابقة لها تماما. ترجمة - بتصرّف - لمقال How to Create Virtual Machines in Linux Using KVM (Kernel-based Virtual Machine) – Part 1 لصاحبه Mohammad Dosoukey.
  10. استخدم اسم المشروع الذي خصّصه لك Heroku بعد تنفيذ الأمر create (راجع مخرجات الأمر heroku create)؛ nameless-chamber-90421 هو اسم المشروع الذي أعطاه Heroku لي عند كتابة الدرس وليست لديك صلاحيات الوصول إليه.
  11. يتناول هذا المقال المفاهيم الأساسيّة للاستيثاق باستخدام LDAP: ماهيّته، متى يُستخدَم ولماذا؛ وكيفيةَ ضبط خادوم LDAP وإعداد عميل للاستيثاق عن طريقه اعتمادا على Red Hat Enterprise Linux 7. سنركّز في هذا الدّرس على الاستيثاق المعتمد على LDAP رغم وجود طرق أخرى للاستيثاق. بيئة المختبر سنفترض وجود بيئة اختبار مكوّنة من جهازيْن تشغّلهما توزيعة RHEL 7: الخادوم: 192.168.2.100 واسم نطاقه المعرَّف بالكامل academy1.virtuallab.dev. العميل: 192.168.2.200 واسم نطاقه المعرَّف بالكامل ldapclient.virtuallab.dev. راجع مقال إنشاء شبكة داخلية افتراضية باستخدام Oracle VirtualBox للمزيد حول إنشاء هذه البيئة باستخدام Oracle VirtualBox، ومقال حول إعداد أسماء المضيفات. ما هو LDAP؟ يعرفّ LDAP (اختصار لـLightweight Directory Access Protocol، الميثاق الخفيف للوصول إلى الدّليل) مجموعة من البروتوكولات التي تسمح لعميل بالوصول إلى معلومات مخزّنة مركزيًّا عبر الشبكة. يتعلّق الأمر بمعلومات يُتاح الوصول إليها من أماكن مختلفة أو لمستخدمين كثر؛ مثل صدفات الدّخول (الولوج) Login shells، مسارات الوصول إلى المجلّدات الشخصيّة للمستخدمين أو معلومات أخرى - عناوين أشخاص، أرقام هواتف - عن مستخدمي النظام، على سبيل المثال لا الحصر. جعلُ هذه المعلومات في مكان مركزي يسهّل من صيانتها وتحديثها ويعني أنه يمكن لأي شخص الوصول إليها إذا كان لديه التصريح بذلك. يدور LDAP حول مفاهيم أساسية ثلاثة: مَدخَل Entry: يُمثّل المَدخَل في LDAP معلومة أو وحدة معلومات، ويُعرَّف باسم مُميَّز Distinguished name. خاصيّة Attribute: وهي بيانات تتعلّق بالمَدخَل. قيم Values: تُسنَد لكلّ خاصيّة قيمة أو مجموعة قيم تفصِل بينها مسافة. يُشار إلى القيمة الوحيدة في المَدخَل (أي التي لا توجد إلا مرة واحدة في خاصيّات المدخل) بالاسم المُميَّز النسبي Relative Distinguished Name. سنرى في الخطوة التاليّة كيفية تثبيت LDAP على كلّ من الخادوم والعميل. تثبيت LDAP وإعداده على الخادوم والعميل يُنفّذ برنامج OpenLDAP مبادئ LDAP على RHEL 7؛ يمكن تثبيته على الخادوم والعميل على التوالي بالأمرين التاليّين: # yum update && yum install openldap openldap-clients openldap-servers # yum update && yum install openldap openldap-clients nss-pam-ldapd تحوي الحزمة nss-pam-ldapd التي نثبّتها على العميل مجموعة دوال مهمّتها البحث عن معلومات في دليل LDAP والاستيثاق من المستخدمين بناءً على المعلومات المتوفّرة على خادوم LDAP. يجب أن نعدّ بضعة أمور بعد اكتمال التثبيت. تأكّد من SELinux يسمح لـLDAP بالعمل عن طريق تفعيل الإعدادات التاليّة على كلّ من الخادوم والعميل. لا تنس استخدام الخيار P- حتى تستمر التعديلات لما بعد إعادة تشغيل النّظام: # setsebool -P allow_ypbind=1 authlogin_nsswitch_use_ldap=1 يتطلّب الاستيثاق عبر LDAP تفعيل الإعداد allow_ypbind، بينما تحتاج برامج أخرى تفعيل الإعداد authlogin_nsswitch_use_ldap حتى يمكنها استخدام LDAP للاستيثاق. ثم نفعّل خدمة slapd التي تنصِت للاتصالات القادمة إلى خادوم LDAP، مبدئيّا على المنفذ 389 وتجيب على الطلبات التي تتلقّاها عبر هذه الاتصالات: # systemctl enable slapd.service # systemctl start slapd.service تذكّر أن نظام التمهيد SystemD يمكّنك من تعطيل الخدمة، إعادة تشغيلها أو إيقافها: # systemctl disable slapd.service # systemctl restart slapd.service # systemctl stop slapd.service تعمل خدمة slapd بصلاحيّات الحساب ldap (وهو ما يمكن التأكد منه بتنفيذ الأمر ps -e -o pid,uname,comm | grep slapd)، الأمر الذي يعني أن هذا الحساب يجب أن يكون مالكَ المجلّد var/lib/ldap/ من أجل أن تستطيع خدمة slapd تعديل مداخل LDAP التي تُنشئها الأدوات الإداريّة، التي لا تعمل إلا بصلاحيّات الحساب الجذر، في المجلّد المذكور. تأكّد قبل تغيير ملكيّة المجلّد var/lib/ldap/ من نسخ نموذج ملفّ الإعداد لقاعدة بيانات LDAP: # cp /usr/share/openldap-servers/DB_CONFIG.example /var/lib/ldap/DB_CONFIG # chown -R ldap:ldap /var/lib/ldap ضبط الحساب الإداري لـOpenLDAP وتعيين كلمة سرّ له: # slappasswd مثل ما يظهر في الصّورة التالية: ثم ننشئ ملفّ LDIF باسم ldaprootpasswd.ldif. ملفّات LDIF (اختصار لـ LDAP Data Interchange Format، صيغة تبادل البيانات في LDAP) هي تمثيل للبيانات الموجودة في خادوم LDAP والتغيرات التي تطرأ عليها. نضع البيانات التاليّة في الملفّ ldaprootpasswd.ldif: dn: olcDatabase={0}config,cn=config changetype: modify add: olcRootPW olcRootPW: {SSHA}PASSWORD حيث: PASSWORD: كلمة السّر التي حصلنا عليها أعلاه. cn=config: تشير إلى خيار إعداد عامّ. olcDatabase: اسم قاعادة بيانات معيّنة، توجد عادة على المسار etc/openldap/slapd.d/cn=config/. يُضيف ملفّ ldaprootpasswd.ldif مدخلا إلى دليل LDAP. يشير كلّ سطر في هذا المدخل إلى خاصيّة وقيمتها اللتين تفصل بينهما نقطتان عموديّتان :؛ أي أن changetype، add، dn وolcRootPW خاصيّات توجد قيمها على نفس السّطر، يمين النقطتين. تنبغي المحافظة في الخطوات المواليّة على نفس الأسماء الشائعة =Common Names, cn المستخدمة أعلاه. نضيف الآن مدخلا إلى دليل LDAP باستخدام الأمر ldapadd وتحديد عنوان URI الخاصّ بالخادوم وملفّ LDIF الخاصّ بالمدخل على النحو التالي: # ldapadd -Y EXTERNAL -H ldapi:/// -f ldaprootpasswd.ldif ثم نضيف تعريفات للدليل باستيراد الأمثلة الموجودة في المجلّد etc/openldap/schema/: # for def in cosine.ldif nis.ldif inetorgperson.ldif; do ldapadd -Y EXTERNAL -H ldapi:/// -f /etc/openldap/schema/$def; done جعل LDAP يستخدم اسم النطاق في قاعدة بياناته. dn:olcDatabase={1}monitor,cn=config changetype:modify replace: olcAccess olcAccess: {0}to * by dn.base="gidNumber=0+uidNumber=0,cn=peercred,cn=external,cn=auth" read by dn.base="cn=Manager,dc=virtuallab,dc=dev" read by * none dn:olcDatabase={2}hdb,cn=config changetype: modify replace: olcSuffix olcSuffix: dc=virtuallab,dc=dev dn: olcDatabase={2}hdb,cn=config changetype: modify replace: olcRootDN olcRootDN: cn=Manager,dc=virtuallab,dc=dev dn: olcDatabase={2}hdb,cn=config changetype: modify add: olcRootPW olcRootPW: {SSHA}07/TOeaEPDlpTscePfA5kyij9mmepdr0 dn: olcDatabase={2}hdb,cn=config changetype: modify add: olcAccess olcAccess: {0}to attrs=userPassword,shadowLastChange by dn="cn=Manager,dc=virtuallab,dc=dev" write by anonymous auth by self write by * none olcAccess: {1}to dn.base="" by * read olcAccess: {2}to * by dn="cn=Manager,dc=virtuallab,dc=dev" write by * read نستخدم الأمر ldapmodify للتعديل على مَداخل الدليل: # ldapmodify -Y EXTERNAL -H ldapi:/// -f ldapdomain.ldif ننشئ ملفّ LDIF باسم baseldapdomain.ldif ونحدّد فيه مداخل لإضافتها إلى الدليل: dn: dc=virtuallab,dc=dev objectClass: top objectClass: dcObject objectclass: organization o: virtuallab dev dc: virtuallab dn: cn=Manager,dc=virtuallab,dc=dev objectClass: organizationalRole cn: Manager description: Directory Manager dn: ou=People,dc=virtuallab,dc=dev objectClass: organizationalUnit ou: People dn: ou=Group,dc=virtuallab,dc=dev objectClass: organizationalUnit ou: Group ثم نضيفها إلى الدليل بتنفيذ الأمر ldapadd (مع تحديد اسم النطاق المناسب): # ldapadd -x -D cn=Manager,dc=virtuallab,dc=dev -W -f baseldapdomain.ldif ملحوظة: أدخل كلمة السّر التي أنشأتها سابقا عندما تُطلب منك. ننشئ حسابَ مستخدم لإضافته إلى دليل LDAP: # adduser ldapuser نبدأ بإنشاء ملفّ LDIF باسم ldapgroup.ldif سنستخدمه لإضافة المجموعة التي ينتمي إليها المستخدم السّابق إلى الدّليل: # nano ldapgroup.ldif نضيف المحتوى التالي إلى الملفّ: dn: cn=Manager,ou=Group,dc=virtuallab,dc=dev objectClass: top objectClass: posixGroup gidNumber: 1012 قيمة الخاصيّة gidNumber هي معرّف المجموعة التي ينتمي إليها المستخدم ldapuserالسابق. يمكن الحصول عليه بتنفيذ الأمر id -g ldapuser. ننفذ الأمر ldapaddعلى النحو التالي لإضافة مدخل لمجموعة المستخدم إلى الدليل: # ldapadd -x -W -D "cn=Manager,dc=virtuallab,dc=dev" -f ldapgroup.ldif نعرّف في ملفّ LDIF جديد باسم ldapuser.ldif بيانات المستخدم ldapuser (استخدم الأمر slappasswd لإنشاء كلمة سرّ للمستخدم وإضافتها للملف): dn: uid=ldapuser,ou=People,dc=virtuallab,dc=dev objectClass: top objectClass: account objectClass: posixAccount objectClass: shadowAccount cn: ldapuser uid: ldapuser uidNumber: 1009 gidNumber: 1012 homeDirectory: /home/ldapuser userPassword: {SSHA}9A93CS4LCUx9L8s6HPw1PB3ClH5JEI0U loginShell: /bin/bash gecos: ldapuser shadowLastChange: 0 shadowMax: 0 shadowWarning: 0 ثم ننفّذ الأمر: # ldapadd -x -D cn=Manager,dc=virtuallab,dc=dev -W -f ldapuser.ldif يمكن باستخدام الأمر ldapdelete حذفُ نفس المستخدم من الدليل على النحو التالي (لن نفعل ذلك هنا): # ldapdelete -x -W -D cn=Manager,dc=virtuallab,dc=dev "uid=ldapuser,ou=People,dc=virtuallab,dc=dev" نطلُب من الجدار الناري السماح لخدمة LDAP بالعمل على المنفذ 389: # firewall-cmd --add-service=ldap # firewall-cmd --permanent --add-port=389/tcp # firewall-cmd reload ننتقل إلى العميل لتفعيل إمكانيّة التسجيل لمستخدمي LDAP. تأكّد من أن العميل يمكنه الوصول إلى الخادوم (والعكس). غيّر - إن لزم الأمر - ملفّ etc/hosts/ لإضافة عنوان الخادوم على العميل (والعكس، العميل على الخادوم). ننفّذ الأمر التالي - على العميل - لضبط طرق الاستيثاق عليه: # authoconfig-tui تظهر الواجهة التالية: تأكد من تحديد خياري Use LDAP وUse LDAP authentication بوضع علامة * أمامهما. استخدم الأسهم للانتقال بين الخيارات، والمسافة لتفعيل خيّار أو تعطيله؛ ثم Enter عند الوصول إلى زرّ Next. أضف في الشاشة المواليّة عنوان خادوم LDAP بالصّيغة ldap://academy1.virtuallab.dev ثم الاسم المُميّز القاعدي DN. لم نستخدم شهادة TLS لذا اترك الخيّار TLS غير محدّد. يمكننا الآن اختبار عمل LDAP على العميل بالبحث في الدليل عن المستخدم ldapuser: # ldapsearch -x cn=ldapuser -b dc=virtuallab,dc=dev يجب أن تظهر جميع معلومات المستخدم ldapuser في الدليل: # extended LDIF # # LDAPv3 # base <dc=virtuallab,dc=dev> with scope subtree # filter: cn=ldapuser # requesting: ALL # # ldapuser, People, virtuallab.dev dn: uid=ldapuser,ou=People,dc=virtuallab,dc=dev objectClass: top objectClass: account objectClass: posixAccount objectClass: shadowAccount cn: ldapuser uid: ldapuser uidNumber: 1009 gidNumber: 1012 homeDirectory: /home/ldapuser userPassword:: e1NTSEF9OUE5M0NTNExDVXg5TDhzNkhQdzFQQjNDbEg1SkVJMFU= (...) نفس الشيء عند استخدام الأمر getent على العميل: # getent passwd ldapuser ldapuser:x:1009:1012:ldapuser:/home/ldapuser:/bin/bash ترجمة - يتصرّف - لمقال RHCSA Series: Setting Up LDAP-based Authentication in RHEL 7 – Part 14 لصاحبه Gabriel Cánepa.
  12. 15 مثالا لإدارة الحزم باستخدام RPM

    تستخدم بعض توزيعات لينكس، خصوصا تلك المعتمدة على Fedora مثل CentOS وRed Hat Enterprise Linux، حزم RPM لتثبيت البرامج وإدارتها. ملف rpm. هو عبارة عن تجميع لبرامج ومكتبات تحتاجها هذه البرامج في حزمة تُستخدَم أداة باسم rpm لتثبيتها. الأداة rpm هي أداة مفتوحة المصدر تحتفظ ببيانات الحزم المثبّتة على النظام في المجلّد var/lib/rpm/. من المهم الانتباه إلى أن أداة rpm لا تستطيع التعامل مع البرامج التي ثُبِّتت انطلاقا من المصدر Source. يحتوي ملفّ rpm. على معلومات من قبيل ماهية الحزمة، من أين تأتي، الاعتماديات التي تحتاجها للعمل، الإصدار… إلخ. توجد خمسة أوضاع أساسية لأداة rpm: التثبيت i-: يُستخدَم لتثبيت حزم RPM. الحذف e-: يُستخدَم لحذف حزمة وإلغاء تثبيتها. الترقية U-: يُحدّث حزمة مثبّتة (تثبيت إصدار جديد من الحزمة). التحقق V-: يُستخدَم للتحقّق من حزمة RPM. الاستعلام q-: يُستخدَم للاستعلام عن حزم RPM. حزم RPM هي حزم قائمة بذاتها؛ تمكن الاستفادة من المواقع التالية للحصول على حزم RPM الخاصّة ببرنامج تريده: redhat.com rpmfind.net. rpm.pbone.net rpmseek.com يقدّم هذا المقال أمثلة لأوامر RPM من أجل المساعدة في تثبيت البرامج، تحديثها أو حذفها على توزيعة تستخدم RPM للتحزيم. ملحوظة: يجب أن تكون لديك صلاحيات الجذر حتى تستطيع إدارة الحزم. التحقق من توقيع حزمة يجب أولا التحقّق من مصدر الحزمة وموثوقيتها قبل تثبيتها. يُستخدم الخيار checksig-- مع الأمر rpm لهذا الغرض. لكي نستطيع التحقق من حزمة فإنه يجب أولا استيراد المفاتيح العمومية Public keys الخاصّة بالنظام الذي نعمل عليه (Fedora، CentOS، RHEL أو غيرها). بالنسبة لتوزيعة Red Hat Enterprise Linux فالأمر على النحو التالي: rpm --import /etc/pki/rpm-gpg/RPM-GPG-KEY-redhat-* نطلب في الأمر التالي التحقق من الحزمة pidgin-1.5.1-6.el3.x86_64.rpm: [root@academy1 ~]# rpm --checksig pidgin-1.5.1-6.el3.x86_64.rpm pidgin-1.5.1-6.el3.x86_64.rpm: (sha1) dsa sha1 md5 gpg OK لاحظ أن نتيجة التحقق هي OK. تثبيت حزمة يطلُب الأمر التالي تثبيت الحزمة pidgin-1.5.1-6.el3.x86_64.rpm التي تحققنا من موثوقيتها أعلاه: [root@academy1 ~]# rpm -ivh pidgin-1.5.1-6.el3.x86_64.rpm Preparing... ########################################### [100%] 1:pidgin ########################################### [100%] تظهر في الأمر أعلاه الخيارات التالية: i-: وضع التثبيت، v-: طلب إظهار رسائل بتقدم عملية التثبيت، h-: طلب إظهار علامات # للدلالة على التقدم الحاصل في تنفيذ خطوات التثبيت. الخياران الأخيران v- وh- ليسا ضروريين. التحقق من اعتماديات حزمة قبل تثبيتها تتيح أداة rpm التحقق من اعتماديات حزمة قبل تثبيتها، مثلا للتحقق من اعتماديات الحزمة pidgin-2.11.0-1.fc24.x86_64.rpm: [root@academy1 ~]# rpm -qpR pidgin-1.5.1-6.el3.x86_64.rpm /bin/sh /bin/sh glib2 >= 2.2 htmlview libICE.so.6()(64bit) libSM.so.6()(64bit) libX11.so.6()(64bit) libXext.so.6()(64bit) libao.so.2()(64bit) libatk-1.0.so.0()(64bit) libaudiofile.so.0()(64bit) (...) يشير الخيار q- إلى أننا في وضع الاستعلام عن الحزمة. يطلب الخيار p- معلومات عن حزمة غير مثبّتة ويسرُد الخيار R- اعتماديّات الحزمة. تثبيت حزمة بغض النظر عن اعتمادياته لا يقبل rpm في الحالة العادية تثبيت حزمة إلا إذا كانت جميع اعتمادياتها مثبتة؛ إلا أنه يمكن إجباره على ذلك باستخدام الخيار nodeps--: [root@academy1 ~]# rpm -ivh --nodeps pidgin-1.5.1-6.el3.x86_64.rpm Preparing... ################################# [100%] Updating / installing... 1:pidgin-1.5.1-6.el3 ################################# [100%] يُثبّت الأمر أعلاه الحزمة بتجاهل الأخطاء المتعلقة باعتماديّاتها؛ إلا أن البرنامج لن يعمل إلا بعد تثبيت الاعتماديات المطلوبة. التحقق من أن حزمة مثبّتة استخدم وضع الاستعلام q- للتأكد من أن حزمة مّا مثبتة على النظام: [root@academy1 ~]# rpm -q BitTorrent package BitTorrent is not installed [root@academy1 ~]# rpm -q pidgin pidgin-1.5.1-6.el3.x86_64.rpm سرد قائمة بجميع ملفات حزمة مثبتة أضف خيار السرد l- إلى وضع الاستعلام q- للحصول على قائمة بملفات حزمة مثبتة على النظام: [root@academy1 ~]# rpm -ql pidgin /etc/gconf/schemas/purple.schemas /usr/bin/gaim /usr/bin/pidgin /usr/lib64/pidgin /usr/lib64/pidgin/convcolors.so /usr/lib64/pidgin/extplacement.so /usr/lib64/pidgin/gestures.so /usr/lib64/pidgin/gtkbuddynote.so (...) سرد الحزم المُثبتة يسرُد الخيار a- عند استخدامه في وضع الاستعلام قائمة بأسماء جميع الحزم المثبتة: [root@academy1 ~]# rpm -qa libsss_nss_idmap-1.13.0-40.el7_2.9.x86_64 pygobject3-base-3.14.0-3.el7.x86_64 libfprint-0.5.0-3.el7.x86_64 man-pages-3.53-5.el7.noarch redhat-release-server-7.2-9.el7.x86_64 rsyslog-7.4.7-12.el7.x86_64 libsane-hpaio-3.13.7-6.el7_2.1.x86_64 (...) يمكن ترتيب القائمة لتظهر الحزم المثبتة مؤخرا في الأعلى باستخدام الخيار last--: [root@academy1 ~]# rpm -qa --last pidgin-1.5.1-6.el3.x86_64.rpm Sun 04 Sep 2016 09:47:32 PM GMT glibc-devel-2.17-106.el7_2.8.x86_64 Tue 02 Aug 2016 11:42:29 PM GMT nss-pam-ldapd-0.8.13-8.el7.x86_64 Tue 02 Aug 2016 11:42:23 PM GMT nscd-2.17-106.el7_2.8.x86_64 Tue 02 Aug 2016 11:42:17 PM GMT glibc-headers-2.17-106.el7_2.8.x86_64 Tue 02 Aug 2016 11:42:14 PM GMT glibc-common-2.17-106.el7_2.8.x86_64 Tue 02 Aug 2016 11:42:01 PM GMT (...) تحديث حزمة يُستخدَم وضع الترقية U- لتحديث إصدار حزمة مثبتة. يحتفظ أمر rpm عند تحديث حزمة بنسخة احتياطية من الإصدار المثبت للعودة إليها في حال لم تعمل الحزمة حسب المطلوب بعد تحديثها: [root@academy1 ~]# rpm -Uvh pidgin-1.5.1-6.el3.x86_64.rpm Preparing... ########################################### [100%] 1:pidgin ########################################### [100%] حذف حزمة استخدم الخيار e- مع الأمر rpm لحذف حزمة بذكر اسمها. مثلا؛ لحذف حزمة pidgin السابقة: [root@academy1 ~]# rpm -e pidgin تمكن أيضا إضافة الخيار v إلى الأمر لعرض رسائل بما يحدُث: [root@academy1 ~]# rpm -ev pidgin Preparing packages... pidgin-1.5.1-6.el3.x86_64.rpm كما يمكن الإبقاء على اعتماديات الحزمة باستخدام الخيار nodeps-- مع أمر الحذف وهو ما ينتج عنه حذف الحزمة فقط دون اعتمادياتها: [root@academy1 ~]# rpm -ev --nodeps pidgin انتبه إلى أن حذف حزمة من النظام يمكن أن يؤدي إلى عدم استقراره؛ لذا تأكد من حاجتك فعلا لحذف الحزمة. معرفة الحزمة التي ينتمي إليها ملف استخدم الخيار f- في وضع الاستعلام لمعرفة الحزمة التي ينتمي إليها ملف. مثلا؛ تخبرنا نتيجة الأمر التالي أن الملف usr/bin/htpasswd/ ينتمي للحزمة httpd-tools-2.4.6-40.el7_2.4.x86_64. [root@academy1 ~]# rpm -qf /usr/bin/htpasswd httpd-tools-2.4.6-40.el7_2.4.x86_64 الحصول على معلومات عن حزمة يعرض الخيار i- عند استخدامه في وضع الاستعلام معلومات عن حزمة مثبتة؛ مثلا بالنسبة لحزمة vsftpd: [root@academy1 ~]# rpm -qi vsftpd Name : vsftpd Version : 3.0.2 Release : 11.el7_2 Architecture: x86_64 Install Date: Thu 21 Jul 2016 03:29:37 PM GMT Group : System Environment/Daemons Size : 355788 License : GPLv2 with exceptions Signature : RSA/SHA256, Fri 18 Mar 2016 01:08:35 AM GMT, Key ID 199e2f91fd431d51 Source RPM : vsftpd-3.0.2-11.el7_2.src.rpm Build Date : Wed 24 Feb 2016 11:06:41 AM GMT Build Host : x86-019.build.eng.bos.redhat.com Relocations : (not relocatable) Packager : Red Hat, Inc. <http://bugzilla.redhat.com/bugzilla> Vendor : Red Hat, Inc. URL : https://security.appspot.com/vsftpd.html Summary : Very Secure Ftp Daemon Description : vsftpd is a Very Secure FTP daemon. It was written completely from scratch. أما إن أردت الحصول على معلومات عن حزمة قبل تثبيتها فالأمر هو التالي: [root@academy1 ~]# rpm -qip pidgin-1.5.1-6.el3.x86_64.rpm Name : pidgin Version : 1.5.1 Release : 6.el3 Architecture: x86_64 Install Date: (not installed) Group : Applications/Internet Size : 11168813 License : GPL Signature : DSA/SHA1, Thu 29 Oct 2009 08:14:44 AM GMT, Key ID 219180cddb42a60e Source RPM : pidgin-1.5.1-6.el3.src.rpm Build Date : Wed 28 Oct 2009 03:52:51 PM GMT Build Host : x86-003.build.bos.redhat.com Relocations : (not relocatable) Packager : Red Hat, Inc. <http://bugzilla.redhat.com/bugzilla> Vendor : Red Hat, Inc. URL : http://www.pidgin/im/ Summary : A Gtk+ based multiprotocol instant messaging client Description : Pidgin allows you to talk to anyone using a variety of messaging protocols, including AIM, ICQ, IRC, Yahoo!, Novell Groupwise, MSN Messenger, Jabber, Gadu-Gadu, Napster, and Zephyr. These protocols are implemented using a modular, easy to use design. To use a protocol, just add an account using the account editor. Pidgin supports many common features of other clients, as well as many unique features, such as perl scripting, TCL scripting and C plugins. Pidgin is NOT affiliated with or endorsed by America Online, Inc., Microsoft Corporation, Yahoo! Inc., or ICQ Inc. [root@academy1 ~]# الاستعلام عن التوثيق Documentation الخاص بحزمة مثبتة يمكن الحصول على مستندات التوثيق الخاصّة بحزمة باستخدام الخيارين df- في وضع الاستعلام. يأخذ الأمر معطى عبارة عن مسار الحزمة؛ وهو ما يمكن الحصول عليه عن طريق الأمر which. نطلب في المثال التالي معرفة مسار الحزمة vsftpd ثم نستعلم عن مستدات التوثيق الخاصّة بها: [root@academy1 ~]# which vsftpd /sbin/vsftpd [root@academy1 ~]# rpm -qdf /sbin/vsftpd /usr/share/doc/vsftpd-3.0.2/AUDIT /usr/share/doc/vsftpd-3.0.2/BENCHMARKS /usr/share/doc/vsftpd-3.0.2/BUGS /usr/share/doc/vsftpd-3.0.2/COPYING /usr/share/doc/vsftpd-3.0.2/Changelog (...) التحقق من حزمة تقارن أداة rpm عند طلب التحقق من حزمة المعلومات المستقاة من الملفات المثبتة مع تلك الموجودة في قاعدة بيانات rpm. يُستخدم الخيار V- لوضع التحقق وp لتمرير حزمة إلى الأمر: [root@academy1 ~]# rpm -Vp vsftpd-3.0.3-2.fc24.x86_64.rpm warning: vsftpd-3.0.3-2.fc24.x86_64.rpm: Header V3 RSA/SHA256 Signature, key ID 81b46521: NOKEY .......T. c /etc/logrotate.d/vsftpd .......T. c /etc/pam.d/vsftpd .......T. c /etc/vsftpd/ftpusers .......T. c /etc/vsftpd/user_list .......T. c /etc/vsftpd/vsftpd.conf .......T. /etc/vsftpd/vsftpd_conf_migrate.sh .......T. /usr/lib/systemd/system-generators/vsftpd-generator .......T. /usr/lib/systemd/system/vsftpd.service .......T. /usr/lib/systemd/system/vsftpd.target .......T. /usr/lib/systemd/system/vsftpd@.service S.5....T. /usr/sbin/vsftpd missing /usr/share/doc/vsftpd missing d /usr/share/doc/vsftpd/AUDIT missing d /usr/share/doc/vsftpd/BENCHMARKS missing d /usr/share/doc/vsftpd/BUGS (...) يمكن طلب التحقق من جميع الحزم في النظام بالخيار a في وضع التحقق على النحو التالي: [root@academy1 ~]# rpm -Va S.5....T. c /etc/os-release S.5....T. /usr/src/kernels/3.10.0-327.22.2.el7.x86_64/include/config/auto.conf S.5....T. /usr/src/kernels/3.10.0-327.22.2.el7.x86_64/include/config/auto.conf.cmd S.5....T. /usr/src/kernels/3.10.0-327.22.2.el7.x86_64/include/config/tristate.conf S.5....T. /usr/src/kernels/3.10.0-327.22.2.el7.x86_64/include/generated/autoconf.h (...) سرد جميع المفاتيح العمومية المستوردة يمكنك إن أردت سرد جميع المفاتيح العمومية للحزم المثبتة على نظامك، وذلك على النحو التالي: [root@academy1 ~]# rpm -qa gpg-pubkey* gpg-pubkey-2fa658e0-45700c69 gpg-pubkey-37017186-45761324 gpg-pubkey-897da07a-3c979a7f gpg-pubkey-f21541eb-4a5233e7 gpg-pubkey-42193e6b-4624eff2 gpg-pubkey-fd431d51-4ae0493b gpg-pubkey-db42a60e-37ea5438 تصحيح قاعدة بيانات RPM معطوبة يحدُث أن تُصاب قاعدة البيانات الخاصّة بحزم RPM بعُطب مما ينتُج عنه غياب وظائف من أداة rpm. يمكنك في هذه الحالة محاولة بناء قاعدة البيانات من جديد بالأوامر التالية: [root@academy1 ~]# cd /var/lib/rpm [root@academy1 rpm]# rm __db* [root@academy1 rpm]# rpm --rebuilddb [root@academy1 rpm]# /usr/lib/rpm/rpmdb_verify Packages BDB5105 Verification of Packages succeeded. ترجمة - وبتصرف - لمقال 20 Practical Examples of RPM Commands in Linux لصاحبه Ravi Saive.
  13. يتغيّر إطار العمل Laravel باستمرار بإدراج تحديثات جديدة على كلّ إصدار من أجل تحسين تجربة التطوير وإضافة ميزات جديدة تسهّل عمل المطوّر. سنتناول في هذا الدرس، الأول من سلسلة مكونة من ستة أجزاء، التغييرات الحاصلة في هيكلة مشاريع في الإصدار 5.3 من إطار العمل Laravel. سنستخدم لأغراض الشرح مشروعي Laravel؛ الأول يعمل بالإصدار 5.2 والثاني بالإصدار 5.3 (الأخير أثناء كتابة هذا المقال). ننشئ المشروعين بمدير الاعتمادات composer على النحو التالي: بالنسبة للإصدار 5.2: composer create-project --prefer-dist laravel/laravel laravel52 "5.2.*" بالنسبة للإصدار 5.3: composer create-project --prefer-dist laravel/laravel laravel53 "5.3.*" يظهر الإصدار المستهدَف في كل من الأمرين أعلاه (5.2 و5.3 على التوالي). ملف المسارات Routes تعدّ المسارات الخطوة الأولى في معالجة الطلبات التي يتلقاها التطبيق. في الإصدارات السابقة من Laravel (قبل 5.3) كانت المسارات تُضبَط في الملف app/Http/routes.php بغضّ النظر عن طبيعة الطلبات التي تتعامل معها. إن بحثت عن هذا الملف في الإصدار الجديد فلن تجده؛ إذ أنه اختفى مع إنشاء مجلّد جديد باسم routes يوجد في جذر المشروع ويحوي ملفّين: ملف web.php: يحوي هذا الملف المسارات الخاصّة بواجهة الوِب. تعتمد المسارات الموجودة في هذا الملفّ على وسيط برمجي Middleware يحمل نفس الاسم (web) ويوفّر خاصيّات مثل حالة الجلسة Session state (الاحتفاظ ببيانات العميل لاستخدامها في الطلبات الواردة منه أثناء صلاحية الجلسة) والحماية من هجمات تزوير الطلب عبر الموقع Cross-site request forgery, CSRF. ملف api.php: يتوجّه هذا الملف للتطبيقات التي توفّر واجهة تطبيقات برمجية Application programming interface. تعتمد المسارات الموجودة في الملف api.php على وسيط برمجي أخف من السابق (يُسمّى هذا الوسيط api)؛ إذ لا يحتوي على الخاصيّات الموجودة في الوسيط web والتي لا تحتاجها واجهات التطبيقات البرمجية. يمكن القول إن الملف web.php معدّ للتعامل مع الطلبات ذات الحالة Stateful، بينما الملف api.php معدّ للطلبات عديمة الحالة Stateless. من الجيد الانتباه إلى أن جميع المسارات المذكورة في الملف api تُطبَّق عليها لاحقة api. لو نظرنا إلى الملف api.php المبدئي فسنجد أن محتواه على النحو التالي: <?php use Illuminate\Http\Request; Route::get('/user', function (Request $request) { return $request->user(); })->middleware('auth:api'); وبالنسبة للملف web.php: <?php Route::get('/', function () { return view('welcome'); }); إن نفذنا أمر سرد مسارات المشروع فستكون على النحو التالي: لاحظ كيف أن Laravel أضاف تلقائيا اللاحقة api إلى المسار user/ مع تحديد الوسيط البرمجي المستخدم بـ api, auth:api. فلنضف مسارا باسم user/ إلى الملف web.php: Route::get('/user', function () { return "1,2,3,4"; }); ثم نسرُد المسارات المتوفرة: يظهر المسار الجديد في القائمة، ولكن بدون اللاحقة api، ومع تحديد الوسيط بـweb. تغيير الهيكلة المبدئية للمجلدات تخَلّص الإصدار 5.3 من Laravel من مجلدات كانت تأتي مبدئيا مع إطار العمل. يمكن ملاحظة هذا الأمرحيث تظهر، في الصورة أدناه، المجلدات الفرعية من المجلد app لكل من الإصدارين 5.2 (في اليسار) و5.3 (في اليمين). نلاحظ إخفاء المجلدات Listeners، Jobs، Events و Policies. لا يعني الإخفاء المبدئي لهذه الملفات أن الوظائف المرتبطة بها لم تعد موجودة؛ بل يتعلّق الأمر بالتخفيف من المجلدات الموجودة مبدئيا وإنشائها فقط عند الحاجة لذلك. مثلا؛ إن أردنا إنشاء حدث Event باسم ContactHandlerAction فسنفّذ الأمر التالي: php artisan make:event ContactHandlerAction إن نفذت الأمر أعلاه فستجد أن Laravel أنشأ مجلدا فرعيا في app باسم Events وأنشأ داخله الملف ContactHandlerAction.php. نفس الشيء بالنسبة للمستمعات Listeners: php artisan make:listener EmailContactListener --event="ContactHandlerAction" يظهر جليا أن Laravel 5.3 تخفّف من مجلداتٍ مبدئيا؛ إلا أنه ينشئها إن احتاج إليها.
  14. عرضنا في دروس سابقة من هذه السلسلة لوسيلتين من وسائل التحكّم في الوصول على Red Hat Enterprise Linux، وهما إدارة الأذونات وقوائم التحكّم في الوصول. هاتان الآليتان مهمتان وأساسيّتان؛ إلا أن لديهما نقاطَ قصور تأتي آليّة أخرى تُسمّى Security Enhanced Linux (التأمين المُعزَّز في لينكس، وتشتهر بـSELinux) للتغلّب عليها. تنبغي الإشارة هنا إلى أن SELinux ليست الآليّة الوحيدة المستخدمة في توزيعات لينكس للتغلّب على عوامل النّقص في الآليتين المُشار إليهما سابقا. توجد آليّة أخرى تُسمّى AppArmor تتبناها توزيعات مثل Suse وUbuntu. عند ما يُشغّل مستخدم برنامجا فإن العمليّة Process التي يُنفّذها تعمل بصلاحيّات هذا المستخدِم. يمكن في غياب آليّة مثل SELinux أن تنفّذ أي عمليّة الإجراءات التي تريد على الملفات التي يمكن للمستخدم الذي تعمل بصلاحياته الوصول إليها، مما قد يتسبّب الوصول غير المرخَّص إلى كامل النظام. بدأت وكالة الأمن القومي الأمريكية NSA بتصميم SELinux من أجل تقييد قدرة العمليّات على الوصول إلى الكائنات (مثل الملفات، المجلدات، منافذ الشبكة، ..إلخ) أو إجراء عمليّات عليها؛ وقصرها على الحدّ الأدنى. يمكن تعديل التقييدات التي يُطبّقها نظام SELinux حسب الحاجة. تُضمِّن توزيعة Red Hat Enterprise Linux 7 وحدة خاصّة بنظام SELinux في النواة وتُفعّل مبدئيا وضع Enforcing. سنشرح في هذا الدّرس المفاهيم الأساسيّة التي يعتمد عليها نظام SELinux في عمله. الأوضاع Modes في SELinux يعمل SELinux في أحد الأوضاع الثلاثة التالية: Enforcing: يمنع SELinux في هذه الحالة الوصول إلى الكائنات بناءً على مجموعة قواعد السياسة الأمنيّة المعرَّفة في النظام. Permissive: لا يمنع SELinux في هذا الوضع العمليّات من الوصول إلى الكائنات، ويكتفي بتسجيل الإجراءات التي كانت ستُمنَع لو كان الوضع Enforcing مستخدما. Disabled. نظام SELinux معطَّل. يُظهر الأمر getenforce الوضع الذي يعمل عليه SELinux حاليا؛ بينما يضبط الأمر setenforce وضع SELinux للجلسة الحاليّة، ويتلقّى أحد معطييْن: 1 لوضع Enforcing و0 لوضع Permissive. إن أردت أن يكون تغيير وضع عمل SELinux دائما ولا يقتصر على الجلسة الحاليّة فستحتاج لتحرير الملفّ etc/selinux/config/ وتعيين قيمة التعليمة SELINUX بالوضع الذي تريد: permissive، enforcing أو disabled: # getenforce Enforcing # setenforce 0 # getenforce Permissive # setenforce 1 # getenforce Enforcing # cat /etc/selinux/config # This file controls the state of SELinux on the system. # SELINUX= can take one of these three values: # enforcing - SELinux security policy is enforced. # permissive - SELinux prints warnings instead of enforcing. # disabled - No SELinux policy is loaded. SELINUX=enforcing # SELINUXTYPE= can take one of three two values: # targeted - Targeted processes are protected, # minimum - Modification of targeted policy. Only selected processes are protected. # mls - Multi Level Security protection. SELINUXTYPE=targeted يُستخدَم الأمر setenforce عادةً للانتقال بين وضعيْ Enforcing وPermissive أثناء البحث عن أسباب مشكل. مثلا؛ إن ظهر المشكال أثناء تفعيل الوضع Enforcing واختفى عند الانتقال إلى الوضع Permissive فهذا يعني أن المشكل يتعلّق بقاعدة في سياسة SELinux الأمنيّة. السيّاقات Contexts في SELinux السيّاقات هي بيئة تحكّم في الوصول تُتخَّذ فيها الإجراءات بناء على مستخدم SELinux، الدّور Role والنوع Type وبصيغة اختيارية المستوى Level: يُكمّل المستخدمون في SELinux المستخدمين العاديّن، إذ يربط SELinux حساب المستخدم العادي بحساب في SELinux. يُستخدَم حساب SELinux في السيّاق للتحكّم في العمليات من أجل تعريف أدوارها ومستوياتها. يعمل الدّور وسيطا بين أنواع الملفّات والمستخدمين في SELinux إذ يُعرّف ماهي أنواع الملفات التي يمكن لمستخدمي SELinux الوصول إليها. تُساهم الأدوار في الحؤول دون الثغرات التي تؤدي إلى هجمات الارتقاء في الصلاحيّات Privilege escalation. يعرّف SELinux نوعًا لكلّ ملفّ أو عمليّة. تُمنَع العمليّات في الظروف العاديّة من الوصول إلى الملفات التي تستخدمها عمليّات أخرى، أو من الوصول إلى هذه العمليّات. لا يُسمَح لعمليّة بالوصول إلى ملفّات إلا إذا أضيفت قاعدة إلى الساسية الأمنية تُصرّح لها بالوصول إلى النوع المستهدف. سنرى في ما يلي أمثلة لعمل SELinux. تغيير المنفذ المبدئي لخدمة SSH شرحنا في درس سابق أن تغيير المنفذ المبدئي الذي تنصت عبره خدمة SSH هو أحد الإجراءات الأمنيّة المتّبعة لتأمين الخادوم ضدّ الهجمات القادمة من شبكة الإنترنت. نحرّر الملفّ etc/ssh/sshd_config/ لتغييرالمنفذ الذي تعمل عبره هذه الخدمة ليصبح المنفذ رقم 9999 (انزع علامة التعليق # عن التعليمة Port وحدّد القيمة 9999 بدلا من 22): Port 9999 نحفظ التعديلات ونعيد تشغيل خدمة SSH (لاحظ الحرف d في نهاية اسم الخدمة): # systemctl restart sshd # systemctl status sshd غيّرنا المنفذ الذي تُنصت عبره خدمة SSH ثم أعدنا تشغيل العمليّة sshd التي تتحكّم في الخدمة ولكن إعادة تشغيل الخدمة أخفقت كما يظهر ذلك عند إظهار حالة الخدمة (الأمر systemctl status). إن كنت تستخدم واجهة رسوميّة فستظهر رسالة بعد تنفيذ أمر إعادة التشغيل تفيد بأن SELinux لاحظ أمرا ما. مالذي حصل إذن؟ يمكّن الفحص السّريع لملف السّجل var/log/audit/audit.log/ الذي يحوي معلومات متعلّقة بالأحداث الأمنية على مستوى نظام التشغيل من الحصول على معلومات عن ما حدث. يُشار في ملفّ السّجل المذكور بالنوع AVC إلى المعلومات المتعلقة بـSELinux؛ تمكننا الاستعانة بهذه المعلومة للبحث في المُدخلات ذات الصّلة بـSELinux: # grep AVC /var/log/audit/audit.log | tail -1 نلاحظ أن الخدمة sshd مُنعت من استخدام المنفذ 9999 نظرا لكونه محجوزا لصالح خدمة أخرى (JBoss Management): يمكنك تعطيل SELinux كما هو موضّح أعلاه وإعادة تشغيل خدمة SSH وستجد أن الخدمة تعمل على المنفذ الجديد؛ لكن لا تفعل. سنستخدم أداة semanage لإخبارنا بما يجب فعله لنتمكّن من تشغيل خدمة SSH على أي منفذ نريده دون مشاكل. ننفّذ الأمر التالي للحصول على لائحة بالمنافذ التي يسمح SELinux لخدمة SSH بالإنصات للاتّصالات القادمة عبرها: # semanage port -l | grep ssh ssh_port_t tcp 22 يظهر في نتيجة الأمر أن SELinux يسمح لـSSH بالإنصات للاتّصالات القادمة عبر المنفذ 22 فقط، لا غير. ما سنفعله الآن هو العودة إلى ملفّ إعداد SSH على المسار etc/ssh/sshd_config/ وتغيير المنفذ من 9999 إلى 9998: Port 9998 ثم نضيف المنفذ 9998 إلى السيّاق ssh_port_t ونعيد تشغيل الخدمة: # semanage port -a -t ssh_port_t -p tcp 9998 # systemctl restart sshd # systemctl is-active sshd تمكن ملاحظة أن الخدمة شُغّلت دون أية مشاكل هذه المرة. يوضّح هذا المثال كيف أن SELinux يتحكّم في منافذ الشبكة بالاعتماد على تعريفاته الخاصّة لنوع كلّ منفذ. السّماح لخدمة httpd بالوصول إلى خدمة sendmail ذكرنا أن SELinux يتحكّم في إمكانيّة وصول عمليّة Process إلى عمليّات أخرى. سنرى الآن مثالا على ذلك. سنفترض أنك تريد تفعيل وحدتي mod_security وmod_evasive على خادوم وِب Apache واستخدامهما لإرسال إشعارات بالبريد الإلكتروني (خدمة sendmail) عند حدوث هجمة حجب خدمة (Dos, Denial of Service). تحتاج في هذه الحالة للسماح لخادوم وِب Apache (خدمة httpd) بالوصول إلى خدمة البريد sendmail. توجد في SELinux إعدادات تشغيل تُسمّى SELinux booleans (قيم منطقيّة) يمكن عبرها تعطيل أو تفعيل قواعد ضمن سياسة SELinux الأمنية. تُستخدم الأداة semanage للحصول على قائمة بجميع هذه الإعدادات: # semanage boolean -l يتحكّم الإعداد httpd_can_sendmail في إمكانيّة وصول الخدمة httpd إلى sendmail: # semanage boolean -l | grep httpd_can_sendmail httpd_can_sendmail (off, off) Allow httpd to can sendmail يظهر في نتيجة الأمر أعلاه أن الإعداد مضبوط على القيمة off بمعنى أن القاعدة الأمنيّة معطّلة، نفعّلها على النحو التالي: # setsebool -P httpd_can_sendmail 1 استخدمنا الأمر setsebool لتفعيل القاعدة بتمرير اسمها والقيمة 1 (تفعيل). يؤدي استخدام الخيار P- إلى استمرار مفعول الأمر (تفعيل القاعدة) بعد إعادة تشغيل الخادوم. يمكن التأكد من تغيير الإعداد بإعادة تنفيذ أمر semanage السابق: # semanage boolean -l | grep httpd_can_sendmail httpd_can_sendmail (on, off) Allow httpd to can sendmail ملحوظة: يشير الحقل الثاني في نتيجة الأمر semanage boolean (أي (on, off)) في المثال أعلاه إلى قيمة الإعداد حاليّا (on)، وقيمته المبدئيّة (off). تقديم صفحة وِب توجد في مجلّد مغاير للمجلد المبدئي سنفترض في هذا المثال أن لديك موقع وِب توجد صفحاته في المجلّد websites/ بدلا من المجلّد المبدئي لخادوم الوِب (var/www/html/). أنشئ ملفا باسم index.html في المجلد websites/ وأضف إليه المحتوى التالي: <html> <h2>SELinux test</h2> </html> إن نفّذت الأمر التالي: # ls -lZ /websites/index.html فستجد أن الملفّ index.html من النوع default_t وهو النوع المبدئي في SELinux ولا يمكن لخادوم الوِب الوصولُ إليه. غيّر قيمة التعليمة DocumentRoot في الملفّ etc/httpd/conf/httpd.conf/ لتصبح websites/. حدّث كتلة Directory التي يظهر فيها اسم المجلّد المبدئي السابق؛ مثلا: <Directory "/var/www/html"> ... </Directory> تصبح: <Directory "/websites"> ... </Directory> أعد تشغيل خادوم الوِب: # systemctl restart httpd أدخل عنوان الخادوم في المتصفّح (أو 127.0.0.1 إن كان المتصفّح على الخادوم). يجب أن تظهر رسالة خطأ 403 تفيد بأن الوصول إلى هذا المحتوى ممنوع. ملحوظة: إن ظهرت الصفحة المبدئيّة لخادوم الوِب فعلّق جميع الأسطر الموجودة في الملفّ etc/httpd/conf.d/welcome.conf/ وأعد تشغيل خادوم الوِب. نعدّل نوع المجلّد websites/ وجميع محتوياته ليصبح httpd_sys_content_t type (بدلا من النوع المبدئي default_t) ممّا يتيح لخادوم الوِب Apache الوصول إلى هذا المحتوى بصلاحيّة القراءة فقط: # semanage fcontext -a -t httpd_sys_content_t "/websites(/.*)?" ثم نطبّق سياسة SELinux التي عدّلناها للتّو: # restorecon -R -v /websites أعد تشغيل خادوم الوِب وافتح عنوان الخادوم في المتصفّح من جديد، وستجد أن خادوم الوِب يعرض محتوى ملفّ HTML الذي أنشأناه سابقا. موضوع SELinux متشعّب جدا؛ إلا أن المفاهيم التي عرضنا لها في هذا المقال ستمكّنك من إيجاد قاعدة تبني عليها للانتقال إلى مواضيع أخرى تتعلّق بنظام الحماية SELinux. ترجمة - بتصرّف - لمقال RHCSA Series: Mandatory Access Control Essentials with SELinux in RHEL 7 – Part 13 لصاحبه Gabriel Cánepa.
  15. أتمتة تثبيت Red Hat Enterprise Linux باستخدام Kickstart

    يتطلّب عملُ مدير الأنظمة غالبا تثبيتَ نظام التشغيل على أكثر من خادوم؛ فسواء تعلّق الأمر بمركز بيانات أو بيئة للاختبار، سيجد مدير الأنظمة نفسه أمام مهمة تثبيت النظام على أكثر من جهاز. تأخذ الطريقة التقليدية في التثبيت الكثير من الوقت، خصوصا إذا كان عدد الخواديم معتبرا. تتيح Red Hat إمكانية تفادي ضياع الوقت في مثل هذه التثبيتات باستخدام أداة تثبيت تُعرَف بـKickstart. سنعرِض في هذا المقال لكيفية استخدام Kickstart لتثبيت Red Hat Enterprise Linux 7. مقدمة عن Kickstart والتثبيت المؤتمَت Automated installation تُستخدَم أداة Kickstart لأتمتة عمليات التثبيت على Red Hat Enterprise Linux وتوزيعات أخرى مشتقة من Fedora (مثل CentOS وOracle Linux)؛ يعني هذا أنّ تدخّل مدير النّظام أثناء عمليّة التثبيت سيكون في الحدّ الأدنى، على عكس عملية التثبيت الاعتيادية التي تتطلّب المتابعة المباشرة من مدير النظام للانتقال من خطوة إلى أخرى. يمكن باستخدام Kickstart الحصول على خواديم بأنظمة تشغيل متطابقة (نفس حزم البرامج وملفات الإعداد) بقليل من الجهد. الاستعداد للتثبيت باستخدام Kickstart نحتاج لاتّباع الخطوات التاليّة لتنفيذ تثبيت عن طريق Kickstart: إنشاء ملفّ Kickstart وهو ملفّ نصيّ توجد به خيّارات إعداد تُعرَّف مسبقا. توفير ملفّ Kickstart على وسيط تخزين (قرص مُدمَج، قرص صلب أو موقع على الشبكة). يستخدم العميل ملفّ rhel-server-7.2-x86_64-boot.iso للإقلاع عليه، وهو ملفّ ISO يحوي الملفات الأساسيّة فقط للتوزيعة؛ ثم نتيح ملف التوزيعة الكامل rhel-server-7.0-x86_64-dvd.iso على مورد على الشبكة، مثل خادوم HTTP (وِب) أو خادوم FTP. يحوي الملفّ الكامل جميع الحزم الموجودة في التوزيعة ويُستخدَم لتثبيت الحزم التي نحدّدها في ملفّ Kickstart. سنستخدم في حالتنا خادوم RHEL 7 آخر على العنوان 192.168.2.100. بدء التثبيت باستخدام Kickstart. يُنشأ ملفّ Kickstart من حساب زبناء Red Hat حيثُ توجد أداة إعداد Kickstart. استخدم هذه الأداة لتحديد خيارات التثبيت التي تناسبك، بعد التمعّن فيها جيدا. تتألف الأداة من مقاطع عدّة تشمل الإعدادات الأساسيّة مثل لغة الواجهة، لوحة المفاتيح والمنطقة الزمنيّة. كما تشمل الأداة جزءًا خاصًّا بمصدر التثبيت (قرص مُدمَج CD، خادوم وِب أو غيرهما). يُعدّ ملفّ Kickstart مبدئيّا لتثبيت الحزم الأساسيّة للنّظام فقط، لكن يمكن تحديد حزم أخرى لتثبيتها. إن قرّرت أن التثبيت سيكون عبر خادوم وِب، FTP أو NFS فتأكّد من أن الجدار الناري على الخادوم معدّ للسماح بهذه الخدمات. حدّد خادوم وِب مصدرا للتثبيت في أداة إنشاء ملفّ Kickstart واستخدم 192.168.2.100 عنوانا للخادوم وkickstart/media/ بوصفه المجلد الذي يوجد فيه ملفّ ISO الخاصّ بالنظام. نزل الملف بعد إكمال الإعدادات. يمكنك أيضا إنشاء ملفّ Kickstart يدويا دون اللّجوء إلى الأداة التي توفّرها Red Hat. نختار في المثال التالي لملفّ Kickstart أن تكون لغة التثبيت الإنكليزية، لوحة المفاتيح us والمنطقة الزمنية Africa/Nouakchott. بالنسبة للحزم فهي توجد بين التعليمتيْن packages% وend% وحدّدنا الحزم الأساسية، خادوم نسخ احتياطي Backup server وخادوم طباعة Print server. تظهر في الملفّ أيضا كلمة سرّ الحساب الجذر المعمّاة (التعليمة rootpw مع الخيار iscrypted--). راجع توثيق Kickstart للمزيد من الخيارات. lang en_US keyboard us timezone Africa/Nouakchott --isUtc rootpw $1$5sOxDlRo$In4KTyX7OmcOW9HUxWtbn0 --iscrypted #platform x86, AMD64, or Intel EM64T text url --url=http://192.168.2.100//kickstart/media bootloader --location=mbr --append="rhgb quiet crashkernel=auto" zerombr clearpart --all --initlabel autopart auth --passalgo=sha512 --useshadow selinux --enforcing firewall --enabled firstboot --disable %packages @base @backup-server @print-server %end انتبه للتعليمة التالية في ملفّ إعداد Kickstart: url --url=http://192.168.2.100//kickstart/media يُستخدَم المجلّد kickstart/media (على خادوم الوِب) لاستخراج محتوى ملف ISO (ملف التثبيت). سنحتاج قبل استخراج محتوى ملف ISO إلى تركيبه Mounting على المسار media/rhel/ مع تحديد خيار التركيب o loop- للتعامل مع الملفّ بوصفه جهازا طرفيا حلقيا Loop device: # mount -o loop /var/www/html/kickstart/rhel-server-7.0-x86_64-dvd.iso /media/rhel يفترض الأمر أعلاه وجود ملف ISO باسم rhel-server-7.0-x86_64-dvd.iso في المجلّد /var/www/html/kickstart/ على الخادوم 192.168.2.100. ملحوظة: الأجهزة الطرفية الحلقيّة Loop devices هي شبه أجهزة تخيّلية (بمعنى أنها لا توجد فعلا، غير ملموسة) تتيح التعامل مع ملف كما لو أنه جهاز طرفي كتلي Block device (وسيط تخزين مثل قرص صلب). يمكّن تركيب ملفات ISO - التي هي تمثيل لنظام ملفات File system أو تجزئة من قرص صلب - بوصفها أجهزة حلقية من الوصول إلى محتواها كما لو كانت أنظمة ملفات فعلية. ثم ننسخ جميع محتويات المجلّد media/rhel/ إلى المجلّد var/www/html/kickstart/media/ ليكون الوصول إليه متاحا عبر خادوم الوِب: # cp -R /media/rhel/* /var/www/html/kickstart/media/ يجب أن نحصُل الآن بسرد محتويات المجلد var/www/html/kickstart/media/ على نتيجة تشبه التالي: من الجيد، بغضّ النظر عن الآليّة المستخدمة لإنشاء ملف Kickstart، التحقّق من صيغة الملفّ قبل البدء في استخدامه لتثبيت النظام. ثبّت، لهذا الغرض، حزمة pykickstart: # yum update && yum install pykickstart ثم استخدم أداة ksvalidator لفحص الملفّ: # ksvalidator /var/www/html/kickstart/academy.bin لن تحصُل على أية مخرجات إن كانت صيغة الملفّ صحيحة؛ أما إذا كانت توجد أخطاء في صيغة الملف فستظهر رسائل توضّح أين توجد التعليمات غير الصّحيحة أو غير المعروفة في الملف. تنفيذ عمليّة التثبيت نبدأ عمليّة التثبيت بالإقلاع على ملفّ rhel-server-7.2-x86_64-boot.iso؛ ثم نحدّد خيار تثبيت Red Hat Enterprise Linux 7 ونضغط على زرّ الجدولة Tab في لوحة المفاتيح من أجل إلحاق التعليمات التاليّة بأمر التثبيت: # inst.ks=http://192.168.2.100/kickstart/academy.bin تشير التعليمات إلى ملفّ academy.bin الذي أنشأناه سابقا؛ ثم نضغط على زرّ Enter لبدء التثبيت. تظهر أثناء التثبيت الحزم ومجموعات الحزم التي اخترتها عند إنشاء ملفّ Kickstart أعلاه؛ كما تظهر بعد انتهاء التثبيت رسالة تطلُب منك إخراج وسيط التثبيت وسيمكنك بعدها تشغيل النظام الجديد. يمكن، كما أشرنا سابقا، إنشاء ملفات Kickstart يدويا إلا أن الطريقة المنصوح بها هي استخدام الأداة التي توفّرها Red Hat كل ما كان ذلك ممكنا. تنشئ عمليّة تثبيت Red Hat Enterprise Linux دائما ملفّا يحوي إعدادات Kickstart للنظام المُثبَّت باسم anaconda-ks.cfg على المسار root/anaconda-ks.cfg/. يمكن استخدام هذا الملفّ أيضا لإعداد خواديم مشابهة باستخدام Kickstart. من الجيّد التعوّد على استخدام Kickstart لأتمتة تثبيت Red Hat Enterprise Linux؛ فهو طريقة سهلة للتثبيت والحصول على خواديم متشابهة في أقل مدة. نشير في اﻷخير إلى أنه يمكن استخدام Kickstart لتثبيت Red Hat Enterprise Linux بصورة مؤتمتة تماما باستخدام خادوم إقلاع عبر الشبكة (PXE Server) وضبط محمّل الإقلاع Boot loader لاستخدامه. ترجمة - بتصرّف - لمقال RHCSA Series: Automate RHEL 7 Installations Using ‘Kickstart’ – Part 12 لصاحبه Gabriel Cánepa.