Change in osmo-bsc[master]: heighbor_ident: add/del neighbor cells via ctrl interface

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
Wed Nov 3 17:25:29 UTC 2021


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

Change subject: heighbor_ident: add/del neighbor cells via ctrl interface
......................................................................

heighbor_ident: add/del neighbor cells via ctrl interface

The VTY allows flexible control over the neighbor cell information via
the neighbor command, which can be found in the configure terminal under
the bts node. Lets add pendant of this command on the control interface
as well.

Change-Id: I343a40e18fa9b91e6c381912c0426a002841e079
Related: SYS#5641
---
M doc/manuals/chapters/control.adoc
M include/osmocom/bsc/neighbor_ident.h
M src/osmo-bsc/Makefile.am
M src/osmo-bsc/bsc_ctrl_commands.c
A src/osmo-bsc/neighbor_ident_ctrl.c
M src/osmo-bsc/neighbor_ident_vty.c
M tests/ctrl_test_runner.py
7 files changed, 958 insertions(+), 29 deletions(-)

Approvals:
  laforge: Looks good to me, but someone else must approve
  pespin: Looks good to me, approved
  Jenkins Builder: Verified



diff --git a/doc/manuals/chapters/control.adoc b/doc/manuals/chapters/control.adoc
index c2d7a82..091cdc6 100644
--- a/doc/manuals/chapters/control.adoc
+++ b/doc/manuals/chapters/control.adoc
@@ -120,4 +120,29 @@
 Set/Get the value of maximum power reduction. Even values between 0 and 22 are
 accepted.
 
+=== add/del neighbor cell
+
+The control interface allows for editing the neighbor cell configuration. Neighbor
+cells can be added or removed during runtime. It is also possible to clear the
+entire neighbor list if necessary.
+
+.Variables available over control interface
+[options="header",width="100%",cols="20%,5%,5%,50%,20%"]
+|===
+|Name|Access|Trap|Value|Comment
+|bts.N.neighbor-bts.add|WO|No|"<num>"|Add neighbor cell by local BTS number.
+|bts.N.neighbor-bts.del|WO|No|"<num>"|Delete neighbor cell by local BTS number.
+|bts.N.neighbor-lac.add|WO|No|"<lac>[-<arfcn>-<bsic>]"|Add neighbor cell by LAC.
+|bts.N.neighbor-lac.del|WO|No|"<lac>[-<arfcn>-<bsic>]"|Delete neighbor cell by LAC.
+|bts.N.neighbor-lac-ci.add|WO|No|"<lac>-<ci>[-<arfcn>-<bsic>]"|Add neighbor cell by LAC and CI.
+|bts.N.neighbor-lac-ci.del|WO|No|"<lac>-<ci>[-<arfcn>-<bsic>]"|Delete neighbor cell by LAC and CI.
+|bts.N.neighbor-cgi.add|WO|No|"<mcc>-<mnc>-<lac>-<ci>[-<arfcn>-<bsic>]"|Add neighbor cell by cgi.
+|bts.N.neighbor-cgi.del|WO|No|"<mcc>-<mnc>-<lac>-<ci>[-<arfcn>-<bsic>]"|Delete neighbor cell by cgi.
+|bts.N.neighbor-cgi-ps.add|WO|No|"<mcc>-<mnc>-<lac>-<rac>-<ci>[-<arfcn>-<bsic>]"|Add neighbor cell by cgi (Packet Switched, with RAC)
+|bts.N.neighbor-cgi-ps.del|WO|No|"<mcc>-<mnc>-<lac>-<rac>-<ci>[-<arfcn>-<bsic>]"|Delete neighbor cell by cgi (Packet Switched, with RAC).
+|bts.N.neighbor-clear|WO|No|Ignored|Delete all neighbor cells.
+|===
+
+NOTE: The bsic-number (<bsic>) can also be set to "any" if no explcit bsic shall be given
+
 FIXME: add variables defined in src/ctrl/control_if.c?
diff --git a/include/osmocom/bsc/neighbor_ident.h b/include/osmocom/bsc/neighbor_ident.h
index 58300ba..c6a2c42 100644
--- a/include/osmocom/bsc/neighbor_ident.h
+++ b/include/osmocom/bsc/neighbor_ident.h
@@ -77,6 +77,10 @@
 void neighbor_ident_vty_write_bts(struct vty *vty, const char *indent, struct gsm_bts *bts);
 void neighbor_ident_vty_write_network(struct vty *vty, const char *indent);
 
+int neighbor_ident_add_neighbor(struct vty *vty, struct gsm_bts *bts, struct neighbor *n);
+int neighbor_ident_del_neighbor(struct vty *vty, struct gsm_bts *bts, struct neighbor *n);
+int neighbor_ident_ctrl_init(void);
+
 int neighbors_check_cfg();
 
 #define CELL_AB_VTY_PARAMS "arfcn <0-1023> bsic (<0-63>|any)"
diff --git a/src/osmo-bsc/Makefile.am b/src/osmo-bsc/Makefile.am
index 19af569..3d6666e 100644
--- a/src/osmo-bsc/Makefile.am
+++ b/src/osmo-bsc/Makefile.am
@@ -78,6 +78,7 @@
 	meas_rep.c \
 	neighbor_ident.c \
 	neighbor_ident_vty.c \
+	neighbor_ident_ctrl.c \
 	net_init.c \
 	nm_common_fsm.c \
 	nm_bb_transc_fsm.c \
diff --git a/src/osmo-bsc/bsc_ctrl_commands.c b/src/osmo-bsc/bsc_ctrl_commands.c
index a94baae..0affee0 100644
--- a/src/osmo-bsc/bsc_ctrl_commands.c
+++ b/src/osmo-bsc/bsc_ctrl_commands.c
@@ -35,6 +35,7 @@
 #include <osmocom/bsc/osmo_bsc_rf.h>
 #include <osmocom/bsc/bsc_msc_data.h>
 #include <osmocom/bsc/bts.h>
+#include <osmocom/bsc/neighbor_ident.h>
 
 static int verify_net_apply_config_file(struct ctrl_cmd *cmd, const char *value, void *_data)
 {
@@ -692,6 +693,8 @@
 	rc |= ctrl_cmd_install(CTRL_NODE_BTS, &cmd_bts_rf_states);
 	rc |= ctrl_cmd_install(CTRL_NODE_BTS, &cmd_bts_c0_power_red);
 
+	rc |= neighbor_ident_ctrl_init();
+
 	rc |= ctrl_cmd_install(CTRL_NODE_TRX, &cmd_trx_max_power);
 	rc |= ctrl_cmd_install(CTRL_NODE_TRX, &cmd_trx_arfcn);
 	rc |= ctrl_cmd_install(CTRL_NODE_TRX, &cmd_trx_rf_locked);
diff --git a/src/osmo-bsc/neighbor_ident_ctrl.c b/src/osmo-bsc/neighbor_ident_ctrl.c
new file mode 100644
index 0000000..8e5e048
--- /dev/null
+++ b/src/osmo-bsc/neighbor_ident_ctrl.c
@@ -0,0 +1,713 @@
+/* CTRL interface implementation to manage identity of neighboring BSS cells for inter-BSC handover. */
+/* (C) 2021 by sysmocom - s.f.m.c. GmbH <info at sysmocom.de>
+ *
+ * All Rights Reserved
+ *
+ * Author: Philipp Maier <pmaier at sysmocom.de>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include <errno.h>
+#include <time.h>
+
+#include <osmocom/ctrl/control_cmd.h>
+#include <osmocom/bsc/neighbor_ident.h>
+#include <osmocom/bsc/gsm_data.h>
+#include <osmocom/bsc/bsc_msc_data.h>
+#include <osmocom/bsc/bts.h>
+#include <osmocom/bsc/vty.h>
+
+/* Continue to parse ARFCN and BSIC, which are optional parameters at the end of the parameter string in most of the
+ * commands. The result is ignored when parameter n is set to NULL. */
+static int continue_parse_arfcn_and_bsic(char **saveptr, struct neighbor *n)
+{
+	int arfcn;
+	int bsic;
+	char *tok;
+
+	tok = strtok_r(NULL, "-", saveptr);
+
+	/* No ARFCN and BSIC persent - stop */
+	if (!tok)
+		return 0;
+
+	if (osmo_str_to_int(&arfcn, tok, 10, 0, 1023) < 0)
+		return -EINVAL;
+
+	tok = strtok_r(NULL, "-", saveptr);
+
+	/* When an ARFCN is given, then the BSIC parameter is
+	 * mandatory */
+	if (!tok)
+		return -EINVAL;
+
+	if (strcmp(tok, "any") == 0) {
+		bsic = BSIC_ANY;
+	} else {
+		if (osmo_str_to_int(&bsic, tok, 10, 0, 63) < 0)
+			return 1;
+	}
+
+	/* Make sure there are no excess parameters */
+	if (strtok_r(NULL, "-", saveptr))
+		return -EINVAL;
+
+	if (n) {
+		n->cell_id.ab_present = true;
+		n->cell_id.ab.arfcn = arfcn;
+		n->cell_id.ab.bsic = bsic;
+	}
+
+	return 0;
+}
+
+/* This and the following: Add/Remove a BTS as neighbor */
+static int verify_neighbor_bts(struct ctrl_cmd *cmd, const char *value, void *_data)
+{
+	struct gsm_bts *bts = cmd->node;
+	const int neigh_bts_nr = atoi(value);
+	struct gsm_bts *neigh_bts = gsm_bts_num(bts->network, neigh_bts_nr);
+
+	if (!neigh_bts) {
+		cmd->reply = "Invalid Neighbor BTS number - no such BTS";
+		return 1;
+	}
+
+	return 0;
+}
+
+static int verify_neighbor_bts_add(struct ctrl_cmd *cmd, const char *value, void *_data)
+{
+	return verify_neighbor_bts(cmd, value, _data);
+}
+
+static int set_neighbor_bts_add(struct ctrl_cmd *cmd, void *data)
+{
+	struct gsm_bts *bts = cmd->node;
+	const int bts_nr = atoi(cmd->value);
+	int rc;
+
+	struct neighbor n = {
+		.type = NEIGHBOR_TYPE_BTS_NR,
+		.bts_nr = bts_nr,
+	};
+	rc = neighbor_ident_add_neighbor(NULL, bts, &n);
+	if (rc != CMD_SUCCESS) {
+		cmd->reply = "Failed to add neighbor";
+		return CTRL_CMD_ERROR;
+	}
+
+	cmd->reply = "OK";
+	return CTRL_CMD_REPLY;
+}
+
+/* Parameter format: "<num>"
+ * num: BTS number (0-255) */
+CTRL_CMD_DEFINE_WO(neighbor_bts_add, "neighbor-bts add");
+
+static int verify_neighbor_bts_del(struct ctrl_cmd *cmd, const char *value, void *_data)
+{
+	return verify_neighbor_bts(cmd, value, _data);
+}
+
+static int set_neighbor_bts_del(struct ctrl_cmd *cmd, void *data)
+{
+	struct gsm_bts *bts = cmd->node;
+	const int bts_nr = atoi(cmd->value);
+	int rc;
+
+	struct neighbor n = {
+		.type = NEIGHBOR_TYPE_BTS_NR,
+		.bts_nr = bts_nr,
+	};
+	rc = neighbor_ident_del_neighbor(NULL, bts, &n);
+	if (rc != CMD_SUCCESS) {
+		cmd->reply = "Failed to delete neighbor";
+		return CTRL_CMD_ERROR;
+	}
+
+	cmd->reply = "OK";
+	return CTRL_CMD_REPLY;
+}
+
+/* Parameter format: (see "add" command above) */
+CTRL_CMD_DEFINE_WO(neighbor_bts_del, "neighbor-bts del");
+
+/* This and the following: Add/Remove a LAC as neighbor */
+static int parse_lac(void *ctx, struct neighbor *n, const char *value)
+{
+	char *tmp = NULL, *tok, *saveptr;
+	int rc = 0;
+	int lac;
+
+	if (n)
+		memset(n, 0, sizeof(*n));
+
+	tmp = talloc_strdup(ctx, value);
+	if (!tmp)
+		return -EINVAL;
+
+	/* Parse LAC */
+	tok = strtok_r(tmp, "-", &saveptr);
+	if (tok) {
+		if (osmo_str_to_int(&lac, tok, 10, 0, 65535) < 0) {
+			rc = -EINVAL;
+			goto exit;
+		}
+	} else {
+		rc = -EINVAL;
+		goto exit;
+	}
+
+	/* Optional parameters: ARFCN and BSIC */
+	if (continue_parse_arfcn_and_bsic(&saveptr, n)) {
+		rc = -EINVAL;
+		goto exit;
+	}
+
+	if (n) {
+		n->type = NEIGHBOR_TYPE_CELL_ID;
+		n->cell_id.id.id_discr = CELL_IDENT_LAC;
+		n->cell_id.id.id.lac = lac;
+	}
+
+exit:
+	talloc_free(tmp);
+	return rc;
+}
+
+static int verify_neighbor_lac_add(struct ctrl_cmd *cmd, const char *value, void *_data)
+{
+	if (parse_lac(cmd, NULL, value))
+		return 1;
+	return 0;
+}
+
+static int set_neighbor_lac_add(struct ctrl_cmd *cmd, void *data)
+{
+	struct gsm_bts *bts = cmd->node;
+	int rc;
+
+	struct neighbor n;
+
+	parse_lac(cmd, &n, cmd->value);
+	rc = neighbor_ident_add_neighbor(NULL, bts, &n);
+	if (rc != CMD_SUCCESS) {
+		cmd->reply = "Failed to add neighbor";
+		return CTRL_CMD_ERROR;
+	}
+
+	cmd->reply = "OK";
+	return CTRL_CMD_REPLY;
+}
+
+/* Parameter format: "<lac>[-<arfcn>-<bsic>]"
+ * lac: Location area of neighbor cell (0-65535)
+ * arfcn: ARFCN of neighbor cell (0-1023)
+ * bsic: BSIC of neighbor cell */
+CTRL_CMD_DEFINE_WO(neighbor_lac_add, "neighbor-lac add");
+
+static int verify_neighbor_lac_del(struct ctrl_cmd *cmd, const char *value, void *_data)
+{
+	if (parse_lac(cmd, NULL, value))
+		return 1;
+	return 0;
+}
+
+static int set_neighbor_lac_del(struct ctrl_cmd *cmd, void *data)
+{
+	struct gsm_bts *bts = cmd->node;
+	int rc;
+
+	struct neighbor n;
+	parse_lac(cmd, &n, cmd->value);
+	rc = neighbor_ident_del_neighbor(NULL, bts, &n);
+	if (rc != CMD_SUCCESS) {
+		cmd->reply = "Failed to delete neighbor";
+		return CTRL_CMD_ERROR;
+	}
+
+	cmd->reply = "OK";
+	return CTRL_CMD_REPLY;
+}
+
+/* Parameter format: (see "add" command above) */
+CTRL_CMD_DEFINE_WO(neighbor_lac_del, "neighbor-lac del");
+
+/* This and the following: Add/Remove a LAC-CI as neighbor */
+static int parse_lac_ci(void *ctx, struct neighbor *n, const char *value)
+{
+	char *tmp = NULL, *tok, *saveptr;
+	int rc = 0;
+	int lac;
+	int ci;
+
+	if (n)
+		memset(n, 0, sizeof(*n));
+
+	tmp = talloc_strdup(ctx, value);
+	if (!tmp)
+		return -EINVAL;
+
+	/* Parse LAC */
+	tok = strtok_r(tmp, "-", &saveptr);
+	if (tok) {
+		if (osmo_str_to_int(&lac, tok, 10, 0, 65535) < 0) {
+			rc = -EINVAL;
+			goto exit;
+		}
+	} else {
+		rc = -EINVAL;
+		goto exit;
+	}
+
+	/* Parse CI */
+	tok = strtok_r(NULL, "-", &saveptr);
+	if (tok) {
+		if (osmo_str_to_int(&ci, tok, 10, 0, 65535) < 0) {
+			rc = -EINVAL;
+			goto exit;
+		}
+	} else {
+		rc = -EINVAL;
+		goto exit;
+	}
+
+	/* Optional parameters: ARFCN and BSIC */
+	if (continue_parse_arfcn_and_bsic(&saveptr, n)) {
+		rc = -EINVAL;
+		goto exit;
+	}
+
+	if (n) {
+		n->type = NEIGHBOR_TYPE_CELL_ID;
+		n->cell_id.id.id_discr = CELL_IDENT_LAC_AND_CI;
+		n->cell_id.id.id.lac = lac;
+		n->cell_id.id.id.ci = ci;
+	}
+
+exit:
+	talloc_free(tmp);
+	return rc;
+}
+
+static int verify_neighbor_lac_ci_add(struct ctrl_cmd *cmd, const char *value, void *_data)
+{
+	if (parse_lac_ci(cmd, NULL, value))
+		return 1;
+	return 0;
+}
+
+static int set_neighbor_lac_ci_add(struct ctrl_cmd *cmd, void *data)
+{
+	struct gsm_bts *bts = cmd->node;
+	int rc;
+
+	struct neighbor n;
+
+	parse_lac_ci(cmd, &n, cmd->value);
+	rc = neighbor_ident_add_neighbor(NULL, bts, &n);
+	if (rc != CMD_SUCCESS) {
+		cmd->reply = "Failed to add neighbor";
+		return CTRL_CMD_ERROR;
+	}
+
+	cmd->reply = "OK";
+	return CTRL_CMD_REPLY;
+}
+
+/* Parameter format: "<lac>-<ci>[-<arfcn>-<bsic>]"
+ * lac: Location area of neighbor cell (0-65535)
+ * ci: Cell ID of neighbor cell (0-65535)
+ * arfcn: ARFCN of neighbor cell (0-1023)
+ * bsic: BSIC of neighbor cell */
+CTRL_CMD_DEFINE_WO(neighbor_lac_ci_add, "neighbor-lac-ci add");
+
+static int verify_neighbor_lac_ci_del(struct ctrl_cmd *cmd, const char *value, void *_data)
+{
+	if (parse_lac_ci(cmd, NULL, value))
+		return 1;
+	return 0;
+}
+
+static int set_neighbor_lac_ci_del(struct ctrl_cmd *cmd, void *data)
+{
+	struct gsm_bts *bts = cmd->node;
+	int rc;
+
+	struct neighbor n;
+	parse_lac_ci(cmd, &n, cmd->value);
+	rc = neighbor_ident_del_neighbor(NULL, bts, &n);
+	if (rc != CMD_SUCCESS) {
+		cmd->reply = "Failed to delete neighbor";
+		return CTRL_CMD_ERROR;
+	}
+
+	cmd->reply = "OK";
+	return CTRL_CMD_REPLY;
+}
+
+/* Parameter format: (see "add" command above) */
+CTRL_CMD_DEFINE_WO(neighbor_lac_ci_del, "neighbor-lac-ci del");
+
+/* This and the following: Add/Remove a CGI as neighbor */
+static int parse_cgi(void *ctx, struct neighbor *n, const char *value)
+{
+	char *tmp = NULL, *tok, *saveptr;
+	int rc = 0;
+	uint16_t mcc;
+	uint16_t mnc;
+	bool mnc_3_digits;
+	int lac;
+	int ci;
+
+	if (n)
+		memset(n, 0, sizeof(*n));
+
+	tmp = talloc_strdup(ctx, value);
+	if (!tmp)
+		return -EINVAL;
+
+	/* Parse MCC */
+	tok = strtok_r(tmp, "-", &saveptr);
+	if (tok) {
+		if (osmo_mcc_from_str(tok, &mcc)) {
+			rc = -EINVAL;
+			goto exit;
+		}
+	} else {
+		rc = -EINVAL;
+		goto exit;
+	}
+
+	/* Parse MNC */
+	tok = strtok_r(NULL, "-", &saveptr);
+	if (tok) {
+		if (osmo_mnc_from_str(tok, &mnc, &mnc_3_digits)) {
+			rc = -EINVAL;
+			goto exit;
+		}
+	} else {
+		rc = -EINVAL;
+		goto exit;
+	}
+
+	/* Parse LAC */
+	tok = strtok_r(NULL, "-", &saveptr);
+	if (tok) {
+		if (osmo_str_to_int(&lac, tok, 10, 0, 65535) < 0) {
+			rc = -EINVAL;
+			goto exit;
+		}
+	} else {
+		rc = -EINVAL;
+		goto exit;
+	}
+
+	/* Parse CI */
+	tok = strtok_r(NULL, "-", &saveptr);
+	if (tok) {
+		if (osmo_str_to_int(&ci, tok, 10, 0, 65535) < 0) {
+			rc = -EINVAL;
+			goto exit;
+		}
+	} else {
+		rc = -EINVAL;
+		goto exit;
+	}
+
+	/* Optional parameters: ARFCN and BSIC */
+	if (continue_parse_arfcn_and_bsic(&saveptr, n)) {
+		rc = -EINVAL;
+		goto exit;
+	}
+
+	if (n) {
+		n->type = NEIGHBOR_TYPE_CELL_ID;
+		n->cell_id.id.id_discr = CELL_IDENT_WHOLE_GLOBAL;
+		n->cell_id.id.id.global.lai.lac = lac;
+		n->cell_id.id.id.global.lai.plmn.mcc = mcc;
+		n->cell_id.id.id.global.lai.plmn.mnc = mnc;
+		n->cell_id.id.id.global.lai.plmn.mnc_3_digits = mnc_3_digits;
+		n->cell_id.id.id.global.cell_identity = ci;
+	}
+
+exit:
+	talloc_free(tmp);
+	return rc;
+}
+
+static int verify_neighbor_cgi_add(struct ctrl_cmd *cmd, const char *value, void *_data)
+{
+	if (parse_cgi(cmd, NULL, value))
+		return 1;
+	return 0;
+}
+
+static int set_neighbor_cgi_add(struct ctrl_cmd *cmd, void *data)
+{
+	struct gsm_bts *bts = cmd->node;
+	int rc;
+
+	struct neighbor n;
+
+	parse_cgi(cmd, &n, cmd->value);
+	rc = neighbor_ident_add_neighbor(NULL, bts, &n);
+	if (rc != CMD_SUCCESS) {
+		cmd->reply = "Failed to add neighbor";
+		return CTRL_CMD_ERROR;
+	}
+
+	cmd->reply = "OK";
+	return CTRL_CMD_REPLY;
+}
+
+/* Parameter format: "<mcc>-<mnc>-<lac>-<ci>[-<arfcn>-<bsic>]"
+ * mcc: Mobile country code of neighbor cell (0-999)
+ * mnc: Mobile network code of neighbor cell (0-999)
+ * lac: Location area of neighbor cell (0-65535)
+ * ci: Cell ID of neighbor cell (0-65535)
+ * arfcn: ARFCN of neighbor cell (0-1023)
+ * bsic: BSIC of neighbor cell */
+CTRL_CMD_DEFINE_WO(neighbor_cgi_add, "neighbor-cgi add");
+
+static int verify_neighbor_cgi_del(struct ctrl_cmd *cmd, const char *value, void *_data)
+{
+	if (parse_cgi(cmd, NULL, value))
+		return 1;
+	return 0;
+}
+
+static int set_neighbor_cgi_del(struct ctrl_cmd *cmd, void *data)
+{
+	struct gsm_bts *bts = cmd->node;
+	int rc;
+
+	struct neighbor n;
+	parse_cgi(cmd, &n, cmd->value);
+	rc = neighbor_ident_del_neighbor(NULL, bts, &n);
+	if (rc != CMD_SUCCESS) {
+		cmd->reply = "Failed to delete neighbor";
+		return CTRL_CMD_ERROR;
+	}
+
+	cmd->reply = "OK";
+	return CTRL_CMD_REPLY;
+}
+
+/* Parameter format: (see "add" command above) */
+CTRL_CMD_DEFINE_WO(neighbor_cgi_del, "neighbor-cgi del");
+
+/* This and the following: Add/Remove a CGI-PS as neighbor */
+static int parse_cgi_ps(void *ctx, struct neighbor *n, const char *value)
+{
+	char *tmp = NULL, *tok, *saveptr;
+	int rc = 0;
+	uint16_t mcc;
+	uint16_t mnc;
+	bool mnc_3_digits;
+	int lac;
+	int rac;
+	int ci;
+
+	if (n)
+		memset(n, 0, sizeof(*n));
+
+	tmp = talloc_strdup(ctx, value);
+	if (!tmp)
+		return -EINVAL;
+
+	/* Parse MCC */
+	tok = strtok_r(tmp, "-", &saveptr);
+	if (tok) {
+		if (osmo_mcc_from_str(tok, &mcc)) {
+			rc = -EINVAL;
+			goto exit;
+		}
+	} else {
+		rc = -EINVAL;
+		goto exit;
+	}
+
+	/* Parse MNC */
+	tok = strtok_r(NULL, "-", &saveptr);
+	if (tok) {
+		if (osmo_mnc_from_str(tok, &mnc, &mnc_3_digits)) {
+			rc = -EINVAL;
+			goto exit;
+		}
+	} else {
+		rc = -EINVAL;
+		goto exit;
+	}
+
+	/* Parse LAC */
+	tok = strtok_r(NULL, "-", &saveptr);
+	if (tok) {
+		if (osmo_str_to_int(&lac, tok, 10, 0, 65535) < 0) {
+			rc = -EINVAL;
+			goto exit;
+		}
+	} else {
+		rc = -EINVAL;
+		goto exit;
+	}
+
+	/* Parse RAC */
+	tok = strtok_r(NULL, "-", &saveptr);
+	if (tok) {
+		if (osmo_str_to_int(&rac, tok, 10, 0, 255) < 0) {
+			rc = -EINVAL;
+			goto exit;
+		}
+	} else {
+		rc = -EINVAL;
+		goto exit;
+	}
+
+	/* Parse CI */
+	tok = strtok_r(NULL, "-", &saveptr);
+	if (tok) {
+		if (osmo_str_to_int(&ci, tok, 10, 0, 65535) < 0) {
+			rc = -EINVAL;
+			goto exit;
+		}
+	} else {
+		rc = -EINVAL;
+		goto exit;
+	}
+
+	/* Optional parameters: ARFCN and BSIC */
+	if (continue_parse_arfcn_and_bsic(&saveptr, n)) {
+		rc = -EINVAL;
+		goto exit;
+	}
+
+	if (n) {
+		n->type = NEIGHBOR_TYPE_CELL_ID;
+		n->cell_id.id.id_discr = CELL_IDENT_WHOLE_GLOBAL_PS;
+		n->cell_id.id.id.global_ps.rai.lac.lac = lac;
+		n->cell_id.id.id.global_ps.rai.rac = lac;
+		n->cell_id.id.id.global_ps.rai.lac.plmn.mcc = mcc;
+		n->cell_id.id.id.global_ps.rai.lac.plmn.mnc = mnc;
+		n->cell_id.id.id.global_ps.rai.lac.plmn.mnc_3_digits = mnc_3_digits;
+		n->cell_id.id.id.global_ps.cell_identity = ci;
+	}
+
+exit:
+	talloc_free(tmp);
+	return rc;
+}
+
+static int verify_neighbor_cgi_ps_add(struct ctrl_cmd *cmd, const char *value, void *_data)
+{
+	if (parse_cgi_ps(cmd, NULL, value))
+		return 1;
+	return 0;
+}
+
+static int set_neighbor_cgi_ps_add(struct ctrl_cmd *cmd, void *data)
+{
+	struct gsm_bts *bts = cmd->node;
+	int rc;
+
+	struct neighbor n;
+
+	parse_cgi_ps(cmd, &n, cmd->value);
+	rc = neighbor_ident_add_neighbor(NULL, bts, &n);
+	if (rc != CMD_SUCCESS) {
+		cmd->reply = "Failed to add neighbor";
+		return CTRL_CMD_ERROR;
+	}
+
+	cmd->reply = "OK";
+	return CTRL_CMD_REPLY;
+}
+
+/* Parameter format: "<mcc>-<mnc>-<lac>-<rac>-<ci>[-<arfcn>-<bsic>]"
+ * mcc: Mobile country code of neighbor cell (0-999)
+ * mnc: Mobile network code of neighbor cell (0-999)
+ * lac: Location area of neighbor cell (0-65535)
+ * rac: Routing area of neighbor cell (0-65535)
+ * ci: Cell ID of neighbor cell (0-65535)
+ * arfcn: ARFCN of neighbor cell (0-1023)
+ * bsic: BSIC of neighbor cell */
+CTRL_CMD_DEFINE_WO(neighbor_cgi_ps_add, "neighbor-cgi-ps add");
+
+static int verify_neighbor_cgi_ps_del(struct ctrl_cmd *cmd, const char *value, void *_data)
+{
+	if (parse_cgi_ps(cmd, NULL, value))
+		return 1;
+	return 0;
+}
+
+static int set_neighbor_cgi_ps_del(struct ctrl_cmd *cmd, void *data)
+{
+	struct gsm_bts *bts = cmd->node;
+	int rc;
+
+	struct neighbor n;
+	parse_cgi_ps(cmd, &n, cmd->value);
+	rc = neighbor_ident_del_neighbor(NULL, bts, &n);
+	if (rc != CMD_SUCCESS) {
+		cmd->reply = "Failed to delete neighbor";
+		return CTRL_CMD_ERROR;
+	}
+
+	cmd->reply = "OK";
+	return CTRL_CMD_REPLY;
+}
+
+/* Parameter format: (see "add" command above) */
+CTRL_CMD_DEFINE_WO(neighbor_cgi_ps_del, "neighbor-cgi-ps del");
+
+/* This and the following: clear all neighbor cell information */
+static int set_neighbor_clear(struct ctrl_cmd *cmd, void *data)
+{
+	struct gsm_bts *bts = cmd->node;
+	struct neighbor *neighbor;
+	struct neighbor *neighbor_tmp;
+
+	llist_for_each_entry_safe(neighbor, neighbor_tmp, &bts->neighbors, entry) {
+		llist_del(&neighbor->entry);
+		talloc_free(neighbor);
+	}
+
+	cmd->reply = "OK";
+	return CTRL_CMD_REPLY;
+}
+
+CTRL_CMD_DEFINE_WO_NOVRF(neighbor_clear, "neighbor-clear");
+
+/* Register control interface commands implemented above */
+int neighbor_ident_ctrl_init(void)
+{
+	int rc = 0;
+
+	rc |= ctrl_cmd_install(CTRL_NODE_BTS, &cmd_neighbor_bts_add);
+	rc |= ctrl_cmd_install(CTRL_NODE_BTS, &cmd_neighbor_bts_del);
+	rc |= ctrl_cmd_install(CTRL_NODE_BTS, &cmd_neighbor_lac_add);
+	rc |= ctrl_cmd_install(CTRL_NODE_BTS, &cmd_neighbor_lac_del);
+	rc |= ctrl_cmd_install(CTRL_NODE_BTS, &cmd_neighbor_lac_ci_add);
+	rc |= ctrl_cmd_install(CTRL_NODE_BTS, &cmd_neighbor_lac_ci_del);
+	rc |= ctrl_cmd_install(CTRL_NODE_BTS, &cmd_neighbor_cgi_add);
+	rc |= ctrl_cmd_install(CTRL_NODE_BTS, &cmd_neighbor_cgi_del);
+	rc |= ctrl_cmd_install(CTRL_NODE_BTS, &cmd_neighbor_cgi_ps_add);
+	rc |= ctrl_cmd_install(CTRL_NODE_BTS, &cmd_neighbor_cgi_ps_del);
+	rc |= ctrl_cmd_install(CTRL_NODE_BTS, &cmd_neighbor_clear);
+
+	return rc;
+}
diff --git a/src/osmo-bsc/neighbor_ident_vty.c b/src/osmo-bsc/neighbor_ident_vty.c
index b500b34..e40fa8c 100644
--- a/src/osmo-bsc/neighbor_ident_vty.c
+++ b/src/osmo-bsc/neighbor_ident_vty.c
@@ -146,23 +146,32 @@
 	};
 }
 
-static int add_neighbor(struct vty *vty, struct neighbor *n)
+#define LOGPORVTY(vty, fmt, args...) \
+{ \
+	if (vty) \
+		vty_out(vty, "%% " fmt "%s", ## args, VTY_NEWLINE); \
+	else \
+		LOGP(DLINP, LOGL_NOTICE, fmt "\n", ## args); \
+} while (0) \
+
+/* Delete a neighbor from neighborlist. When the parameter *vty is set to NULL all error messages are redirected to the
+ * logtext. */
+int neighbor_ident_add_neighbor(struct vty *vty, struct gsm_bts *bts, struct neighbor *n)
 {
-	struct gsm_bts *bts = vty->index;
 	struct neighbor *neighbor;
 
-	OSMO_ASSERT((vty->node == BTS_NODE) && bts);
+	OSMO_ASSERT(bts);
+	OSMO_ASSERT(!vty || (vty->node == BTS_NODE));
 
 	llist_for_each_entry(neighbor, &bts->neighbors, entry) {
 		/* Check against duplicates */
 		if (neighbor_same(neighbor, n, false)) {
 			/* Found a match on Cell ID or BTS number, without ARFCN+BSIC. If they are fully identical, ignore the
 			 * duplicate. If the ARFCN+BSIC part differs, it's an error. */
-			vty_out(vty, "%% BTS %u already had neighbor %s%s", bts->nr, neighbor_to_str_c(OTC_SELECT, neighbor),
-				VTY_NEWLINE);
+			LOGPORVTY(vty, "BTS %u already had neighbor %s", bts->nr, neighbor_to_str_c(OTC_SELECT, neighbor));
 			if (!neighbor_same(neighbor, n, true)) {
-				vty_out(vty, "%% ERROR: duplicate Cell ID in neighbor config, with differing ARFCN+BSIC: %s%s",
-					neighbor_to_str_c(OTC_SELECT, n), VTY_NEWLINE);
+				LOGPORVTY(vty, "ERROR: duplicate Cell ID in neighbor config, with differing ARFCN+BSIC: %s",
+					neighbor_to_str_c(OTC_SELECT, n));
 				return CMD_WARNING;
 			}
 			/* Exact same neighbor again, just ignore. */
@@ -173,9 +182,9 @@
 		if (n->type == NEIGHBOR_TYPE_CELL_ID
 		    && n->cell_id.ab_present && neighbor->cell_id.ab_present
 		    && cell_ab_match(&n->cell_id.ab, &neighbor->cell_id.ab, true)) {
-			vty_out(vty, "%% Error: only one Cell Identifier entry is allowed per remote neighbor."
-				" Already have: BTS %u -> %s%s", bts->nr,
-				neighbor_to_str_c(OTC_SELECT, neighbor), VTY_NEWLINE);
+			LOGPORVTY(vty, "Error: only one Cell Identifier entry is allowed per remote neighbor."
+				" Already have: BTS %u -> %s", bts->nr,
+				neighbor_to_str_c(OTC_SELECT, neighbor));
 			return CMD_WARNING;
 		}
 	}
@@ -186,12 +195,14 @@
 	return CMD_SUCCESS;
 }
 
-static int del_neighbor(struct vty *vty, struct neighbor *n)
+/* Delete a neighbor from neighborlist. When the parameter *vty is set to NULL all error messages are redirected to the
+ * logtext. */
+int neighbor_ident_del_neighbor(struct vty *vty, struct gsm_bts *bts, struct neighbor *n)
 {
-	struct gsm_bts *bts = vty->index;
 	struct neighbor *neighbor;
 
-	OSMO_ASSERT((vty->node == BTS_NODE) && bts);
+	OSMO_ASSERT(bts);
+	OSMO_ASSERT(!vty || (vty->node == BTS_NODE));
 
 	llist_for_each_entry(neighbor, &bts->neighbors, entry) {
 		if (neighbor->type != n->type)
@@ -216,8 +227,8 @@
 		return CMD_SUCCESS;
 	}
 
-	vty_out(vty, "%% Error: no such neighbor on BTS %d: %s%s",
-		bts->nr, neighbor_to_str_c(OTC_SELECT, n), VTY_NEWLINE);
+	LOGPORVTY(vty, "Error: no such neighbor on BTS %d: %s",
+		  bts->nr, neighbor_to_str_c(OTC_SELECT, n));
 	return CMD_WARNING;
 }
 
@@ -271,7 +282,7 @@
 		.type = NEIGHBOR_TYPE_BTS_NR,
 		.bts_nr = atoi(argv[0]),
 	};
-	return add_neighbor(vty, &n);
+	return neighbor_ident_add_neighbor(vty, vty->index, &n);
 }
 
 DEFUN(cfg_neighbor_add_lac, cfg_neighbor_add_lac_cmd,
@@ -283,7 +294,7 @@
 	};
 	if (neighbor_ident_vty_parse_lac(vty, &n.cell_id.id, argv))
 		return CMD_WARNING;
-	return add_neighbor(vty, &n);
+	return neighbor_ident_add_neighbor(vty, vty->index, &n);
 }
 
 DEFUN(cfg_neighbor_add_lac_ci, cfg_neighbor_add_lac_ci_cmd,
@@ -295,7 +306,7 @@
 	};
 	if (neighbor_ident_vty_parse_lac_ci(vty, &n.cell_id.id, argv))
 		return CMD_WARNING;
-	return add_neighbor(vty, &n);
+	return neighbor_ident_add_neighbor(vty, vty->index, &n);
 }
 
 DEFUN(cfg_neighbor_add_cgi, cfg_neighbor_add_cgi_cmd,
@@ -307,7 +318,7 @@
 	};
 	if (neighbor_ident_vty_parse_cgi(vty, &n.cell_id.id, argv))
 		return CMD_WARNING;
-	return add_neighbor(vty, &n);
+	return neighbor_ident_add_neighbor(vty, vty->index, &n);
 }
 
 DEFUN(cfg_neighbor_add_cgi_ps, cfg_neighbor_add_cgi_ps_cmd,
@@ -319,7 +330,7 @@
 	};
 	if (neighbor_ident_vty_parse_cgi_ps(vty, &n.cell_id.id, argv))
 		return CMD_WARNING;
-	return add_neighbor(vty, &n);
+	return neighbor_ident_add_neighbor(vty, vty->index, &n);
 }
 
 static int neighbor_del_all(struct vty *vty)
@@ -354,7 +365,7 @@
 	if (neighbor_ident_vty_parse_lac(vty, &n.cell_id.id, argv))
 		return CMD_WARNING;
 	neighbor_ident_vty_parse_arfcn_bsic(&n.cell_id.ab, argv + LAC_ARGC);
-	return add_neighbor(vty, &n);
+	return neighbor_ident_add_neighbor(vty, vty->index, &n);
 }
 
 DEFUN(cfg_neighbor_add_lac_ci_arfcn_bsic, cfg_neighbor_add_lac_ci_arfcn_bsic_cmd,
@@ -368,7 +379,7 @@
 	if (neighbor_ident_vty_parse_lac_ci(vty, &n.cell_id.id, argv))
 		return CMD_WARNING;
 	neighbor_ident_vty_parse_arfcn_bsic(&n.cell_id.ab, argv + LAC_CI_ARGC);
-	return add_neighbor(vty, &n);
+	return neighbor_ident_add_neighbor(vty, vty->index, &n);
 }
 
 DEFUN(cfg_neighbor_add_cgi_arfcn_bsic, cfg_neighbor_add_cgi_arfcn_bsic_cmd,
@@ -382,7 +393,7 @@
 	if (neighbor_ident_vty_parse_cgi(vty, &n.cell_id.id, argv))
 		return CMD_WARNING;
 	neighbor_ident_vty_parse_arfcn_bsic(&n.cell_id.ab, argv + CGI_ARGC);
-	return add_neighbor(vty, &n);
+	return neighbor_ident_add_neighbor(vty, vty->index, &n);
 }
 
 DEFUN(cfg_neighbor_add_cgi_ps_arfcn_bsic, cfg_neighbor_add_cgi_ps_arfcn_bsic_cmd,
@@ -396,7 +407,7 @@
 	if (neighbor_ident_vty_parse_cgi_ps(vty, &n.cell_id.id, argv))
 		return CMD_WARNING;
 	neighbor_ident_vty_parse_arfcn_bsic(&n.cell_id.ab, argv + CGI_PS_ARGC);
-	return add_neighbor(vty, &n);
+	return neighbor_ident_add_neighbor(vty, vty->index, &n);
 }
 
 DEFUN(cfg_neighbor_del_bts_nr, cfg_neighbor_del_bts_nr_cmd,
@@ -407,7 +418,7 @@
 		.type = NEIGHBOR_TYPE_BTS_NR,
 		.bts_nr = atoi(argv[0]),
 	};
-	return del_neighbor(vty, &n);
+	return neighbor_ident_del_neighbor(vty, vty->index, &n);
 }
 
 DEFUN(cfg_neighbor_del_lac, cfg_neighbor_del_lac_cmd,
@@ -419,7 +430,7 @@
 	};
 	if (neighbor_ident_vty_parse_lac(vty, &n.cell_id.id, argv))
 		return CMD_WARNING;
-	return del_neighbor(vty, &n);
+	return neighbor_ident_del_neighbor(vty, vty->index, &n);
 }
 
 DEFUN(cfg_neighbor_del_lac_ci, cfg_neighbor_del_lac_ci_cmd,
@@ -431,7 +442,7 @@
 	};
 	if (neighbor_ident_vty_parse_lac_ci(vty, &n.cell_id.id, argv))
 		return CMD_WARNING;
-	return del_neighbor(vty, &n);
+	return neighbor_ident_del_neighbor(vty, vty->index, &n);
 }
 
 DEFUN(cfg_neighbor_del_cgi, cfg_neighbor_del_cgi_cmd,
@@ -443,7 +454,7 @@
 	};
 	if (neighbor_ident_vty_parse_cgi(vty, &n.cell_id.id, argv))
 		return CMD_WARNING;
-	return del_neighbor(vty, &n);
+	return neighbor_ident_del_neighbor(vty, vty->index, &n);
 }
 
 DEFUN(cfg_neighbor_del_cgi_ps, cfg_neighbor_del_cgi_ps_cmd,
@@ -455,7 +466,7 @@
 	};
 	if (neighbor_ident_vty_parse_cgi_ps(vty, &n.cell_id.id, argv))
 		return CMD_WARNING;
-	return del_neighbor(vty, &n);
+	return neighbor_ident_del_neighbor(vty, vty->index, &n);
 }
 
 DEFUN(cfg_neighbor_del_arfcn_bsic, cfg_neighbor_del_arfcn_bsic_cmd,
diff --git a/tests/ctrl_test_runner.py b/tests/ctrl_test_runner.py
index bd2cb10..5e9bcef 100755
--- a/tests/ctrl_test_runner.py
+++ b/tests/ctrl_test_runner.py
@@ -562,6 +562,177 @@
         self.assertEqual(r['var'], 'neighbor_resolve_cgi_ps_from_lac_ci.1.6969.23.32')
         self.assertEqual(r['value'], '023-42-423-2-5')
 
+
+class TestCtrlBSCNeighborCell(TestCtrlBase):
+
+    def tearDown(self):
+        TestCtrlBase.tearDown(self)
+        os.unlink("tmp_dummy_sock")
+
+    def ctrl_command(self):
+        return ["./src/osmo-bsc/osmo-bsc", "-r", "tmp_dummy_sock", "-c",
+                "tests/ctrl/osmo-bsc-neigh-test.cfg"]
+
+    def ctrl_app(self):
+        return (4249, "./src/osmo-bsc/osmo-bsc", "OsmoBSC", "bsc")
+
+    def testCtrlAddDelBTS(self):
+        r = self.do_set('bts.0.neighbor-bts.add', '1')
+        print('respose: ' + str(r))
+        self.assertEqual(r['mtype'], 'SET_REPLY')
+        self.assertEqual(r['var'], 'bts.0.neighbor-bts.add')
+        self.assertEqual(r['value'], 'OK')
+        r = self.do_set('bts.0.neighbor-bts.del', '1')
+        print('respose: ' + str(r))
+        self.assertEqual(r['mtype'], 'SET_REPLY')
+        self.assertEqual(r['var'], 'bts.0.neighbor-bts.del')
+        self.assertEqual(r['value'], 'OK')
+
+    def testCtrlAddDelLAC(self):
+	# without ARFCN+BSIC:
+        r = self.do_set('bts.0.neighbor-lac.add', '100')
+        print('respose: ' + str(r))
+        self.assertEqual(r['mtype'], 'SET_REPLY')
+        self.assertEqual(r['var'], 'bts.0.neighbor-lac.add')
+        self.assertEqual(r['value'], 'OK')
+        r = self.do_set('bts.0.neighbor-lac.del', '100')
+        print('respose: ' + str(r))
+        self.assertEqual(r['mtype'], 'SET_REPLY')
+        self.assertEqual(r['var'], 'bts.0.neighbor-lac.del')
+        self.assertEqual(r['value'], 'OK')
+
+	# with ARFCN+BSIC:
+        r = self.do_set('bts.0.neighbor-lac.add', '100-123-4')
+        print('respose: ' + str(r))
+        self.assertEqual(r['mtype'], 'SET_REPLY')
+        self.assertEqual(r['var'], 'bts.0.neighbor-lac.add')
+        self.assertEqual(r['value'], 'OK')
+        r = self.do_set('bts.0.neighbor-lac.del', '100-123-4')
+        print('respose: ' + str(r))
+        self.assertEqual(r['mtype'], 'SET_REPLY')
+        self.assertEqual(r['var'], 'bts.0.neighbor-lac.del')
+        self.assertEqual(r['value'], 'OK')
+
+    def testCtrlAddDelLACCI(self):
+	# without ARFCN+BSIC:
+        r = self.do_set('bts.0.neighbor-lac-ci.add', '100-200')
+        print('respose: ' + str(r))
+        self.assertEqual(r['mtype'], 'SET_REPLY')
+        self.assertEqual(r['var'], 'bts.0.neighbor-lac-ci.add')
+        self.assertEqual(r['value'], 'OK')
+        r = self.do_set('bts.0.neighbor-lac-ci.del', '100-200')
+        print('respose: ' + str(r))
+        self.assertEqual(r['mtype'], 'SET_REPLY')
+        self.assertEqual(r['var'], 'bts.0.neighbor-lac-ci.del')
+        self.assertEqual(r['value'], 'OK')
+
+	# with ARFCN+BSIC:
+        r = self.do_set('bts.0.neighbor-lac-ci.add', '100-200-123-any')
+        print('respose: ' + str(r))
+        self.assertEqual(r['mtype'], 'SET_REPLY')
+        self.assertEqual(r['var'], 'bts.0.neighbor-lac-ci.add')
+        self.assertEqual(r['value'], 'OK')
+        r = self.do_set('bts.0.neighbor-lac-ci.del', '100-200-123-any')
+        print('respose: ' + str(r))
+        self.assertEqual(r['mtype'], 'SET_REPLY')
+        self.assertEqual(r['var'], 'bts.0.neighbor-lac-ci.del')
+        self.assertEqual(r['value'], 'OK')
+
+    def testCtrlAddDelCGI(self):
+	# without ARFCN+BSIC:
+        r = self.do_set('bts.0.neighbor-cgi.add', '001-01-100-200')
+        print('respose: ' + str(r))
+        self.assertEqual(r['mtype'], 'SET_REPLY')
+        self.assertEqual(r['var'], 'bts.0.neighbor-cgi.add')
+        self.assertEqual(r['value'], 'OK')
+        r = self.do_set('bts.0.neighbor-cgi.del', '001-01-100-200')
+        print('respose: ' + str(r))
+        self.assertEqual(r['mtype'], 'SET_REPLY')
+        self.assertEqual(r['var'], 'bts.0.neighbor-cgi.del')
+        self.assertEqual(r['value'], 'OK')
+
+	# with ARFCN+BSIC:
+        r = self.do_set('bts.0.neighbor-cgi.add', '001-01-100-200-123-4')
+        print('respose: ' + str(r))
+        self.assertEqual(r['mtype'], 'SET_REPLY')
+        self.assertEqual(r['var'], 'bts.0.neighbor-cgi.add')
+        self.assertEqual(r['value'], 'OK')
+        r = self.do_set('bts.0.neighbor-cgi.del', '001-01-100-200-123-4')
+        print('respose: ' + str(r))
+        self.assertEqual(r['mtype'], 'SET_REPLY')
+        self.assertEqual(r['var'], 'bts.0.neighbor-cgi.del')
+        self.assertEqual(r['value'], 'OK')
+
+    def testCtrlAddDelCGIPS(self):
+	# without ARFCN+BSIC:
+        r = self.do_set('bts.0.neighbor-cgi-ps.add', '001-01-100-33-200')
+        print('respose: ' + str(r))
+        self.assertEqual(r['mtype'], 'SET_REPLY')
+        self.assertEqual(r['var'], 'bts.0.neighbor-cgi-ps.add')
+        self.assertEqual(r['value'], 'OK')
+        r = self.do_set('bts.0.neighbor-cgi-ps.del', '001-01-100-33-200')
+        print('respose: ' + str(r))
+        self.assertEqual(r['mtype'], 'SET_REPLY')
+        self.assertEqual(r['var'], 'bts.0.neighbor-cgi-ps.del')
+        self.assertEqual(r['value'], 'OK')
+
+	# with ARFCN+BSIC:
+        r = self.do_set('bts.0.neighbor-cgi-ps.add', '001-01-100-33-200-123-any')
+        print('respose: ' + str(r))
+        self.assertEqual(r['mtype'], 'SET_REPLY')
+        self.assertEqual(r['var'], 'bts.0.neighbor-cgi-ps.add')
+        self.assertEqual(r['value'], 'OK')
+        r = self.do_set('bts.0.neighbor-cgi-ps.del', '001-01-100-33-200-123-any')
+        print('respose: ' + str(r))
+        self.assertEqual(r['mtype'], 'SET_REPLY')
+        self.assertEqual(r['var'], 'bts.0.neighbor-cgi-ps.del')
+        self.assertEqual(r['value'], 'OK')
+
+    def testCtrlClearNeighbors(self):
+        r = self.do_set('bts.0.neighbor-clear', 'ignored')
+        print('respose: ' + str(r))
+        self.assertEqual(r['mtype'], 'SET_REPLY')
+        self.assertEqual(r['var'], 'bts.0.neighbor-clear')
+        self.assertEqual(r['value'], 'OK')
+
+    def testCtrlErrs(self):
+	# Missing BSIC
+        r = self.do_set('bts.0.neighbor-lac.add', '100-123')
+        print('respose: ' + str(r))
+        self.assertEqual(r['mtype'], 'ERROR')
+        self.assertEqual(r['error'], 'Value failed verification.')
+
+	# Short value (missing RAC)
+        r = self.do_set('bts.0.neighbor-cgi-ps.del', '001-01-100-200-123-1')
+        print('respose: ' + str(r))
+        self.assertEqual(r['mtype'], 'ERROR')
+        self.assertEqual(r['error'], 'Value failed verification.')
+
+	# Long value
+        r = self.do_set('bts.0.neighbor-cgi-ps.del', '001-01-100-33-200-123-1-2')
+        print('respose: ' + str(r))
+        self.assertEqual(r['mtype'], 'ERROR')
+        self.assertEqual(r['error'], 'Value failed verification.')
+
+	# Out of range values
+        r = self.do_set('bts.0.neighbor-cgi.add', '100001-1123401-100-200')
+        print('respose: ' + str(r))
+        self.assertEqual(r['mtype'], 'ERROR')
+        self.assertEqual(r['error'], 'Value failed verification.')
+
+	# Garbage
+        r = self.do_set('bts.0.neighbor-lac-ci.add', '0G1-Z1-1U0-a3-2p0')
+        print('respose: ' + str(r))
+        self.assertEqual(r['mtype'], 'ERROR')
+        self.assertEqual(r['error'], 'Value failed verification.')
+
+	# Delete something that shouldn't be there
+        r = self.do_set('bts.0.neighbor-cgi-ps.del', '001-01-100-33-200-123-any')
+        print('respose: ' + str(r))
+        self.assertEqual(r['mtype'], 'ERROR')
+        self.assertEqual(r['error'], 'Failed to delete neighbor')
+
+
 def add_bsc_test(suite, workdir, klass):
     if not os.path.isfile(os.path.join(workdir, "src/osmo-bsc/osmo-bsc")):
         print("Skipping the BSC test")
@@ -601,5 +772,6 @@
     suite = unittest.TestSuite()
     add_bsc_test(suite, workdir, TestCtrlBSC)
     add_bsc_test(suite, workdir, TestCtrlBSCNeighbor)
+    add_bsc_test(suite, workdir, TestCtrlBSCNeighborCell)
     res = unittest.TextTestRunner(verbosity=verbose_level).run(suite)
     sys.exit(len(res.errors) + len(res.failures))

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

Gerrit-Project: osmo-bsc
Gerrit-Branch: master
Gerrit-Change-Id: I343a40e18fa9b91e6c381912c0426a002841e079
Gerrit-Change-Number: 25976
Gerrit-PatchSet: 7
Gerrit-Owner: dexter <pmaier at sysmocom.de>
Gerrit-Reviewer: Jenkins Builder
Gerrit-Reviewer: daniel <dwillmann at sysmocom.de>
Gerrit-Reviewer: laforge <laforge at osmocom.org>
Gerrit-Reviewer: pespin <pespin at sysmocom.de>
Gerrit-CC: fixeria <vyanitskiy at sysmocom.de>
Gerrit-MessageType: merged
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.osmocom.org/pipermail/gerrit-log/attachments/20211103/fef7a848/attachment.htm>


More information about the gerrit-log mailing list