@@ -40,6 +40,7 @@ type ClusterNodes struct {
40
40
// - Can find nodes that are part of ReplicaSet
41
41
// - Can find nodes that are "not hidden", passives and arbiters
42
42
// - Returns the hostname information that mongod has - could be PRIVATE hostnames as well!!!!
43
+ // - If multiple hostnames point to same IP address only the unique IP is returned
43
44
44
45
type TopologyFinder struct {
45
46
Allnodes ClusterNodes
@@ -87,14 +88,14 @@ func (tf *TopologyFinder) parseHelloOutput() error {
87
88
var hostsArray []string
88
89
89
90
if err := json .Unmarshal (tf .GetHelloOutput .Bytes (), & hostsArray ); err != nil {
90
- log .Fatalf ("Error parsing hello output during topology discovery: %s" , err )
91
+ log .Fatalf ("tftf - error parsing hello output during topology discovery: %s" , err )
91
92
}
92
93
93
94
for _ , mongonodestring := range hostsArray {
94
95
95
96
mongonodeslice := strings .Split (mongonodestring , ":" )
96
97
if len (mongonodeslice ) != 2 {
97
- log .Fatalf ("Invalid mongo node string: %s" , mongonodeslice )
98
+ log .Fatalf ("tftf - invalid mongo node string: %s" , mongonodeslice )
98
99
}
99
100
100
101
hostname := mongonodeslice [0 ]
@@ -103,7 +104,7 @@ func (tf *TopologyFinder) parseHelloOutput() error {
103
104
port , err := strconv .Atoi (portStr )
104
105
if err != nil {
105
106
log .Fatalf (
106
- "In parseHelloOutput: Invalid port string format for node %s: %s " ,
107
+ "tftf - in parseHelloOutput: Invalid port string format for node %s: %s" ,
107
108
mongonodestring ,
108
109
portStr ,
109
110
)
@@ -114,7 +115,7 @@ func (tf *TopologyFinder) parseHelloOutput() error {
114
115
Port : port ,
115
116
}
116
117
117
- tf .Dcrlog .Debug (fmt .Sprintf ("appending node %s to allnodes list" , mongonodestring ))
118
+ tf .Dcrlog .Debug (fmt .Sprintf ("tftf - appending node %s to allnodes list" , mongonodestring ))
118
119
tf .Allnodes .Nodes = append (tf .Allnodes .Nodes , mongonode )
119
120
120
121
}
@@ -124,38 +125,63 @@ func (tf *TopologyFinder) parseHelloOutput() error {
124
125
func (tf * TopologyFinder ) parseShardMapOutput () error {
125
126
var shardMap map [string ]interface {}
126
127
127
- tf .Dcrlog .Debug ("parsing shard cluster output" )
128
+ tf .Dcrlog .Debug ("tftf - parsing shard cluster output" )
128
129
if err := json .Unmarshal (tf .GetShardMapOutput .Bytes (), & shardMap ); err != nil {
129
- log .Fatalf ("Error parsing shardmap output: %s" , err )
130
+ log .Fatalf ("tftf - error parsing shardmap output: %s" , err )
130
131
}
131
132
132
- tf .Dcrlog .Debug ("extract hosts from shard cluster output" )
133
+ tf .Dcrlog .Debug ("tftf - extract hosts from shard cluster output" )
134
+
135
+ // hosts document is of format {'hostname1:port1' : 'shardn/config'... }
133
136
allhosts , ok := shardMap ["hosts" ].(map [string ]interface {})
134
137
if ! ok {
135
- log .Fatalf ("error reading sharmap hosts document" )
138
+ log .Fatalf ("tftf - error reading sharmap hosts document" )
136
139
}
137
140
141
+ tf .Dcrlog .Debug (fmt .Sprintf ("tftf - parsing shard output allhosts is: %s" , allhosts ))
142
+ // hosts document is of format {'hostname1:port1' : 'shardn/config'... }
143
+ // ignore the values part only need the keys which are 'hostname1:port1' ...
138
144
for mongonodestring := range allhosts {
139
145
140
146
mongonodeslice := strings .Split (mongonodestring , ":" )
147
+ tf .Dcrlog .Debug (
148
+ fmt .Sprintf ("tftf - parsing shard output mongonodestring is: %s" , mongonodestring ),
149
+ )
150
+ tf .Dcrlog .Debug (
151
+ fmt .Sprintf ("tftf - parsing shard output mongonodeslice is: %s" , mongonodeslice ),
152
+ )
153
+ tf .Dcrlog .Debug (
154
+ fmt .Sprintf (
155
+ "tftf - parsing shard output length of mongonodeslice is: %d" ,
156
+ len (mongonodeslice ),
157
+ ),
158
+ )
141
159
if len (mongonodeslice ) != 2 {
142
- log .Fatalf ("invalid mongo node string: %s" , mongonodeslice )
160
+ log .Fatalf ("tftf - invalid mongo node string: %s" , mongonodeslice )
143
161
}
144
162
163
+ // hosts document is of format {'hostname1:port1' : 'shardn/config'... }
145
164
hostname := mongonodeslice [0 ]
165
+ tf .Dcrlog .Debug (fmt .Sprintf ("tftf - parsing shard output hostname is: %s" , hostname ))
166
+
146
167
portStr := mongonodeslice [1 ]
168
+ tf .Dcrlog .Debug (fmt .Sprintf ("tftf - parsing shard output portStr is: %s" , portStr ))
147
169
148
170
port , err := strconv .Atoi (portStr )
149
171
if err != nil {
150
- log .Fatalf ("invalid port string format for node %s: %s " , mongonodestring , portStr )
172
+ log .Fatalf (
173
+ "tftf - invalid port string format for node %s: %s" ,
174
+ mongonodestring ,
175
+ portStr ,
176
+ )
151
177
}
152
178
153
179
mongonode := ClusterNode {
154
180
Hostname : hostname ,
155
181
Port : port ,
156
182
}
157
183
158
- tf .Dcrlog .Debug (fmt .Sprintf ("appending node %s to allnodes list" , mongonodestring ))
184
+ tf .Dcrlog .Debug (fmt .Sprintf ("tftf - appending node %s to allnodes list" , mongonodestring ))
159
185
tf .Allnodes .Nodes = append (tf .Allnodes .Nodes , mongonode )
160
186
161
187
}
@@ -168,17 +194,17 @@ func (tf *TopologyFinder) addSeedMongosNode() error {
168
194
seedhostname := tf .MongoshCapture .S .Seedmongodhost
169
195
isSeedHostinList := false
170
196
171
- tf .Dcrlog .Debug ("looking up seed node in the allnodes list" )
197
+ tf .Dcrlog .Debug ("tftf - looking up seed node in the allnodes list" )
172
198
for _ , host := range tf .Allnodes .Nodes {
173
199
if seedhostname == string (host .Hostname ) &&
174
200
seedport == strconv .Itoa (host .Port ) {
175
201
isSeedHostinList = true
176
- tf .Dcrlog .Debug ("found seed node in the allnodes list" )
202
+ tf .Dcrlog .Debug ("tftf - found seed node in the allnodes list" )
177
203
}
178
204
}
179
205
180
206
if ! isSeedHostinList {
181
- tf .Dcrlog .Debug ("seed node not in the list, adding seed node in the allnodes list" )
207
+ tf .Dcrlog .Debug ("tftf - seed node not in the list, adding seed node in the allnodes list" )
182
208
err := tf .addSeedNode ()
183
209
if err != nil {
184
210
return err
@@ -188,7 +214,7 @@ func (tf *TopologyFinder) addSeedMongosNode() error {
188
214
}
189
215
190
216
func (tf * TopologyFinder ) GetAllNodes () error {
191
- tf .Dcrlog .Debug ("building allnodes list for data collection" )
217
+ tf .Dcrlog .Debug ("tftf - building allnodes list for data collection" )
192
218
err := tf .runShardMapDBCommand ()
193
219
if err != nil {
194
220
return err
@@ -199,7 +225,7 @@ func (tf *TopologyFinder) GetAllNodes() error {
199
225
if tf .isShardMap () {
200
226
201
227
tf .Dcrlog .Debug (
202
- "we are connected to sharded cluster proceeding with extracting mongo hostnames" ,
228
+ "tftf - we are connected to sharded cluster proceeding with extracting mongo hostnames" ,
203
229
)
204
230
err := tf .parseShardMapOutput ()
205
231
if err != nil {
@@ -221,6 +247,73 @@ func (tf *TopologyFinder) GetAllNodes() error {
221
247
return nil
222
248
}
223
249
250
+ // find and retain only unique nodes
251
+ func (tf * TopologyFinder ) KeepUniqueNodes () error {
252
+ tf .Dcrlog .Debug ("tftf - will attempt to find if multiple hostnames mapped to single IP" )
253
+ uniqueipfinder := UniqueIPfinder {}
254
+ uniqueipfinder .Dcrlog = tf .Dcrlog
255
+ uniqueipfinder .AllNodes = tf .Allnodes
256
+
257
+ // build list of strings with hostnames and port from the obtained Allnodes
258
+ hostportList := make ([]string , 0 )
259
+ for _ , node := range tf .Allnodes .Nodes {
260
+ hostportList = append (hostportList , node .Hostname + ":" + fmt .Sprintf ("%d" , node .Port ))
261
+ }
262
+
263
+ // generate a set of unique IP address to possible multiple hostnames
264
+ ipportTohostportset , err := uniqueipfinder .IpportTohostportMap (hostportList )
265
+ if err != nil {
266
+ // TODO: add a help message here
267
+ // Allnodes is intact at this point
268
+ return err
269
+ }
270
+
271
+ allNodesNew := make ([]ClusterNode , 0 )
272
+ tf .Allnodes .Nodes = allNodesNew
273
+
274
+ // make split the ip port string and build Allnodes again
275
+ // if there are more than one hostname mapped to single ip keep the hostname which is not internal
276
+ // Allnodes is rebuilt with the hostnames removing duplicates
277
+ for ipportKey , hostportList := range ipportTohostportset {
278
+ if len (hostportList ) == 0 {
279
+ tf .Dcrlog .Warn (fmt .Sprintf (
280
+ "tftf - warn - the ipport %s has no corresponding hostname match hence skipping" ,
281
+ ipportKey ,
282
+ ))
283
+ // TODO: fill uniqueHostname as the IP
284
+ continue
285
+ }
286
+
287
+ // of the possible multiple hostnames from the set keep only the first
288
+ uniqueHostname , uniqueListenPort , err := splitHostPort (hostportList [0 ], tf .Dcrlog )
289
+ if err != nil {
290
+ tf .Dcrlog .Warn (
291
+ fmt .Sprintf (
292
+ "tftf - warn - unable to properly split hosport string: %s" ,
293
+ hostportList [0 ],
294
+ ),
295
+ )
296
+ // TODO: fill uniqueHostname as the IP
297
+ continue
298
+ }
299
+
300
+ // add to the Allnodes
301
+ mongonode := ClusterNode {
302
+ Hostname : uniqueHostname ,
303
+ Port : uniqueListenPort ,
304
+ }
305
+
306
+ tf .Dcrlog .Debug (
307
+ fmt .Sprintf ("tftf - appending node %s to allnodes list" , hostportList [0 ]),
308
+ )
309
+ allNodesNew = append (allNodesNew , mongonode )
310
+ }
311
+
312
+ // replace existing Allnodes
313
+ tf .Allnodes .Nodes = allNodesNew
314
+ return nil
315
+ }
316
+
224
317
func (tf * TopologyFinder ) addSeedNode () error {
225
318
seedport , err := strconv .Atoi (tf .MongoshCapture .S .Seedmongodport )
226
319
if err != nil {
0 commit comments