انتقل إلى المحتوى الرئيسي

أدلة أنسبل: دليل مفصل

تتم تهيئة منتجات OmniCRM باستخدام أنسبل، مما يسمح بإدارة الخدمات بشكل تلقائي بناءً على المتطلبات المحددة لكل منتج ومخزونه المرتبط.

انظر أيضًا: تهيئة بطاقة SIM للحصول على مثال كامل عن التهيئة المعتمدة على أنسبل للخدمات المحمولة، بما في ذلك بطاقات SIM الفعلية وeSIMs.

كيف تعمل دفاتر اللعب والمنتجات معًا

مفهوم حاسم: دفاتر اللعب هي ما ينشئ فعليًا الخدمات في OmniCRM. عندما تعين دفتر لعب لمنتج، فإنك تحدد ما يحدث عندما يتم تهيئة ذلك المنتج - ولكن يمكن أن يعني ذلك أشياء مختلفة لمنتجات مختلفة.

المنتجات تحفز دفاتر اللعب

عندما يتم تهيئة منتج في OmniCRM:

  1. يحدد تعريف المنتج أي دفتر لعب يجب تشغيله (عبر حقل provisioning_play)
  2. يمرر المنتج المتغيرات إلى دفتر اللعب (عبر provisioning_json_vars واختيارات المخزون)
  3. ينفذ دفتر اللعب ويفعل ما تم برمجته للقيام به
  4. يحدد دفتر اللعب ما الذي سيتم إنشاؤه (إذا كان هناك شيء)

ما يمكن أن تفعله دفاتر اللعب

يمكن أن يقوم دفتر لعب تهيئة واحد بـ:

إنشاء خدمات متعددة
قد ينشئ دفتر لعب منتج مجمع:

  • سجل خدمة الإنترنت الرئيسي
  • سجل خدمة IPTV الإضافية
  • سجل خدمة VoIP
  • كل ذلك من خلال إجراء تهيئة منتج واحد

إنشاء خدمات بدون
بعض دفاتر اللعب لا تنشئ سجلات خدمة على الإطلاق:

  • دفتر لعب يقوم فقط بتكوين معدات CPE
  • دفتر لعب يرسل التكوين إلى معدات الشبكة
  • دفتر لعب يقوم بتحديث الأنظمة الخارجية

إنشاء خدمة واحدة
النمط الأكثر شيوعًا:

  • إنشاء سجل خدمة واحد للعميل
  • ربط المخزون ��تلك الخدمة
  • إعداد الفوترة لتلك الخدمة

تعديل الخدمات الموجودة
دفاتر اللعب الخاصة بالتعبئة والإضافات:

  • لا تنشئ خدمات جديدة
  • تحديث سجلات الخدمة الموجودة (إضافة بيانات، تمديد انتهاء الصلاحية، إلخ)
  • إضافة أرصدة إلى حسابات الفوترة الموجودة

تنفيذ إجراءات بدون سجلات الخدمة
بعض دفاتر اللعب هي عمليات تشغيلية بحتة:

  • إعادة تعيين أرصدة الحسابات
  • تبديل عناصر المخزون بين العملاء
  • توليد تقارير أو تكوينات

مثال: سلوكيات دفاتر اللعب المختلفة

# المنتج 1: خدمة SIM المحمولة (تنشئ خدمة واحدة)
# provisioning_play: play_simple_service
- ينشئ سجل الخدمة في CRM
- ينشئ حساب فوترة في OCS
- يعين مخزون بطاقة SIM ورقم الهاتف
- يرسل بريدًا إلكترونيًا ترحيبيًا

# المنتج 2: حزمة الإنترنت (تنشئ 3 خدمات)
# provisioning_play: play_bundle_internet_tv_voice
- ينشئ سجل خدمة الإنترنت
- ينشئ سجل خدمة IPTV
- ينشئ سجل خدمة VoIP
- يربط الجميع بنفس العميل
- حساب فوترة واحد للحزمة

# المنتج 3: تعبئة البيانات (تنشئ 0 خدمات)
# provisioning_play: play_topup_no_charge
- يجد الخدمة الموجودة بواسطة service_id
- يضيف رصيد البيانات إلى حساب OCS الموجود
- يحدث تاريخ انتهاء الخدمة
- لا يتم إنشاء خدمة جديدة

# المنتج 4: تكوين CPE (تنشئ 0 خدمات)
# provisioning_play: play_prov_cpe_mikrotik
- يولد تكوين جهاز التوجيه
- يحدث سجل المخزون بالتكوين
- يرسل بريدًا إلكترونيًا بالتكوين إلى فريق الدعم
- لا يتم إنشاء خدمة (فقط إعداد المعدات)

النقطة الرئيسية: يحدد دفتر اللعب السلوك، والمنتج هو مجرد محفز.

اللعب مقابل المهام

فهم التمييز بين اللعب والمهام هو أمر أساسي للعمل مع دفاتر لعب OmniCRM.

لعبة (دفتر لعب)
سير عمل تهيئة كامل ينظم مهام متعددة لتحقيق هدف تجاري. الألعاب هي دفاتر اللعب على المستوى الأعلى المخزنة في OmniCRM-API/Provisioners/plays/ ويتم الإشارة إليها في تعريفات المنتجات.

أمثلة:

  • play_simple_service.yaml - تهيئة خدمة أساسية
  • play_topup_no_charge.yaml - تطبيق تعبئة مجانية على خدمة
  • play_prov_cpe_mikrotik.yaml - تكوين معدات العملاء

مهمة (مكون قابل لإعادة الاستخدام)
مجموعة من العمليات المستقلة والقابلة لإعادة الاستخدام التي يمكن تضمينها بواسطة ألعاب متعددة. المهام مسبوقة بـ task_ وتعيش في نفس الدليل.

أمثلة:

  • task_welcome_email.yaml - إرسال بريد إلكتروني ترحيبي إلى عميل
  • task_activate_olt.yaml - تنشيط معدات OLT
  • task_notify_ocs.yaml - إرسال إشعارات إلى نظام الفوترة

العلاقة بينهما:

# play_simple_service.yaml (لعبة)
- name: لعبة تهيئة بسيطة
hosts: localhost
tasks:
- name: الكتلة الرئيسية للتهيئة
block:
- name: إنشاء الخدمة
uri: ...

- name: تكوين الفوترة
uri: ...

# تضمين مهمة قابلة لإعادة الاستخدام
- include_tasks: task_welcome_email.yaml

# تضمين المهام بعد التهيئة
- include_tasks: post_provisioning_tasks.yaml

هيكل دفتر اللعب وتشريحه

تتبع جميع دفاتر لعب OmniCRM هيكلًا متسقًا. فهم هذا الهيكل أمر أساسي لإنشاء وصيانة دفاتر اللعب.

الهيكل الأساسي

يبدأ كل دفتر لعب بهذه العناوين القياسية:

- name: الاسم الوصفي لدفتر اللعب
hosts: localhost # دائمًا localhost لـ OmniCRM
gather_facts: no # معطل لأداء أفضل
become: False # لا تصعيد للامتيازات

tasks:
- name: الكتلة الرئيسية
block:
# مهام التهيئة تذهب هنا

rescue:
# مهام التراجع/التنظيف تذهب هنا

شرح العنوان

name
اسم وصفي يظهر في سجلات التهيئة وواجهة المستخدم. يظهر هذا كـ playbook_description في سجل التهيئة.

hosts: localhost
تعمل جميع دفاتر لعب OmniCRM على localhost لأنها تتفاعل مع الأنظمة البعيدة عبر واجهات برمجة التطبيقات، وليس SSH.

gather_facts: no
تم تعطيل جمع الحقائق في أنسبل لأن:

  • لا نحتاج إلى معلومات النظام
  • يضيف عبئًا غير ضروري
  • يمكن أن يتعطل المتصفحات إذا تم عرضه في مخرجات التصحيح

become: False
لا حاجة لتصعيد الامتيازات لأننا نقوم بإجراء مكالمات API، وليس تعديل ملفات النظام.

تحميل التكوين

يجب على كل دفتر لعب تحميل ملف التكوين المركزي:

tasks:
- name: تضمين متغيرات crm_config
ansible.builtin.include_vars:
file: "../../crm_config.yaml"
name: crm_config

هذا يجعل التكوين متاحًا كـ crm_config.ocs.cgrates، crm_config.crm.base_url، إلخ.

عادةً ما يحتوي crm_config.yaml على:

ocs:
cgrates: "10.0.1.100:2080"
ocsTenant: "default_tenant"
crm:
base_url: "https://crm.example.com"

أنماط الوصول إلى المتغيرات

يمكن أن تأتي المتغيرات من عدة مصادر:

من تعريف المنتج:

- name: الوصول إلى product_id الممرر بواسطة OmniCRM
debug:
msg: "تهيئة المنتج {{ product_id }}"

من اختيار المخزون:

- name: الحصول على معرف المخزون لبطاقة SIM
set_fact:
sim_card_id: "{{ hostvars[inventory_hostname]['SIM Card'] | int }}"
when: "'SIM Card' in hostvars[inventory_hostname]"

من استجابات API:

- name: الحصول على معلومات المنتج من واجهة برمجة تطبيقات CRM
uri:
url: "http://localhost:5000/crm/product/product_id/{{ product_id }}"
method: GET
headers:
Authorization: "Bearer {{ access_token }}"
return_content: yes
register: api_response_product

- name: استخدام اسم المنتج
debug:
msg: "اسم المنتج هو {{ api_response_product.json.product_name }}"

أنماط دفاتر اللعب الشائعة

نمط تهيئة الخدمة

هذا هو النمط الأكثر شيوعًا لإنشاء خدمات جديدة.

- name: دفتر لعب تهيئة الخدمة
hosts: localhost
gather_facts: no
become: False

tasks:
- name: الكتلة الرئيسية
block:

# 1. تحميل التكوين
- name: تضمين متغيرات crm_config
ansible.builtin.include_vars:
file: "../../crm_config.yaml"
name: crm_config

# 2. الحصول على معلومات المنتج
- name: الحصول على معلومات المنتج من واجهة برمجة التطبيقات CRM
uri:
url: "http://localhost:5000/crm/product/product_id/{{ product_id }}"
method: GET
headers:
Authorization: "Bearer {{ access_token }}"
return_content: yes
validate_certs: no
register: api_response_product

# 3. الحصول على معلومات العميل
- name: الحصول على معلومات العميل من واجهة برمجة التطبيقات CRM
uri:
url: "http://localhost:5000/crm/customer/customer_id/{{ customer_id }}"
method: GET
headers:
Authorization: "Bearer {{ access_token }}"
return_content: yes
register: api_response_customer

# 4. تعيين الحقائق من البيانات المستردة
- name: تعيين حقائق الحزمة
set_fact:
package_name: "{{ api_response_product.json.product_name }}"
package_comment: "{{ api_response_product.json.comment }}"
setup_cost: "{{ api_response_product.json.retail_setup_cost }}"
monthly_cost: "{{ api_response_product.json.retail_cost }}"

# 5. توليد معرفات فريدة
- name: توليد UUID
set_fact:
uuid: "{{ 99999999 | random | to_uuid }}"

- name: توليد UUID الخدمة
set_fact:
service_uuid: "Service_{{ uuid[0:8] }}"

# 6. إنشاء حساب في نظام الفوترة
- name: إنشاء حساب في OCS/CGRateS
uri:
url: "http://{{ crm_config.ocs.cgrates }}/jsonrpc"
method: POST
body_format: json
headers:
Content-Type: "application/json"
body:
{
"method": "ApierV2.SetAccount",
"params": [{
"Tenant": "{{ crm_config.ocs.ocsTenant }}",
"Account": "{{ service_uuid }}",
"ActionPlanIds": [],
"ActionPlansOverwrite": true,
"ExtraOptions": {
"AllowNegative": false,
"Disabled": false
},
"ReloadScheduler": true
}]
}
status_code: 200
register: ocs_response

- name: التحقق من إنشاء حساب OCS
assert:
that:
- ocs_response.status == 200
- ocs_response.json.result == "OK"

# 7. إضافة رصيد أولي
- name: إضافة رصيد نقدي 0
uri:
url: "http://{{ crm_config.ocs.cgrates }}/jsonrpc"
method: POST
body_format: json
body:
{
"method": "ApierV1.AddBalance",
"params": [{
"Tenant": "{{ crm_config.ocs.ocsTenant }}",
"Account": "{{ service_uuid }}",
"BalanceType": "*monetary",
"Categories": "*any",
"Balance": {
"ID": "Initial Balance",
"Value": 0,
"ExpiryTime": "+4320h",
"Weight": 1,
"Blocker": true
}
}]
}
status_code: 200
register: balance_response

# 8. إنشاء سجل الخدمة في CRM
- name: الحصول على التاريخ والوقت الحالي بتنسيق ISO 8601
command: date --utc +%Y-%m-%dT%H:%M:%S%z
register: current_date_time

- name: إضافة الخدمة عبر API
uri:
url: "http://localhost:5000/crm/service/"
method: PUT
body_format: json
headers:
Content-Type: "application/json"
Authorization: "Bearer {{ access_token }}"
body:
{
"customer_id": "{{ customer_id }}",
"product_id": "{{ product_id }}",
"service_name": "{{ package_name }} - {{ service_uuid }}",
"service_type": "generic",
"service_uuid": "{{ service_uuid }}",
"service_billed": true,
"service_taxable": true,
"service_provisioned_date": "{{ current_date_time.stdout }}",
"service_status": "Active",
"wholesale_cost": "{{ api_response_product.json.wholesale_cost | float }}",
"retail_cost": "{{ monthly_cost | float }}"
}
status_code: 200
register: service_creation_response

# 9. إضافة معاملة تكلفة الإعداد
- name: إضافة معاملة تكلفة الإعداد عبر API
uri:
url: "http://localhost:5000/crm/transaction/"
method: PUT
headers:
Content-Type: "application/json"
Authorization: "Bearer {{ access_token }}"
body_format: json
body:
{
"customer_id": {{ customer_id | int }},
"service_id": {{ service_creation_response.json.service_id | int }},
"title": "{{ package_name }} - تكاليف الإعداد",
"description": "تكاليف الإعداد لـ {{ package_comment }}",
"invoice_id": null,
"retail_cost": "{{ setup_cost | float }}"
}
return_content: yes
register: transaction_response

# 10. تضمين المهام بعد التهيئة
- include_tasks: post_provisioning_tasks.yaml

rescue:

# قسم التراجع/التنظيف
- name: طباعة جميع المتغيرات للتصحيح
debug:
var: hostvars[inventory_hostname]

- name: إزالة الحساب في OCS
uri:
url: "http://{{ crm_config.ocs.cgrates }}/jsonrpc"
method: POST
body_format: json
body:
{
"method": "ApierV2.RemoveAccount",
"params": [{
"Tenant": "{{ crm_config.ocs.ocsTenant }}",
"Account": "{{ service_uuid }}",
"ReloadScheduler": true
}]
}
status_code: 200
ignore_errors: True
when: service_uuid is defined

- name: حذف الخدمة من CRM إذا تم إنشاؤها
uri:
url: "http://localhost:5000/crm/service/service_id/{{ service_creation_response.json.service_id }}"
method: DELETE
headers:
Authorization: "Bearer {{ access_token }}"
status_code: 200
ignore_errors: True
when: service_creation_response is defined

- name: الفشل إذا لم يكن إلغاء التهيئة مقصودًا
assert:
that:
- action == "deprovision"

نمط التعبئة/إعادة الشحن

يستخدم لإضافة أرصدة أو بيانات أو وقت إلى الخدمات الموجودة.

- name: دفتر لعب تعبئة الخدمة
hosts: localhost
gather_facts: no
become: False

tasks:
- name: تضمين متغيرات crm_config
ansible.builtin.include_vars:
file: "../../crm_config.yaml"
name: crm_config

# 1. الحصول على معلومات الخدمة
- name: الحصول على معلومات الخدمة من واجهة برمجة التطبيقات CRM
uri:
url: "http://localhost:5000/crm/service/service_id/{{ service_id }}"
method: GET
headers:
Authorization: "Bearer {{ access_token }}"
return_content: yes
register: api_response_service

# 2. الحصول على معلومات المنتج (ما يجب تعبئته)
- name: الحصول على معلومات المنتج من واجهة برمجة التطبيقات CRM
uri:
url: "http://localhost:5000/crm/product/product_id/{{ product_id }}"
method: GET
headers:
Authorization: "Bearer {{ access_token }}"
return_content: yes
register: api_response_product

# 3. استخراج تفاصيل الخدمة
- name: تعيين حقائق الخدمة
set_fact:
service_uuid: "{{ api_response_service.json.service_uuid }}"
customer_id: "{{ api_response_service.json.customer_id }}"
package_name: "{{ api_response_product.json.product_name }}"
topup_value: "{{ api_response_product.json.retail_cost }}"

# 4. تنفيذ الإجراء في نظام الفوترة (تعبئة مجانية)
- name: تنفيذ الإجراء لإضافة أرصدة
uri:
url: "http://{{ crm_config.ocs.cgrates }}/jsonrpc"
method: POST
body_format: json
body:
{
"method": "APIerSv1.ExecuteAction",
"params": [{
"Tenant": "{{ crm_config.ocs.ocsTenant }}",
"Account": "{{ service_uuid }}",
"ActionsId": "Action_Topup_Standard"
}]
}
status_code: 200
register: action_response

- name: التحقق من تنفيذ الإجراء بنجاح
assert:
that:
- action_response.status == 200
- action_response.json.result == "OK"

# 5. إعادة تعيين أي حدود تم تحفيزها
- name: إعادة تعيين ActionTriggers
uri:
url: "http://{{ crm_config.ocs.cgrates }}/jsonrpc"
method: POST
body_format: json
body:
{
"method": "APIerSv1.ResetAccountActionTriggers",
"params": [{
"Tenant": "{{ crm_config.ocs.ocsTenant }}",
"Account": "{{ service_uuid }}",
"Executed": false
}]
}
status_code: 200

# 6. تحديث تواريخ الخدمة
- name: حساب تاريخ انتهاء جديد
command: "date --utc +%Y-%m-%dT%H:%M:%S%z -d '+30 days'"
register: new_expiry_date

- name: تحديث الخدمة مع انتهاء جديد
uri:
url: "http://localhost:5000/crm/service/{{ service_id }}"
method: PATCH
headers:
Authorization: "Bearer {{ access_token }}"
Content-Type: "application/json"
body_format: json
body:
{
"service_deactivate_date": "{{ new_expiry_date.stdout }}",
"service_status": "Active"
}

# 7. اختياري: إرسال إشعار
- name: إرسال رسالة نصية للإشعار
uri:
url: "http://sms-gateway/api/send"
method: POST
body_format: json
body:
{
"source": "اسم الشركة",
"destination": "{{ customer_phone }}",
"message": "تم تعبئة خدمتك. انتهاء جديد: {{ new_expiry_date.stdout }}"
}
status_code: 201
ignore_errors: True

نمط تهيئة CPE

يستخدم لتكوين معدات العملاء (أجهزة التوجيه، المودمات، ONTs).

- name: دفتر لعب تهيئة CPE
hosts: localhost
gather_facts: no
become: False

tasks:
- name: تضمين متغيرات crm_config
ansible.builtin.include_vars:
file: "../../crm_config.yaml"
name: crm_config

# 1. الحصول على عنصر المخزون لـ CPE
- name: تعيين معرف مخزون CPE من hostvars
set_fact:
cpe_inventory_id: "{{ hostvars[inventory_hostname]['WiFi Router CPE'] | int }}"
when: "'WiFi Router CPE' in hostvars[inventory_hostname]"

# 2. الحصول على تفاصيل CPE من المخزون
- name: الحصول على بيانات المخزون لـ CPE
uri:
url: "{{ crm_config.crm.base_url }}/crm/inventory/inventory_id/{{ cpe_inventory_id }}"
method: GET
headers:
Authorization: "Bearer {{ access_token }}"
return_content: yes
register: api_response_cpe

# 3. الحصول على معلومات موقع العميل
- name: الحصول على معلومات الموقع من API
uri:
url: "{{ crm_config.crm.base_url }}/crm/site/customer_id/{{ customer_id }}"
method: GET
headers:
Authorization: "Bearer {{ access_token }}"
return_content: yes
register: api_response_site

# 4. تحديث مخزون CPE بالموقع
- name: تصحيح عنصر مخزون CPE بالموقع
uri:
url: "{{ crm_config.crm.base_url }}/crm/inventory/inventory_id/{{ cpe_inventory_id }}"
method: PATCH
body_format: json
headers:
Authorization: "Bearer {{ access_token }}"
body:
{
"address_line_1": "{{ api_response_site.json.0.address_line_1 }}",
"city": "{{ api_response_site.json.0.city }}",
"state": "{{ api_response_site.json.0.state }}",
"latitude": "{{ api_response_site.json.0.latitude }}",
"longitude": "{{ api_response_site.json.0.longitude }}"
}
status_code: 200

# 5. توليد بيانات الاعتماد
- name: تعيين اسم مضيف CPE
set_fact:
cpe_hostname: "CPE_{{ cpe_inventory_id }}"
cpe_username: "admin_{{ cpe_inventory_id }}"

- name: توليد كلمة مرور عشوائية
set_fact:
cpe_password: "{{ lookup('pipe', 'cat /dev/urandom | tr -dc a-zA-Z0-9 | head -c 16') }}"

# 6. توليد بيانات اعتماد WiFi
- name: تعيين SSID WiFi
set_fact:
wifi_ssid: "Network_{{ cpe_inventory_id }}"

- name: توليد كلمة مرور WiFi
set_fact:
word_list:
- apple
- cloud
- river
- mountain
- ocean

- name: إنشاء PSK WiFi
set_fact:
random_word: "{{ word_list | random }}"
random_number: "{{ 99999 | random(start=10000) }}"

- name: دمج PSK WiFi
set_fact:
wifi_psk: "{{ random_word }}{{ random_number }}"

# 7. توليد ملف التكوين
- name: تعيين اسم ملف التكوين
set_fact:
config_name: "{{ cpe_hostname }}_{{ lookup('pipe', 'date +%Y%m%d%H%M%S') }}.cfg"
config_dest: "/tmp/{{ cpe_hostname }}_{{ lookup('pipe', 'date +%Y%m%d%H%M%S') }}.cfg"

- name: إنشاء التكوين من القالب
template:
src: "templates/cpe_router_config.j2"
dest: "{{ config_dest }}"

# 8. قراءة التكوين الذي تم إنشاؤه
- name: قراءة ملف التكوين
ansible.builtin.slurp:
src: "{{ config_dest }}"
register: config_content

# 9. تحديث المخزون بمعلومات التهيئة
- name: تصحيح مخزون CPE بالتكوين
uri:
url: "{{ crm_config.crm.base_url }}/crm/inventory/inventory_id/{{ cpe_inventory_id }}"
method: PATCH
body_format: json
headers:
Authorization: "Bearer {{ access_token }}"
body:
{
"itemtext3": "{{ wifi_ssid }}",
"itemtext4": "{{ wifi_psk }}",
"management_url": "{{ cpe_hostname }}",
"management_username": "{{ cpe_username }}",
"management_password": "{{ cpe_password }}",
"config_content": "{{ config_content.content | b64decode }}",
"inventory_notes": "تم تهيئته: {{ lookup('pipe', 'date +%Y-%m-%d') }}"
}
status_code: 200

# 10. إرسال التكوين إلى فريق الدعم
- name: إرسال التكوين إلى الدعم
uri:
url: "https://api.mailjet.com/v3.1/send"
method: POST
body_format: json
headers:
Content-Type: "application/json"
body:
{
"Messages": [{
"From": {
"Email": "provisioning@example.com",
"Name": "نظام التهيئة"
},
"To": [{
"Email": "support@example.com",
"Name": "فريق الدعم"
}],
"Subject": "تكوين CPE - {{ cpe_hostname }}",
"Attachments": [{
"ContentType": "text/plain",
"Filename": "{{ config_name }}",
"Base64Content": "{{ config_content.content }}"
}]
}]
}
user: "{{ mailjet_api_key }}"
password: "{{ mailjet_api_secret }}"
force_basic_auth: true
status_code: 200

نمط التجديد التلقائي

تهيئة الرسوم المتكررة التلقائية أو التجديدات باستخدام خطط العمل CGRateS.

# جزء من دفتر لعب التعبئة الذي يهيئ التجديد التلقائي

# 1. تطبيع معلمة auto_renew
- name: تطبيع auto_renew إلى قيمة منطقية
set_fact:
auto_renew_bool: "{{ (auto_renew | string | lower) in ['true', '1', 'yes'] }}"

# 2. إنشاء إجراء للتجديد التلقائي
- name: إنشاء إجراء للتجديد التلقائي
uri:
url: "http://{{ crm_config.ocs.cgrates }}/jsonrpc"
method: POST
body_format: json
body:
{
"method": "ApierV1.SetActions",
"params": [{
"ActionsId": "Action_AutoTopup_{{ service_uuid }}_{{ product_id }}",
"Overwrite": true,
"Actions": [
{
"Identifier": "*http_post",
"ExtraParameters": "{{ crm_config.crm.base_url }}/crm/provision/simple_provision_addon/service_id/{{ service_id }}/product_id/{{ product_id }}"
},
{
"Identifier": "*cdrlog",
"BalanceType": "*generic",
"ExtraParameters": "{\"Category\":\"^activation\",\"Destination\":\"Auto Renewal\"}"
}
]
}]
}
status_code: 200
register: action_response
when: auto_renew_bool

# 3. إنشاء خطة العمل الشهرية
- name: إنشاء خطة العمل للتجديد الشهري
uri:
url: "http://{{ crm_config.ocs.cgrates }}/jsonrpc"
method: POST
body_format: json
body:
{
"method": "ApierV1.SetActionPlan",
"params": [{
"Id": "ActionPlan_Monthly_{{ service_uuid }}_{{ product_id }}",
"Tenant": "{{ crm_config.ocs.ocsTenant }}",
"ActionPlan": [{
"ActionsId": "Action_AutoTopup_{{ service_uuid }}_{{ product_id }}",
"Years": "*any",
"Months": "*any",
"MonthDays": "*any",
"WeekDays": "*any",
"Time": "*monthly",
"StartTime": "*now",
"Weight": 10
}],
"Overwrite": true,
"ReloadScheduler": true
}]
}
status_code: 200
when: auto_renew_bool

# 4. تعيين خطة العمل للحساب
- name: تعيين خطة العمل للحساب
uri:
url: "http://{{ crm_config.ocs.cgrates }}/jsonrpc"
method: POST
body_format: json
body:
{
"method": "ApierV2.SetAccount",
"params": [{
"Tenant": "{{ crm_config.ocs.ocsTenant }}",
"Account": "{{ service_uuid }}",
"ActionPlanIds": ["ActionPlan_Monthly_{{ service_uuid }}_{{ product_id }}"],
"ActionPlansOverwrite": true,
"ReloadScheduler": true
}]
}
status_code: 200
when: auto_renew_bool

# 5. إزالة خطة ال��مل إذا تم تعطيل التجديد التلقائي
- name: إزالة خطة العمل من الحساب
uri:
url: "http://{{ crm_config.ocs.cgrates }}/jsonrpc"
method: POST
body_format: json
body:
{
"method": "ApierV1.RemoveActionPlan",
"params": [{
"Tenant": "{{ crm_config.ocs.ocsTenant }}",
"Id": "ActionPlan_Monthly_{{ service_uuid }}_{{ product_id }}"
}]
}
status_code: 200
ignore_errors: true
when: not auto_renew_bool

المهام القابلة لإعادة الاستخدام

المهام القابلة لإعادة الاستخدام هي دفاتر لعب صغيرة ومستقلة يمكن تضمينها بواسطة ألعاب متعددة. تعزز إعادة استخدام الكود والاتساق.

مهمة البريد الإلكتروني الترحيبي

task_welcome_email.yaml - يرسل بريدًا إلكترونيًا ترحيبيًا للعملاء الجدد.

# تتوقع هذه المهمة أن يتم تعيين هذه المتغيرات بواسطة اللعبة الأم:
# - api_response_customer (تفاصيل العميل)
# - package_name (اسم المنتج)
# - monthly_cost (التكلفة المتكررة)
# - setup_cost (التكلفة لمرة وا��دة)

- name: تعيين تكوين البريد الإلكتروني
set_fact:
mailjet_api_key: "{{ lookup('env', 'MAILJET_API_KEY') }}"
mailjet_api_secret: "{{ lookup('env', 'MAILJET_SECRET') }}"
email_from: "noreply@example.com"
recipients: []

- name: تعيين موضوع البريد الإلكتروني واسم المرسل
set_fact:
email_subject: "مرحبًا بك في خدمتنا!"
email_from_name: "فريق خدمة العملاء"

- name: إعداد قائمة المستلمين من جهات اتصال العميل
loop: "{{ api_response_customer.json.contacts }}"
set_fact:
recipients: "{{ recipients + [{'Email': item.contact_email, 'Name': item.contact_firstname ~ ' ' ~ item.contact_lastname}] }}"

- name: الحصول على اسم جهة الاتصال الأولى
set_fact:
first_contact: "{{ api_response_customer.json.contacts[0].contact_firstname }}"

- name: إرسال البريد الإلكتروني الترحيبي
uri:
url: "https://api.mailjet.com/v3.1/send"
method: POST
body_format: json
headers:
Content-Type: "application/json"
body:
{
"Messages": [{
"From": {
"Email": "{{ email_from }}",
"Name": "{{ email_from_name }}"
},
"To": "{{ recipients }}",
"Subject": "{{ email_subject }}",
"TextPart": "عزيزي {{ first_contact }}, مرحبًا! خدمتك جاهزة.",
"HTMLPart": "عزيزي {{ first_contact }},<br/><h3>مرحبًا!</h3><br/>خدمة {{ package_name }} الخاصة بك نشطة الآن.<br/>التكلفة الشهرية: ${{ monthly_cost }}<br/>رسوم الإعداد: ${{ setup_cost }}<br/>إذا كان لديك أي أسئلة، اتصل بـ support@example.com"
}]
}
user: "{{ mailjet_api_key }}"
password: "{{ mailjet_api_secret }}"
force_basic_auth: true
status_code: 200
register: email_response

المهام بعد التهيئة

post_provisioning_tasks.yaml - عمليات التنظيف والإشعارات القياسية التي تتم بعد كل تهيئة.

# يتم تضمين هذا الملف في نهاية معظم دفاتر لعب التهيئة
# يتعامل مع العمليات الشائعة بعد التهيئة

- include_tasks: task_notify_ocs.yaml

قد تحتوي task_notify_ocs.yaml على:

- name: إبلاغ OCS بإكمال التهيئة
uri:
url: "http://{{ crm_config.ocs.cgrates }}/jsonrpc"
method: POST
body_format: json
body:
{
"method": "APIerSv1.ReloadCache",
"params": [{
"ArgsCache": "*all"
}]
}
status_code: 200
ignore_errors: true

العمليات الشائعة

العمل مع المخزون

استرجاع تفاصيل المخزون:

- name: الحصول على معرف مخزون بطاقة SIM
set_fact:
sim_inventory_id: "{{ hostvars[inventory_hostname]['SIM Card'] | int }}"
when: "'SIM Card' in hostvars[inventory_hostname]"

- name: الحصول على تفاصيل بطاقة SIM
uri:
url: "{{ crm_config.crm.base_url }}/crm/inventory/inventory_id/{{ sim_inventory_id }}"
method: GET
headers:
Authorization: "Bearer {{ access_token }}"
return_content: yes
register: sim_response

- name: استخراج تفاصيل SIM
set_fact:
iccid: "{{ sim_response.json.iccid }}"
imsi: "{{ sim_response.json.imsi }}"
ki: "{{ sim_response.json.ki }}"

تعيين المخزون للعميل:

- name: تعيين SIM للعميل
uri:
url: "{{ crm_config.crm.base_url }}/crm/inventory/inventory_id/{{ sim_inventory_id }}"
method: PATCH
headers:
Authorization: "Bearer {{ access_token }}"
body_format: json
body:
{
"customer_id": {{ customer_id }},
"service_id": {{ service_id }},
"item_state": "Assigned"
}
status_code: 200

عمليات التاريخ والوقت

الحصول على التاريخ/الوقت الحالي:

- name: الحصول على التاريخ والوقت الحالي بتنسيق ISO 8601
command: date --utc +%Y-%m-%dT%H:%M:%S%z
register: current_date_time

- name: الحصول على تاريخ اليوم فقط
set_fact:
today: "{{ lookup('pipe', 'date +%Y-%m-%d') }}"

حساب التواريخ المستقبلية:

- name: حساب تاريخ انتهاء بعد 30 يومًا
command: "date --utc +%Y-%m-%dT%H:%M:%S%z -d '+30 days'"
register: expiry_date

- name: حساب تاريخ بعد 90 يومًا في المستقبل
command: "date --utc +%Y-%m-%d -d '+{{ days }} days'"
register: future_date
vars:
days: 90

توليد قيم عشوائية

UUIDs والمعرفات:

- name: توليد UUID
set_fact:
uuid: "{{ 99999999 | random | to_uuid }}"

- name: توليد معرف الخدمة
set_fact:
service_uuid: "SVC_{{ uuid[0:8] }}"

كلمات مرور عشوائية:

- name: توليد كلمة مرور آمنة
set_fact:
password: "{{ lookup('pipe', 'cat /dev/urandom | tr -dc a-zA-Z0-9 | head -c 16') }}"

عبارات مرور سهلة التذكر:

- name: تعيين قائمة الكلمات
set_fact:
words:
- alpha
- bravo
- charlie
- delta
- echo

- name: توليد عبارة مرور
set_fact:
word: "{{ words | random }}"
number: "{{ 99999 | random(start=10000) }}"

- name: دمجها في عبارة مرور
set_fact:
passphrase: "{{ word }}{{ number }}"

العمل مع CGRateS/OCS

إنشاء الحسابات:

- name: إنشاء حساب فوترة
uri:
url: "http://{{ crm_config.ocs.cgrates }}/jsonrpc"
method: POST
body_format: json
body:
{
"method": "ApierV2.SetAccount",
"params": [{
"Tenant": "{{ crm_config.ocs.ocsTenant }}",
"Account": "{{ service_uuid }}",
"ActionPlanIds": [],
"ActionPlansOverwrite": true,
"ExtraOptions": {
"AllowNegative": false,
"Disabled": false
},
"ReloadScheduler": true
}]
}
status_code: 200
register: account_response

إضافة الأرصدة:

- name: إضافة رصيد ال��يانات
uri:
url: "http://{{ crm_config.ocs.cgrates }}/jsonrpc"
method: POST
body_format: json
body:
{
"method": "ApierV1.AddBalance",
"params": [{
"Tenant": "{{ crm_config.ocs.ocsTenant }}",
"Account": "{{ service_uuid }}",
"BalanceType": "*data",
"Categories": "*any",
"Balance": {
"ID": "Data Package",
"Value": 10737418240,
"ExpiryTime": "+720h",
"Weight": 10
}
}]
}
status_code: 200

تنفيذ الإجراءات:

- name: تنفيذ إجراء الشحن
uri:
url: "http://{{ crm_config.ocs.cgrates }}/jsonrpc"
method: POST
body_format: json
body:
{
"method": "APIerSv1.ExecuteAction",
"params": [{
"Tenant": "{{ crm_config.ocs.ocsTenant }}",
"Account": "{{ service_uuid }}",
"ActionsId": "Action_Standard_Charge"
}]
}
status_code: 200

الحصول على معلومات الحساب:

- name: الحصول على تفاصيل الحساب
uri:
url: "http://{{ crm_config.ocs.cgrates }}/jsonrpc"
method: POST
body_format: json
body:
{
"method": "ApierV2.GetAccount",
"params": [{
"Tenant": "{{ crm_config.ocs.ocsTenant }}",
"Account": "{{ service_uuid }}"
}]
}
status_code: 200
register: account_info

العمل مع ملفات تعريف السمات:

- name: الحصول على ملف تعريف السمة
uri:
url: "http://{{ crm_config.ocs.cgrates }}/jsonrpc"
method: POST
body_format: json
body:
{
"method": "APIerSv1.GetAttributeProfile",
"params": [{
"Tenant": "{{ crm_config.ocs.ocsTenant }}",
"ID": "ATTR_{{ service_uuid }}"
}]
}
return_content: yes
status_code: 200
register: attr_response
ignore_errors: true

- name: استخراج قيمة السمة
set_fact:
phone_number: "{{ attr_response.json.result.Attributes | json_query(\"[?Path=='*req.PhoneNumber'].Value[0].Rules\") | first }}"
when: attr_response is defined

المنطق الشرطي

التحقق مما إذا كانت المتغيرات موجودة:

- name: استخدام قيمة مخصصة أو افتراضية
set_fact:
monthly_cost: "{{ custom_cost | default(50.00) }}"

- name: تشغيل فقط إذا تم تعريف المتغير
debug:
msg: "معرف الخدمة هو {{ service_uuid }}"
when: service_uuid is defined

الشروط المنطقية:

- name: تهيئة المعدات
include_tasks: configure_cpe.yaml
when: provision_cpe | default(false) | bool

- name: تخطي إذا كان إلغاء التهيئة
assert:
that:
- action != "deprovision"
when: action is defined

شروط متعددة:

- name: مهمة شرطية معقدة
uri:
url: "{{ endpoint }}"
method: POST
when:
- service_uuid is defined
- customer_id is defined
- action != "deprovision"
- enable_feature | default(true) | bool

الحلقات والتكرار

الحلقات البسيطة:

- name: إنشاء أرصدة متعددة
uri:
url: "http://{{ crm_config.ocs.cgrates }}/jsonrpc"
method: POST
body_format: json
body:
{
"method": "ApierV1.AddBalance",
"params": [{
"Account": "{{ service_uuid }}",
"BalanceType": "{{ item.type }}",
"Balance": {
"Value": "{{ item.value }}"
}
}]
}
loop:
- { type: "*voice", value: 3600 }
- { type: "*data", value: 10737418240 }
- { type: "*sms", value: 100 }

ا��تكرار عبر استجابات API:

- name: الحصول على جميع مواقع العملاء
uri:
url: "{{ crm_config.crm.base_url }}/crm/site/customer_id/{{ customer_id }}"
method: GET
headers:
Authorization: "Bearer {{ access_token }}"
register: sites_response

- name: تكوين المعدات في كل موقع
debug:
msg: "تكوين الموقع في {{ item.address_line_1 }}"
loop: "{{ sites_response.json }}"

معالجة الأخطاء

استخدام ignore_errors:

- name: إشعار SMS اختياري
uri:
url: "http://sms-gateway/send"
method: POST
body: {...}
ignore_errors: true

التحقق من صحة الاستجابات:

- name: التحقق من استجابة API
assert:
that:
- response.status == 200
- response.json.result == "OK"
fail_msg: "فشلت مكالمة API: {{ response.json }}"

معالجة الأخطاء الشرطية:

- name: محاولة الحصول على الخدمة الموجودة
uri:
url: "{{ crm_config.crm.base_url }}/crm/service/service_uuid/{{ service_uuid }}"
method: GET
headers:
Authorization: "Bearer {{ access_token }}"
register: service_lookup
failed_when: false

- name: إنشاء الخدمة إذا لم تكن موجودة
uri:
url: "{{ crm_config.crm.base_url }}/crm/service/"
method: PUT
body: {...}
when: service_lookup.status == 404

أفضل الممارسات

تسمية المتغيرات

استخدم أسماء وصفية ومتسقة:

# جيد
service_uuid: "SVC_12345"
customer_name: "جون سميث"
monthly_cost: 49.99

# سيء
svc: "SVC_12345"
name: "جون سميث"
cost: 49.99

قم ببادئة المتغيرات حسب المصدر:

api_response_customer: {...}
api_response_product: {...}
cgr_account_info: {...}

التصحيح

طباعة المتغيرات لأغراض استكشاف الأخطاء:

- name: طباعة جميع المتغيرات
debug:
var: hostvars[inventory_hostname]

- name: طباعة متغير محدد
debug:
msg: "معرف الخدمة: {{ service_uuid }}"

- name: طباعة استجابة API
debug:
var: api_response_product.json

التحقق

تحقق دائمًا من استجابات API الحرجة:

- name: إنشاء حساب
uri:
url: "{{ billing_endpoint }}"
method: POST
body: {...}
register: response

- name: التحقق من إنشاء الحساب
assert:
that:
- response.status == 200
- response.json.result == "OK"
fail_msg: "فشل إنشاء الحساب: {{ response.json }}"

اللامبالاة

صمم المهام لتكون قابلة للتكرار بأمان:

# تحقق مما إذا كان المورد موجودًا أولاً
- name: تحقق مما إذا كان الحساب موجودًا
uri:
url: "{{ ocs_endpoint }}/get_account"
method: POST
body: {"Account": "{{ service_uuid }}"}
register: account_check
failed_when: false

# أنشئ فقط إذا لم يكن موجودًا
- name: إنشاء حساب
uri:
url: "{{ ocs_endpoint }}/create_account"
method: POST
body: {...}
when: account_check.status == 404

الأمان

لا تقم بتشفير بيانات الاعتماد:

# سيء
mailjet_api_key: "abc123def456"

# جيد - استخدم متغيرات البيئة
mailjet_api_key: "{{ lookup('env', 'MAILJET_API_KEY') }}"

# جيد - استخدم ملف التكوين
mailjet_api_key: "{{ crm_config.email.api_key }}"

استخدم دائمًا HTTPS والمصادقة:

- name: استدعاء واجهة برمجة التطبيقات الخارجية
uri:
url: "https://api.example.com/endpoint"
method: POST
headers:
Authorization: "Bearer {{ access_token }}"
validate_certs: yes

التوثيق

وثق المنطق المعقد:

# حساب الرسوم النسبية لشهر جزئي
# إذا قام العميل بالتسجيل في اليوم الخامس عشر وكانت الفوترة في اليوم الأول،
# يتم فرض 50% من التكلفة الشهرية للأيام المتبقية
- name: حساب الأيام حتى نهاية الشهر
command: "date -d 'last day of this month' +%d"
register: days_in_month

- name: الحصول على اليوم الحالي
command: "date +%d"
register: current_day

- name: حساب المبلغ النسبي
set_fact:
days_remaining: "{{ (days_in_month.stdout | int) - (current_day.stdout | int) }}"
pro_rata_cost: "{{ (monthly_cost | float) * (days_remaining | float) / (days_in_month.stdout | float) }}"

اختبار دفاتر اللعب

نهج الاختبار

  1. جرب أولاً: اختبر مع الأنظمة غير الإنتاجية
  2. تحقق من المتغيرات: استخدم مهام التصحيح للتأكد من وجود جميع المتغيرات المطلوبة
  3. تحقق من الاستجابات: تحقق من استجابات API قبل ا��متابعة
  4. اختبار التراجع: افشل المهام عمدًا للتحقق من عمل كتل الإنقاذ
  5. اختبار إلغاء التهيئة: اختبر باستخدام action: "deprovision" للتحقق من التنظيف

مثال على دفتر اختبار:

- name: اختبار تهيئة الخدمة
hosts: localhost
gather_facts: no

tasks:
- name: التحقق من المتغيرات المطلوبة
assert:
that:
- product_id is defined
- customer_id is defined
- access_token is defined
fail_msg: "المتغيرات المطلوبة مفقودة"

- name: اختبار الاتصال بواجهة برمجة التطبيقات
uri:
url: "http://localhost:5000/crm/health"
method: GET
register: health_check

- name: التحقق من صحة الفحص الصحي
assert:
that:
- health_check.status == 200

الأخطاء الشائعة

عدم وجود تحويلات نوعية:

# خاطئ - قد يكون سلسلة
customer_id: "{{ customer_id }}"

# صحيح - تأكد من أنه عدد صحيح
customer_id: {{ customer_id | int }}

عدم معالجة المتغيرات غير المعرفة:

# خاطئ - يفشل إذا لم يكن معرفًا
service_uuid: "{{ service_uuid }}"

# صحيح - قدم افتراضي
service_uuid: "{{ service_uuid | default('') }}"

نسيان التحقق:

# خاطئ - لا يتحقق من الاستجابة
- name: إنشاء حساب
uri: ...
register: response

# صحيح - تحقق من الاستجابة
- name: إنشاء حساب
uri: ...
register: response

- name: التحقق من الإنشاء
assert:
that:
- response.json.result == "OK"

سير عمل التهيئة

بشكل عام، سيعمل موظفو Omnitouch مع العميل على:

  1. تحديد متطلبات المنتج
  2. تطوير دفاتر اللعب اللازمة لأتمتة عملية التهيئة
  3. اختبار دفاتر اللعب في بيئة staging
  4. نشرها في الإنتاج

يضمن ذلك أن كل خدمة يتم نشرها بشكل متسق وموثوق، مما يقلل من خطر الأخطاء ويضمن إكمال جميع الخطوات الضرورية بالترتيب الصحيح.

متغيرات أنسبل

تشمل المتغيرات الممررة إلى دفاتر لعب أنسبل:

متغيرات المنتج
مشتقة من تكوينات منتج OmniCRM وتحدد كيفية إعداد الخدمة.

متغيرات المخزون
محددة من المخزون، تشمل عناصر مثل المودمات، بطاقات SIM، كتل عناوين IP، أو أرقام الهواتف المطلوبة للتهيئة.

المتغيرات النظامية
تضاف تلقائيًا بواسطة OmniCRM:

  • product_id - المنتج الذي يتم تهيئته
  • customer_id - العميل الذي يتلقى الخدمة
  • service_id - الخدمة التي يتم تعديلها (للتعبئة/التغييرات)
  • access_token - JWT لمصادقة API

إلغاء التهيئة

عندما لم تعد الخدمة مطلوبة، يتم أيضًا استخدام دفاتر لعب أنسبل لإلغاء تهيئة الخدمة باستخدام نمط كتلة rescue. هذا:

  • يزيل أي تكوينات
  • يحرر المخزون مرة أخرى إلى المجموعة
  • يحذف حسابات الفوترة
  • يضمن الحفاظ على نظافة النظام

طرق إلغاء التهيئة

هناك طريقتان رئيسيتان يتم تحفيز إلغاء التهيئة من خلالهما:

1. التراجع التلقائي (فشل التهيئة)
عندما تفشل أي مهمة في كتلة التهيئة الرئيسية، يتم تنفيذ قسم الإنقاذ تلقائيًا لتنظيف التغييرات الجزئية.

2. إلغاء التهيئة اليدوي
تعيين action: "deprovision" عند تشغيل دفتر اللعب يحفز كتلة الإنقاذ عمدًا لإزالة خدمة.

نمط إلغاء التهيئة

تتبع جميع دفاتر لعب OmniCRM هذا الهيكل للتنظيف الآمن:

- name: دفتر لعب تهيئة الخدمة
hosts: localhost
gather_facts: no
become: False

tasks:
- name: الكتلة الرئيسية
block:
# جميع مهام التهيئة تذهب هنا
- name: إنشاء حساب OCS
uri: ...

- name: إنشاء سجل الخدمة
uri: ...
register: service_creation_response

- name: إضافة معاملة
uri: ...

rescue:
# مهام إلغاء التهيئة/التراجع تذهب هنا
- name: طباعة جميع المتغيرات للتصحيح
debug:
var: hostvars[inventory_hostname]

# يتم تنفيذ مهام التنظيف بترتيب عكسي
- name: إزالة الحساب في OCS
uri: ...

- name: حذف الخدمة من CRM
uri: ...

- name: الفشل إذا لم يكن إلغاء التهيئة مقصودًا
assert:
that:
- action == "deprovision"

مثال كامل لإلغاء التهيئة

إليك مثال كامل من play_simple_service.yaml:

rescue:
# 1. معلومات التصحيح
- name: طباعة جميع المتغيرات لإلغاء التهيئة/التراجع
debug:
var: hostvars[inventory_hostname]

- name: الحصول على تاريخ اليوم
set_fact:
today: "{{ lookup('pipe', 'date +%Y-%m-%d') }}"

# 2. الحصول على معلومات الخدمة إذا كانت متاحة
- name: محاولة الحصول على معلومات الخدمة من واجهة برمجة التطبيقات CRM لإلغاء التهيئة
uri:
url: "http://localhost:5000/crm/service/service_id/{{ service_creation_response.json.service_id }}"
method: GET
headers:
Authorization: "Bearer {{ access_token }}"
return_content: yes
register: api_response_service
ignore_errors: True
when: service_creation_response is defined and service_creation_response.json is defined and service_creation_response.json.service_id is defined

- name: طباعة api_response_service
debug:
var: api_response_service
when: api_response_service is defined

# 3. تعيين service_uuid للتنظيف
- name: تعيين حقائق service_uuid لإلغاء التهيئة
set_fact:
service_uuid: "{{ api_response_service.json.service_uuid }}"
when:
- service_uuid is not defined
- api_response_service is defined
- api_response_service.json is defined

# 4. إزالة حساب OCS
- name: إزالة الحساب في OCS
uri:
url: "http://{{ crm_config.ocs.cgrates }}/jsonrpc"
method: POST
body_format: json
headers:
Content-Type: "application/json"
Authorization: "Bearer {{ access_token }}"
body:
{
"method": "ApierV2.RemoveAccount",
"params": [{
"Tenant": "{{ crm_config.ocs.ocsTenant }}",
"Account": "{{ service_uuid }}",
"ReloadScheduler": true
}]
}
status_code: 200
register: response
ignore_errors: True
when: service_uuid is defined

- name: طباعة الاستجابة من إزالة حساب OCS
debug:
var: response
when: response is defined

# 5. حذف سجل الخدمة من CRM
- name: حذف الخدمة من CRM إذا تم إنشاؤها
uri:
url: "http://localhost:5000/crm/service/service_id/{{ service_creation_response.json.service_id }}"
method: DELETE
headers:
Authorization: "Bearer {{ access_token }}"
status_code: 200
register: delete_service_response
ignore_errors: True
when: service_creation_response is defined and service_creation_response.json is defined and service_creation_response.json.service_id is defined

- name: طباعة استجابة حذف الخدمة
debug:
var: delete_service_response
when: delete_service_response is defined

# 6. تحديد ما إذا كان هذا مقصودًا أم فشلًا
- name: تعيين الحالة إلى "نجاح" إذا كان إلغاء التهيئة يدويًا / الفشل إذا فشلت التهيئة
assert:
that:
- action == "deprovision"
fail_msg: "فشلت التهيئة وتم التراجع"
success_msg: "تم إلغاء تهيئة الخدمة بنجاح"

مفاهيم رئيسية لإلغاء التهيئة

ignore_errors: True
تستخدم معظم مهام التنظيف ignore_errors: True لأن الموارد قد لا توجد (على سبيل المثال، إذا فشلت التهيئة قبل إنشائها).

التنفيذ الشرطي
استخدم عبارات when لتنظيف الموارد التي تم إنشاؤها فقط:

when: service_creation_response is defined

ترتيب عكسي
يحدث التنظيف بترتيب عكسي لإنشاء:

  1. حذف الموارد التابعة أولاً (المعاملات، تعيينات المخزون)
  2. حذف سجل الخدمة
  3. حذف حساب OCS/الفوترة
  4. تحرير أي موارد محتجزة

التحقق النهائي
التحقق النهائي يميز بين:

  • إلغاء التهيئة المقصود (action == "deprovision") → نجاح
  • فشل التهيئة (لم يتم تعيين الإجراء) → فشل

إلغاء التهيئة لموارد مختلفة

حسابات OCS/CGRateS:

- name: إزالة الحساب في OCS
uri:
url: "http://{{ crm_config.ocs.cgrates }}/jsonrpc"
method: POST
body_format: json
body:
{
"method": "ApierV2.RemoveAccount",
"params": [{
"Tenant": "{{ crm_config.ocs.ocsTenant }}",
"Account": "{{ service_uuid }}",
"ReloadScheduler": true
}]
}
status_code: 200
ignore_errors: True
when: service_uuid is defined

سجلات خدمة CRM:

- name: حذف الخدمة من CRM
uri:
url: "http://localhost:5000/crm/service/service_id/{{ service_id }}"
method: DELETE
headers:
Authorization: "Bearer {{ access_token }}"
status_code: 200
ignore_errors: True
when: service_id is defined

عناصر المخزون (إعادة إلى المجموعة):

- name: تحرير بطاقة SIM مرة أخرى إلى مجموعة المخزون
uri:
url: "http://localhost:5000/crm/inventory/inventory_id/{{ sim_inventory_id }}"
method: PATCH
headers:
Authorization: "Bearer {{ access_token }}"
body_format: json
body:
{
"customer_id": null,
"service_id": null,
"item_state": "Available"
}
status_code: 200
ignore_errors: True
when: sim_inventory_id is defined

خطط العمل (الرسوم المتكررة):

- name: إزالة خطة العمل من الحساب
uri:
url: "http://{{ crm_config.ocs.cgrates }}/jsonrpc"
method: POST
body_format: json
body:
{
"method": "ApierV1.RemoveActionPlan",
"params": [{
"Tenant": "{{ crm_config.ocs.ocsTenant }}",
"Id": "ActionPlan_Monthly_{{ service_uuid }}_{{ product_id }}"
}]
}
status_code: 200
ignore_errors: True

تفويضات الدفع:

- name: تحرير تفويض الدفع
uri:
url: "http://localhost:5000/crm/payments/release/{{ authorization_id }}"
method: POST
headers:
Authorization: "Bearer {{ access_token }}"
body_format: json
body:
{
"metadata": {
"release_reason": "provisioning_failed"
}
}
return_content: yes
ignore_errors: True
when: authorization_id is defined

سير عمل إلغاء التهيئة اليدوي

لإلغاء تهيئة خدمة يدويًا:

  1. تحديد الخدمة لإلغاء تهيئتها
  2. تشغيل دفتر التهيئة الأصلي مع action: "deprovision"
  3. يدخل دفتر اللعب كتلة الإنقاذ على الفور
  4. يتم تنفيذ جميع مهام التنظيف
  5. تتم إزالة الخدمة بشكل نظيف

مثال على استدعاء API:

POST /crm/provision/run_playbook
{
"product_id": 123,
"customer_id": 456,
"service_id": 789,
"action": "deprovision"
}

سيناريوهات التنظيف الجزئي

السيناريو 1: تم إنشاء حساب OCS، فشلت إنشاء الخدمة

  • حساب OCS موجود
  • سجل الخدمة غير موجود
  • يقوم قسم الإنقاذ بإزالة حساب OCS
  • لا توجد خدمة للحذف (تم تخطيها بأمان مع شرط when)

السيناريو 2: تم إنشاء الخدمة، فشلت المعاملة

  • حساب OCS موجود
  • سجل الخدمة موجود
  • المعاملة غير موجودة
  • يقوم قسم الإنقاذ بإزالة كل من حساب OCS والخدمة
  • لا توجد معاملة لإبطالها (لم يتم إنشاؤها أبدًا)

السيناريو 3: التهيئة الكاملة، فشل التقاط الدفع

  • حساب OCS موجود
  • سجل الخدمة موجود
  • تم تفويض الدفع ولكن لم يتم التقاطه
  • يقوم قسم الإنقاذ:
    • بإصدار تفويض الدفع (لم يتم تحصيل رسوم من العميل)
    • إزالة سجل الخدمة
    • إزالة حساب OCS

أفضل الممارسات لإلغاء التهيئة

1. استخدم ignore_errors بحرية
يجب أن يكون التنظيف متساهلاً - لا تفشل إذا لم يكن المورد موجودًا.

2. تحقق من وجود المورد باستخدام عبارات when
حاول التنظيف فقط إذا تم إنشاء المورد:

when: service_creation_response is defined

3. طباعة معلومات التصحيح
تأكد دائمًا من تضمين مهام التصحيح لمساعدة في استكشاف الأخطاء في التهيئات الفاشلة:

- name: طباعة جميع المتغيرات للتصحيح
debug:
var: hostvars[inventory_hostname]

4. قم بالتنظيف بترتيب عكسي حسب الاعتماد
احذف الأطفال قبل الآباء:

  • المعاملات قبل الخدمات
  • الخدمات قبل حسابات OCS
  • تعيينات المخزون قبل تحرير المخزون

5. التعامل مع تفويضات الدفع
قم دائمًا بإصدار تفويضات الدفع في كتل الإنقاذ لتجنب فرض رسوم على العملاء بسبب التهيئات الفاشلة.

6. إعادة تحميل المجدولين بعد التنظيف
عند إزالة موارد OCS، قم بتضمين ReloadScheduler: true لضمان تحديث CGRateS على الفور.

إلغاء التهيئة مقابل الحذف

إلغاء التهيئة (عبر كتلة الإنقاذ):

  • يزيل الخدمة من جميع الأنظمة
  • يحرر المخزون
  • يلغي الرسوم المتكررة
  • يترك أثر تدقيق
  • نهج موصى به

الحذف المباشر (عبر API):

  • يحذف فقط سجل CRM
  • لا ينظف حساب OCS
  • لا يحرر المخزون
  • يمكن أن يترك موارد يتيمة
  • غير موصى به للإنتاج

التراجع ومعالجة الأخطاء

تستخدم ميزة block/rescue في أنسبل أثناء كل من التهيئة وإلغاء التهيئة للتعامل مع الأخطاء بشكل سلس. إذا فشلت أي مهمة في أي نقطة أثناء التهيئة، يتم تنفيذ قسم الإنقاذ تلقائيًا للتراجع عن التغييرات للعودة إلى حالة متسقة. يضمن ذلك الموثوقية ويقلل من خطر النشر الجزئي أو الفاشل.

مثال على معالجة الأخطاء

- name: الكتلة الرئيسية
block:
- name: الخطوة 1: إنشاء الحساب
uri: ...
register: response

- name: التحقق من الخطوة 1
assert:
that:
- response.status == 200

- name: الخطوة 2: إنشاء الخدمة
uri: ...

rescue:
# إذا فشلت أي تأكيد أو حدث خطأ في أي مهمة:
# 1. ينتقل التنفيذ إلى كتلة الإنقاذ
# 2. يتم تنفيذ مهام التنظيف
# 3. يفشل دفتر اللعب مع رسالة خطأ
- name: تنظيف التغييرات الجزئية
uri: ...

للحصول على تفاصيل كاملة حول نظام التهيئة، وسير العمل، والمصادقة، انظر concepts_provisioning.