1 | <?php |
||
2 | |||
3 | /* |
||
4 | * LibreNMS |
||
5 | * |
||
6 | * Copyright (c) 2014 Neil Lathwood <https://github.com/laf/ http://www.lathwood.co.uk/fa> |
||
7 | * |
||
8 | * This program is free software: you can redistribute it and/or modify it |
||
9 | * under the terms of the GNU General Public License as published by the |
||
10 | * Free Software Foundation, either version 3 of the License, or (at your |
||
11 | * option) any later version. Please see LICENSE.txt at the top level of |
||
12 | * the source code distribution for details. |
||
13 | */ |
||
14 | |||
15 | use LibreNMS\Config; |
||
16 | |||
17 | $highlight_node = $vars['highlight_node'] ?? 0; |
||
18 | $group = $vars['group'] ?? 0; |
||
19 | |||
20 | //Don't know where this should come from, but it is used later, so I just define it here. |
||
21 | $row_colour = '#ffffff'; |
||
22 | |||
23 | $sql_array = []; |
||
24 | if (! empty($device['hostname'])) { |
||
25 | $sql = ' AND (`D1`.`hostname`=? OR `D2`.`hostname`=?)'; |
||
26 | $sql_array = [$device['hostname'], $device['hostname']]; |
||
27 | $mac_sql = ' AND `D`.`hostname` = ?'; |
||
28 | $mac_array = [$device['hostname']]; |
||
29 | } else { |
||
30 | $sql = ' '; |
||
31 | } |
||
32 | |||
33 | if (! Auth::user()->hasGlobalRead()) { |
||
0 ignored issues
–
show
Bug
introduced
by
Loading history...
|
|||
34 | $device_ids = Permissions::devicesForUser()->toArray() ?: [0]; |
||
35 | $sql .= ' AND `D1`.`device_id` IN ' . dbGenPlaceholders(count($device_ids)); |
||
36 | $sql .= ' AND `D2`.`device_id` IN ' . dbGenPlaceholders(count($device_ids)); |
||
37 | $sql_array = array_merge($sql_array, $device_ids, $device_ids); |
||
38 | } |
||
39 | |||
40 | $devices_by_id = []; |
||
41 | $links = []; |
||
42 | $link_assoc_seen = []; |
||
43 | $device_assoc_seen = []; |
||
44 | $ports = []; |
||
45 | $devices = []; |
||
46 | |||
47 | $group_name = ''; |
||
48 | $where = ''; |
||
49 | if (is_numeric($group) && $group) { |
||
50 | $group_name = dbFetchCell('SELECT `name` from `device_groups` WHERE `id` = ?', [$group]); |
||
51 | $where .= ' AND D1.device_id IN (SELECT `device_id` FROM `device_group_device` WHERE `device_group_id` = ?)'; |
||
52 | $sql_array[] = $group; |
||
53 | $where .= ' OR D2.device_id IN (SELECT `device_id` FROM `device_group_device` WHERE `device_group_id` = ?)'; |
||
54 | $sql_array[] = $group; |
||
55 | } |
||
56 | |||
57 | if (in_array('mac', Config::get('network_map_items'))) { |
||
58 | $ports = dbFetchRows("SELECT |
||
59 | `D1`.`status` AS `local_status`, |
||
60 | `D1`.`device_id` AS `local_device_id`, |
||
61 | `D1`.`disabled` AS `local_disabled`, |
||
62 | `D1`.`os` AS `local_os`, |
||
63 | `D1`.`hostname` AS `local_hostname`, |
||
64 | `D1`.`sysName` AS `local_sysName`, |
||
65 | `D2`.`status` AS `remote_status`, |
||
66 | `D2`.`device_id` AS `remote_device_id`, |
||
67 | `D2`.`disabled` AS `remote_disabled`, |
||
68 | `D2`.`os` AS `remote_os`, |
||
69 | `D2`.`hostname` AS `remote_hostname`, |
||
70 | `D2`.`sysName` AS `remote_sysName`, |
||
71 | `P1`.`port_id` AS `local_port_id`, |
||
72 | `P1`.`device_id` AS `local_port_device_id`, |
||
73 | `P1`.`ifName` AS `local_ifname`, |
||
74 | `P1`.`ifSpeed` AS `local_ifspeed`, |
||
75 | `P1`.`ifOperStatus` AS `local_ifoperstatus`, |
||
76 | `P1`.`ifAdminStatus` AS `local_ifadminstatus`, |
||
77 | `P1`.`ifInOctets_rate` AS `local_ifinoctets_rate`, |
||
78 | `P1`.`ifOutOctets_rate` AS `local_ifoutoctets_rate`, |
||
79 | `P2`.`port_id` AS `remote_port_id`, |
||
80 | `P2`.`device_id` AS `remote_port_device_id`, |
||
81 | `P2`.`ifName` AS `remote_ifname`, |
||
82 | `P2`.`ifSpeed` AS `remote_ifspeed`, |
||
83 | `P2`.`ifOperStatus` AS `remote_ifoperstatus`, |
||
84 | `P2`.`ifAdminStatus` AS `remote_ifadminstatus`, |
||
85 | `P2`.`ifInOctets_rate` AS `remote_ifinoctets_rate`, |
||
86 | `P2`.`ifOutOctets_rate` AS `remote_ifoutoctets_rate`, |
||
87 | SUM(IF(`P2_ip`.`ipv4_address` = `M`.`ipv4_address`, 1, 0)) |
||
88 | AS `remote_matching_ips` |
||
89 | FROM `ipv4_mac` AS `M` |
||
90 | INNER JOIN `ports` AS `P1` ON `P1`.`port_id`=`M`.`port_id` |
||
91 | INNER JOIN `ports` AS `P2` ON `P2`.`ifPhysAddress`=`M`.`mac_address` |
||
92 | INNER JOIN `devices` AS `D1` ON `P1`.`device_id`=`D1`.`device_id` |
||
93 | INNER JOIN `devices` AS `D2` ON `P2`.`device_id`=`D2`.`device_id` |
||
94 | INNER JOIN `ipv4_addresses` AS `P2_ip` ON `P2_ip`.`port_id` = `P2`.`port_id` |
||
95 | $join_sql |
||
96 | WHERE |
||
97 | `M`.`mac_address` NOT IN ('000000000000','ffffffffffff') AND |
||
98 | `D1`.`device_id` != `D2`.`device_id` |
||
99 | $where |
||
100 | $sql |
||
101 | GROUP BY `P1`.`port_id`,`P2`.`port_id`,`D1`.`device_id`, `D1`.`os`, `D1`.`hostname`, `D2`.`device_id`, `D2`.`os`, `D2`.`hostname`, `P1`.`port_id`, `P1`.`device_id`, `P1`.`ifName`, `P1`.`ifSpeed`, `P1`.`ifOperStatus`, `P1`.`ifAdminStatus`, `P1`.`ifInOctets_rate`, `P1`.`ifOutOctets_rate`, `P2`.`port_id`, `P2`.`device_id`, `P2`.`ifName`, `P2`.`ifSpeed`, `P2`.`ifOperStatus`, `P2`.`ifAdminStatus`, `P2`.`ifInOctets_rate`, `P2`.`ifOutOctets_rate` |
||
102 | ORDER BY `remote_matching_ips` DESC, `local_ifname`, `remote_ifname` |
||
103 | ", $sql_array); |
||
104 | } |
||
105 | |||
106 | if (in_array('xdp', Config::get('network_map_items'))) { |
||
107 | $devices = dbFetchRows("SELECT |
||
108 | `D1`.`status` AS `local_status`, |
||
109 | `D1`.`device_id` AS `local_device_id`, |
||
110 | `D1`.`os` AS `local_os`, |
||
111 | `D1`.`disabled` AS `local_disabled`, |
||
112 | `D1`.`hostname` AS `local_hostname`, |
||
113 | `D1`.`sysName` AS `local_sysName`, |
||
114 | `D2`.`status` AS `remote_status`, |
||
115 | `D2`.`device_id` AS `remote_device_id`, |
||
116 | `D2`.`disabled` AS `remote_disabled`, |
||
117 | `D2`.`os` AS `remote_os`, |
||
118 | `D2`.`hostname` AS `remote_hostname`, |
||
119 | `D2`.`sysName` AS `remote_sysName`, |
||
120 | `P1`.`port_id` AS `local_port_id`, |
||
121 | `P1`.`device_id` AS `local_port_device_id`, |
||
122 | `P1`.`ifName` AS `local_ifname`, |
||
123 | `P1`.`ifSpeed` AS `local_ifspeed`, |
||
124 | `P1`.`ifOperStatus` AS `local_ifoperstatus`, |
||
125 | `P1`.`ifAdminStatus` AS `local_ifadminstatus`, |
||
126 | `P1`.`ifInOctets_rate` AS `local_ifinoctets_rate`, |
||
127 | `P1`.`ifOutOctets_rate` AS `local_ifoutoctets_rate`, |
||
128 | `P2`.`port_id` AS `remote_port_id`, |
||
129 | `P2`.`device_id` AS `remote_port_device_id`, |
||
130 | `P2`.`ifName` AS `remote_ifname`, |
||
131 | `P2`.`ifSpeed` AS `remote_ifspeed`, |
||
132 | `P2`.`ifOperStatus` AS `remote_ifoperstatus`, |
||
133 | `P2`.`ifAdminStatus` AS `remote_ifadminstatus`, |
||
134 | `P2`.`ifInOctets_rate` AS `remote_ifinoctets_rate`, |
||
135 | `P2`.`ifOutOctets_rate` AS `remote_ifoutoctets_rate` |
||
136 | FROM `links` |
||
137 | INNER JOIN `devices` AS `D1` ON `D1`.`device_id`=`links`.`local_device_id` |
||
138 | INNER JOIN `devices` AS `D2` ON `D2`.`device_id`=`links`.`remote_device_id` |
||
139 | INNER JOIN `ports` AS `P1` ON `P1`.`port_id`=`links`.`local_port_id` |
||
140 | INNER JOIN `ports` AS `P2` ON `P2`.`port_id`=`links`.`remote_port_id` |
||
141 | $join_sql |
||
142 | WHERE |
||
143 | `active`=1 AND |
||
144 | `local_device_id` != 0 AND |
||
145 | `remote_device_id` != 0 |
||
146 | $where |
||
147 | $sql |
||
148 | GROUP BY `P1`.`port_id`,`P2`.`port_id`,`D1`.`device_id`, `D1`.`os`, `D1`.`hostname`, `D2`.`device_id`, `D2`.`os`, `D2`.`hostname`, `P1`.`port_id`, `P1`.`device_id`, `P1`.`ifName`, `P1`.`ifSpeed`, `P1`.`ifOperStatus`, `P1`.`ifAdminStatus`, `P1`.`ifInOctets_rate`, `P1`.`ifOutOctets_rate`, `P2`.`port_id`, `P2`.`device_id`, `P2`.`ifName`, `P2`.`ifSpeed`, `P2`.`ifOperStatus`, `P2`.`ifAdminStatus`, `P2`.`ifInOctets_rate`, `P2`.`ifOutOctets_rate` |
||
149 | ORDER BY `local_ifname`, `remote_ifname` |
||
150 | ", $sql_array); |
||
151 | } |
||
152 | |||
153 | $list = array_merge($ports, $devices); |
||
154 | |||
155 | // Build the style variables we need |
||
156 | |||
157 | $node_disabled_style = [ |
||
158 | 'color' => [ |
||
159 | 'highlight' => [ |
||
160 | 'background' => Config::get('network_map_legend.di.node'), |
||
161 | ], |
||
162 | 'border' => Config::get('network_map_legend.di.border'), |
||
163 | 'background' => Config::get('network_map_legend.di.node'), |
||
164 | ], |
||
165 | ]; |
||
166 | $node_down_style = [ |
||
167 | 'color' => [ |
||
168 | 'highlight' => [ |
||
169 | 'background' => Config::get('network_map_legend.dn.node'), |
||
170 | 'border' => Config::get('network_map_legend.dn.border'), |
||
171 | ], |
||
172 | 'border' => Config::get('network_map_legend.dn.border'), |
||
173 | 'background' => Config::get('network_map_legend.dn.node'), |
||
174 | ], |
||
175 | ]; |
||
176 | $node_highlight_style = [ |
||
177 | 'color' => [ |
||
178 | 'highlight' => [ |
||
179 | 'border' => Config::get('network_map_legend.highlight.border'), |
||
180 | ], |
||
181 | 'border' => Config::get('network_map_legend.highlight.border'), |
||
182 | ], |
||
183 | 'borderWidth' => Config::get('network_map_legend.highlight.borderWidth'), |
||
184 | ]; |
||
185 | $edge_disabled_style = [ |
||
186 | 'dashes' => [8, 12], |
||
187 | 'color' => [ |
||
188 | 'color' => Config::get('network_map_legend.di.edge'), |
||
189 | 'highlight' => Config::get('network_map_legend.di.edge'), |
||
190 | ], |
||
191 | ]; |
||
192 | $edge_down_style = [ |
||
193 | 'dashes' => [8, 12], |
||
194 | 'color' => [ |
||
195 | 'border' => Config::get('network_map_legend.dn.border'), |
||
196 | 'highlight' => Config::get('network_map_legend.dn.edge'), |
||
197 | 'color' => Config::get('network_map_legend.dn.edge'), |
||
198 | ], |
||
199 | ]; |
||
200 | |||
201 | // Iterate though ports and links, generating a set of devices (nodes) |
||
202 | // and links (edges) that make up the topology graph. |
||
203 | |||
204 | foreach ($list as $items) { |
||
205 | $local_device = [ |
||
206 | 'device_id'=>$items['local_device_id'], |
||
207 | 'os'=>$items['local_os'], |
||
208 | 'hostname'=>$items['local_hostname'], |
||
209 | ]; |
||
210 | $remote_device = [ |
||
211 | 'device_id'=>$items['remote_device_id'], |
||
212 | 'os'=>$items['remote_os'], |
||
213 | 'hostname'=>$items['remote_hostname'], |
||
214 | ]; |
||
215 | $local_port = [ |
||
216 | 'port_id'=>$items['local_port_id'], |
||
217 | 'device_id'=>$items['local_port_device_id'], |
||
218 | 'ifName'=>$items['local_ifname'], |
||
219 | 'ifSpeed'=>$items['local_ifspeed'], |
||
220 | 'ifOperStatus'=>$items['local_ifoperstatus'], |
||
221 | 'ifAdminStatus'=>$items['local_adminstatus'], |
||
222 | ]; |
||
223 | $remote_port = [ |
||
224 | 'port_id'=>$items['remote_port_id'], |
||
225 | 'device_id'=>$items['remote_port_device_id'], |
||
226 | 'ifName'=>$items['remote_ifname'], |
||
227 | 'ifSpeed'=>$items['remote_ifspeed'], |
||
228 | 'ifOperStatus'=>$items['remote_ifoperstatus'], |
||
229 | 'ifAdminStatus'=>$items['remote_adminstatus'], |
||
230 | ]; |
||
231 | |||
232 | $local_device_id = $items['local_device_id']; |
||
233 | if (! array_key_exists($local_device_id, $devices_by_id)) { |
||
234 | $items['sysName'] = $items['local_sysName']; |
||
235 | $devices_by_id[$local_device_id] = [ |
||
236 | 'id'=>$local_device_id, |
||
237 | 'label'=>shorthost(format_hostname($items, $items['local_hostname']), 1), |
||
238 | 'title'=>generate_device_link($local_device, '', [], '', '', '', 0), |
||
239 | 'shape'=>'box', |
||
240 | ]; |
||
241 | if ($items['local_disabled'] != '0') { |
||
242 | $devices_by_id[$local_device_id] = array_merge($devices_by_id[$local_device_id], $node_disabled_style); |
||
243 | } elseif ($items['local_status'] == '0') { |
||
244 | $devices_by_id[$local_device_id] = array_merge($devices_by_id[$local_device_id], $node_down_style); |
||
245 | } |
||
246 | |||
247 | if ((empty($device['hostname'])) && ($local_device_id == $highlight_node)) { |
||
248 | $devices_by_id[$local_device_id] = array_merge($devices_by_id[$local_device_id], $node_highlight_style); |
||
249 | } |
||
250 | } |
||
251 | |||
252 | $remote_device_id = $items['remote_device_id']; |
||
253 | if (! array_key_exists($remote_device_id, $devices_by_id)) { |
||
254 | $items['sysName'] = $items['remote_sysName']; |
||
255 | $devices_by_id[$remote_device_id] = ['id'=>$remote_device_id, 'label'=>shorthost(format_hostname($items, $items['remote_hostname']), 1), 'title'=>generate_device_link($remote_device, '', [], '', '', '', 0), 'shape'=>'box']; |
||
256 | if ($items['remote_disabled'] != '0') { |
||
257 | $devices_by_id[$remote_device_id] = array_merge($devices_by_id[$remote_device_id], $node_disabled_style); |
||
258 | } elseif ($items['remote_status'] == '0') { |
||
259 | $devices_by_id[$remote_device_id] = array_merge($devices_by_id[$remote_device_id], $node_down_style); |
||
260 | } |
||
261 | |||
262 | if ((empty($device['hostname'])) && ($remote_device_id == $highlight_node)) { |
||
263 | $devices_by_id[$remote_device_id] = array_merge($devices_by_id[$remote_device_id], $node_highlight_style); |
||
264 | } |
||
265 | } |
||
266 | |||
267 | $speed = $items['local_ifspeed'] / 1000 / 1000; |
||
268 | if ($speed > 500000) { |
||
269 | $width = 20; |
||
270 | } else { |
||
271 | $width = round(0.77 * pow($speed, 0.25)); |
||
272 | } |
||
273 | $link_in_used = $items['local_ifspeed'] ? (($items['local_ifinoctets_rate'] * 8) / $items['local_ifspeed'] * 100) : 0; |
||
274 | $link_out_used = $items['local_ifspeed'] ? (($items['local_ifoutoctets_rate'] * 8) / $items['local_ifspeed'] * 100) : 0; |
||
275 | if ($link_in_used > $link_out_used) { |
||
276 | $link_used = $link_in_used; |
||
277 | } else { |
||
278 | $link_used = $link_out_used; |
||
279 | } |
||
280 | $link_used = round(2 * $link_used, -1) / 2; |
||
281 | if ($link_used > 100) { |
||
282 | $link_used = 100; |
||
283 | } |
||
284 | if (is_nan($link_used)) { |
||
285 | $link_used = 0; |
||
286 | } |
||
287 | $link_style = [ |
||
288 | 'color' => [ |
||
289 | 'border' => Config::get("network_map_legend.$link_used"), |
||
290 | 'highlight' => Config::get("network_map_legend.$link_used"), |
||
291 | 'color' => Config::get("network_map_legend.$link_used"), |
||
292 | ], |
||
293 | ]; |
||
294 | |||
295 | if (($items['remote_ifoperstatus'] == 'down') || ($items['local_ifoperstatus'] == 'down')) { |
||
296 | $link_style = $edge_down_style; |
||
297 | } |
||
298 | if (($items['remote_disabled'] != '0') && ($items['local_disabled'] != '0')) { |
||
299 | $link_style = $edge_disabled_style; |
||
300 | } elseif (($items['remote_status'] == '0') && ($items['local_status'] == '0')) { |
||
301 | $link_style = $edge_down_style; |
||
302 | } elseif (($items['remote_status'] == '1' && $items['remote_ifoperstatus'] == 'down') || ($items['local_status'] == '1' && $items['local_ifoperstatus'] == 'down')) { |
||
303 | $link_style = $edge_down_style; |
||
304 | } |
||
305 | |||
306 | $link_id1 = $items['local_port_id'] . ':' . $items['remote_port_id']; |
||
307 | $link_id2 = $items['remote_port_id'] . ':' . $items['local_port_id']; |
||
308 | $device_id1 = $items['local_device_id'] . ':' . $items['remote_device_id']; |
||
309 | $device_id2 = $items['remote_device_id'] . ':' . $items['local_device_id']; |
||
310 | |||
311 | // If mac is choosen to graph, ensure only one link exists between any two ports, or any two devices. |
||
312 | // else ensure only one link exists between any two ports |
||
313 | if (! array_key_exists($link_id1, $link_assoc_seen) && |
||
314 | ! array_key_exists($link_id2, $link_assoc_seen) && |
||
315 | (! in_array('mac', Config::get('network_map_items')) || |
||
316 | (! array_key_exists($device_id1, $device_assoc_seen) && |
||
317 | ! array_key_exists($device_id2, $device_assoc_seen)))) { |
||
318 | $local_port = cleanPort($local_port); |
||
319 | $remote_port = cleanPort($remote_port); |
||
320 | $links[] = array_merge( |
||
321 | [ |
||
322 | 'from'=>$items['local_device_id'], |
||
323 | 'to'=>$items['remote_device_id'], |
||
324 | 'label'=> \LibreNMS\Util\Rewrite::shortenIfType($local_port['ifName']) . ' > ' . \LibreNMS\Util\Rewrite::shortenIfType($remote_port['ifName']), |
||
325 | 'title' => generate_port_link($local_port, "<img src='graph.php?type=port_bits&id=" . $items['local_port_id'] . '&from=' . Config::get('time.day') . '&to=' . Config::get('time.now') . '&width=100&height=20&legend=no&bg=' . str_replace('#', '', $row_colour) . "'>\n", '', 0, 1), |
||
326 | 'width'=>$width, |
||
327 | ], |
||
328 | $link_style |
||
329 | ); |
||
330 | } |
||
331 | $link_assoc_seen[$link_id1] = 1; |
||
332 | $link_assoc_seen[$link_id2] = 1; |
||
333 | $device_assoc_seen[$device_id1] = 1; |
||
334 | $device_assoc_seen[$device_id2] = 1; |
||
335 | } |
||
336 | |||
337 | $nodes = json_encode(array_values($devices_by_id)); |
||
338 | $edges = json_encode($links); |
||
339 | |||
340 | array_multisort(array_column($devices_by_id, 'label'), SORT_ASC, $devices_by_id); |
||
341 | |||
342 | if (count($devices_by_id) > 1 && count($links) > 0) { |
||
343 | ?> |
||
344 | |||
345 | <form name="printmapform" method="get" action="" class="form-horizontal" role="form"> |
||
346 | <?php if (empty($device['hostname'])) { ?> |
||
347 | <big><b><?=$group_name?></b></big> |
||
348 | <div class="pull-right"> |
||
349 | <select name="highlight_node" id="highlight_node" class="input-sm" onChange="highlightNode()";> |
||
350 | <option value="0">None</option> |
||
351 | <?php foreach ($devices_by_id as $dev) { ?> |
||
352 | <option value="<?=$dev['id']?>"><?=$dev['label']?></option> |
||
353 | <?php } ?> |
||
354 | </select> |
||
355 | </div> |
||
356 | <?php } ?> |
||
357 | <div id="visualization"></div> |
||
358 | </form> |
||
359 | <script src="js/vis.min.js"></script> |
||
360 | <script type="text/javascript"> |
||
361 | var height = $(window).height() - 100; |
||
362 | $('#visualization').height(height + 'px'); |
||
363 | // create an array with nodes |
||
364 | var nodes = |
||
365 | <?php |
||
366 | echo $nodes; ?> |
||
367 | ; |
||
368 | |||
369 | // create an array with edges |
||
370 | var edges = |
||
371 | <?php |
||
372 | echo $edges; ?> |
||
373 | ; |
||
374 | |||
375 | // create a network |
||
376 | var container = document.getElementById('visualization'); |
||
377 | var data = { |
||
378 | nodes: nodes, |
||
379 | edges: edges, |
||
380 | stabilize: true |
||
381 | }; |
||
382 | var options = <?php echo Config::get('network_map_vis_options'); ?>; |
||
383 | var network = new vis.Network(container, data, options); |
||
384 | network.on('click', function (properties) { |
||
385 | if (properties.nodes > 0) { |
||
386 | window.location.href = "device/device="+properties.nodes+"/tab=neighbours/selection=map/" |
||
387 | } |
||
388 | }); |
||
389 | |||
390 | function highlightNode(e) { |
||
391 | highlight_node = document.getElementById("highlight_node").value; |
||
392 | <?php |
||
393 | if ($group) { |
||
394 | echo "window.location.pathname = 'map/group=" . $group . "/highlight_node=' + highlight_node;"; |
||
395 | } else { |
||
396 | echo "window.location.pathname = 'map/highlight_node=' + highlight_node;"; |
||
397 | } ?> |
||
398 | } |
||
399 | |||
400 | $('#highlight_node option[value="<?=$highlight_node?>"]').prop('selected', true); |
||
401 | </script> |
||
402 | |||
403 | <?php |
||
404 | } else { |
||
405 | print_message("No map to display, this may be because you aren't running autodiscovery or no devices are linked by mac address."); |
||
406 | } |
||
407 | |||
408 | $pagetitle[] = 'Map'; |
||
409 |