[asterisk-dev] PJSIP Device Feature Key Synchronization architecture
Joshua C. Colp
jcolp at sangoma.com
Wed Nov 9 09:55:01 CST 2022
On Wed, Nov 9, 2022 at 11:38 AM <asterisk at phreaknet.org> wrote:
> On 10/21/2022 7:53 AM, Joshua C. Colp wrote:
> > On Fri, Oct 21, 2022 at 8:29 AM <asterisk at phreaknet.org
> > <mailto:asterisk at phreaknet.org>> wrote:
> >
> > On 10/21/2022 5:34 AM, Joshua C. Colp wrote:
> > > On Thu, Oct 20, 2022 at 8:23 PM <asterisk at phreaknet.org
> > <mailto:asterisk at phreaknet.org>
> > > <mailto:asterisk at phreaknet.org <mailto:asterisk at phreaknet.org>>>
> > wrote:
> > >
> > > Hi, all,
> > >
> > > Something I have been working on recently is adding
> > support to
> > > PJSIP for device feature key synchronization (the
> > as-feature-event
> > > Broadworks spec that many common IP phones, e.g. Polycom
> > support) to
> > > control server-side features from endpoints. It's using the
> > PJSIP
> > > pub/sub capabilities; I had to add the ability to execute a
> > custom
> > > module callback when a SUBSCRIBE is refreshed, but with that
> > > addition,
> > > it works as it's supposed to.
> > >
> > >
> > > You should further elaborate on all of the server-side features you
> > > expect to implement, if it extends beyond DND.
> >
> > The other one is Call Forwarding (Always/Busy/No Answer, with
> > number of
> > rings)
> >
> > >
> > > I wanted to solicit some input on what an ideal way of
> > triggering
> > > updates should be. Currently we have hints, which actually work
> > > reasonably all right for Do Not Disturb, which is simply a
> > boolean
> > > on/off, easily represented with a hint and custom device
> > state for
> > > DND.
> > > The PJSIP module emits an AMI event, the user can process it
> and
> > > change
> > > the device state if needed, which will trigger a NOTIFY to
> > go out
> > > to the
> > > endpoint.
> > >
> > >
> > > What user? An outside AMI application? An internal consumer in
> > Asterisk?
> >
> > The administrator of the Asterisk system, who can add AMI logic to
> > receive the event and then do something with it.
> > The reason this is needed is the phone isn't turning DND on directly,
> > for example. It's merely a request. The server can decide to not
> > allow
> > it, for example if that phone isn't allowed to toggle DND. The server
> > will process it, and send it the updated status. This usually
> > reflects
> > what the phone wanted, but not necessarily.
> > (I've elaborated on this more below)
> >
> >
> > Okay, that's not the administrator of an Asterisk system. That's a
> > developer using Asterisk who may be the administrator. Those are two
> > separate things. As soon as you bring AMI into the mix then it's
> > making a solution using Asterisk. That's not a wrong thing, but it's
> > important to be clear in the audience. Many average users of Asterisk
> > doesn't know or care about AMI, 'nor do they code for it. They may use
> > additional solutions that utilize AMI but they themselves haven't the
> > foggiest of the details.
> >
> >
> > >
> > > This sort of came up about 12 years ago[1]. The actual SIP
> stuff
> > > is not
> > > complicated; it's the user interface to it that requires more
> > > thought.
> > > For call forwarding, there are more moving pieces and abusing
> > > hints/custom device state for that is super clunky. You can't
> > > communicate the call forwarding target, # of rings, etc. in
> > a device
> > > state, so additional hints are then needed for that. It
> > works but
> > > it's
> > > super clunky and I don't think this is a great pipeline.
> > >
> > >
> > > Okay, so this covers call forwarding as well.
> > Yes.
> > >
> > >
> > > I'm wondering if people have thoughts on what an ideal
> mechanism
> > > would
> > > be for users, once they process a request to enable/disable
> > a feature
> > > from the phone, to communicate that to the PJSIP module. The
> > problem
> > > with abusing hints, especially for call forwarding, is it's
> > not a
> > > good
> > > way to communicate details into the module. One option
> > perhaps is to
> > > have dialplan extensions, setup in a manner similar to use with
> > > EVAL_EXTEN, where it returns the current value needed, as any
> > > relevant
> > > function, DB, ODBC, CURL, custom function, etc. could be used
> to
> > > retrieve the current feature value. The clunky part is more
> > > signaling to
> > > the PJSIP module that it needs to send the phone the updated
> > > status (by
> > > checking those extensions, for example). The device state
> > callback
> > > happens to be convenient for this kind of signaling but not
> > really
> > > appropriate here. It would be better to push the info into
> > the module
> > > directly rather than the signaling it and making it retrieve
> the
> > > updated
> > > data in some arbitrary way.
> > >
> > > So with this in mind, I'm currently leaning in the direction
> > of a
> > > dialplan function/AMI action that could be used to set the
> > > appropriate
> > > info for a subscription, which would trigger the NOTIFY, and
> > then
> > > nothing would actually need to be added to the dialplan at all
> > > (inasmuch
> > > as hints and things of that nature). One d I think starting
> > purely
> > > from that perspective
> > >
> > > isadvantage of this is that
> > > for every single update, unlike callbacks, we have to
> > traverse the
> > > entire list of subscriptions (though maybe that's not a big
> > deal).
> > > The
> > > bigger problem is this is push only and the PJSIP module still
> > > needs to
> > > be able to "pull" feature statuses on demand, which is where
> the
> > > hint/lookup model is useful. A potential middle ground
> > solution is
> > > use
> > > the dialplan function/AMI action to push only, but cache all of
> > > this in
> > > AstDB (as subscriptions themselves are), so that we can
> > retrieve the
> > > latest/most current value at any point if needed. Then we don't
> > > need to
> > > be concerned at all with how the user is managing state as
> > that is
> > > fully
> > > decoupled, although obviously this would lead to a little
> > > duplication/redundancy. Any thoughts?
> > >
> > >
> > > You've thrown a lot of lower level implementation details at us
> > and to
> > > be quite honest it's overwhelming. There's no full user facing
> > > examples of how it would all work, beyond the bits and pieces in
> > your
> > > text that we'd then have to deduce and after reading a few times it
> > > doesn't feel very friendly. To start off with: Is this a
> > developer API
> > > and interface, or is this also meant for the common everyday
> > user? I
> > > would hope it's for the common everyday user, in which case it
> > should
> > > be approached from that perspective first with implementation
> > details
> > > following.
> >
> > Yes, it's for the common every day end user. A callback mechanism if
> > used would be more of a development one but that would be more a
> > means
> > to an end.
> >
> > Here's an example of what I have in my dialplan right now, in the
> > subscribe_context for the endpoint:
> > exten => dnd_Polycom5,hint,Custom:${EXTEN}
> > exten => callfwd_Polycom5,hint,Custom:${EXTEN}
> > exten => callfwdalways_Polycom5,1,${FOOBAR(callforward,2135)}
> > exten => callfwdbusy_Polycom5,1,${FOOBAR(callforwardbusy,2135)}
> > exten =>
> > callfwdnoanswer_Polycom5,1,${FOOBAR(callforwardnoanswer,2135)}
> >
> > The user gets the AMI event, processes it with whatever processing is
> > needed (e.g. checking that the DND feature is available for that
> > line,
> > setting it in AstDB, ODBC, or whatever is the source of truth for
> > feature statuses), and then updates the relevant hints above.
> >
> > The module is currently hardcoded to use these extensions in the
> > subscribe context: the prefix + the endpoint name. Obviously
> > that's also
> > inflexible.
> > Right now, the first 2 extensions the user will set to signal the
> > module
> > to send an updated NOTIFY. The first hint by virtue of being binary
> > contains the DND status itself, and the 3 bottom extensions are
> > needed
> > to retrieve the call forwarding numbers from the source of truth for
> > these features. (Here, FOOBAR is a custom - but any arbitrary -
> > dialplan
> > function I have that retrieves the status).
> >
> > I bring this up only to show the current implementation and how
> > hacky it
> > is; I don't like this at all or think it's appropriate (except for
> > DND,
> > possibly). It was more a proof of concept of testing the
> > underlying SIP
> > technology.
> >
> > A better implementation might look like:
> >
> > User gets the AMI event and processes it as usual, and then simply
> > does
> > Set(PJSIP_DEVICE_FEATURE_STATE(PJSIP/Polycom1,donotdisturb)=enabled)
> > or
> >
> Set(PJSIP_DEVICE_FEATURE_STATE(PJSIP/Polycom1,callforwardingnoanswer)=8675309,4)
> >
> > (forward on no answer to 8675309 after 4 rings).
> >
> > Internally, PJSIP_DEVICE_FEATURE_STATE could also persist input to
> > AstDB
> > so it's available to the module.
> >
> > > As an everyday user I'd expect not to have to deal with AMI or
> > complex
> > > dialplan. I'd expect to be able to set and get the information from
> > > the dialplan using dialplan functions (or a single function) so I
> > > could use that in the dialplan, and have it "just work" with my
> > phones
> > > that support the feature. I'd expect it to persist across Asterisk
> > > restarts. For example ${EXTENSION_DND(alice)} for retrieving DND
> > > status of Alice, and if Asterisk restarted then that should stay
> > the same.
> >
> > That's a good point. The problem is that means that Asterisk
> > internally
> > is the source of truth of DND, and that may not necessarily be what
> > people want. For instance, that wouldn't meet my own requirements.
> > The
> > way that Broadworks works is the phone is merely requesting a certain
> > disposition, but the server isn't under any obligation to carry it
> > out.
> > So I think there needs to be some mechanism for the user to be
> > involved
> > in that pipeline, to be able to deny something that a phone wants.
> > Maybe
> > the user doesn't have DND, maybe certain phones aren't allowed to
> > toggle, whatever. Lots of people store their feature states in MySQL
> > databases and use them for Asterisk clusters. Some systems might have
> > specific requirements for that. So users should have flexibility to
> > reject it. On some systems, maybe the same DND status is used for
> > several lines and Asterisk internally would not have any idea
> > about this.
> > There are lots of different scenarios that are beyond what I think
> > Asterisk itself should handle, hence the "two-part" process described
> > above: the user (system admin) can do whatever needs to be done, and
> > then just tell PJSIP what the new state is. PJSIP doesn't need to
> > know
> > or care about where feature states are actually stored or what the
> > logic
> > is or how they are mapped to endpoints.
> >
> > I do see your point though and I think it would be nice to have a
> > "simple, default mode" where Asterisk will internally just "approve
> > everything" that the phone wants, and users can use that if that
> > suits
> > their needs, but fundamentally I think users should be able to be
> > involved in the decision pipeline if they want/need to. I'm not
> > yet sure
> > what that would look like: maybe a pjsip.conf option to emit AMI
> > events
> > rather than auto-handling them? And then the
> > PJSIP_DEVICE_FEATURE_STATE
> > function would have to be used to tell PJSIP what to do, and Asterisk
> > itself would not be the source of truth for feature statuses in this
> > case (but it would cache them as described above) (though in this
> > case
> > reading the PJSIP_DEVICE_FEATURE_STATE could still return the cached
> > disposition)
> >
> >
> > Okay, YOUR usage is based on an API interface for developers to allow
> > external source of truth and logic. That's what it is. My response did
> > not approach it from that perspective.
> >
> > I don't have thoughts on that aspect currently. It requires more time.
>
> The new architecture proposed (not using hints) has been working pretty
> well for a few weeks. I'm using it in the more "developer" mode but the
> other mode exists as well. The last hurdle is being able to send a
> multipart XML response when necessary, since if more than one feature
> needs to be sent to the device, the Broadworks spec outlines a multipart
> response devices will accept.
>
> Right now I'm using a body_generator module in order to generate the
> actual content for the NOTIFY, similar to how res_pjsip_exten_state and
> res_pjsip_mwi do things. However the body generator only gets a pointer
> to an ast_str, so there's no way for it to do anything PJSIP related
> like multipart. I was thinking that one elegant option would be to have
> a separate body generator for each of the features, and then
> res_pjsip_pubsub would handle creating the multipart body.
>
> There is some stuff in res_pjsip_pubsub currently to handle multipart
> subscriptions, for subscriptions that have "children". The individual
> features are not "child subscriptions" in the right sense, but if they
> were, then it might work within the constraints of the pubsub interface.
> However, I know you suggested it would be preferable to handle any
> multipart stuff in the module itself using the PJSIP multipart APIs, as
> opposed to changing anything in res_pjsip_pubsub.
>
> Looking at the existing support for multipart in res_pjsip_pubsub, it
> seems this is limited to subscriptions as discussed in RFC 4662, which
> require a "Supported: eventlist" header in them, even if you define a
> resource list in pjsip.conf. The Broadworks device feature sync
> subscriptions do not include such a header in the SUBSCRIBE. So, the
> subscription in question seems to be a single, childless subscription,
> that nonetheless needs to send a multipart response, potentially.
>
> So, a couple questions, assuming my understanding of the above is correct:
>
> * Since it doesn't appear that res_pjsip_pubsub currently supports
> multipart in the way that's needed here, do you think there's any
> approach we could take that retains being able to use body generator
> modules? I don't see how we can, unless res_pjsip_pubsub itself
> supported it. The support it has is close, but not really what it
> would need to be. Would it be reasonable to extend such
> functionality to support this kind of subscription? i.e. not require
> "Supported: eventlist" in order to be considered a resource list?
> * Alternately, if we didn't want to modify res_pjsip_pubsub to support
> this unconventional usage, perhaps the module itself could directly
> add some "dummy" child subscriptions to itself, one for each
> feature, so that one body generator could then be used for each of
> them. ast_sip_subscription is an opaque structure though, so this
> would require breaking some abstraction and using the internals of
> ast_sip_subscription in the module, in order to be able to add
> children to it.
> * Assuming we don't want to do anything with pubsub children, would it
> be fine/preferred to ditch the body generator altogether and do
> everything in a single module? It just feels a bit clunkier this
> way, since it'll likely duplicate a lot of what res_pjsip_pubsub is
> doing internally for building NOTIFYs, in fact this would likely
> require bypassing much of what res_pjsip_pubsub does in order to
> generate the entire response itself.
> * Is it even worth trying to support multipart responses for non
> resource list subscriptions? Would sending up to 4 NOTIFYs instead
> of a single multipart one be an "acceptable kludge", given the
> requirements of res_pjsip_pubsub?
>
> All of the approaches listed seem a bit hacky to me, though in different
> ways. Wondering if you think a particular approach is more promising
> than the others...
>
I don't have any further input, and it is unlikely I will have any for
quite some time. I'm also not that familiar with the res_pjsip_pubsub RLS
implementation which is what you are referring to and haven't looked at the
underlying specification for your Broadsoft stuff. Someone else may have
input.
--
Joshua C. Colp
Asterisk Project Lead
Sangoma Technologies
Check us out at www.sangoma.com and www.asterisk.org
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.digium.com/pipermail/asterisk-dev/attachments/20221109/e9182955/attachment-0001.html>
More information about the asterisk-dev
mailing list