Change in osmo-hlr[master]: Add vty command to show summary of all or filtered subscribers

This is merely a historical archive of years 2008-2021, before the migration to mailman3.

A maintained and still updated list archive can be found at https://lists.osmocom.org/hyperkitty/list/gerrit-log@lists.osmocom.org/.

laforge gerrit-no-reply at lists.osmocom.org
Fri Jan 29 21:26:09 UTC 2021


laforge has submitted this change. ( https://gerrit.osmocom.org/c/osmo-hlr/+/22311 )

Change subject: Add vty command to show summary of all or filtered subscribers
......................................................................

Add vty command to show summary of all or filtered subscribers

Adds the following commands:

show subscribers all - Display summary of all entries in HLR
show subscribers (imsi|msisdn|cs|ps) ... As above but filter on search field/string
show subscribers last seen - Display only subscribers with data in
Last LU update field, and sorts by Last LU.

Change-Id: I7f0573381a6d0d13841ac6d42d50f0e8389decf4
---
M include/osmocom/hlr/db.h
M src/db.c
M src/db_hlr.c
M src/hlr_vty_subscr.c
M tests/test_nodes.vty
5 files changed, 228 insertions(+), 5 deletions(-)

Approvals:
  laforge: Looks good to me, approved
  Jenkins Builder: Verified



diff --git a/include/osmocom/hlr/db.h b/include/osmocom/hlr/db.h
index ca336a0..f70df83 100644
--- a/include/osmocom/hlr/db.h
+++ b/include/osmocom/hlr/db.h
@@ -8,6 +8,12 @@
 struct hlr;
 
 enum stmt_idx {
+	DB_STMT_SEL_ALL,
+	DB_STMT_SEL_ALL_ORDER_LAST_SEEN,
+	DB_STMT_SEL_FILTER_MSISDN,
+	DB_STMT_SEL_FILTER_IMSI,
+	DB_STMT_SEL_FILTER_CS,
+	DB_STMT_SEL_FILTER_PS,
 	DB_STMT_SEL_BY_IMSI,
 	DB_STMT_SEL_BY_MSISDN,
 	DB_STMT_SEL_BY_ID,
@@ -148,6 +154,9 @@
 int db_subscr_exists_by_imsi(struct db_context *dbc, const char *imsi);
 int db_subscr_exists_by_msisdn(struct db_context *dbc, const char *msisdn);
 
+int db_subscrs_get(struct db_context *dbc, const char *filter_type, const char *filter,
+		   void (*get_cb)(struct hlr_subscriber *subscr, void *data), void *data,
+		   int *count, const char **err);
 int db_subscr_get_by_imsi(struct db_context *dbc, const char *imsi,
 			  struct hlr_subscriber *subscr);
 int db_subscr_get_by_msisdn(struct db_context *dbc, const char *msisdn,
diff --git a/src/db.c b/src/db.c
index 5ec20e2..09a17b6 100644
--- a/src/db.c
+++ b/src/db.c
@@ -51,6 +51,13 @@
 	"sgsn_via_proxy"
 
 static const char *stmt_sql[] = {
+	[DB_STMT_SEL_ALL] = "SELECT " SEL_COLUMNS " FROM subscriber;",
+	[DB_STMT_SEL_ALL_ORDER_LAST_SEEN] = "SELECT " SEL_COLUMNS " FROM subscriber "
+		"WHERE last_lu_seen IS NOT NULL ORDER BY last_lu_seen;",
+	[DB_STMT_SEL_FILTER_MSISDN] = "SELECT " SEL_COLUMNS " FROM subscriber WHERE msisdn LIKE $search ORDER BY msisdn",
+	[DB_STMT_SEL_FILTER_IMSI] = "SELECT " SEL_COLUMNS " FROM subscriber WHERE imsi LIKE $search ORDER BY imsi",
+	[DB_STMT_SEL_FILTER_CS] = "SELECT " SEL_COLUMNS " FROM subscriber WHERE nam_cs = $search ORDER BY last_lu_seen",
+	[DB_STMT_SEL_FILTER_PS] = "SELECT " SEL_COLUMNS " FROM subscriber WHERE nam_ps = $search ORDER BY last_lu_seen",
 	[DB_STMT_SEL_BY_IMSI] = "SELECT " SEL_COLUMNS " FROM subscriber WHERE imsi = ?",
 	[DB_STMT_SEL_BY_MSISDN] = "SELECT " SEL_COLUMNS " FROM subscriber WHERE msisdn = ?",
 	[DB_STMT_SEL_BY_ID] = "SELECT " SEL_COLUMNS " FROM subscriber WHERE id = ?",
diff --git a/src/db_hlr.c b/src/db_hlr.c
index 6ba43c2..a181c0c 100644
--- a/src/db_hlr.c
+++ b/src/db_hlr.c
@@ -627,6 +627,96 @@
 
 /*! Retrieve subscriber data from the HLR database.
  * \param[in,out] dbc  database context.
+ * \param[in] filter_type  ASCII string of identifier type to search.
+ * \param[in] filter  ASCII string to search.
+ * \param[in] get_cb  pointer to call back function for data.
+ * \param[in,out] data  pointer to pass to callback function.
+ * \param[in,out] count  counter for number of matched subscribers.
+ * \param[in,our] err
+ * \returns 0 on success, -ENOENT if no subscriber was found, -EIO on
+ *          database error.
+ */
+int db_subscrs_get(struct db_context *dbc, const char *filter_type, const char *filter,
+		   void (*get_cb)(struct hlr_subscriber *subscr, void *data), void *data,
+		   int *count, const char **err)
+{
+	sqlite3_stmt *stmt;
+	char search[256];
+	int rc;
+	struct hlr_subscriber subscr;
+	bool show_ls = false;
+
+	if (!filter_type) {
+		stmt = dbc->stmt[DB_STMT_SEL_ALL];
+	} else if (strcmp(filter_type, "imsi") == 0) {
+		stmt = dbc->stmt[DB_STMT_SEL_FILTER_IMSI];
+	} else if (strcmp(filter_type, "msisdn") == 0) {
+		stmt = dbc->stmt[DB_STMT_SEL_FILTER_MSISDN];
+	} else if (strcmp(filter_type, "cs") == 0) {
+		stmt = dbc->stmt[DB_STMT_SEL_FILTER_CS];
+	} else if (strcmp(filter_type, "ps") == 0) {
+		stmt = dbc->stmt[DB_STMT_SEL_FILTER_PS];
+	} else if (strcmp(filter_type, "last_lu_seen") == 0) {
+		show_ls = true;
+		stmt = dbc->stmt[DB_STMT_SEL_ALL_ORDER_LAST_SEEN];
+	} else {
+		return -EIO;
+	}
+
+	if (filter && strcmp(filter_type, "last_lu_seen") != 0) {
+		if (strcmp(filter, "on") == 0) {
+			sprintf(search, "%s", "1");
+		} else if (strcmp(filter, "off") == 0) {
+			sprintf(search, "%s", "0");
+		} else {
+			sprintf(search, "%%%s%%", filter);
+		}
+		if (!db_bind_text(stmt, "$search", search)) {
+			*err = sqlite3_errmsg(dbc->db);
+			return -EIO;
+		}
+	}
+
+	rc = sqlite3_step(stmt);
+
+	if (rc == SQLITE_DONE) {
+		db_remove_reset(stmt);
+		*err = "No matching subscriber(s)";
+		return -ENOENT;
+	}
+
+	while (rc == SQLITE_ROW) {
+		subscr = (struct hlr_subscriber){
+			  .id = sqlite3_column_int64(stmt, 0),};
+		copy_sqlite3_text_to_buf(subscr.imsi, stmt, 1);
+		copy_sqlite3_text_to_buf(subscr.msisdn, stmt, 2);
+		copy_sqlite3_text_to_buf(subscr.imei, stmt, 3);
+		subscr.nam_cs = sqlite3_column_int(stmt, 9);
+		subscr.nam_ps = sqlite3_column_int(stmt, 10);
+		if (show_ls)
+			parse_last_lu_seen(&subscr.last_lu_seen, (const char *)sqlite3_column_text(stmt, 14),
+					   subscr.imsi, "CS");
+		get_cb(&subscr, data);
+		rc = sqlite3_step(stmt);
+		(*count)++;
+	}
+
+	db_remove_reset(stmt);
+	if (rc != SQLITE_DONE) {
+		*err = sqlite3_errmsg(dbc->db);
+		return -EIO;
+	} else if (rc == SQLITE_DONE) {
+		*err = NULL;
+		return 0;
+	} else {
+		*err = sqlite3_errmsg(dbc->db);
+		LOGP(DAUC, LOGL_ERROR, "Cannot read subscribers from db:: %s\n", *err);
+		return rc;
+	}
+}
+
+/*! Retrieve subscriber data from the HLR database.
+ * \param[in,out] dbc  database context.
  * \param[in] id  ID of the subscriber in the HLR db.
  * \param[out] subscr  place retrieved data in this struct.
  * \returns 0 on success, -ENOENT if no such subscriber was found, -EIO on
diff --git a/src/hlr_vty_subscr.c b/src/hlr_vty_subscr.c
index f5066c1..ad16045 100644
--- a/src/hlr_vty_subscr.c
+++ b/src/hlr_vty_subscr.c
@@ -44,13 +44,14 @@
 	return buf;
 }
 
-static void dump_last_lu_seen(struct vty *vty, const char *domain_label, time_t last_lu_seen)
+static void dump_last_lu_seen(struct vty *vty, const char *domain_label, time_t last_lu_seen, bool only_age)
 {
 	uint32_t age;
 	char datebuf[32];
 	if (!last_lu_seen)
 		return;
-	vty_out(vty, "    last LU seen on %s: %s", domain_label, get_datestr(&last_lu_seen, datebuf, sizeof(datebuf)));
+	if (!only_age)
+		vty_out(vty, "    last LU seen on %s: %s", domain_label, get_datestr(&last_lu_seen, datebuf, sizeof(datebuf)));
 	if (!timestamp_age(&last_lu_seen, &age))
 		vty_out(vty, " (invalid timestamp)%s", VTY_NEWLINE);
 	else {
@@ -64,7 +65,10 @@
 		UNIT_AGO("h", 60*60);
 		UNIT_AGO("m", 60);
 		UNIT_AGO("s", 1);
-		vty_out(vty, " ago)%s", VTY_NEWLINE);
+		if (!only_age)
+			vty_out(vty, " ago)%s", VTY_NEWLINE);
+		else
+			vty_out(vty, " ago)");
 #undef UNIT_AGO
 	}
 }
@@ -108,8 +112,8 @@
 		vty_out(vty, "    PS disabled%s", VTY_NEWLINE);
 	if (subscr->ms_purged_ps)
 		vty_out(vty, "    PS purged%s", VTY_NEWLINE);
-	dump_last_lu_seen(vty, "CS", subscr->last_lu_seen);
-	dump_last_lu_seen(vty, "PS", subscr->last_lu_seen_ps);
+	dump_last_lu_seen(vty, "CS", subscr->last_lu_seen, false);
+	dump_last_lu_seen(vty, "PS", subscr->last_lu_seen_ps, false);
 
 	if (!*subscr->imsi)
 		return;
@@ -159,6 +163,28 @@
 	}
 }
 
+static void subscr_dump_summary_vty(struct hlr_subscriber *subscr, void *data)
+{
+	struct vty *vty = data;
+	vty_out(vty, "%-5"PRIu64"  %-12s  %-16s", subscr->id,
+		*subscr->msisdn ? subscr->msisdn : "none",
+		*subscr->imsi ? subscr->imsi : "none");
+
+	if (*subscr->imei) {
+		char checksum = osmo_luhn(subscr->imei, 14);
+		if (checksum == -EINVAL)
+			vty_out(vty, "  %-14s (INVALID LENGTH!)", subscr->imei);
+		else
+			vty_out(vty, "  %-14s%c", subscr->imei, checksum);
+	} else {
+		vty_out(vty,"   ------------- ");
+	}
+	vty_out(vty, "   %-2s%-2s  ", subscr->nam_cs ? "CS" : "", subscr->nam_ps ? "PS" : "");
+	if (subscr->last_lu_seen)
+		dump_last_lu_seen(vty, "CS", subscr->last_lu_seen, true);
+	vty_out_newline(vty);
+}
+
 static int get_subscr_by_argv(struct vty *vty, const char *type, const char *id, struct hlr_subscriber *subscr)
 {
 	char imei_buf[GSM23003_IMEI_NUM_DIGITS_NO_CHK+1];
@@ -186,11 +212,52 @@
 	return rc;
 }
 
+static void dump_summary_table_vty(struct vty *vty, bool header, bool show_ls)
+{
+	const char *texts = "ID     MSISDN        IMSI              IMEI              NAM";
+	const char *lines = "-----  ------------  ----------------  ----------------  -----";
+	const char *ls_text = "    LAST SEEN";
+	const char *ls_line = "  ------------";
+	if (header) {
+		if (!show_ls)
+			vty_out(vty, "%s%s%s%s", texts, VTY_NEWLINE, lines, VTY_NEWLINE);
+		else
+			vty_out(vty, "%s%s%s%s%s%s", texts, ls_text, VTY_NEWLINE, lines, ls_line, VTY_NEWLINE);
+	} else {
+		if (!show_ls)
+			vty_out(vty, "%s%s%s%s", lines, VTY_NEWLINE, texts, VTY_NEWLINE);
+		else
+			vty_out(vty, "%s%s%s%s%s%s", lines, ls_line, VTY_NEWLINE, texts, ls_text, VTY_NEWLINE);
+	}
+}
+
+static int get_subscrs(struct vty *vty, const char *filter_type, const char *filter)
+{
+	int rc = -1;
+	int count = 0;
+	const char *err;
+	bool show_ls = (filter_type && strcmp(filter_type, "last_lu_seen") == 0);
+	dump_summary_table_vty(vty, true, show_ls);
+	rc = db_subscrs_get(g_hlr->dbc, filter_type, filter, subscr_dump_summary_vty, vty, &count, &err);
+	if (count > 40) {
+		dump_summary_table_vty(vty, false, show_ls);
+	}
+	if (count > 0)
+		vty_out(vty, " Subscribers Shown: %d%s", count, VTY_NEWLINE);
+	if (rc)
+		vty_out(vty, "%% %s%s", err, VTY_NEWLINE);
+	return rc;
+}
+
+
 #define SUBSCR_CMD "subscriber "
 #define SUBSCR_CMD_HELP "Subscriber management commands\n"
 #define SUBSCR_SHOW_HELP "Show subscriber information\n"
+#define SUBSCRS_SHOW_HELP "Show all subscribers (with filter possibility)\n"
 
 #define SUBSCR_ID "(imsi|msisdn|id|imei) IDENT"
+#define SUBSCR_FILTER "(imsi|msisdn) FILTER"
+
 #define SUBSCR_ID_HELP \
 	"Identify subscriber by IMSI\n" \
 	"Identify subscriber by MSISDN (phone number)\n" \
@@ -225,6 +292,48 @@
       "show " SUBSCR_CMD SUBSCR_ID,
       SHOW_STR SUBSCR_SHOW_HELP SUBSCR_ID_HELP);
 
+DEFUN(show_subscriber_all,
+      show_subscriber_all_cmd,
+      "show subscribers all",
+      SHOW_STR SUBSCRS_SHOW_HELP "Show summary of all subscribers\n")
+{
+	if (get_subscrs(vty, NULL, NULL))
+		return CMD_WARNING;
+
+	return CMD_SUCCESS;
+}
+
+DEFUN(show_subscriber_filtered,
+      show_subscriber_filtered_cmd,
+      "show subscribers " SUBSCR_FILTER,
+      SHOW_STR SUBSCRS_SHOW_HELP
+      "Filter Subscribers by IMSI\n" "Filter Subscribers by MSISDN\n" "String to match in msisdn or imsi\n")
+{
+	const char *filter_type = argv[0];
+	const char *filter = argv[1];
+
+	if (get_subscrs(vty, filter_type, filter))
+		return CMD_WARNING;
+
+	return CMD_SUCCESS;
+}
+
+ALIAS(show_subscriber_filtered, show_subscriber_filtered_cmd2,
+      "show subscribers (cs|ps) (on|off)",
+      SHOW_STR SUBSCR_SHOW_HELP
+      "Filter Subscribers by CS Network Access Mode\n" "Filter Subscribers by PS Network Access Mode\n"
+      "Authorised\n" "Not Authorised\n");
+
+DEFUN(show_subscriber_order_last_seen, show_subscriber_order_last_seen_cmd,
+      "show subscribers last-seen",
+      SHOW_STR SUBSCR_SHOW_HELP "Show Subscribers Ordered by Last Seen Time\n")
+{
+	if (get_subscrs(vty, "last_lu_seen", NULL))
+		return CMD_WARNING;
+
+	return CMD_SUCCESS;
+}
+
 DEFUN(subscriber_create,
       subscriber_create_cmd,
       SUBSCR_CMD "imsi IDENT create",
@@ -678,6 +787,10 @@
 
 void hlr_vty_subscriber_init(void)
 {
+	install_element_ve(&show_subscriber_all_cmd);
+	install_element_ve(&show_subscriber_filtered_cmd);
+	install_element_ve(&show_subscriber_filtered_cmd2);
+	install_element_ve(&show_subscriber_order_last_seen_cmd);
 	install_element_ve(&subscriber_show_cmd);
 	install_element_ve(&show_subscriber_cmd);
 	install_element(ENABLE_NODE, &subscriber_create_cmd);
diff --git a/tests/test_nodes.vty b/tests/test_nodes.vty
index cf1707a..5848fe1 100644
--- a/tests/test_nodes.vty
+++ b/tests/test_nodes.vty
@@ -13,6 +13,10 @@
 OsmoHLR> list
 ...
   show gsup-connections
+  show subscribers all
+  show subscribers (imsi|msisdn) FILTER
+  show subscribers (cs|ps) (on|off)
+  show subscribers last-seen
   subscriber (imsi|msisdn|id|imei) IDENT show
   show subscriber (imsi|msisdn|id|imei) IDENT
   show mslookup services

-- 
To view, visit https://gerrit.osmocom.org/c/osmo-hlr/+/22311
To unsubscribe, or for help writing mail filters, visit https://gerrit.osmocom.org/settings

Gerrit-Project: osmo-hlr
Gerrit-Branch: master
Gerrit-Change-Id: I7f0573381a6d0d13841ac6d42d50f0e8389decf4
Gerrit-Change-Number: 22311
Gerrit-PatchSet: 12
Gerrit-Owner: keith <keith at rhizomatica.org>
Gerrit-Reviewer: Jenkins Builder
Gerrit-Reviewer: laforge <laforge at osmocom.org>
Gerrit-CC: neels <nhofmeyr at sysmocom.de>
Gerrit-MessageType: merged
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.osmocom.org/pipermail/gerrit-log/attachments/20210129/7630c639/attachment.htm>


More information about the gerrit-log mailing list