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

أدلة تفصيلية لبرامج Ansible

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

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

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

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

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

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

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

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

يمكن لبرنامج تهيئة واحد أن:

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

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

لا ينشئ أي خدمات
بعض البرامج لا تنشئ سجلات خدمات على الإطلاق:

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

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

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

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

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

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

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

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

# المنتج 1: خدمة SIM المحمولة (تنشئ خدمة واحدة)
# provisioning_play: play_simple_service
- Creates service record in CRM
- Creates billing account in OCS
- Assigns SIM card and phone number inventory
- Sends welcome email

# المنتج 2: حزمة الإنترنت (تنشئ 3 خدمات)
# provisioning_play: play_bundle_internet_tv_voice
- Creates internet service record
- Creates IPTV service record
- Creates VoIP service record
- Links all to same customer
- Single billing account for the bundle

# المنتج 3: تعبئة البيانات (تنشئ 0 خدمات)
# provisioning_play: play_topup_no_charge
- Finds existing service by service_id
- Adds data balance to existing OCS account
- Updates service expiry date
- NO new service created

# المنتج 4: تكوين CPE (تنشئ 0 خدمات)
# provisioning_play: play_prov_cpe_mikrotik
- Generates router configuration
- Updates inventory record with config
- Emails config to support team
- NO service created (just equipment setup)

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

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

فهم التمييز بين الألعاب والمهام هو أمر أساسي للعمل مع برامج 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: Simple Provisioning Play
hosts: localhost
tasks:
- name: Main provisioning block
block:
- name: Create service
uri: ...

- name: Configure billing
uri: ...

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

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

هيكل البرنامج وتشريحه

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

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

يبدأ كل برنامج بهذه الرؤوس القياسية:

- name: Descriptive Name of the Playbook
hosts: localhost # دائمًا localhost لـ OmniCRM
gather_facts: no # معطل لأداء أفضل
become: False # لا تصعيد الامتيازات

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

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

شرح الرأس

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

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

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

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

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

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

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

tasks:
- name: Include vars of 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: Access product_id passed by OmniCRM
debug:
msg: "Provisioning product {{ product_id }}"

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

- name: Get inventory ID for SIM Card
set_fact:
sim_card_id: "{{ hostvars[inventory_hostname]['SIM Card'] | int }}"
when: "'SIM Card' in hostvars[inventory_hostname]"

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

- name: Get Product information from CRM API
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: Use the product name
debug:
msg: "Product name is {{ api_response_product.json.product_name }}"

أنماط البرامج الشائعة

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

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

- name: Service Provisioning Playbook
hosts: localhost
gather_facts: no
become: False

tasks:
- name: Main block
block:

# 1. تحميل التكوين
- name: Include vars of crm_config
ansible.builtin.include_vars:
file: "../../crm_config.yaml"
name: crm_config

# 2. الحصول على معلومات المنتج
- name: Get Product information from CRM API
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: Get Customer information from CRM API
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 package facts
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: Generate UUID
set_fact:
uuid: "{{ 99999999 | random | to_uuid }}"

- name: Generate Service UUID
set_fact:
service_uuid: "Service_{{ uuid[0:8] }}"

# 6. إنشاء حساب في نظام الفوترة
- name: Create account in 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: Verify OCS account creation
assert:
that:
- ocs_response.status == 200
- ocs_response.json.result == "OK"

# 7. إضافة رصيد أولي
- name: Add 0 Monetary Balance
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: Get current date and time in ISO 8601 format
command: date --utc +%Y-%m-%dT%H:%M:%S%z
register: current_date_time

- name: Add Service via 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: Add Setup Cost Transaction via 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 }} - Setup Costs",
"description": "Setup costs for {{ 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: Print all vars for debugging
debug:
var: hostvars[inventory_hostname]

- name: Remove account in 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: Delete Service from CRM if it was created
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: Fail if not intentional deprovision
assert:
that:
- action == "deprovision"

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

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

- name: Service Topup Playbook
hosts: localhost
gather_facts: no
become: False

tasks:
- name: Include vars of crm_config
ansible.builtin.include_vars:
file: "../../crm_config.yaml"
name: crm_config

# 1. الحصول على معلومات الخدمة
- name: Get Service information from CRM API
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: Get Product information from CRM API
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 service facts
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: Execute Action to add credits
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: Verify action executed successfully
assert:
that:
- action_response.status == 200
- action_response.json.result == "OK"

# 5. إعادة تعيين أي حدود تم تفعيلها
- name: Reset 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: Calculate new expiry date
command: "date --utc +%Y-%m-%dT%H:%M:%S%z -d '+30 days'"
register: new_expiry_date

- name: Update Service with new expiry
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: Send Notification SMS
uri:
url: "http://sms-gateway/api/send"
method: POST
body_format: json
body:
{
"source": "CompanyName",
"destination": "{{ customer_phone }}",
"message": "Your service has been topped up. New expiry: {{ new_expiry_date.stdout }}"
}
status_code: 201
ignore_errors: True

نمط تهيئة CPE

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

- name: CPE Provisioning Playbook
hosts: localhost
gather_facts: no
become: False

tasks:
- name: Include vars of crm_config
ansible.builtin.include_vars:
file: "../../crm_config.yaml"
name: crm_config

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

# 2. الحصول على تفاصيل CPE من المخزون
- name: Get Inventory data for 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: Get Site info from 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: Patch CPE inventory item with location
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: Set CPE hostname
set_fact:
cpe_hostname: "CPE_{{ cpe_inventory_id }}"
cpe_username: "admin_{{ cpe_inventory_id }}"

- name: Generate random password
set_fact:
cpe_password: "{{ lookup('pipe', 'cat /dev/urandom | tr -dc a-zA-Z0-9 | head -c 16') }}"

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

- name: Generate WiFi password
set_fact:
word_list:
- apple
- cloud
- river
- mountain
- ocean

- name: Create WiFi PSK
set_fact:
random_word: "{{ word_list | random }}"
random_number: "{{ 99999 | random(start=10000) }}"

- name: Combine WiFi PSK
set_fact:
wifi_psk: "{{ random_word }}{{ random_number }}"

# 7. توليد ملف التكوين
- name: Set config filename
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: Create config from template
template:
src: "templates/cpe_router_config.j2"
dest: "{{ config_dest }}"

# 8. قراءة التكوين الناتج
- name: Read config file
ansible.builtin.slurp:
src: "{{ config_dest }}"
register: config_content

# 9. تحديث المخزون بمعلومات التهيئة
- name: Patch CPE inventory with config
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": "Provisioned: {{ lookup('pipe', 'date +%Y-%m-%d') }}"
}
status_code: 200

# 10. إرسال التكوين إلى فريق الدعم
- name: Email configuration to support
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": "Provisioning System"
},
"To": [{
"Email": "support@example.com",
"Name": "Support Team"
}],
"Subject": "CPE Config - {{ 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: Normalize auto_renew to boolean
set_fact:
auto_renew_bool: "{{ (auto_renew | string | lower) in ['true', '1', 'yes'] }}"

# 2. إنشاء إجراء للتجديد التلقائي
- name: Create Action for AutoRenew
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: Create ActionPlan for Monthly Renewal
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: Assign ActionPlan to account
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: Remove ActionPlan from account
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 email configuration
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 email subject and sender name
set_fact:
email_subject: "Welcome to our service!"
email_from_name: "Customer Service Team"

- name: Prepare list of recipients from customer contacts
loop: "{{ api_response_customer.json.contacts }}"
set_fact:
recipients: "{{ recipients + [{'Email': item.contact_email, 'Name': item.contact_firstname ~ ' ' ~ item.contact_lastname}] }}"

- name: Get first contact name
set_fact:
first_contact: "{{ api_response_customer.json.contacts[0].contact_firstname }}"

- name: Send welcome email
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": "Dear {{ first_contact }}, welcome! Your service is ready.",
"HTMLPart": "Dear {{ first_contact }},<br/><h3>Welcome!</h3><br/>Your {{ package_name }} service is now active.<br/>Monthly cost: ${{ monthly_cost }}<br/>Setup fee: ${{ setup_cost }}<br/>If you have any questions, contact 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: Notify OCS of provisioning completion
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: Get SIM Card inventory ID
set_fact:
sim_inventory_id: "{{ hostvars[inventory_hostname]['SIM Card'] | int }}"
when: "'SIM Card' in hostvars[inventory_hostname]"

- name: Get SIM Card details
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: Extract SIM details
set_fact:
iccid: "{{ sim_response.json.iccid }}"
imsi: "{{ sim_response.json.imsi }}"
ki: "{{ sim_response.json.ki }}"

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

- name: Assign SIM to customer
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: Get current date and time in ISO 8601 format
command: date --utc +%Y-%m-%dT%H:%M:%S%z
register: current_date_time

- name: Get today's date only
set_fact:
today: "{{ lookup('pipe', 'date +%Y-%m-%d') }}"

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

- name: Calculate expiry date 30 days from now
command: "date --utc +%Y-%m-%dT%H:%M:%S%z -d '+30 days'"
register: expiry_date

- name: Calculate date 90 days in future
command: "date --utc +%Y-%m-%d -d '+{{ days }} days'"
register: future_date
vars:
days: 90

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

UUIDs والمعرفات:

- name: Generate UUID
set_fact:
uuid: "{{ 99999999 | random | to_uuid }}"

- name: Generate service identifier
set_fact:
service_uuid: "SVC_{{ uuid[0:8] }}"

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

- name: Generate secure password
set_fact:
password: "{{ lookup('pipe', 'cat /dev/urandom | tr -dc a-zA-Z0-9 | head -c 16') }}"

عبارات مرور يمكن تذكرها:

- name: Set word list
set_fact:
words:
- alpha
- bravo
- charlie
- delta
- echo

- name: Generate passphrase
set_fact:
word: "{{ words | random }}"
number: "{{ 99999 | random(start=10000) }}"

- name: Combine into passphrase
set_fact:
passphrase: "{{ word }}{{ number }}"

العمل مع CGRateS/OCS

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

- name: Create billing account
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: Add data balance
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: Execute charging action
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: Get account details
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: Get AttributeProfile
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: Extract attribute value
set_fact:
phone_number: "{{ attr_response.json.result.Attributes | json_query(\"[?Path=='*req.PhoneNumber'].Value[0].Rules\") | first }}"
when: attr_response is defined

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

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

- name: Use custom value or default
set_fact:
monthly_cost: "{{ custom_cost | default(50.00) }}"

- name: Only run if variable is defined
debug:
msg: "Service UUID is {{ service_uuid }}"
when: service_uuid is defined

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

- name: Provision equipment
include_tasks: configure_cpe.yaml
when: provision_cpe | default(false) | bool

- name: Skip if deprovision
assert:
that:
- action != "deprovision"
when: action is defined

شروط متعددة:

- name: Complex conditional task
uri:
url: "{{ endpoint }}"
method: POST
when:
- service_uuid is defined
- customer_id is defined
- action != "deprovision"
- enable_feature | default(true) | bool

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

حلقات بسيطة:

- name: Create multiple balances
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: Get all customer sites
uri:
url: "{{ crm_config.crm.base_url }}/crm/site/customer_id/{{ customer_id }}"
method: GET
headers:
Authorization: "Bearer {{ access_token }}"
register: sites_response

- name: Configure equipment at each site
debug:
msg: "Configuring site at {{ item.address_line_1 }}"
loop: "{{ sites_response.json }}"

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

استخدام ignore_errors:

- name: Optional SMS notification
uri:
url: "http://sms-gateway/send"
method: POST
body: {...}
ignore_errors: true

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

- name: Verify API response
assert:
that:
- response.status == 200
- response.json.result == "OK"
fail_msg: "API call failed: {{ response.json }}"

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

- name: Try to get existing service
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: Create service if it doesn't exist
uri:
url: "{{ crm_config.crm.base_url }}/crm/service/"
method: PUT
body: {...}
when: service_lookup.status == 404

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

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

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

# جيد
service_uuid: "SVC_12345"
customer_name: "John Smith"
monthly_cost: 49.99

# سيء
svc: "SVC_12345"
name: "John Smith"
cost: 49.99

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

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

تصحيح الأخطاء

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

- name: Print all variables
debug:
var: hostvars[inventory_hostname]

- name: Print specific variable
debug:
msg: "Service UUID: {{ service_uuid }}"

- name: Print API response
debug:
var: api_response_product.json

التحقق من الصحة

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

- name: Create account
uri:
url: "{{ billing_endpoint }}"
method: POST
body: {...}
register: response

- name: Verify account creation
assert:
that:
- response.status == 200
- response.json.result == "OK"
fail_msg: "Failed to create account: {{ response.json }}"

التكرار

صمم المهام لتكون قابلة للتشغيل بأمان:

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

# أنشئ فقط إذا لم يكن موجودًا
- name: Create account
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: Call external API
uri:
url: "https://api.example.com/endpoint"
method: POST
headers:
Authorization: "Bearer {{ access_token }}"
validate_certs: yes

الوثائق

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

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

- name: Get current day
command: "date +%d"
register: current_day

- name: Calculate pro-rata amount
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: Test Service Provisioning
hosts: localhost
gather_facts: no

tasks:
- name: Verify required variables
assert:
that:
- product_id is defined
- customer_id is defined
- access_token is defined
fail_msg: "Missing required variables"

- name: Test API connectivity
uri:
url: "http://localhost:5000/crm/health"
method: GET
register: health_check

- name: Verify health check
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: Create account
uri: ...
register: response

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

- name: Verify creation
assert:
that:
- response.json.result == "OK"

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

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

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

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

متغيرات Ansible

تشمل المتغيرات المرسلة إلى برامج Ansible:

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

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

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

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

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

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

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

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

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

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

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

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

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

- name: Service Provisioning Playbook
hosts: localhost
gather_facts: no
become: False

tasks:
- name: Main block
block:
# جميع مهام التهيئة تذهب هنا
- name: Create OCS account
uri: ...

- name: Create service record
uri: ...
register: service_creation_response

- name: Add transaction
uri: ...

rescue:
# مهام إلغاء التهيئة/التراجع تذهب هنا
- name: Print all vars for debugging
debug:
var: hostvars[inventory_hostname]

# تنفيذ مهام التنظيف بترتيب عكسي
- name: Remove account in OCS
uri: ...

- name: Delete Service from CRM
uri: ...

- name: Fail if not intentional deprovision
assert:
that:
- action == "deprovision"

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

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

rescue:
# 1. معلومات التصحيح
- name: Print all vars for Deprovision/Rollback
debug:
var: hostvars[inventory_hostname]

- name: Get today's date
set_fact:
today: "{{ lookup('pipe', 'date +%Y-%m-%d') }}"

# 2. الحصول على معلومات الخدمة إذا كانت متاحة
- name: Try to get Service information from CRM API to Deprovision
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: Print api_response_service
debug:
var: api_response_service
when: api_response_service is defined

# 3. تعيين service_uuid للتنظيف
- name: Set service_uuid facts for Deprovision
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: Remove account in 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: Print response from OCS account removal
debug:
var: response
when: response is defined

# 5. حذف سجل الخدمة من CRM
- name: Delete Service from CRM if it was created
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: Print delete service response
debug:
var: delete_service_response
when: delete_service_response is defined

# 6. تحديد ما إذا كان هذا إلغاء تهيئة مقصودًا أو فشلًا
- name: Set status to "Success" if Manual deprovision / Fail if failed provision
assert:
that:
- action == "deprovision"
fail_msg: "Provisioning failed and was rolled back"
success_msg: "Service deprovisioned successfully"

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

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

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

when: service_creation_response is defined

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

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

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

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

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

حسابات OCS/CGRateS:

- name: Remove account in 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: Delete Service from 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: Release SIM card back to inventory pool
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: Remove ActionPlan from account
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: Release payment authorization
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: Print all vars for debugging
debug:
var: hostvars[inventory_hostname]

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

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

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

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

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

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

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

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

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

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

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

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

- name: Main block
block:
- name: Step 1: Create account
uri: ...
register: response

- name: Verify step 1
assert:
that:
- response.status == 200

- name: Step 2: Create service
uri: ...

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

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