跳到主要内容

SIM卡配置

OmniCRM为移动网络运营商和虚拟移动网络运营商(MVNO)提供全面的物理SIM卡eSIM(嵌入式SIM)配置支持。该系统处理从库存管理到激活、分配和取消配置的完整生命周期。

另请参阅:配置系统以了解一般配置概念,库存以了解库存管理,Ansible剧本以了解配置自动化。

概述

OmniCRM中的SIM配置涉及多个集成系统共同工作:

  1. 库存管理 - 跟踪可用的SIM卡(物理和eSIM配置文件)
  2. HSS/IMS集成 - 配置用户凭据和语音服务
  3. OCS集成 - 设置服务的计费和收费
  4. Ansible自动化 - 协调配置工作流
  5. 自助注册支持 - 使客户能够激活自己的SIM卡

物理SIM卡配置

物理SIM卡是客户插入其设备的传统可拆卸SIM卡。OmniCRM通过库存系统管理这些SIM卡,并将其配置到HSS/IMS以获得网络访问。

物理SIM工作流

1. 库存设置

SIM卡必须首先加载到库存系统中:

  • ICCID(集成电路卡标识符) - 唯一的SIM卡标识符
  • IMSI(国际移动用户身份) - 网络的用户身份

这些信息存储在库存项目字段中:

  • itemtext1: ICCID
  • itemtext2: IMSI
  • itemtext3: SIM类型(物理/eSIM) - 可选

示例物理SIM库存项目:

{
"inventory_id": 1001,
"item": "SIM Card",
"itemtext1": "8961234567890123456", // ICCID
"itemtext2": "310120123456789", // IMSI
"itemtext3": "Physical", // SIM Type
"item_location": "Warehouse A, Shelf 3",
"item_state": "New",
"wholesale_cost": 2.50,
"retail_cost": 10.00
}

有关所有库存字段(itemtext1-20、item_state值、地址字段、management_url等)的完整说明,请参见库存概述 - 库存项目字段

身份验证凭据存储

SIM卡的身份验证凭据存储在**HSS AuC(认证中心)**中,而不是CRM库存中:

  • Ki(认证密钥) - 用于认证的密钥(存储在HSS中)
  • OPC(运营商代码) - 运营商特定的认证参数(存储在HSS中)
  • PIN1/PIN2 - 用户PIN码(存储在HSS中)
  • PUK1/PUK2 - PIN解锁码(存储在HSS中)

CRM库存跟踪哪个SIM(通过ICCID/IMSI)分配给哪个客户,而HSS使用与物理SIM卡匹配的Ki/OPC凭据处理实际的网络认证。

2. 服务分配

当客户订购移动服务时:

  1. 员工或客户从库存中选择一个可用的SIM卡
  2. 从电话号码库存中选择一个手机号码(MSISDN)
  3. 触发产品的配置剧本(例如,play_psim_only.yaml

3. HSS配置

Ansible剧本将用户配置到家庭用户服务器(HSS):

首先,它检索auc_id(对已经存储在HSS中的认证凭据的引用):

- name: Get AuC ID for IMSI
uri:
url: "{{ item }}/auc/imsi/{{ imsi }}"
method: GET
loop: "{{ hss_peers }}"
register: auc_lookup

- name: Extract auc_id
set_fact:
auc_id: "{{ auc_lookup.results[0].json.auc_id }}"

然后创建引用该auc_id的用户记录:

- name: Provision subscriber on HSS
uri:
url: "{{ item }}/subscriber/"
method: PUT
body_format: json
body:
enabled: true
roaming_enabled: true
auc_id: "{{ auc_id }}"
msisdn: "{{ phone_number }}"
imsi: "{{ imsi }}"
ue_ambr_dl: 9999999
ue_ambr_ul: 9999999
apn_list: "1,2,3"
default_apn: 1
loop: "{{ hss_peers }}"

身份验证凭据(Ki、OPC)保留在HSS AuC中,永远不会暴露给CRM或配置剧本。

4. IMS配置(用于语音服务)

为了实现语音通话能力,创建IMS用户:

- name: Create IMS subscriber for voice services
uri:
url: "{{ item }}/ims_subscriber/"
method: PUT
body_format: json
body:
imsi: "{{ imsi }}"
msisdn: "{{ phone_number }}"
msisdn_list: "{{ phone_number }}"
ifc_path: "default_ifc.xml"
sh_profile: "{{ sh_profile_xml }}"
loop: "{{ hss_peers }}"

5. 计费系统设置

OCS(在线计费系统)配置为:

  • 创建账户
  • 通过属性���置文件进行IMSI/MSISDN映射
  • 识别用户的过滤规则
  • 资源限制(并发会话)
  • 初始余额

6. 服务创建和库存分配

最后:

  • 在CRM中创建服务记录
  • 将SIM卡库存项目分配给客户(customer_id设置)
  • 将SIM卡链接到服务(service_id设置)
  • 更新库存状态为“已分配”

物理SIM身份验证

物理SIM使用存储在HSS中的AuC(认证中心)凭据:

  • KiOPC用于4G/5G认证(MILENAGE算法)
  • 这些凭据在SIM导入期间预加载到HSS AuC数据库中
  • 物理SIM卡包含匹配的Ki/OPC值
  • 在配置期间,剧本引用auc_id以将用户与这些凭据链接
  • 网络认证通过SIM和HSS之间的挑战-响应进行
  • CRM从未看到或处理实际的Ki/OPC值

eSIM配置

eSIM(嵌入式SIM)是可以下载到兼容设备的软件基础SIM配置文件,无需物理SIM卡。OmniCRM支持使用LPA(本地配置助手)激活代码进行eSIM配置。

eSIM功能

LPA激活代码

eSIM使用LPA代码进行激活:

LPA:1$smdp.example.com$ACTIVATION-CODE-ABC123XYZ

其中:

  • LPA:1 - LPA版本标识符
  • smdp.example.com - SM-DP+(订阅管理数据准备)服务器地址
  • ACTIVATION-CODE-ABC123XYZ - 此eSIM配置文件的唯一激活代码

二维码生成

OmniCRM自动从LPA激活代码生成二维码:

  • 存储在库存management_url字段中
  • 在用户界面中显示为可扫描的二维码
  • 客户使用设备相机扫描以安装eSIM配置文件
  • 无需手动输入长激活代码

示例eSIM库存项目:

{
"inventory_id": 1002,
"item": "eSIM",
"itemtext1": "8961234567890123457",
"itemtext2": "310120123456790",
"itemtext3": "eSIM",
"management_url": "LPA:1$smdp.example.com$ACTIVATION-CODE-ABC123XYZ",
"item_location": "Virtual Inventory",
"item_state": "New",
"wholesale_cost": 0.00,
"retail_cost": 0.00
}

在用户界面查看此库存项目时,会自动生成并显示二维码以便于扫描。

eSIM配置工作流

eSIM配置工作流与物理SIM配置几乎相同,但有几个关键区别:

1. 自动eSIM分配

如果产品需要SIM但未选择物理SIM,系统可以自动分配一个可用的eSIM:

# 自动eSIM查找
search_filters = {
"customer_id": [None],
"item_state": ["New"],
"itemtext3": ["eSIM"] # 过滤eSIM类型
}
available_esim = search_inventory("SIM Card", filters=search_filters)

2. HSS/IMS配置

eSIM与物理SIM的配置方式完全相同:

  • 相同的IMSI配置
  • 相同的IMS用户创建
  • ��同的身份验证凭据(Ki、OPC)

3. 客户激活

配置后:

  1. 客户通过电子邮件或客户门户接收eSIM激活详细信息
  2. 显示二维码以供扫描
  3. 客户使用设备扫描二维码
  4. 设备从SM-DP+服务器下载eSIM配置文件
  5. eSIM安装并准备使用

公共eSIM发行者API

OmniCRM提供一个公共API端点以支持自助eSIM请求,使客户能够直接从网站或移动应用请求eSIM,而无需工作人员干预。

端点: POST /crm/oam/issue-esim

请求参数

参数类型必需默认描述
email字符串-客户电子邮件地址,用于eSIM交付。必须是有效的电子邮件格式。
name字符串从电子邮件派生客户名称,用于电子邮件个性化。如果未提供,则使用电子邮件地址的本地部分。
customer_id整数null现有客户ID。当提供时,创建与客户关联的活动记录以进行跟踪。
device_info对象{}设备信息,用于分析和兼容性跟踪。
device_info.compatibility_status字符串"unknown"eSIM兼容性状态:supportedmay-supportunknown。用于分析。
device_info.user_agent字符串请求头用户代理字符串。如果未提供,则从请求头自动捕获。

速率限制

该端点实现双重速率限制以防止滥用:

限制窗口范围描述
10请求1分钟每个IP地址通过装饰器应用的一般速率限制
3请求24小时每个电子邮件地址防止向同一电子邮件发送过多eSIM请求

当超过速率限制时,端点返回HTTP 429,带有error_type: "RateLimitExceeded"

响应格式

成功响应(HTTP 200):

{
"result": "Success",
"message": "eSIM request received. You will receive your eSIM QR code via email shortly.",
"esim_id": 8472,
"iccid": "8944200000001234567",
"email_sent": true
}
字段类型描述
result字符串对于HTTP 200响应始终为"Success"
message字符串可读的确认消息
esim_id整数分配的eSIM的库存ID
iccid字符串分配的eSIM的ICCID
email_sent布尔值交付电子邮件是否成功发送

错误响应

HTTP代码错误类型描述
400-无效或缺失的电子邮件地址
429RateLimitExceeded每个电子邮件或每个IP的速率限制超出
503NoInventory库存中没有可用的eSIM
500-服务器内部错误

错误响应格式:

{
"result": "Failed",
"reason": "Error description",
"error_type": "ErrorType"
}

电子邮件交付

该端点通过Mailjet发送包含以下内容的eSIM交付电子邮件:

  • ICCID
  • 二维码(如果在itemtext2中可用)
  • 激活代码/LPA字符串(如果在itemtext3management_url中可用)
  • 步骤激活说明

crm_config.yaml中配置Mailjet模板:

mailjet:
api_crmCommunicationEsimDelivery:
from_email: "support@aimobile.ac"
from_name: "Ascension Island Mobile"
template_id: 1234567
subject: "Your eSIM is Ready"
参数类型必需描述
from_email字符串发件人电子邮件地址
from_name字符串发件人显示名称
template_id整数Mailjet模板ID。如果未提供,则生成默认HTML电子邮件。
subject字符串电子邮件主题行

如果未配置模板,系统将生成带有以下内容的品牌HTML电子邮件:

  • 公司品牌和徽标
  • eSIM激活的二维码(如果可用)
  • 手动激活代码说明
  • 针对iOS和Android的设备特定设置指南

活动日志记录

每个eSIM请求都会创建一个活动记录,包含:

  • 活动类型:esim_request
  • 客户ID链接(如果提供)
  • 设备兼容性状态
  • 用户代理信息

这使得跟踪eSIM请求和转化分析成为可能。

4. HSS中的eSIM数据结构

HSS AuC(认证中心)中的eSIM标记为esim: true标志:

{
"esim": true,
"lpa": "LPA:1$smdp.example.com$ACTIVATION-CODE-ABC123XYZ",
"iccid": "8961234567890123457",
"imsi": "310120123456790",
"ki": "00112233445566778899AABBCCDDEEFF",
"opc": "FFEEDDCCBBAA99887766554433221100",
"pin1": "1234",
"pin2": "5678",
"puk1": "12345678",
"puk2": "87654321",
"batch_name": "eSIM_Batch_2024_Q1",
"sim_vendor": "Thales"
}

eSIM导入过程

eSIM通常从eSIM供应商批量导入:

导入脚本: /OmniCRM-API/Provisioners/customer_product_creation/import_keys_esim.py

导入过程:

  1. 从供应商提供的Excel文件中读取eSIM数据
  2. 从CSV文件中加载LPA激活代码
  3. 更新HSS以包含eSIM凭据
  4. 在CRM中创建相应的库存项目
  5. 将eSIM配置文件链接到库存系统

这确保eSIM配置文件在保持适当库存跟踪的同时可用于分配。

库存集成

库存系统是SIM配置的核心,跟踪移动服务所需的物理和虚拟资源。

SIM卡库存模板

典型的SIM卡库存模板定义:

{
"item": "SIM Card",
"itemtext1_label": "ICCID",
"itemtext2_label": "IMSI",
"itemtext3_label": "SIM Type",
"wholesale_cost": 2.50,
"retail_cost": 10.00
}

注意:身份验证凭据(Ki、OPC、PIN、PUK)存储在HSS AuC中,而不是在CRM库存中。

有关创建和管理库存模板的详细信息,包括如何定义自定义字段(itemtext1-20)和将模板链接到产品,请参见库存管理 - 库存模板部分。

手机号码库存

电话号码作为单独的库存���目进行管理:

{
"inventory_id": 4001,
"item": "Mobile Number",
"itemtext1": "+61412345678",
"itemtext2": "Melbourne",
"itemtext3": "Mobile",
"item_location": "Australia - VIC",
"item_state": "New",
"wholesale_cost": 1.00,
"retail_cost": 0.00
}

SIM的库存状态

SIM卡经历多个库存状态:

  • - 未使用的SIM,可供分配
  • 已分配 - 当前与客户活跃
  • 已使用 - 以前分配,返回库存(可以重复使用)
  • 内部使用 - 用于测试或员工使用
  • 损坏 - 无法使用,需要更换
  • 丢失 - 无法找到
  • 被盗 - 报告被盗

产品集成

产品通过inventory_items_list指定所需库存:

{
"product_id": 1,
"product_slug": "Mobile-SIM",
"product_name": "Mobile SIM Only",
"provisioning_play": "play_psim_only",
"inventory_items_list": "['SIM Card', 'Mobile Number']"
}

在配置此产品时:

  1. 用户界面显示库存选择器,带有两个下拉菜单
  2. 用户选择可用的SIM卡(物理或eSIM)
  3. 用户选择可用的手机号码
  4. 库存ID传递给Ansible剧本
  5. 剧本通过API检索完整的库存详细信息
  6. 配置继续使用所选资源

有关产品如何驱动配置、如何定义库存要求以及如何将变量传递给剧本的完整说明,请参见配置系统 - 产品如何驱动配置

库存分配流程

Ansible剧本处理库存分配。

有关剧本如何使用CRM API进行身份验证(Bearer令牌、API密钥、刷新令牌)的更多详细信息,请参见配置系统 - 身份验证和授权

- name: Get SIM Card details from inventory
uri:
url: "{{ crm_config.crm.base_url }}/crm/inventory/inventory_id/{{ inventory_id_sim_card }}"
method: GET
headers:
Authorization: "Bearer {{ access_token }}"
register: sim_inventory_response

- name: Extract SIM identifiers
set_fact:
iccid: "{{ sim_inventory_response.json.itemtext1 }}"
imsi: "{{ sim_inventory_response.json.itemtext2 }}"

- name: Get AuC ID from HSS (reference to authentication credentials)
uri:
url: "{{ item }}/auc/imsi/{{ imsi }}"
method: GET
loop: "{{ hss_peers }}"
register: auc_response

- name: Extract AuC ID
set_fact:
auc_id: "{{ auc_response.results[0].json.auc_id }}"

# ... provision to HSS/IMS/OCS ...

- name: Assign SIM to customer
uri:
url: "{{ crm_config.crm.base_url }}/crm/inventory/inventory_id/{{ inventory_id_sim_card }}"
method: PATCH
headers:
Authorization: "Bearer {{ access_token }}"
body_format: json
body:
customer_id: "{{ customer_id }}"
service_id: "{{ service_id }}"
item_state: "Assigned"

这确保:

  • SIM标记为分配给客户
  • 服务ID链接以进行跟踪
  • 库存状态反映当前状态
  • 其他系统可以查询库存以查找客户的SIM

自助注册与SIM配置

OmniCRM支持客户自助注册,客户可以在没有工作人员干预的情况下激活自己的SIM卡或eSIM。

自助注册流程

1. 客户启动注册

客户访问自助注册页面并开始创建账户:

  • 提供个人信息
  • 选择服务计划(产品)
  • 输入支付详情

2. SIM号码验证

客户可以提供自己的SIM号码(可选):

GET /auth/self-signup/validate-itemtext1/{sim_number}

该端点检查:

  • SIM是否存在于库存中
  • SIM是否可用(未分配)
  • SIM是否处于正确状态(“新”或“已使用”)

3. 自动SIM分配(如果未提供SIM)

如果客户没有SIM,系统将自动分配一个:

def _populate_base_plan_inventory(sim_number, plan):
if sim_number:
# 客户提供的SIM - 验证其存在
sim_inventory = get_inventory_by_itemtext1(session, sim_number, None)
if not sim_inventory:
# 检查是否有与此号码相关的非活动服务
service = get_inactive_service_by_sim(sim_number)
if service:
sim_inventory = get_sim_for_service(service.service_id)
else:
# 从库存中自动分配可用的SIM
sim_inventory = get_first_available_inventory_by_item_name(
session,
"SIM Card",
filters={"item_state": ["New"], "customer_id": [None]}
)

# 获取ICCID和MSISDN
iccid = sim_inventory.itemtext1

# 自动分配电话号码
number_inventory = get_first_available_inventory_by_item_name(
session,
"Mobile Number"
)
msisdn = number_inventory.itemtext1

return {
"SIM Card": sim_inventory.inventory_id,
"Mobile Number": number_inventory.inventory_id
}

4. 账户创建和配置

一旦支付获得授权:

def _post_signup_tasks(customer_id, plan, payment_method):
# 1. 保存支付方式
save_stripe_payment_method(customer_id, payment_method)

# 2. 向客户卡收费
charge_customer_card(customer_id, plan.retail_setup_cost)

# 3. 登录用户(获取JWT令牌)
tokens = login_customer(customer_id)

# 4. 填充库存(SIM和电话号码)
inventory_vars = _populate_base_plan_inventory(sim_number, plan)

# 5. 创建配置作业
provision_vars = {
"product_id": plan.product_id,
"customer_id": customer_id,
**inventory_vars # 包括SIM卡和手机号码ID
}

# 可选:链接附加产品
if plan.addon_chain:
provision_vars["addon_chain"] = plan.addon_chain

provision_id = create_provisioning_job(provision_vars)

# 6. 返回成功与令牌
return {
"access_token": tokens.access_token,
"refresh_token": tokens.refresh_token,
"customer_id": customer_id,
"provision_id": provision_id
}

5. 客户接收激活详细信息

配置完成后:

  • 物理SIM:欢迎电子邮件,包含电话号码和激活说明
  • eSIM:电子邮件包括eSIM配置文件下载的二维码,或客户可以在客户门户中查看二维码

6. 自助注册的特殊权限

在注册期间,JWT令牌包含一个特殊声明:

{
"sub": "customer_123",
"signup_process": true,
"permissions": ["VIEW_OWN_PROVISION", "UPDATE_OWN_INVENTORY"]
}

这允许:

  • 客户在注册期间读取/更新库存
  • 仅限于他们自己的客户账户
  • 防止分配属于其他客户的SIM

自助注册安全性

验证检查:

  • SIM必须存在于库存中
  • SIM不得分配给其他客户
  • SIM必须处于可接受状态(“新”、“已使用”)
  • 客户只能将SIM分配给自己的账户
  • 在配置之前,必须授权支付

库存保护措施:

  • 数据库事务确保原子分配
  • 通过数据库锁防止并发注册尝试
  • 库存状态更改记录以供审计

基于Ansible的配置

所有SIM配置通过Ansible剧本进行协调,提供一致、可审计和可重复的过程。

有关Ansible剧本结构、块/恢复模式、剧本如何与产品交互以及最佳实践的全面文档,请参见Ansible剧本:详细指南

主要SIM配置剧本

文件: /OmniCRM-API/Provisioners/plays/play_psim_only.yaml

这是移动SIM配置(物理和eSIM)的主要剧本。它处理:

  • 移动语音和数据服务
  • 仅数据服务
  • 仅语音IMS用户

剧本结构(1,561行):

有关剧本头部、hosts/gather_facts/become指令和整体结构的详细说明,请参见Ansible剧本 - 剧本结构和解剖

- name: OmniCore Service Provisioning 2024
hosts: localhost
gather_facts: no
become: False

tasks:
- name: Main provisioning block
block:
# --- 预配置(行240-396) ---
- name: Get SIM information from inventory
# Retrieves IMSI, ICCID, etc.

- name: Validate IMSI length
# Must be exactly 15 digits

- name: Check if IMSI exists in HSS
# Prevents duplicate provisioning

- name: Get AuC ID for IMSI
# Retrieves auc_id reference from HSS

# --- HSS配置(行398-534) ---
- name: Create/Update HSS Subscriber
# Provisions to all HSS peers

- name: Create IMS Subscriber
# Enables voice calling

# --- OCS设置(行536-837) ---
- name: Create ENUM entry
# E.164 number mapping

- name: Create FilterS rule
# Account identification

- name: Create AttributeS profile
# IMSI/MSISDN mapping

- name: Create ResourceS profile
# Concurrent session limits

- name: Create OCS account
# Billing account creation

# --- 服务和库存(行865-946) ---
- name: Create service record
# In CRM database

- name: Assign SIM to service
# Update inventory

- name: Send welcome SMS
# Customer notification

rescue:
# --- 清理/取消配置(行975-1562) ---
- name: Return inventory to pool
# Reset customer_id, service_id to null

- name: Delete OCS account
# Remove billing account

- name: Remove HSS subscribers
# Or set to dormant state

- name: Determine success/failure
assert:
that:
- action == "deprovision"

关键配置步骤

1. 预配置验证

- name: Get SIM information from CRM inventory
uri:
url: "{{ crm_config.crm.base_url }}/crm/inventory/inventory_id/{{ inventory_id_sim_card }}"
method: GET
headers:
Authorization: "Bearer {{ access_token }}"
register: api_response_sim

- name: Extract and validate IMSI
set_fact:
imsi: "{{ api_response_sim.json.itemtext2 }}"

- name: Validate IMSI format
assert:
that:
- imsi | length == 15
- imsi is match('^[0-9]+$')
fail_msg: "IMSI must be exactly 15 digits"

2. 从HSS检索AuC ID

- name: Get AuC ID for IMSI from HSS
uri:
url: "{{ item }}/auc/imsi/{{ imsi }}"
method: GET
loop: "{{ crm_config.hss.hss_peers }}"
register: auc_lookup

- name: Extract auc_id from first HSS response
set_fact:
auc_id: "{{ auc_lookup.results[0].json.auc_id }}"

3. HSS用户配置

- name: Create subscriber in each HSS peer
uri:
url: "{{ item }}/subscriber/"
method: PUT
body_format: json
body:
enabled: true
roaming_enabled: true
auc_id: "{{ auc_id }}"
msisdn: "{{ phone_number }}"
imsi: "{{ imsi }}"
ue_ambr_dl: 9999999 # 下载速度限制
ue_ambr_ul: 9999999 # 上传速度限制
apn_list: "1,2,3"
default_apn: 1
status_code: [200, 201]
loop: "{{ crm_config.hss.hss_peers }}"
register: hss_responses

4. IMS用户配置(语音)

- name: Create IMS subscriber for voice services
uri:
url: "{{ item }}/ims_subscriber/"
method: PUT
body_format: json
body:
imsi: "{{ imsi }}"
msisdn: "{{ phone_number }}"
msisdn_list: "{{ phone_number }}"
ifc_path: "default_ifc.xml"
sh_profile: |
<?xml version="1.0"?>
<IMSSubscription>
<PrivateID>{{ imsi }}@ims.mnc{{ mnc }}.mcc{{ mcc }}.3gppnetwork.org</PrivateID>
<ServiceProfile>
<PublicIdentity>
<Identity>sip:{{ phone_number }}@ims.mnc{{ mnc }}.mcc{{ mcc }}.3gppnetwork.org</Identity>
</PublicIdentity>
</ServiceProfile>
</IMSSubscription>
status_code: [200, 201]
loop: "{{ crm_config.hss.hss_peers }}"

5. OCS计费配置

# 创建账户过滤器(通过IMSI/MSISDN识别用户)
- name: Create FilterS for IMSI identification
uri:
url: "{{ crm_config.ocs.ocsApi }}/jsonrpc"
method: POST
body_format: json
body:
method: "ApierV1.SetFilter"
params:
- ID: "FILTER_IMSI_{{ service_uuid }}"
Type: "*string"
Element: "~*req.OriginHost"
Values: ["{{ imsi }}"]

# 创建属性配置文件(将IMSI映射到账户)
- name: Create AttributeS profile
uri:
url: "{{ crm_config.ocs.ocsApi }}/jsonrpc"
method: POST
body_format: json
body:
method: "ApierV1.SetAttributeProfile"
params:
- ID: "ATTR_ACCOUNT_{{ service_uuid }}"
FilterIDs: ["FILTER_IMSI_{{ service_uuid }}"]
Attributes:
- Path: "*req.Account"
Value: "{{ service_uuid }}"

# 创建计费账户
- name: Create OCS account
uri:
url: "{{ crm_config.ocs.ocsApi }}/jsonrpc"
method: POST
body_format: json
body:
method: "ApierV2.SetAccount"
params:
- Tenant: "{{ crm_config.ocs.ocsTenant }}"
Account: "{{ service_uuid }}"
ActionPlanIds: []
ExtraOptions:
AllowNegative: false
Disabled: false

# 添加初始余额
- name: Add initial monetary balance
uri:
url: "{{ crm_config.ocs.ocsApi }}/jsonrpc"
method: POST
body_format: json
body:
method: "ApierV1.AddBalance"
params:
- Tenant: "{{ crm_config.ocs.ocsTenant }}"
Account: "{{ service_uuid }}"
BalanceType: "*monetary"
Balance:
Value: 0
ExpiryTime: "+8760h" # 1 year

6. 服务创建

- name: Create service record in CRM
uri:
url: "{{ crm_config.crm.base_url }}/crm/service/"
method: PUT
headers:
Authorization: "Bearer {{ access_token }}"
body_format: json
body:
customer_id: "{{ customer_id }}"
product_id: "{{ product_id }}"
service_name: "Mobile Service - {{ phone_number }}"
service_uuid: "{{ service_uuid }}"
service_status: "Active"
retail_cost: "{{ monthly_cost }}"
status_code: 200
register: service_creation_response

- name: Extract service_id
set_fact:
service_id: "{{ service_creation_response.json.service_id }}"

7. 库存分配

- name: Assign SIM card to customer and service
uri:
url: "{{ crm_config.crm.base_url }}/crm/inventory/inventory_id/{{ inventory_id_sim_card }}"
method: PATCH
headers:
Authorization: "Bearer {{ access_token }}"
body_format: json
body:
customer_id: "{{ customer_id }}"
service_id: "{{ service_id }}"
item_state: "Assigned"
sold_date: "{{ ansible_date_time.iso8601 }}"

- name: Assign phone number to customer and service
uri:
url: "{{ crm_config.crm.base_url }}/crm/inventory/inventory_id/{{ inventory_id_phone_number }}"
method: PATCH
headers:
Authorization: "Bearer {{ access_token }}"
body_format: json
body:
customer_id: "{{ customer_id }}"
service_id: "{{ service_id }}"
item_state: "Assigned"
sold_date: "{{ ansible_date_time.iso8601 }}"

取消配置和回滚

同一剧本处理失败的配置回滚和有意的取消配置,使用Ansible的rescue块。

有关块/恢复模式的详细说明以及为什么这是最佳实践,请参见配置系统 - 回滚和清理:最佳实践模式

rescue:
# 此部分在以下情况下运行:
# 1. 块中的任何任务失败(自动回滚)
# 2. action == "deprovision"(有意清理)

- name: Get inventory items linked to service
uri:
url: "{{ crm_config.crm.base_url }}/crm/inventory/customer_id/{{ customer_id }}"
method: GET
headers:
Authorization: "Bearer {{ access_token }}"
register: inventory_items
ignore_errors: true

- name: Return inventory to available pool
uri:
url: "{{ crm_config.crm.base_url }}/crm/inventory/inventory_id/{{ item.inventory_id }}"
method: PATCH
headers:
Authorization: "Bearer {{ access_token }}"
body_format: json
body:
customer_id: null
service_id: null
item_state: "Used"
loop: "{{ inventory_items.json.data }}"
when: inventory_items.json.data is defined
ignore_errors: true

- name: Delete OCS account
uri:
url: "{{ crm_config.ocs.ocsApi }}/jsonrpc"
method: POST
body_format: json
body:
method: "ApierV1.RemoveAccount"
params:
- Tenant: "{{ crm_config.ocs.ocsTenant }}"
Account: "{{ service_uuid }}"
ignore_errors: true

- name: Delete or disable HSS subscriber
uri:
url: "{{ item.key }}/subscriber/{{ item.value.subscriber_id }}"
method: DELETE
loop: "{{ hss_subscriber_data | dict2items }}"
when: deprovision_subscriber | default(false) | bool
ignore_errors: true

# 最终断��确定成功或失败
- name: Determine if this was intentional deprovision or failed provision
assert:
that:
- action == "deprovision"
fail_msg: "Provisioning failed and rollback completed"
success_msg: "Deprovisioning completed successfully"

关键点:

  • 所有清理任务上的ignore_errors: true确保所有清理尝试都被执行
  • 如果action == "deprovision",则断言通过(状态0 = 成功)
  • 如果未设置action,则断言失败(状态2 = 失败的配置)
  • 这确保原子操作:要么完全配置,要么完全清理

其他与SIM相关的剧本

本地开发剧本: play_local_mobile_sim.yaml - 跳过外部HSS/OCS依赖关系的开发版本

MSISDN交换: play_swap_msisdn.yaml - 在现有服务之间交换电话号码

充值/充值剧本:

  • play_topup_monetary.yaml - 添加货币余额
  • play_topup_no_charge.yaml - 添加免费数据/分钟
  • play_topup_dongle.yaml - 为物联网/数据设备充值

HSS和IMS集成

OmniCRM与PyHSS(开源家庭用户服务器)集成,以进行用户管理和认证。

HSS功能

文件: /OmniCRM-API/Provisioners/hss.py

关键功能:

def ProvisionMobileSIM(json_data, hss_urls):
"""
Provisions a mobile SIM to HSS and IMS

Args:
json_data: Dict with IMSI, MSISDN, authentication credentials
hss_urls: List of HSS peer URLs

Returns:
Dict with subscriber IDs from each HSS peer
"""
# Create HSS subscriber (for data)
# Create IMS subscriber (for voice)
# Return subscriber IDs for tracking

def get_subscriber_info_msisdn(msisdn, hss_urls):
"""
Retrieves subscriber details by phone number

Args:
msisdn: Phone number to lookup
hss_urls: List of HSS peer URLs

Returns:
Subscriber information from HSS
"""

多HSS支持

OmniCRM支持向多个HSS对等体进行配置以实现冗余:

# crm_config.yaml
hss:
hss_peers:
- "http://10.12.64.140:8080"
- "http://10.12.64.141:8080"
apn_list: "1,2,3"
default_apn: 1

配置在所有配置的HSS实例之间进行,以确保:

  • 地理冗余
  • 负载均衡
  • 故障转移能力
  • 同步的用户数据

认证中心(AuC)

AuC存储用户认证凭据。在配置期间,仅检索auc_id

获取IMSI的AuC ID:

GET /auc/imsi/{imsi}

响应:

{
"auc_id": 12345,
"imsi": "310120123456789",
"iccid": "8961234567890123456",
"esim": false,
"lpa": null
}

对于eSIM,esim标志为truelpa包含激活代码以供客户显���。

注意: 实际的认证凭据(Ki、OPC、PIN、PUK)保留在HSS中,永远不会通过API暴露。配置系统只需要auc_id来引用这些凭据以创建用户。

OCS集成

在线计费系统(OCS)处理移动服务的实时计费和计费。OmniCRM使用CGRateS作为OCS平台。

OCS配置

文件: /OmniCRM-API/Provisioners/ocs.py

配置:

# crm_config.yaml
ocs:
cgrates: "10.64.12.160:2080"
ocsTenant: "mnc380.mcc313.3gppnetwork.org"
ocsApi: "http://10.64.12.160:2080"

SIM服务的OCS组件

1. 过滤器 - 用户识别

过滤器通过网络请求识别用户:

{
"ID": "FILTER_IMSI_Service_abc123",
"Type": "*string",
"Element": "~*req.OriginHost",
"Values": ["310120123456789"]
}

2. 属性 - 账户映射

属性将IMSI映射到计费账户:

{
"ID": "ATTR_ACCOUNT_Service_abc123",
"FilterIDs": ["FILTER_IMSI_Service_abc123"],
"Attributes": [
{
"Path": "*req.Account",
"Value": "Service_abc123"
},
{
"Path": "*req.Bandwidth",
"Value": "100000000" // 100 Mbps
}
]
}

3. 资源 - 会话限制

资源控制并发会话限制:

{
"ID": "RES_SESSIONS_Service_abc123",
"FilterIDs": ["FILTER_IMSI_Service_abc123"],
"Limit": 5, // 最大5个并发会话
"UsageTTL": "-1"
}

4. 统计 - 使用跟踪

统计队列跟踪使用情况以进行分析:

{
"ID": "STATS_Service_abc123",
"FilterIDs": ["FILTER_IMSI_Service_abc123"],
"Metrics": [
"*sum#~*req.Usage",
"*tcc" // 总通话次数
]
}

5. 行动计划 - 定期收费

行动计划处理每月收费等重复操作:

{
"Id": "ActionPlan_Service_abc123_Monthly",
"ActionsId": "Action_Add_Monthly_Data",
"Timing": {
"MonthDays": [1], // 每月1日
"Time": "00:00:00Z"
}
}

OCS API功能

ocs.py中的关键功能:

async def async_get_balance(account, server, tenant):
"""获取当前账户余额"""

async def async_topup_dongle(tenant, account, server, days):
"""添加基于时间的数据配额"""

def Get_Account_Status(account, server, tenant):
"""获取全面的账户信息"""

def CGRateS_API_Call(method, params, server, tenant):
"""通用CGRateS JSON-RPC API调用"""

SIM库存对账

OmniCRM包括工具以确保CRM库存与HSS之间的一致性。

对账脚本

文件: /OmniCRM-API/Provisioners/hss_reconcile.py

目的:

  • 识别HSS中但不在库存中的SIM(孤立的)
  • 查找未在HSS中配置的分配SIM
  • 验证MSISDN格式
  • 生成HTML报告

执行的检查:

  1. HSS中的已售SIM:

    • 从库存查询所有分配的SIM
    • 检查每个IMSI是否存在于HSS中
    • 报告分配给客户但不在HSS中的SIM
  2. 库存中的已配置SIM:

    • 从HSS查询所有用户
    • 检查每个IMSI是否存在于库存中
    • 报告孤立的HSS条目
  3. MSISDN验证:

    • 验证电话号码格式
    • 检查是否符合澳大利亚号码标准
    • 报告格式错误的号码

运行对账:

python hss_reconcile.py

输出:

  • 带有颜色编码结果的HTML报告
  • 不匹配项的列表以供调查
  • 清理建议

最佳实践:

  • 每周运行对账
  • 及时调查差异
  • 使用报告识别配置失败
  • 清理孤立条目以释放资源

SIM配置故障排除

常见问题及解决方案

问题:SIM配置失败,显示“IMSI已存在于HSS”

  • 原因: IMSI已在先前尝试中配置
  • 解决方案: 检查HSS中是否存在现有用户,采取以下措施:
    • 如果孤立,删除现有用户
    • 更新现有用户,而不是创建新用户
    • 运行对账以识别冲突

问题:eSIM二维码未生成

  • 原因: management_url字段未填充LPA代码
  • 解决方案: 确保eSIM导入脚本正确设置management_url
  • 格式: 必须为LPA:1$server$activation-code

问题:库存项目显示为“可用”,但已分配

  • 原因: 配置在库存分配之前失败
  • 解决方案: 检查配置事件以获取失败原因
  • 修复: 完成配置或将库存返回池中

问题:客户无法拨打电话(数据正常)

  • 原因: IMS用户未配置
  • 解决方案: 检查IMS配置步骤是否完成
  • 修复: 手动配置IMS用户或重新运行剧本

问题:认证失败(SIM无法连接)

  • 原因: SIM卡和HSS之间的Ki/OPC不匹配
  • 解决方案: 验证AuC凭据是否与物理SIM匹配
  • 修复: 使用正确的凭据更新HSS AuC

问题:自助注册未能分配SIM

  • 原因: 库存中没有可用的SIM
  • 解决方案: 检查库存中状态为“新”的SIM和customer_id为NULL的SIM
  • 修复: 导入新的SIM批次或将返回的SIM标记为“已使用”

调试提示

1. 检查配置事件:

GET /crm/provision/provision_id/{id}

查找失败的任务(状态2)并查看错误消息。

2. 验证HSS用户:

GET {hss_url}/subscriber/msisdn/{phone_number}

检查用户是否存在并具有正确的配置。

3. 检查OCS账户:

{
"method": "ApierV2.GetAccount",
"params": [
{
"Tenant": "mnc380.mcc313.3gppnetwork.org",
"Account": "Service_abc123"
}
]
}

4. 查询库存状态:

GET /crm/inventory/?filters={"itemtext2":["310120123456789"]}

通过IMSI搜索以查找库存项目并检查其状态。

5. 查看Ansible日志:

检查/var/log/ansible/或配置事件详细信息以获取完整的剧本输出。

最佳实践

SIM库存管理

  1. 批量导入: 使用导入脚本处理大型SIM批次
  2. 定期对账: 每周运行HSS/库存对账
  3. 状态管理: 保持库存状态准确(新、已分配、已使用)
  4. 供应商跟踪: 记录SIM供应商和批次信息
  5. 成本跟踪: 维护准确的批发/零售成本

安全性

  1. 凭据分离: 身份验证凭据(Ki/OPC)仅保留在HSS中,永远不在CRM中
  2. 访问控制: 限制谁可以分配/修改SIM库存
  3. 审计日志: 跟踪所有SIM分配和更改
  4. PIN/PUK管理: PIN/PUK代码存储在HSS中,通过安全渠道提供给客户
  5. 丢失/被盗流程: 立即在HSS中暂停服务并标记库存状���

配置

  1. 验证: 始终验证IMSI格式和唯一性
  2. 回滚: 使用块/恢复模式在失败时进行自动清理
  3. 通知: 向客户发送明确的激活说明
  4. 测试: 在生产之前测试配置
  5. 监控: 跟踪配置成功率和失败原因

eSIM特定

  1. LPA格式: 在存储之前验证LPA代码格式
  2. 二维码测试: 在多台设备上测试二维码
  3. 激活说明: 提供清晰的逐步指南
  4. 配置文件管理: 跟踪eSIM配置文件的下载和安装
  5. 备份代码: 在二维码扫描失败的情况下提供手动LPA代码

相关文档