s3:ctdbd_conn: add ctdbd_all_ip_foreach() helper
authorStefan Metzmacher <metze@samba.org>
Fri, 11 Aug 2023 11:07:46 +0000 (13:07 +0200)
committerStefan Metzmacher <metze@samba.org>
Fri, 26 Jan 2024 17:00:32 +0000 (17:00 +0000)
This can we used to traverse through all ip addresses ctdb knows
about.

The caller can select node ips and/or public ips.

This will we useful to monitor the addresses from a witness
service...

Signed-off-by: Stefan Metzmacher <metze@samba.org>
Reviewed-by: Günther Deschner <gd@samba.org>
source3/include/ctdbd_conn.h
source3/lib/ctdb_dummy.c
source3/lib/ctdbd_conn.c

index 312f0eea810ef4d242d24dcc4e26744d868df40a..0618d07b9a84ebf5739da37de3410a6135d3d236 100644 (file)
@@ -121,6 +121,15 @@ int ctdbd_public_ip_foreach(struct ctdbd_connection *conn,
                                      bool is_movable_ip,
                                      void *private_data),
                            void *private_data);
+int ctdbd_all_ip_foreach(struct ctdbd_connection *conn,
+                        bool include_node_ips,
+                        bool include_public_ips,
+                        int (*cb)(uint32_t total_ip_count,
+                                  const struct sockaddr_storage *ip,
+                                  uint32_t pinned_pnn,
+                                  uint32_t current_pnn,
+                                  void *private_data),
+                        void *private_data);
 
 int ctdbd_control_local(struct ctdbd_connection *conn, uint32_t opcode,
                        uint64_t srvid, uint32_t flags, TDB_DATA data,
index 3954fac22c82693678bbe080905edc4550fc195d..f159b7d772110759f24551b99f2f2aa57f4e0fe0 100644 (file)
@@ -114,6 +114,19 @@ int ctdbd_public_ip_foreach(struct ctdbd_connection *conn,
        return ENOSYS;
 }
 
+int ctdbd_all_ip_foreach(struct ctdbd_connection *conn,
+                        bool include_node_ips,
+                        bool include_public_ips,
+                        int (*cb)(uint32_t total_ip_count,
+                                  const struct sockaddr_storage *ip,
+                                  uint32_t pinned_pnn,
+                                  uint32_t current_pnn,
+                                  void *private_data),
+                        void *private_data)
+{
+       return ENOSYS;
+}
+
 bool ctdbd_process_exists(struct ctdbd_connection *conn, uint32_t vnn,
                          pid_t pid, uint64_t unique_id)
 {
index fa18071a78104500a427c66ffeca7366bd6ca3d5..9f8dce684bcddb2c9938d048bd5aad779664094a 100644 (file)
@@ -38,6 +38,8 @@
 #include "lib/util/blocking.h"
 #include "ctdb/include/ctdb_protocol.h"
 #include "lib/async_req/async_sock.h"
+#include "lib/dbwrap/dbwrap.h"
+#include "lib/dbwrap/dbwrap_rbt.h"
 
 /* paths to these include files come from --with-ctdb= in configure */
 
@@ -1517,6 +1519,265 @@ out_free:
        return ret;
 }
 
+static int count_ips(struct db_record *rec, void *private_data)
+{
+       return 0;
+}
+
+static int collect_ips(struct db_record *rec, void *private_data)
+{
+       struct ctdb_public_ip_list_old *ips = talloc_get_type_abort(
+               private_data, struct ctdb_public_ip_list_old);
+       struct ctdb_public_ip *ip;
+       TDB_DATA val = dbwrap_record_get_value(rec);
+
+       SMB_ASSERT(val.dsize == sizeof(*ip));
+
+       ip = (struct ctdb_public_ip *)val.dptr;
+       ips->ips[ips->num] = *ip;
+       ips->num += 1;
+
+       return 0;
+}
+
+static int ctdbd_control_get_all_public_ips(struct ctdbd_connection *conn,
+                                           const struct ctdb_node_map_old *nodemap,
+                                           TALLOC_CTX *mem_ctx,
+                                           struct ctdb_public_ip_list_old **_ips)
+{
+       TALLOC_CTX *frame = talloc_stackframe();
+       uint32_t ni;
+       struct ctdb_public_ip_list_old *ips = NULL;
+       struct db_context *rbt = NULL;
+       NTSTATUS status;
+       size_t len;
+       int ret;
+       int count;
+
+       rbt = db_open_rbt(frame);
+       if (rbt == NULL) {
+               DBG_WARNING("db_open_rbt() failed\n");
+               TALLOC_FREE(frame);
+               return -1;
+       }
+
+       for (ni=0; ni < nodemap->num; ni++) {
+               const struct ctdb_node_and_flags *n = &nodemap->nodes[ni];
+               uint32_t j;
+
+               if (n->flags & NODE_FLAGS_INACTIVE) {
+                       continue;
+               }
+
+               ret = ctdbd_control_get_public_ips(conn,
+                                                  n->pnn,
+                                                  0,
+                                                  frame,
+                                                  &ips);
+               if (ret != 0) {
+                       TALLOC_FREE(frame);
+                       return -1;
+               }
+
+               for (j=0; j<ips->num; j++) {
+                       struct ctdb_public_ip ip;
+                       TDB_DATA key;
+                       TDB_DATA val;
+
+                       ip.pnn = ips->ips[j].pnn;
+                       ip.addr = ips->ips[j].addr;
+
+                       key = make_tdb_data((uint8_t *)&ip.addr, sizeof(ip.addr));
+                       val = make_tdb_data((uint8_t *)&ip, sizeof(ip));
+
+                       if (n->pnn == ip.pnn) {
+                               /*
+                                * Node claims IP is hosted on it, so
+                                * save that information
+                                */
+                               status = dbwrap_store(rbt, key, val,
+                                                     TDB_REPLACE);
+                               if (!NT_STATUS_IS_OK(status)) {
+                                       TALLOC_FREE(frame);
+                                       return -1;
+                               }
+                       } else {
+                               /*
+                                * Node thinks IP is hosted elsewhere,
+                                * so overwrite with CTDB_UNKNOWN_PNN
+                                * if there's no existing entry
+                                */
+                               bool exists = dbwrap_exists(rbt, key);
+                               if (!exists) {
+                                       ip.pnn = CTDB_UNKNOWN_PNN;
+                                       status = dbwrap_store(rbt, key, val,
+                                                             TDB_INSERT);
+                                       if (!NT_STATUS_IS_OK(status)) {
+                                               TALLOC_FREE(frame);
+                                               return -1;
+                                       }
+                               }
+                       }
+               }
+
+               TALLOC_FREE(ips);
+       }
+
+       status = dbwrap_traverse_read(rbt, count_ips, NULL, &count);
+       if (!NT_STATUS_IS_OK(status)) {
+               TALLOC_FREE(frame);
+               return -1;
+       }
+
+       len = offsetof(struct ctdb_public_ip_list_old, ips) +
+                      count*sizeof(struct ctdb_public_ip);
+       ips = talloc_zero_size(mem_ctx, len);
+       if (ips == NULL) {
+               TALLOC_FREE(frame);
+               return -1;
+       }
+       talloc_set_type(ips, struct ctdb_public_ip_list_old);
+       talloc_reparent(mem_ctx, frame, ips);
+
+       status = dbwrap_traverse_read(rbt, collect_ips, ips, &count);
+       if (!NT_STATUS_IS_OK(status)) {
+               TALLOC_FREE(frame);
+               return -1;
+       }
+
+       if ((unsigned int)count != ips->num) {
+               TALLOC_FREE(frame);
+               return -1;
+       }
+
+       *_ips = talloc_move(mem_ctx, &ips);
+       TALLOC_FREE(frame);
+       return 0;
+}
+
+/*
+ * This includes all node and/or public ips
+ * of the whole cluster.
+ *
+ * node ips have:
+ * - a valid pinned_pnn value.
+ * - current_pnn is valid if the node is healthy
+ *
+ * public ips have:
+ * - pinned_pnn as CTDB_UNKNOWN_PNN
+ * - current_pnn is valid if a node healthy and hosting this ip.
+ */
+int ctdbd_all_ip_foreach(struct ctdbd_connection *conn,
+                        bool include_node_ips,
+                        bool include_public_ips,
+                        int (*cb)(uint32_t total_ip_count,
+                                  const struct sockaddr_storage *ip,
+                                  uint32_t pinned_pnn,
+                                  uint32_t current_pnn,
+                                  void *private_data),
+                        void *private_data)
+{
+       TALLOC_CTX *frame = talloc_stackframe();
+       struct ctdb_node_map_old *nodemap = NULL;
+       struct ctdb_public_ip_list_old *ips = NULL;
+       int ret = ENOMEM;
+       uint32_t total_ip_count = 0;
+       uint32_t i;
+
+       ret = ctdbd_control_get_nodemap(conn, frame, &nodemap);
+       if (ret != 0) {
+               DBG_WARNING("ctdbd_control_get_nodemap() failed: %s\n", strerror(ret));
+               TALLOC_FREE(frame);
+               return -1;
+       }
+
+       for (i=0; include_node_ips && i < nodemap->num; i++) {
+               const struct ctdb_node_and_flags *n = &nodemap->nodes[i];
+
+               if (n->flags & NODE_FLAGS_DELETED) {
+                       continue;
+               }
+
+               total_ip_count += 1;
+       }
+
+       if (include_public_ips) {
+               ret = ctdbd_control_get_all_public_ips(conn, nodemap,
+                                                      frame, &ips);
+               if (ret < 0) {
+                       ret = EIO;
+                       goto out_free;
+               }
+
+               total_ip_count += ips->num;
+       }
+
+       for (i=0; include_node_ips && i < nodemap->num; i++) {
+               const struct ctdb_node_and_flags *n = &nodemap->nodes[i];
+               struct samba_sockaddr tmp = ctdbd_sock_addr_to_samba(&n->addr);
+               uint32_t pinned_pnn = n->pnn;
+               uint32_t current_pnn = n->pnn;
+
+               if (n->flags & NODE_FLAGS_DELETED) {
+                       continue;
+               }
+
+               if (n->flags & (NODE_FLAGS_INACTIVE|NODE_FLAGS_DISABLED)) {
+                       /*
+                        * The ip address is not available
+                        * unless the node is up and
+                        * healthy.
+                        */
+                       current_pnn = CTDB_UNKNOWN_PNN;
+               }
+
+               ret = cb(total_ip_count,
+                        &tmp.u.ss,
+                        pinned_pnn,
+                        current_pnn,
+                        private_data);
+               if (ret != 0) {
+                       goto out_free;
+               }
+       }
+
+       for (i=0; include_public_ips && i < ips->num; i++) {
+               const ctdb_sock_addr *addr = &ips->ips[i].addr;
+               struct samba_sockaddr tmp = ctdbd_sock_addr_to_samba(addr);
+               /* all ctdb public ips are movable and not pinned */
+               uint32_t pinned_pnn = CTDB_UNKNOWN_PNN;
+               uint32_t current_pnn = ips->ips[i].pnn;
+               uint32_t ni;
+
+               for (ni=0; ni < nodemap->num; ni++) {
+                       const struct ctdb_node_and_flags *n = &nodemap->nodes[ni];
+
+                       if (n->pnn != current_pnn) {
+                               continue;
+                       }
+
+                       if (n->flags & (NODE_FLAGS_INACTIVE|NODE_FLAGS_DISABLED)) {
+                               current_pnn = CTDB_UNKNOWN_PNN;
+                       }
+                       break;
+               }
+
+               ret = cb(total_ip_count,
+                        &tmp.u.ss,
+                        pinned_pnn,
+                        current_pnn,
+                        private_data);
+               if (ret != 0) {
+                       goto out_free;
+               }
+       }
+
+       ret = 0;
+out_free:
+       TALLOC_FREE(frame);
+       return ret;
+}
+
 /*
   call a control on the local node
  */