TeamsACS 对于 BootStrap Event 的处理逻辑

文章目录

    实际上类似于 TeamsACS 对于 PERIODIC Event 的处理逻辑 的处理逻辑,
    只不过多了一个 UpdateManagementAuthInfo 的处理流程。

    UpdateManagementAuthInfo 的调用参数

    err = cpe.UpdateManagementAuthInfo("bootstrap-session-"+common.UUID(), 1000, false)
    

    UpdateManagementAuthInfo 的定义

    主要逻辑就是由 ACS 向 CPE 发送 auth 用的 username / password。

    • username 直接使用了 sn. 并没有使用 tr069 协议规定的格式
    • password 的生成,需要看一下具体逻辑
      func (c *CwmpCpe) UpdateManagementAuthInfo(session string, timeout int, hp bool) error { return c.SendCwmpEventData(models.CwmpEventData{ Session: session, Sn: c.Sn, Message: &cwmp.SetParameterValues{ ID: session, Name: "", NoMore: 0, Params: map[string]cwmp.ValueStruct{ "Device.ManagementServer.ConnectionRequestUsername": { Type: "xsd:string", Value: c.Sn, }, "Device.ManagementServer.ConnectionRequestPassword": { Type: "xsd:string", Value: app.GetTr069SettingsStringValue("CpeConnectionRequestPassword"), }, }, }, }, timeout, hp) }

    SendCwmpEventData

    // SendCwmpEventData 发送一个 Cwmp 事件,
    func (c *CwmpCpe) SendCwmpEventData(data models.CwmpEventData, timeoutMsec int, hp bool) error {
    	select {
    	case c.getQueue(hp) <- data:
    		return nil
    	case <-time.After(time.Millisecond * time.Duration(timeoutMsec)):
    		return errors.New("cwmp event channel full, write timeout")
    	}
    }
    
    func (c *CwmpCpe) getQueue(hp bool) chan models.CwmpEventData {
    	var que = c.cwmpQueueMap
    	if hp {
    		que = c.cwmpHPQueueMap
    	}
    	return que
    }
    
    type CwmpCpe struct {
    	Sn              string `json:"sn"`
    	OUI             string `json:"oui"`
    	taskTags        []string
    	SoftwareVersion string `json:"software_version"`
    	Manufacturer    string `json:"manufacturer"`
    	ProductClass    string `json:"product_class"`
    	cwmpQueueMap    chan models.CwmpEventData
    	cwmpHPQueueMap  chan models.CwmpEventData
    	LastInform      *cwmp.Inform `json:"latest_message"`
    	LastUpdate      time.Time    `json:"last_update"`
    	LastDataNotify  time.Time    `json:"last_data_notify"`
    	IsRegister      bool         `json:"is_register"`
    }
    
    // RecvCwmpEventData 接收一个 Cwmp 事件
    func (c *CwmpCpe) RecvCwmpEventData(timeoutMsec int, hp bool) (data *models.CwmpEventData, err error) {
    	select {
    	case _data := <-c.getQueue(hp):
    		return &_data, nil
    	case <-time.After(time.Millisecond * time.Duration(timeoutMsec)):
    		return nil, errors.New("read cwmp event channel timeout")
    	}
    }
    
    

    消息接收/处理

    至于调用 RecvCwmpEventData 则是在 tr069/handlers.go 中

    // 当 CPE 发送空消息时检测 CPE任务队列
    lastestSn := s.GetLatestCookieSn(c)
    if lastestSn == "" {
    	return noContentResp(c)
    }
    
    cpe := app.GApp().CwmpTable().GetCwmpCpe(lastestSn)
    
    // 首先处理预设任务
    ptask, err := cpe.GetLatestCwmpPresetTask()
    if err == nil && ptask != nil && len(ptask.Request) > 0 {
    	return xmlCwmpMessage(c, []byte(ptask.Request))
    }
    
    // 获取队列任务
    msg, err := cpe.RecvCwmpEventData(1000, true)
    if err != nil {
    	msg, _ = cpe.RecvCwmpEventData(1000, false)
    }
    
    if msg != nil {
    	if msg.Session != "" {
    		events.PubEventCwmpSuperviseStatus(lastestSn, msg.Session, "info",
    			fmt.Sprintf("Send Cwmp %s Message %s", msg.Message.GetName(), common.ToJson(msg.Message)))
    	}
    	return xmlCwmpMessage(c, msg.Message.CreateXML())
    }
    

    密码的生成

    // GetTr069SettingsStringValue Get tr069 settings string value
    func (a *Application) GetTr069SettingsStringValue(name string) string {
    	return a.GetSettingsStringValue("tr069", name)
    }
    

    搜索 CpeConnectionRequestPassword 来看看有哪些地方会生成密码:

    > grep CpeConnectionRequestPassword -r ./
    ./app/constant.go:      ConfigCpeConnectionRequestPassword = "CpeConnectionRequestPassword"
    ./app/constant.go:      ConfigCpeConnectionRequestPassword,
    ./app/cwmp.go:                                  Value: app.GetTr069SettingsStringValue("CpeConnectionRequestPassword"),
    ./app/cwmp.go:          ConfigCpeConnectionRequestPassword: a.GetTr069SettingsStringValue(ConfigCpeConnectionRequestPassword),
    ./app/initdb.go:                case ConfigCpeConnectionRequestPassword:
    ./app/initdb.go:                        checkConfig(sortid, "tr069", ConfigCpeConnectionRequestPassword, "teamsacscpepassword", "CPE Connection authentication password, It is provided to TeamsACS to access CPE")
    ./assets/static/views/settings.js:                        name: "CpeConnectionRequestPassword",
    ./assets/static/views/settings.min.js:name:"TR069AccessPassword",labelPosition:"top",label:tr("settings","TR069 access password"),bottomLabel:tr("settings","Teamsacs TR069 access password, It is provided to CPE to access TeamsACS")},{view:"text",name:"CpeConnectionRequestPassword",labelPosition:"top",label:tr("settings","CPE Connection authentication password"),bottomLabel:tr("settings","tr069 The authentication password used when the server connects to cpe")},{view:"template",css:"form-desc",height:200,borderless:!0,src:"/admin/settings/tr069/quickset"}]}]}};
    ./controllers/supervise/cwmp.go:        isok, err := cwmp.ConnectionRequestAuth(dev.Sn, app.GApp().GetTr069SettingsStringValue("CpeConnectionRequestPassword"), dev.CwmpUrl)
    

    疑问

    但是此处有几个疑问:

    • 为何只有当 ACS 收到空消息时才检查消息队列。为何不是立即/异步处理
    • 这里使用 golang channel 的好处是什么?相对于普通的消息队列服务
    • 密码的生成机制是啥

    关于作者 🌱

    我是来自山东烟台的一名开发者,有感兴趣的话题,或者软件开发需求,欢迎加微信 zhongwei 聊聊,或者关注我的个人公众号“大象工具”, 查看更多联系方式