res_pjsip: Add AMI events for chan_pjsip contact lifecycle changes
Add a new ContactStatus AMI event. Publish the following status/state changes: Created Removed Reachable Unreachable Unknown Contact URI, new status/state, aor and endpoint names, and the last qualify rtt result are included in the event. ASTERISK-25114 #close Change-Id: Id25aae5f7122facba183273efb3e8f36c20fb61e Reported-by: George Joseph <george.joseph@fairview5.com> Tested-by: George Joseph <george.joseph@fairview5.com>
This commit is contained in:
parent
3a8eb3c476
commit
b8ac683822
8
CHANGES
8
CHANGES
|
@ -177,6 +177,14 @@ cdr_adaptive_odbc
|
|||
names. This setting is configurable for cdr_adaptive_odbc via the
|
||||
quoted_identifiers in configuration file cdr_adaptive_odbc.conf.
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
--- Functionality changes from Asterisk 13.4.0 to Asterisk 13.5.0 ------------
|
||||
------------------------------------------------------------------------------
|
||||
|
||||
AMI
|
||||
------------------
|
||||
* A new ContactStatus event has been added that reflects res_pjsip contact
|
||||
lifecycle changes: Created, Removed, Reachable, Unreachable, Unknown.
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
--- Functionality changes from Asterisk 13.3.0 to Asterisk 13.4.0 ------------
|
||||
|
|
|
@ -178,7 +178,9 @@ struct ast_sip_contact {
|
|||
enum ast_sip_contact_status_type {
|
||||
UNAVAILABLE,
|
||||
AVAILABLE,
|
||||
UNKNOWN
|
||||
UNKNOWN,
|
||||
CREATED,
|
||||
REMOVED,
|
||||
};
|
||||
|
||||
/*!
|
||||
|
|
|
@ -118,6 +118,12 @@ void ast_endpoint_blob_publish(struct ast_endpoint *endpoint, struct stasis_mess
|
|||
*/
|
||||
struct stasis_message_type *ast_endpoint_state_type(void);
|
||||
|
||||
/*!
|
||||
* \brief Message type for endpoint contact state changes.
|
||||
* \since 13.5
|
||||
*/
|
||||
struct stasis_message_type *ast_endpoint_contact_state_type(void);
|
||||
|
||||
/*!
|
||||
* \brief Message type for \ref ast_endpoint_snapshot.
|
||||
* \since 12
|
||||
|
|
|
@ -75,6 +75,7 @@ int manager_endpoints_init(void)
|
|||
}
|
||||
|
||||
ret |= stasis_message_router_add(endpoint_router, ast_endpoint_state_type(), endpoint_state_cb, NULL);
|
||||
ret |= stasis_message_router_add(endpoint_router, ast_endpoint_contact_state_type(), endpoint_state_cb, NULL);
|
||||
|
||||
/* If somehow we failed to add any routes, just shut down the whole
|
||||
* thing and fail it.
|
||||
|
|
|
@ -71,6 +71,35 @@ ASTERISK_REGISTER_FILE()
|
|||
</syntax>
|
||||
</managerEventInstance>
|
||||
</managerEvent>
|
||||
<managerEvent language="en_US" name="ContactStatus">
|
||||
<managerEventInstance class="EVENT_FLAG_SYSTEM">
|
||||
<synopsis>Raised when the state of a contact changes.</synopsis>
|
||||
<syntax>
|
||||
<parameter name="URI">
|
||||
<para>This contact's URI.</para>
|
||||
</parameter>
|
||||
<parameter name="ContactStatus">
|
||||
<para>New status of the contact.</para>
|
||||
<enumlist>
|
||||
<enum name="Unknown"/>
|
||||
<enum name="Unreachable"/>
|
||||
<enum name="Reachable"/>
|
||||
<enum name="Created"/>
|
||||
<enum name="Removed"/>
|
||||
</enumlist>
|
||||
</parameter>
|
||||
<parameter name="AOR">
|
||||
<para>The name of the associated aor.</para>
|
||||
</parameter>
|
||||
<parameter name="EndpointName">
|
||||
<para>The name of the associated endpoint.</para>
|
||||
</parameter>
|
||||
<parameter name="RoundtripUsec">
|
||||
<para>The RTT measured during the last qualify.</para>
|
||||
</parameter>
|
||||
</syntax>
|
||||
</managerEventInstance>
|
||||
</managerEvent>
|
||||
***/
|
||||
|
||||
static struct stasis_cp_all *endpoint_cache_all;
|
||||
|
@ -137,6 +166,46 @@ static struct ast_manager_event_blob *peerstatus_to_ami(struct stasis_message *m
|
|||
ast_str_buffer(peerstatus_event_string));
|
||||
}
|
||||
|
||||
static struct ast_manager_event_blob *contactstatus_to_ami(struct stasis_message *msg);
|
||||
|
||||
STASIS_MESSAGE_TYPE_DEFN(ast_endpoint_contact_state_type,
|
||||
.to_ami = contactstatus_to_ami,
|
||||
);
|
||||
|
||||
static struct ast_manager_event_blob *contactstatus_to_ami(struct stasis_message *msg)
|
||||
{
|
||||
struct ast_endpoint_blob *obj = stasis_message_data(msg);
|
||||
RAII_VAR(struct ast_str *, contactstatus_event_string, ast_str_create(64), ast_free);
|
||||
const char *value;
|
||||
|
||||
if (!(value = ast_json_string_get(ast_json_object_get(obj->blob, "uri")))) {
|
||||
return NULL;
|
||||
}
|
||||
ast_str_append(&contactstatus_event_string, 0, "URI: %s\r\n", value);
|
||||
|
||||
if (!(value = ast_json_string_get(ast_json_object_get(obj->blob, "contact_status")))) {
|
||||
return NULL;
|
||||
}
|
||||
ast_str_append(&contactstatus_event_string, 0, "ContactStatus: %s\r\n", value);
|
||||
|
||||
if (!(value = ast_json_string_get(ast_json_object_get(obj->blob, "aor")))) {
|
||||
return NULL;
|
||||
}
|
||||
ast_str_append(&contactstatus_event_string, 0, "AOR: %s\r\n", value);
|
||||
|
||||
if (!(value = ast_json_string_get(ast_json_object_get(obj->blob, "endpoint_name")))) {
|
||||
return NULL;
|
||||
}
|
||||
ast_str_append(&contactstatus_event_string, 0, "EndpointName: %s\r\n", value);
|
||||
|
||||
if ((value = ast_json_string_get(ast_json_object_get(obj->blob, "roundtrip_usec")))) {
|
||||
ast_str_append(&contactstatus_event_string, 0, "RoundtripUsec: %s\r\n", value);
|
||||
}
|
||||
|
||||
return ast_manager_event_blob_create(EVENT_FLAG_SYSTEM, "ContactStatus",
|
||||
"%s", ast_str_buffer(contactstatus_event_string));
|
||||
}
|
||||
|
||||
static void endpoint_blob_dtor(void *obj)
|
||||
{
|
||||
struct ast_endpoint_blob *event = obj;
|
||||
|
@ -294,6 +363,7 @@ static void endpoints_stasis_cleanup(void)
|
|||
{
|
||||
STASIS_MESSAGE_TYPE_CLEANUP(ast_endpoint_snapshot_type);
|
||||
STASIS_MESSAGE_TYPE_CLEANUP(ast_endpoint_state_type);
|
||||
STASIS_MESSAGE_TYPE_CLEANUP(ast_endpoint_contact_state_type);
|
||||
|
||||
ao2_cleanup(endpoint_cache_all);
|
||||
endpoint_cache_all = NULL;
|
||||
|
@ -312,6 +382,7 @@ int ast_endpoint_stasis_init(void)
|
|||
|
||||
res |= STASIS_MESSAGE_TYPE_INIT(ast_endpoint_snapshot_type);
|
||||
res |= STASIS_MESSAGE_TYPE_INIT(ast_endpoint_state_type);
|
||||
res |= STASIS_MESSAGE_TYPE_INIT(ast_endpoint_contact_state_type);
|
||||
|
||||
return res;
|
||||
}
|
||||
|
|
|
@ -56,22 +56,47 @@ static int persistent_endpoint_cmp(void *obj, void *arg, int flags)
|
|||
return !strcmp(ast_endpoint_get_resource(persistent1->endpoint), id) ? CMP_MATCH | CMP_STOP : 0;
|
||||
}
|
||||
|
||||
/*! \brief Structure for communicating contact status to
|
||||
* persistent_endpoint_update_state from the contact/contact_status
|
||||
* observers.
|
||||
*/
|
||||
struct sip_contact_status {
|
||||
char *uri;
|
||||
enum ast_sip_contact_status_type status;
|
||||
int64_t rtt;
|
||||
};
|
||||
|
||||
/*! \brief Callback function for changing the state of an endpoint */
|
||||
static int persistent_endpoint_update_state(void *obj, void *arg, int flags)
|
||||
static int persistent_endpoint_update_state(void *obj, void *arg, void *data, int flags)
|
||||
{
|
||||
struct sip_persistent_endpoint *persistent = obj;
|
||||
struct ast_endpoint *endpoint = persistent->endpoint;
|
||||
char *aor = arg;
|
||||
struct sip_contact_status *status = data;
|
||||
struct ao2_container *contacts;
|
||||
struct ast_json *blob;
|
||||
struct ao2_iterator i;
|
||||
struct ast_sip_contact *contact;
|
||||
enum ast_endpoint_state state = AST_ENDPOINT_OFFLINE;
|
||||
|
||||
if (!ast_strlen_zero(aor) && !strstr(persistent->aors, aor)) {
|
||||
return 0;
|
||||
}
|
||||
if (!ast_strlen_zero(aor)) {
|
||||
if (!strstr(persistent->aors, aor)) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (status) {
|
||||
char rtt[32];
|
||||
snprintf(rtt, 31, "%" PRId64, status->rtt);
|
||||
blob = ast_json_pack("{s: s, s: s, s: s, s: s, s: s}",
|
||||
"contact_status", ast_sip_get_contact_status_label(status->status),
|
||||
"aor", aor,
|
||||
"uri", status->uri,
|
||||
"roundtrip_usec", rtt,
|
||||
"endpoint_name", ast_endpoint_get_resource(endpoint));
|
||||
ast_endpoint_blob_publish(endpoint, ast_endpoint_contact_state_type(), blob);
|
||||
ast_json_unref(blob);
|
||||
}
|
||||
}
|
||||
/* Find all the contacts for this endpoint. If ANY are available,
|
||||
* mark the endpoint as ONLINE.
|
||||
*/
|
||||
|
@ -121,22 +146,28 @@ static int persistent_endpoint_update_state(void *obj, void *arg, int flags)
|
|||
/*! \brief Function called when stuff relating to a contact happens (created/deleted) */
|
||||
static void persistent_endpoint_contact_created_observer(const void *object)
|
||||
{
|
||||
char *id = ast_strdupa(ast_sorcery_object_get_id(object));
|
||||
const struct ast_sip_contact *contact = object;
|
||||
char *id = ast_strdupa(ast_sorcery_object_get_id(contact));
|
||||
char *aor = NULL;
|
||||
char *contact = NULL;
|
||||
char *contact_uri = NULL;
|
||||
struct sip_contact_status status;
|
||||
|
||||
aor = id;
|
||||
/* Dynamic contacts are delimited with ";@" and static ones with "@@" */
|
||||
if ((contact = strstr(id, ";@")) || (contact = strstr(id, "@@"))) {
|
||||
*contact = '\0';
|
||||
contact += 2;
|
||||
if ((contact_uri = strstr(id, ";@")) || (contact_uri = strstr(id, "@@"))) {
|
||||
*contact_uri = '\0';
|
||||
contact_uri += 2;
|
||||
} else {
|
||||
contact = id;
|
||||
contact_uri = id;
|
||||
}
|
||||
|
||||
ast_verb(1, "Contact %s/%s has been created\n", aor, contact);
|
||||
status.uri = contact_uri;
|
||||
status.status = CREATED;
|
||||
status.rtt = 0;
|
||||
|
||||
ao2_callback(persistent_endpoints, OBJ_NODATA, persistent_endpoint_update_state, aor);
|
||||
ast_verb(1, "Contact %s/%s has been created\n", aor, contact_uri);
|
||||
|
||||
ao2_callback_data(persistent_endpoints, OBJ_NODATA, persistent_endpoint_update_state, aor, &status);
|
||||
}
|
||||
|
||||
/*! \brief Function called when stuff relating to a contact happens (created/deleted) */
|
||||
|
@ -144,20 +175,25 @@ static void persistent_endpoint_contact_deleted_observer(const void *object)
|
|||
{
|
||||
char *id = ast_strdupa(ast_sorcery_object_get_id(object));
|
||||
char *aor = NULL;
|
||||
char *contact = NULL;
|
||||
char *contact_uri = NULL;
|
||||
struct sip_contact_status status;
|
||||
|
||||
aor = id;
|
||||
/* Dynamic contacts are delimited with ";@" and static ones with "@@" */
|
||||
if ((contact = strstr(id, ";@")) || (contact = strstr(id, "@@"))) {
|
||||
*contact = '\0';
|
||||
contact += 2;
|
||||
if ((contact_uri = strstr(id, ";@")) || (contact_uri = strstr(id, "@@"))) {
|
||||
*contact_uri = '\0';
|
||||
contact_uri += 2;
|
||||
} else {
|
||||
contact = id;
|
||||
contact_uri = id;
|
||||
}
|
||||
|
||||
ast_verb(1, "Contact %s/%s has been deleted\n", aor, contact);
|
||||
ast_verb(1, "Contact %s/%s has been deleted\n", aor, contact_uri);
|
||||
|
||||
ao2_callback(persistent_endpoints, OBJ_NODATA, persistent_endpoint_update_state, aor);
|
||||
status.uri = contact_uri;
|
||||
status.status = REMOVED;
|
||||
status.rtt = 0;
|
||||
|
||||
ao2_callback_data(persistent_endpoints, OBJ_NODATA, persistent_endpoint_update_state, aor, &status);
|
||||
}
|
||||
|
||||
/*! \brief Observer for contacts so state can be updated on respective endpoints */
|
||||
|
@ -172,23 +208,32 @@ static void persistent_endpoint_contact_status_observer(const void *object)
|
|||
const struct ast_sip_contact_status *contact_status = object;
|
||||
char *id = ast_strdupa(ast_sorcery_object_get_id(object));
|
||||
char *aor = NULL;
|
||||
char *contact = NULL;
|
||||
char *contact_uri = NULL;
|
||||
struct sip_contact_status status;
|
||||
|
||||
/* If rtt_start is set (this is the outgoing OPTIONS) or
|
||||
* there's no status change, ignore.
|
||||
*/
|
||||
if (contact_status->rtt_start.tv_sec > 0
|
||||
|| contact_status->status == contact_status->last_status) {
|
||||
/* If rtt_start is set (this is the outgoing OPTIONS), ignore. */
|
||||
if (contact_status->rtt_start.tv_sec > 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
aor = id;
|
||||
/* Dynamic contacts are delimited with ";@" and static ones with "@@" */
|
||||
if ((contact = strstr(id, ";@")) || (contact = strstr(id, "@@"))) {
|
||||
*contact = '\0';
|
||||
contact += 2;
|
||||
if ((contact_uri = strstr(id, ";@")) || (contact_uri = strstr(id, "@@"))) {
|
||||
*contact_uri = '\0';
|
||||
contact_uri += 2;
|
||||
} else {
|
||||
contact = id;
|
||||
contact_uri = id;
|
||||
}
|
||||
|
||||
if (contact_status->status == contact_status->last_status) {
|
||||
ast_debug(3, "Contact %s status didn't change: %s, RTT: %.3f msec\n",
|
||||
contact_uri, ast_sip_get_contact_status_label(contact_status->status),
|
||||
contact_status->rtt / 1000.0);
|
||||
return;
|
||||
} else {
|
||||
ast_verb(1, "Contact %s/%s is now %s. RTT: %.3f msec\n", aor, contact_uri,
|
||||
ast_sip_get_contact_status_label(contact_status->status),
|
||||
contact_status->rtt / 1000.0);
|
||||
}
|
||||
|
||||
ast_test_suite_event_notify("AOR_CONTACT_UPDATE",
|
||||
|
@ -197,10 +242,11 @@ static void persistent_endpoint_contact_status_observer(const void *object)
|
|||
ast_sorcery_object_get_id(contact_status),
|
||||
ast_sip_get_contact_status_label(contact_status->status));
|
||||
|
||||
ast_verb(1, "Contact %s/%s is now %s\n", aor, contact,
|
||||
ast_sip_get_contact_status_label(contact_status->status));
|
||||
status.uri = contact_uri;
|
||||
status.status = contact_status->status;
|
||||
status.rtt = contact_status->rtt;
|
||||
|
||||
ao2_callback(persistent_endpoints, OBJ_NODATA, persistent_endpoint_update_state, aor);
|
||||
ao2_callback_data(persistent_endpoints, OBJ_NODATA, persistent_endpoint_update_state, aor, &status);
|
||||
}
|
||||
|
||||
/*! \brief Observer for contacts so state can be updated on respective endpoints */
|
||||
|
@ -1025,7 +1071,7 @@ static struct ast_endpoint *persistent_endpoint_find_or_create(const struct ast_
|
|||
if (ast_strlen_zero(persistent->aors)) {
|
||||
ast_endpoint_set_state(persistent->endpoint, AST_ENDPOINT_UNKNOWN);
|
||||
} else {
|
||||
persistent_endpoint_update_state(persistent, NULL, 0);
|
||||
persistent_endpoint_update_state(persistent, NULL, NULL, 0);
|
||||
}
|
||||
|
||||
ao2_link_flags(persistent_endpoints, persistent, OBJ_NOLOCK);
|
||||
|
|
|
@ -39,12 +39,17 @@ static const char *status_map [] = {
|
|||
[UNAVAILABLE] = "Unreachable",
|
||||
[AVAILABLE] = "Reachable",
|
||||
[UNKNOWN] = "Unknown",
|
||||
[CREATED] = "Created",
|
||||
[REMOVED] = "Removed",
|
||||
|
||||
};
|
||||
|
||||
static const char *short_status_map [] = {
|
||||
[UNAVAILABLE] = "Unavail",
|
||||
[AVAILABLE] = "Avail",
|
||||
[UNKNOWN] = "Unknown",
|
||||
[CREATED] = "Created",
|
||||
[REMOVED] = "Removed",
|
||||
};
|
||||
|
||||
const char *ast_sip_get_contact_status_label(const enum ast_sip_contact_status_type status)
|
||||
|
|
Loading…
Reference in New Issue