<p>laforge <strong>submitted</strong> this change.</p><p><a href="https://gerrit.osmocom.org/c/osmo-bsc/+/25976">View Change</a></p><div style="white-space:pre-wrap">Approvals:
  laforge: Looks good to me, but someone else must approve
  pespin: Looks good to me, approved
  Jenkins Builder: Verified

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

<div style="display:none"> Gerrit-Project: osmo-bsc </div>
<div style="display:none"> Gerrit-Branch: master </div>
<div style="display:none"> Gerrit-Change-Id: I343a40e18fa9b91e6c381912c0426a002841e079 </div>
<div style="display:none"> Gerrit-Change-Number: 25976 </div>
<div style="display:none"> Gerrit-PatchSet: 7 </div>
<div style="display:none"> Gerrit-Owner: dexter <pmaier@sysmocom.de> </div>
<div style="display:none"> Gerrit-Reviewer: Jenkins Builder </div>
<div style="display:none"> Gerrit-Reviewer: daniel <dwillmann@sysmocom.de> </div>
<div style="display:none"> Gerrit-Reviewer: laforge <laforge@osmocom.org> </div>
<div style="display:none"> Gerrit-Reviewer: pespin <pespin@sysmocom.de> </div>
<div style="display:none"> Gerrit-CC: fixeria <vyanitskiy@sysmocom.de> </div>
<div style="display:none"> Gerrit-MessageType: merged </div>