res_pjsip: Fix statsd regression.
authorRichard Mudgett <rmudgett@digium.com>
Mon, 11 Jul 2016 15:25:04 +0000 (10:25 -0500)
committerRichard Mudgett <rmudgett@digium.com>
Tue, 12 Jul 2016 17:03:20 +0000 (12:03 -0500)
The ASTERISK-25904 change-id I8fad8aae9305481469c38d2146e1ba3a56d3108f
patch introduced several regressions when the newly created "Updated"
state goes out for each endpoint registration refresh.

1) It restarted any OPTIONS RTT ping cycle.

2) It would interfere with a currently active ping and throw off that
ping's resulting RTT calculation.

3) It cleared the RTT time each time the endpoint was refreshed.

4) The cleared RTT time was sent out as a statsd update each time.

5) It created two AMI events for each update.

* Revert the original patch and reimplement it.  Now the current contact
status state is re-sent instead of the state being momentarily toggled
every time the endpoint refreshes its registration.  The statsd events are
not created for the re-sent refresh because they are sent after every
OPTIONS ping.

ASTERISK-26160 #close
Reported by: Matt Jordan

Change-Id: Ie072be790fbb2a8f5c1c874266e4143fa31f66d1

CHANGES
include/asterisk/res_pjsip.h
res/res_pjsip/pjsip_configuration.c
res/res_pjsip/pjsip_options.c

diff --git a/CHANGES b/CHANGES
index 6e6bec6..cbe03fd 100644 (file)
--- a/CHANGES
+++ b/CHANGES
@@ -397,8 +397,6 @@ res_pjsip
     "contact_deny" - List of Contact header addresses to deny
     "contact_permit" - List of Contact header addresses to permit
 
- * Added new status Updated to AMI event ContactStatus on update registration
-
  * Added "reg_server" to contacts.
    If the Asterisk system name is set in asterisk.conf, it will be stored
    into the "reg_server" field in the ps_contacts table to facilitate
index f9f9e20..f43a11c 100644 (file)
@@ -271,7 +271,6 @@ enum ast_sip_contact_status_type {
        UNKNOWN,
        CREATED,
        REMOVED,
-       UPDATED,
 };
 
 /*!
@@ -296,6 +295,8 @@ struct ast_sip_contact_status {
        int64_t rtt;
        /*! Last status for a contact (default - unavailable) */
        enum ast_sip_contact_status_type last_status;
+       /*! TRUE if the contact was refreshed. e.g., re-registered */
+       unsigned int refresh:1;
 };
 
 /*!
index 8a5ff41..6890ced 100644 (file)
@@ -105,6 +105,40 @@ static void endpoint_update_state(struct ast_endpoint *endpoint, enum ast_endpoi
        ast_devstate_changed(AST_DEVICE_UNKNOWN, AST_DEVSTATE_CACHABLE, "PJSIP/%s", ast_endpoint_get_resource(endpoint));
 }
 
+static void endpoint_publish_contact_status(struct ast_endpoint *endpoint, struct ast_sip_contact_status *contact)
+{
+       struct ast_json *blob;
+       char rtt[32];
+
+       snprintf(rtt, sizeof(rtt), "%" PRId64, contact->rtt);
+       blob = ast_json_pack("{s: s, s: s, s: s, s: s, s: s}",
+               "contact_status", ast_sip_get_contact_status_label(contact->status),
+               "aor", contact->aor,
+               "uri", contact->uri,
+               "roundtrip_usec", rtt,
+               "endpoint_name", ast_endpoint_get_resource(endpoint));
+       if (blob) {
+               ast_endpoint_blob_publish(endpoint, ast_endpoint_contact_state_type(), blob);
+               ast_json_unref(blob);
+       }
+}
+
+/*! \brief Callback function for publishing the status of an endpoint */
+static int persistent_endpoint_publish_status(void *obj, void *arg, int flags)
+{
+       struct sip_persistent_endpoint *persistent = obj;
+       struct ast_endpoint *endpoint = persistent->endpoint;
+       struct ast_sip_contact_status *status = arg;
+
+       /* If the status' aor isn't one of the endpoint's, we skip */
+       if (!strstr(persistent->aors, status->aor)) {
+               return 0;
+       }
+
+       endpoint_publish_contact_status(endpoint, status);
+       return 0;
+}
+
 /*! \brief Callback function for changing the state of an endpoint */
 static int persistent_endpoint_update_state(void *obj, void *arg, int flags)
 {
@@ -112,30 +146,17 @@ static int persistent_endpoint_update_state(void *obj, void *arg, int flags)
        struct ast_endpoint *endpoint = persistent->endpoint;
        struct ast_sip_contact_status *status = arg;
        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 (status) {
-               char rtt[32];
-
-               /* If the status' aor isn't one of the endpoint's, we skip */
-               if (!strstr(persistent->aors, status->aor)) {
-                       return 0;
-               }
-
-               snprintf(rtt, sizeof(rtt), "%" 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", status->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);
+       /* If the status' aor isn't one of the endpoint's, we skip */
+       if (!strstr(persistent->aors, status->aor)) {
+               return 0;
        }
 
+       endpoint_publish_contact_status(endpoint, status);
+
        /* Find all the contacts for this endpoint.  If ANY are available,
         * mark the endpoint as ONLINE.
         */
@@ -222,6 +243,13 @@ static void persistent_endpoint_contact_status_observer(const void *object)
 {
        struct ast_sip_contact_status *contact_status = (struct ast_sip_contact_status *)object;
 
+       if (contact_status->refresh) {
+               /* We are only re-publishing the contact status. */
+               ao2_callback(persistent_endpoints, OBJ_NODATA,
+                       persistent_endpoint_publish_status, contact_status);
+               return;
+       }
+
        /* If rtt_start is set (this is the outgoing OPTIONS), ignore. */
        if (contact_status->rtt_start.tv_sec > 0) {
                return;
index dc3bce0..808ee17 100644 (file)
@@ -43,7 +43,6 @@ static const char *status_map [] = {
        [UNKNOWN] = "Unknown",
        [CREATED] = "Created",
        [REMOVED] = "Removed",
-       [UPDATED] = "Updated",
 };
 
 static const char *short_status_map [] = {
@@ -52,7 +51,6 @@ static const char *short_status_map [] = {
        [UNKNOWN] = "Unknown",
        [CREATED] = "Created",
        [REMOVED] = "Removed",
-       [UPDATED] = "Updated",
 };
 
 const char *ast_sip_get_contact_status_label(const enum ast_sip_contact_status_type status)
@@ -157,7 +155,7 @@ struct ast_sip_contact_status *ast_res_pjsip_find_or_create_contact_status(const
  * \brief Update an ast_sip_contact_status's elements.
  */
 static void update_contact_status(const struct ast_sip_contact *contact,
-       enum ast_sip_contact_status_type value)
+       enum ast_sip_contact_status_type value, int is_contact_refresh)
 {
        RAII_VAR(struct ast_sip_contact_status *, status, NULL, ao2_cleanup);
        RAII_VAR(struct ast_sip_contact_status *, update, NULL, ao2_cleanup);
@@ -169,6 +167,26 @@ static void update_contact_status(const struct ast_sip_contact *contact,
                return;
        }
 
+       if (is_contact_refresh
+               && status->status == CREATED) {
+               /*
+                * The contact status hasn't been updated since creation
+                * and we don't want to re-send a created status.
+                */
+               if (contact->qualify_frequency
+                       || status->rtt_start.tv_sec > 0) {
+                       /* Ignore, the status will change soon. */
+                       return;
+               }
+
+               /*
+                * Convert to a regular contact status update
+                * because the status may never change.
+                */
+               is_contact_refresh = 0;
+               value = UNKNOWN;
+       }
+
        update = ast_sorcery_alloc(ast_sip_get_sorcery(), CONTACT_STATUS,
                ast_sorcery_object_get_id(status));
        if (!update) {
@@ -178,22 +196,35 @@ static void update_contact_status(const struct ast_sip_contact *contact,
        }
 
        ast_string_field_set(update, uri, contact->uri);
-       update->last_status = status->status;
-       update->status = value;
-
-       /* if the contact is available calculate the rtt as
-          the diff between the last start time and "now" */
-       update->rtt = update->status == AVAILABLE && status->rtt_start.tv_sec > 0 ?
-               ast_tvdiff_us(ast_tvnow(), status->rtt_start) : 0;
-       update->rtt_start = ast_tv(0, 0);
-
-       ast_test_suite_event_notify("AOR_CONTACT_QUALIFY_RESULT",
-               "Contact: %s\r\n"
-               "Status: %s\r\n"
-               "RTT: %" PRId64,
-               ast_sorcery_object_get_id(update),
-               ast_sip_get_contact_status_label(update->status),
-               update->rtt);
+
+       if (is_contact_refresh) {
+               /* Copy everything just to set the refresh flag. */
+               update->status = status->status;
+               update->last_status = status->last_status;
+               update->rtt = status->rtt;
+               update->rtt_start = status->rtt_start;
+               update->refresh = 1;
+       } else {
+               update->last_status = status->status;
+               update->status = value;
+
+               /*
+                * if the contact is available calculate the rtt as
+                * the diff between the last start time and "now"
+                */
+               update->rtt = update->status == AVAILABLE && status->rtt_start.tv_sec > 0
+                       ? ast_tvdiff_us(ast_tvnow(), status->rtt_start)
+                       : 0;
+               update->rtt_start = ast_tv(0, 0);
+
+               ast_test_suite_event_notify("AOR_CONTACT_QUALIFY_RESULT",
+                       "Contact: %s\r\n"
+                       "Status: %s\r\n"
+                       "RTT: %" PRId64,
+                       ast_sorcery_object_get_id(update),
+                       ast_sip_get_contact_status_label(update->status),
+                       update->rtt);
+       }
 
        if (ast_sorcery_update(ast_sip_get_sorcery(), update)) {
                ast_log(LOG_ERROR, "Unable to update ast_sip_contact_status for contact %s\n",
@@ -306,10 +337,10 @@ static void qualify_contact_cb(void *token, pjsip_event *e)
                /* Fall through */
        case PJSIP_EVENT_TRANSPORT_ERROR:
        case PJSIP_EVENT_TIMER:
-               update_contact_status(contact, UNAVAILABLE);
+               update_contact_status(contact, UNAVAILABLE, 0);
                break;
        case PJSIP_EVENT_RX_MSG:
-               update_contact_status(contact, AVAILABLE);
+               update_contact_status(contact, AVAILABLE, 0);
                break;
        }
        ao2_cleanup(contact);
@@ -365,7 +396,7 @@ static int qualify_contact(struct ast_sip_endpoint *endpoint, struct ast_sip_con
                != PJ_SUCCESS) {
                ast_log(LOG_ERROR, "Unable to send request to qualify contact %s\n",
                        contact->uri);
-               update_contact_status(contact, UNAVAILABLE);
+               update_contact_status(contact, UNAVAILABLE, 0);
                ao2_ref(contact, -1);
                return -1;
        }
@@ -525,7 +556,7 @@ static void qualify_and_schedule(struct ast_sip_contact *contact)
 
                schedule_qualify(contact, contact->qualify_frequency * 1000);
        } else {
-               update_contact_status(contact, UNKNOWN);
+               update_contact_status(contact, UNKNOWN, 0);
        }
 }
 
@@ -544,8 +575,7 @@ static void contact_created(const void *obj)
  */
 static void contact_updated(const void *obj)
 {
-       update_contact_status((struct ast_sip_contact *) obj, UPDATED);
-       qualify_and_schedule((struct ast_sip_contact *) obj);
+       update_contact_status(obj, AVAILABLE, 1);
 }
 
 /*!
@@ -574,8 +604,8 @@ static void contact_deleted(const void *obj)
 
 static const struct ast_sorcery_observer contact_observer = {
        .created = contact_created,
+       .updated = contact_updated,
        .deleted = contact_deleted,
-       .updated = contact_updated
 };
 
 static pj_bool_t options_start(void)
@@ -1051,7 +1081,7 @@ static void qualify_and_schedule_contact(struct ast_sip_contact *contact)
        if (contact->qualify_frequency) {
                schedule_qualify(contact, initial_interval);
        } else {
-               update_contact_status(contact, UNKNOWN);
+               update_contact_status(contact, UNKNOWN, 0);
        }
 }