function ios15ShortenIfName_real added to handle port-channel interfaces names correc...
[racktables] / wwwroot / inc / remote.php
1 <?php
2
3 # This file is a part of RackTables, a datacenter and server room management
4 # framework. See accompanying file "COPYING" for the full copyright and
5 # licensing information.
6
7 # The array below maps availability of particular commands for each particular
8 # device breed. Array values are functions implemented in deviceconfig.php, which
9 # is not normally included until any of the functions is actually called.
10 $breedfunc = array
11 (
12 'ios12-getcdpstatus-main' => 'ios12ReadCDPStatus',
13 'ios12-getlldpstatus-main' => 'ios12ReadLLDPStatus',
14 'ios12-get8021q-main' => 'ios12ReadVLANConfig',
15 'ios12-get8021q-swports' => 'ios12ReadSwitchPortList',
16 'ios12-get8021q-top' => 'ios12ScanTopLevel',
17 'ios12-get8021q-readport' => 'ios12PickSwitchportCommand',
18 'ios12-get8021q-readvlan' => 'ios12PickVLANCommand',
19 'ios12-getportstatus-main' => 'ciscoReadInterfaceStatus',
20 'ios12-getmaclist-main' => 'ios12ReadMacList',
21 'ios12-getportmaclist-main'=> 'ios12ReadMacList',
22 'ios12-xlatepushq-main' => 'ios12TranslatePushQueue',
23 'ios12-getallconf-main' => 'ios12SpotConfigText',
24 'fdry5-get8021q-main' => 'fdry5ReadVLANConfig',
25 'fdry5-get8021q-top' => 'fdry5ScanTopLevel',
26 'fdry5-get8021q-readvlan' => 'fdry5PickVLANSubcommand',
27 'fdry5-get8021q-readport' => 'fdry5PickInterfaceSubcommand',
28 'fdry5-xlatepushq-main' => 'fdry5TranslatePushQueue',
29 'fdry5-getallconf-main' => 'fdry5SpotConfigText',
30 'fdry5-getportstatus-main' => 'foundryReadInterfaceStatus',
31 'fdry5-getmaclist-main' => 'fdry5ReadMacList',
32 'fdry5-getportmaclist-main'=> 'fdry5ReadMacList',
33 'vrp53-getlldpstatus-main' => 'vrpReadLLDPStatus',
34 'vrp53-get8021q-main' => 'vrp53ReadVLANConfig',
35 'vrp53-get8021q-top' => 'vrp53ScanTopLevel',
36 'vrp53-get8021q-readport' => 'vrp53PickInterfaceSubcommand',
37 'vrp53-getportstatus-main' => 'vrpReadInterfaceStatus',
38 'vrp53-getmaclist-main' => 'vrp53ReadMacList',
39 'vrp53-getportmaclist-main'=> 'vrp53ReadMacList',
40 'vrp53-xlatepushq-main' => 'vrp53TranslatePushQueue',
41 'vrp53-getallconf-main' => 'vrpSpotConfigText',
42 'vrp55-getlldpstatus-main' => 'vrpReadLLDPStatus',
43 'vrp55-get8021q-main' => 'vrp55Read8021QConfig',
44 'vrp55-getportstatus-main' => 'vrpReadInterfaceStatus',
45 'vrp55-getmaclist-main' => 'vrpReadMacList',
46 'vrp55-getportmaclist-main'=> 'vrpReadMacList',
47 'vrp55-xlatepushq-main' => 'vrp55TranslatePushQueue',
48 'vrp55-getallconf-main' => 'vrpSpotConfigText',
49 'vrp85-getlldpstatus-main' => 'vrpReadLLDPStatus',
50 'vrp85-get8021q-main' => 'vrp85Read8021QConfig',
51 'vrp85-getportstatus-main' => 'vrpReadInterfaceStatus',
52 'vrp85-getmaclist-main' => 'vrpReadMacList',
53 'vrp85-getportmaclist-main'=> 'vrpReadMacList',
54 'vrp85-xlatepushq-main' => 'vrp85TranslatePushQueue',
55 'vrp85-getallconf-main' => 'vrpSpotConfigText',
56 'nxos4-getcdpstatus-main' => 'ios12ReadCDPStatus',
57 'nxos4-getlldpstatus-main' => 'ios12ReadLLDPStatus',
58 'nxos4-get8021q-main' => 'ios12ReadVLANConfig',
59 'nxos4-getportstatus-main' => 'ciscoReadInterfaceStatus',
60 'nxos4-getmaclist-main' => 'nxos4ReadMacList',
61 'nxos4-getportmaclist-main'=> 'nxos4ReadMacList',
62 'nxos4-xlatepushq-main' => 'nxos4TranslatePushQueue',
63 'nxos4-getallconf-main' => 'nxos4SpotConfigText',
64 'dlink-get8021q-main' => 'dlinkReadVLANConfig',
65 'dlink-get8021q-top' => 'dlinkScanTopLevel',
66 'dlink-get8021q-pickvlan' => 'dlinkPickVLANCommand',
67 'dlink-getportstatus-main' => 'dlinkReadInterfaceStatus',
68 'dlink-getmaclist-main' => 'dlinkReadMacList',
69 'dlink-getportmaclist-main'=> 'dlinkReadMacList',
70 'dlink-xlatepushq-main' => 'dlinkTranslatePushQueue',
71 'linux-get8021q-main' => 'linuxReadVLANConfig',
72 'linux-getportstatus-main' => 'linuxReadInterfaceStatus',
73 'linux-getmaclist-main' => 'linuxReadMacList',
74 'linux-getportmaclist-main'=> 'linuxReadMacList',
75 'linux-xlatepushq-main' => 'linuxTranslatePushQueue',
76 'xos12-getlldpstatus-main' => 'xos12ReadLLDPStatus',
77 'xos12-get8021q-main' => 'xos12Read8021QConfig',
78 'xos12-xlatepushq-main' => 'xos12TranslatePushQueue',
79 'xos12-getallconf-main' => 'xos12SpotConfigText',
80 'xos12-getportstatus-main' => 'xos12ReadInterfaceStatus',
81 'xos12-getmaclist-main' => 'xos12ReadMacList',
82 'xos12-getportmaclist-main' => 'xos12ReadMacList',
83 'jun10-get8021q-main' => 'jun10Read8021QConfig',
84 'jun10-xlatepushq-main' => 'jun10TranslatePushQueue',
85 'jun10-getallconf-main' => 'jun10SpotConfigText',
86 'jun10-getlldpstatus-main' => 'jun10ReadLLDPStatus',
87 'jun10-getportstatus-main' => 'jun10ReadInterfaceStatus',
88 'ftos8-xlatepushq-main' => 'ftos8TranslatePushQueue',
89 'ftos8-getlldpstatus-main' => 'ftos8ReadLLDPStatus',
90 'ftos8-getmaclist-main' => 'ftos8ReadMacList',
91 'ftos8-getportmaclist-main'=> 'ftos8ReadMacList',
92 'ftos8-getportstatus-main' => 'ftos8ReadInterfaceStatus',
93 'ftos8-get8021q-main' => 'ftos8Read8021QConfig',
94 'ftos8-getallconf-main' => 'ftos8SpotConfigText',
95 'air12-xlatepushq-main' => 'air12TranslatePushQueue',
96 'air12-getallconf-main' => 'ios12SpotConfigText',
97 'air12-getcdpstatus-main' => 'ios12ReadCDPStatus',
98 'eos4-getallconf-main' => 'eos4SpotConfigText',
99 'eos4-getmaclist-main' => 'eos4ReadMacList',
100 'eos4-getportmaclist-main' => 'eos4ReadMacList',
101 'eos4-getportstatus-main' => 'eos4ReadInterfaceStatus',
102 'eos4-getlldpstatus-main' => 'eos4ReadLLDPStatus',
103 'eos4-get8021q-main' => 'eos4Read8021QConfig',
104 'eos4-xlatepushq-main' => 'eos4TranslatePushQueue',
105 'ros11-getallconf-main' => 'ros11SpotConfigText',
106 'ros11-xlatepushq-main' => 'ros11TranslatePushQueue',
107 'ros11-getlldpstatus-main' => 'ros11ReadLLDPStatus',
108 'ros11-getportstatus-main' => 'ros11ReadInterfaceStatus',
109 'ros11-getmaclist-main' => 'ros11ReadMacList',
110 'ros11-getportmaclist-main'=> 'ros11ReadMacList',
111 'ros11-get8021q-main' => 'ros11Read8021QConfig',
112 'ros11-get8021q-scantop' => 'ros11Read8021QScanTop',
113 'ros11-get8021q-vlandb' => 'ros11Read8021QVLANDatabase',
114 'ros11-get8021q-readports' => 'ros11Read8021QPorts',
115 'iosxr4-xlatepushq-main' => 'iosxr4TranslatePushQueue',
116 'iosxr4-getallconf-main' => 'iosxr4SpotConfigText',
117 'iosxr4-getlldpstatus-main'=> 'iosxr4ReadLLDPStatus',
118 'iosxr4-getportstatus-main'=> 'iosxr4ReadInterfaceStatus',
119 'ucs-xlatepushq-main' => 'ucsTranslatePushQueue',
120 'ucs-getinventory-main' => 'ucsReadInventory',
121 'hpprocurveN1178-getlldpstatus-main' => 'hpprocurveN1178ReadLLDPStatus',
122 'hpprocurveN1178-get8021q-main' => 'hpprocurveN1178Read8021QConfig',
123 'hpprocurveN1178-getportstatus-main' => 'hpprocurveN1178ReadInterfaceStatus',
124 'hpprocurveN1178-getmaclist-main' => 'hpprocurveN1178ReadMacList',
125 'hpprocurveN1178-getportmaclist-main'=> 'hpprocurveN1178ReadMacList',
126 'hpprocurveN1178-xlatepushq-main' => 'hpprocurveN1178TranslatePushQueue',
127 'hpprocurveN1178-getallconf-main' => 'hpprocurveN1178SpotConfigText',
128 'ios15-getcdpstatus-main' => 'ios12ReadCDPStatus',
129 'ios15-getlldpstatus-main' => 'ios15ReadLLDPStatus',
130 'ios15-get8021q-main' => 'ios12ReadVLANConfig',
131 'ios15-get8021q-swports' => 'ios12ReadSwitchPortList',
132 'ios15-get8021q-top' => 'ios12ScanTopLevel',
133 'ios15-get8021q-readport' => 'ios12PickSwitchportCommand',
134 'ios15-get8021q-readvlan' => 'ios12PickVLANCommand',
135 'ios15-getportstatus-main' => 'ciscoReadInterfaceStatus',
136 'ios15-getmaclist-main' => 'ios12ReadMacList',
137 'ios15-getportmaclist-main'=> 'ios12ReadMacList',
138 'ios15-xlatepushq-main' => 'ios15TranslatePushQueue',
139 'ios15-getallconf-main' => 'ios12SpotConfigText',
140 );
141
142 define ('MAX_GW_LOGSIZE', 1024*1024); // do not store more than 1 MB of log data
143
144 $breed_by_swcode = array
145 (
146 244 => 'ios12', // IOS 12.0
147 251 => 'ios12', // IOS 12.1
148 252 => 'ios12', // IOS 12.2
149 254 => 'ios12', // IOS 12.0 (router OS)
150 255 => 'ios12', // IOS 12.1 (router OS)
151 256 => 'ios12', // IOS 12.2 (router OS)
152 257 => 'ios12', // IOS 12.3 (router OS)
153 258 => 'ios12', // IOS 12.4 (router OS)
154 1901 => 'ios15', // IOS 15.0 (switch)
155 2082 => 'ios15', // IOS 15.1 (switch)
156 2142 => 'ios15', // IOS 15.2 (switch)
157 2667 => 'ios15', // IOS 15.0 (router OS)
158 1963 => 'ios15', // IOS 15.1 (router OS)
159 2668 => 'ios15', // IOS 15.2 (router OS)
160 2669 => 'ios15', // IOS 15.3 (router OS)
161 2670 => 'ios15', // IOS 15.4 (router OS)
162 2671 => 'ios15', // IOS 15.5 (router OS)
163 963 => 'nxos4', // NX-OS 4.0
164 964 => 'nxos4', // NX-OS 4.1
165 1365 => 'nxos4', // NX-OS 4.2
166 1410 => 'nxos4', // NX-OS 5.0
167 1411 => 'nxos4', // NX-OS 5.1
168 1809 => 'nxos4', // NX-OS 5.2
169 1643 => 'nxos4', // NX-OS 6.0
170 2028 => 'nxos4', // NX-OS 6.1
171 1352 => 'xos12', // Extreme XOS 12
172 1360 => 'vrp53', // Huawei VRP 5.3
173 1361 => 'vrp55', // Huawei VRP 5.5
174 1369 => 'vrp55', // Huawei VRP 5.7
175 2080 => 'vrp55', // Huawei VRP 5.11
176 2081 => 'vrp55', // Huawei VRP 5.12
177 2027 => 'vrp85', // Huawei VRP 8.5
178 1363 => 'fdry5', // IronWare 5
179 1364 => 'fdry5', // Brocade FastIron LS648
180 1367 => 'jun10', // JunOS 10, switch
181 2151 => 'jun10', // JunOS 11, switch
182 2152 => 'jun10', // JunOS 12, switch
183 2397 => 'jun10', // JunOS 13, switch
184 2398 => 'jun10', // JunOS 14, switch
185 2399 => 'jun10', // JunOS 15, switch
186 1597 => 'jun10', // JunOS 10
187 1598 => 'jun10', // JunOS 11
188 1599 => 'jun10', // JunOS 12
189 2400 => 'jun10', // JunOS 13
190 2401 => 'jun10', // JunOS 14
191 2402 => 'jun10', // JunOS 15
192 1594 => 'ftos8', // Force10 FTOS 8
193 1673 => 'air12', // AIR IOS 12.3
194 1674 => 'air12', // AIR IOS 12.4
195 1675 => 'eos4', // Arista EOS 4
196 1759 => 'iosxr4', // Cisco IOS XR 4.2
197 1786 => 'ros11', // Marvell ROS 1.1
198 3720 => 'hpprocurveN1178', // HP Procurve 11
199 //... linux items added by the loop below
200 );
201
202 $shorten_by_breed = array (
203 'ios12' => 'ios12ShortenIfName_real',
204 'ios15' => 'ios15ShortenIfName_real',
205 'nxos4' => 'nxos4ShortenIfName',
206 'vrp53' => 'vrp5xShortenIfName',
207 'vrp55' => 'vrp5xShortenIfName',
208 'vrp85' => 'vrp85ShortenIfName',
209 'iosxr4' => 'iosxr4ShortenIfName',
210 );
211
212 $breed_by_hwcode = array (
213 1362 => 'fdry5', // Brocade FastIron CX648
214 //... dlink items added by the loop below
215 );
216
217 $breed_by_mgmtcode = array (
218 1788 => 'ucs',
219 );
220
221 // add 'linux' items into $breed_by_swcode
222 $linux_sw_ranges = array (
223 225,235,
224 418,436,
225 1331,1334,
226 1395,1396,
227 1417,1422,
228 1704,1709,
229 );
230 for ($i = 0; $i + 1 < count ($linux_sw_ranges); $i += 2)
231 for ($j = $linux_sw_ranges[$i]; $j <= $linux_sw_ranges[$i + 1]; $j++)
232 $breed_by_swcode[$j] = 'linux';
233
234 // add 'dlink' items into $breed_by_hwcode
235 for ($i = 589; $i <= 637; $i++)
236 $breed_by_hwcode[$i] = 'dlink';
237
238 function detectDeviceBreed ($object_id)
239 {
240 global $breed_by_swcode, $breed_by_hwcode, $breed_by_mgmtcode;
241 foreach (getAttrValues ($object_id) as $record)
242 if ($record['id'] == 4 && array_key_exists ($record['key'], $breed_by_swcode))
243 return $breed_by_swcode[$record['key']];
244 elseif ($record['id'] == 2 && array_key_exists ($record['key'], $breed_by_hwcode))
245 return $breed_by_hwcode[$record['key']];
246 elseif ($record['id'] == 30 && array_key_exists ($record['key'], $breed_by_mgmtcode))
247 return $breed_by_mgmtcode[$record['key']];
248 return '';
249 }
250
251 function assertDeviceBreed ($object_id)
252 {
253 if ('' == $breed = detectDeviceBreed ($object_id))
254 throw new RTGatewayError ('Cannot determine device breed');
255 return $breed;
256 }
257
258 function validBreedFunction ($breed, $command)
259 {
260 global $breedfunc;
261 return array_key_exists ("${breed}-${command}-main", $breedfunc);
262 }
263
264 function assertBreedFunction ($breed, $command)
265 {
266 global $breedfunc;
267 if (! validBreedFunction ($breed, $command))
268 throw new RTGatewayError ("unsupported command '${command}' for breed '${breed}'");
269 return $breedfunc["${breed}-${command}-main"];
270 }
271
272 function queryDevice ($object_id, $command, $args = array())
273 {
274 $ret = NULL;
275 $request = array ('opcode' => $command);
276 if (is_array ($args) && count ($args))
277 {
278 $i = 1;
279 foreach ($args as $arg)
280 {
281 $request["arg$i"] = $arg;
282 $i++;
283 }
284 }
285 $query = translateDeviceCommands ($object_id, array ($request));
286 if ($command == 'xlatepushq')
287 return $query;
288 $breed = assertDeviceBreed ($object_id);
289 $funcname = assertBreedFunction ($breed, $command);
290 require_once 'deviceconfig.php';
291 if (! is_callable ($funcname))
292 throw new RTGatewayError ("undeclared function '${funcname}'");
293
294 global $current_query_breed;
295 $current_query_breed = $breed; // this global is used to auto-detect breed in shortenIfName
296 for ($i = 0; $i < 3; $i++)
297 try
298 {
299 $ret = $funcname (queryTerminal ($object_id, $query, FALSE));
300 break;
301 }
302 catch (ERetryNeeded $e)
303 {
304 // some devices (e.g. Cisco IOS) refuse to print running configuration
305 // while they are busy. The best way of treating this is retry a few times
306 // before failing the request
307 sleep (3);
308 continue;
309 }
310 catch (Exception $e)
311 {
312 $current_query_breed = NULL;
313 throw $e;
314 }
315 $current_query_breed = NULL;
316
317 if (NULL !== ($subst = callHook ('alterDeviceQueryResult', $ret, $object_id, $command)))
318 $ret = $subst;
319 if (! isset ($ret))
320 throw new RTGatewayError ("No result from $command");
321 return $ret;
322 }
323
324 function translateDeviceCommands ($object_id, $crq, $vlan_names = NULL)
325 {
326 $breed = assertDeviceBreed ($object_id);
327 $funcname = assertBreedFunction ($breed, 'xlatepushq');
328 require_once 'deviceconfig.php';
329 if (! is_callable ($funcname))
330 throw new RTGatewayError ("undeclared function '${funcname}'");
331 global $current_query_breed;
332 $current_query_breed = $breed; // this global is used to auto-detect breed in shortenIfName
333 try
334 {
335 $ret = $funcname ($object_id, $crq, $vlan_names);
336 }
337 catch (Exception $e)
338 {
339 $current_query_breed = NULL;
340 throw $e;
341 }
342 $current_query_breed = NULL;
343 return $ret;
344 }
345
346 // takes settings struct (declared in queryTerminal) and CLI commands (plain text) as input by reference
347 // returns an array of command-line parameters to $ref_settings[0]['protocol']
348 // this function is called by callHook, so you can override/chain it
349 // to customize command-line options to particular gateways.
350 function makeGatewayParams ($object_id, /*array(&)*/$ref_settings, /*array(&)*/$ref_commands)
351 {
352 $ret = array();
353 $settings = &$ref_settings[0];
354 $commands = &$ref_commands[0];
355
356 $prepend_credentials = FALSE;
357 switch ($settings['protocol'])
358 {
359 case 'telnet':
360 $prepend_credentials = TRUE;
361 $params_from_settings['port'] = 'port';
362 $params_from_settings['prompt'] = 'prompt';
363 $params_from_settings['connect-timeout'] = 'connect_timeout';
364 $params_from_settings['timeout'] = 'timeout';
365 $params_from_settings['prompt-delay'] = 'prompt_delay';
366 if (isset ($settings['proto']))
367 switch ($settings['proto'])
368 {
369 case 4:
370 $params_from_settings[] = '-4';
371 break;
372 case 6:
373 $params_from_settings[] = '-6';
374 break;
375 default:
376 throw new RTGatewayError ("Proto '${settings['proto']}' is invalid. Valid protocols are: '4', '6'");
377 }
378 $params_from_settings[] = $settings['hostname'];
379 break;
380 case 'netcat':
381 $prepend_credentials = TRUE;
382 $params_from_settings['p'] = 'port';
383 $params_from_settings['w'] = 'timeout';
384 $params_from_settings['b'] = 'ncbin';
385 $params_from_settings[] = $settings['hostname'];
386 break;
387 case 'sshnokey':
388 $prepend_credentials = TRUE;
389 $params_from_settings['proto'] = 'proto';
390 $params_from_settings['prompt'] = 'prompt';
391 $params_from_settings['prompt-delay'] = 'prompt_delay';
392 $params_from_settings['username'] = 'username';
393 $params_from_settings['password'] = 'password';
394 $params_from_settings[] = $settings['hostname'];
395 break;
396 case 'ssh':
397 $params_from_settings['sudo-user'] = 'sudo_user';
398 $params_from_settings[] = '--';
399 $params_from_settings['p'] = 'port';
400 $params_from_settings['l'] = 'username';
401 $params_from_settings['i'] = 'identity_file';
402 if (isset ($settings['proto']))
403 switch ($settings['proto'])
404 {
405 case 4:
406 $params_from_settings[] = '-4';
407 break;
408 case 6:
409 $params_from_settings[] = '-6';
410 break;
411 default:
412 throw new RTGatewayError ("Proto '${settings['proto']}' is invalid. Valid protocols are: '4', '6'");
413 }
414 if (isset ($settings['connect_timeout']))
415 $params_from_settings[] = '-oConnectTimeout=' . $settings['connect_timeout'];
416 $params_from_settings[] = '-T';
417 $params_from_settings[] = '-oStrictHostKeyChecking=no';
418 $params_from_settings[] = '-oBatchMode=yes';
419 $params_from_settings[] = '-oCheckHostIP=no';
420 $params_from_settings[] = '-oLogLevel=ERROR';
421 $params_from_settings[] = $settings['hostname'];
422 break;
423 case 'ucssdk': # remote XML through a Python backend
424 # UCS in its current implementation besides the terminal_settings() provides
425 # an additional username/password feed through the HTML form. Whenever the
426 # user provides the credentials through the form, use these instead of the
427 # credentials [supposedly] set by terminal_settings().
428 global $script_mode;
429 if ($script_mode != TRUE && ! isCheckSet ('use_terminal_settings'))
430 {
431 $settings['username'] = assertStringArg ('ucs_login');
432 $settings['password'] = assertStringArg ('ucs_password');
433 }
434 foreach (array ('hostname', 'username', 'password') as $item)
435 if (empty ($settings[$item]))
436 throw new RTGatewayError ("${item} not available, check terminal_settings()");
437 $commands = "login ${settings['hostname']} ${settings['username']} ${settings['password']}\n" . $commands;
438 break;
439 default:
440 throw new RTGatewayError ("Invalid terminal protocol '${settings['protocol']}' specified");
441 }
442
443 // prepend commands by credentials
444 if ($prepend_credentials)
445 {
446 if (isset ($settings['password']))
447 $commands = $settings['password'] . "\n" . $commands;
448 if (isset ($settings['username']))
449 $commands = $settings['username'] . "\n" . $commands;
450 }
451
452 foreach ($params_from_settings as $param_name => $setting_name)
453 if (is_int ($param_name))
454 $ret[] = $setting_name;
455 elseif (isset ($settings[$setting_name]))
456 $ret[$param_name] = $settings[$setting_name];
457
458 return $ret;
459 }
460
461 // This function returns a text output received from the device
462 // You can override connection settings by implement a callback named 'terminal_settings'.
463 // Errors are thrown as exceptions if not $tolerate_remote_errors, and shown as warnings otherwise.
464 function queryTerminal ($object_id, $commands, $tolerate_remote_errors = TRUE)
465 {
466 $objectInfo = spotEntity ('object', $object_id);
467 $endpoints = findAllEndpoints ($object_id, $objectInfo['name']);
468 if (count ($endpoints) == 0)
469 throw new RTGatewayError ('no management address set');
470 if (count ($endpoints) > 1)
471 throw new RTGatewayError ('cannot pick management address');
472
473 $hide_warnings = FALSE;
474
475 // telnet prompt and mode specification
476 switch ($breed = detectDeviceBreed ($object_id))
477 {
478 case 'ios12':
479 case 'air12':
480 case 'ftos8':
481 $protocol = 'netcat'; // default is netcat mode
482 if ($breed == 'air12')
483 $protocol = 'telnet'; # Aironet IOS is broken
484 $prompt = '^(Login|[Uu]sername|Password): $|^\S+[>#]$|\[[^][]*\]\? $|\?\s+\[[^][]*\]\s*$|\[confirm yes\/no\]: $'; // set the prompt in case user would like to specify telnet protocol
485 $commands = "terminal length 0\nterminal no monitor\n" . $commands;
486 break;
487 case 'fdry5':
488 $protocol = 'netcat'; // default is netcat mode
489 $prompt = '^(Login|Username|Password|Please Enter Password): $|^\S+[>#]$'; // set the prompt in case user would like to specify telnet protocol
490 $commands = "skip-page-display\n" . $commands;
491 # Using ssh and sshnokey will always result in 'Connection to $ip closed by remote host.' upon exit.
492 # let's hide the warnings
493 $tolerate_remote_errors = TRUE;
494 $hide_warnings = TRUE;
495 break;
496 case 'vrp55':
497 $commands = "terminal echo-mode line\n" . $commands;
498 /* fall-through */
499 case 'vrp85':
500 $commands = "screen-length 0 temporary\n" . $commands;
501 /* fall-through */
502 case 'vrp53':
503 $protocol = 'telnet';
504 $prompt = '^\[[^[\]]+\]$|^<[^<>]+>$|^(Username|Password):$|\[[Yy][^\[\]]*\]\s*:?\s*$';
505 break;
506 case 'nxos4':
507 $protocol = 'telnet';
508 $prompt = '(^([Ll]ogin|[Pp]assword):|[>#]) $';
509 $commands = "terminal length 0\nterminal no monitor\n" . $commands;
510 break;
511 case 'xos12':
512 $protocol = 'telnet';
513 $prompt = ': $|\.\d+ # $|\?\s*\([Yy]\/[Nn]\)\s*$';
514 $commands = "disable clipaging\n" . $commands;
515 break;
516 case 'jun10':
517 $protocol = 'telnet';
518 $prompt = '^login: $|^Password:$|^\S+@\S+[>#] $';
519 $commands = "set cli screen-length 0\n" . $commands;
520 break;
521 case 'eos4':
522 $protocol = 'telnet'; # strict RFC854 implementation, netcat won't work
523 $prompt = '^\xf2?(login|Username|Password): $|^\S+[>#]$';
524 $commands = "enable\nno terminal monitor\nterminal length 0\n" . $commands;
525 break;
526 case 'ros11':
527 $protocol = 'netcat'; # see ftos8 case
528 $prompt = '^(User Name|\rPassword):$|^\r?\S+# $';
529 $commands = "terminal datadump\n" . $commands;
530 $commands .= "\n\n"; # temporary workaround for telnet server
531 break;
532 case 'iosxr4':
533 $protocol = 'telnet';
534 $prompt = '^\r?(Login|Username|Password): $|^\r?\S+[>#]$';
535 $commands = "terminal length 0\nterminal monitor disable\n" . $commands;
536 break;
537 case 'ucs':
538 $protocol = 'ucssdk';
539 break;
540 case 'dlink':
541 $protocol = 'netcat';
542 $commands = "disable clipaging\n" . $commands;
543 break;
544 case 'hpprocurveN1178':
545 $protocol = 'sshnokey';
546 $prompt = '(Login|[Uu]sername|[Pp]assword): $|Press any key to continue(\e\[\??\d+(;\d+)*[A-Za-z])*$|[#>].*$';
547 # Console emulator should be set to 'none' to avoid special characters to be sent by stupid default VT100!!!
548 $commands = "configure\nconsole local-terminal none\nexit\nno page\n" . $commands . "configure\nconsole local-terminal vt100\nend\nexit\nexit\ny\n";
549 break;
550 case 'ios15':
551 $commands = "terminal length 0\nterminal no monitor\n" . $commands;
552 break;
553 }
554 if (! isset ($protocol))
555 $protocol = 'netcat';
556 if (! isset ($prompt))
557 $prompt = NULL;
558
559 // set the default settings before calling user-defined callback
560 $settings = array
561 (
562 'hostname' => $endpoints[0],
563 'protocol' => $protocol,
564 'port' => NULL,
565 'prompt' => $prompt,
566 'username' => NULL,
567 'password' => NULL,
568 'timeout' => 15,
569 'connect_timeout' => 2,
570 'prompt_delay' => 0.001, # 1ms
571 'sudo_user' => NULL,
572 'identity_file' => NULL,
573 'tolerate_remote_errors' => $tolerate_remote_errors,
574 );
575
576 // override default settings
577 if (is_callable ('terminal_settings'))
578 call_user_func ('terminal_settings', $objectInfo, array (&$settings));
579 // make gateway-specific CLI params out of settings
580 $params = callHook ('makeGatewayParams', $object_id, array (&$settings), array (&$commands));
581 // call gateway
582 $ret_code = callScript ($settings['protocol'], $params, $commands, $out, $errors);
583
584 if (substr($settings['protocol'],0,3) != 'ssh' || ! $settings['tolerate_remote_errors'])
585 {
586 if ($errors != '')
587 throw new RTGatewayError ("${settings['protocol']} error: " . rtrim ($errors));
588 elseif ($ret_code !== 0)
589 throw new RTGatewayError ("${settings['protocol']} error: result code $ret_code");
590 }
591 elseif ($errors != '') // ssh and tolerate and non-empty $errors
592 foreach (explode ("\n", $errors) as $line)
593 if ($line != '' && ! $hide_warnings)
594 showWarning ("${settings['protocol']} ${settings['hostname']}: $line");
595 return strtr($out, array("\r" => "")); // cut ^M symbols
596 }
597
598 function callScript ($gwname, $params, $in, &$out, &$errors)
599 {
600 global $racktables_gwdir, $local_gwdir, $gateway_log, $script_child_res;
601 if (isset ($gateway_log))
602 $gateway_log = '';
603
604 $cwd = NULL;
605 if ('/' === substr ($gwname, 0, 1))
606 // absolute path to executable
607 $binary = $gwname;
608 else
609 {
610 // path relative to one of RackTables' gwdirs
611 if (isset ($local_gwdir) && file_exists ("$local_gwdir/$gwname"))
612 $cwd = $local_gwdir;
613 elseif (isset ($racktables_gwdir) && file_exists ("$racktables_gwdir/$gwname"))
614 $cwd = $racktables_gwdir;
615 if (! isset ($cwd))
616 throw new RTGatewayError ("Could not find the gateway file called '$gwname'");
617 $binary = "./$gwname";
618 }
619
620 $cmd_line = $binary;
621 foreach ($params as $key => $value)
622 {
623 if (! isset ($value))
624 continue;
625 if (preg_match ('/^\d+$/', $key))
626 $cmd_line .= " " . escapeshellarg ($value);
627 else
628 {
629 if (strlen ($key) == 1)
630 $cmd_line .= " " . escapeshellarg ("-$key") . " " . escapeshellarg ($value);
631 else
632 $cmd_line .= " " . escapeshellarg("--$key=$value");
633 }
634 }
635
636 $pipes = array();
637 $child = proc_open
638 (
639 $cmd_line,
640 array (
641 0 => array ('pipe', 'r'),
642 1 => array ('pipe', 'w'),
643 2 => array ('pipe', 'w'),
644 ),
645 $pipes,
646 $cwd
647 );
648 if (! is_resource ($child))
649 throw new RTGatewayError ("failed to execute $binary");
650 $script_child_res = $child;
651
652 $buff_size = 4096;
653 $write_left = array ($pipes[0]);
654 $read_left = array ($pipes[1], $pipes[2]);
655 $write_fd = $write_left;
656 $read_fd = $read_left;
657 $except_fd = array();
658 $out = '';
659 $errors = '';
660 $write_cursor = 0;
661 while ((! empty ($read_fd) || ! empty ($write_fd)) && stream_select ($read_fd, $write_fd, $except_fd, NULL))
662 {
663 foreach ($write_fd as $fd)
664 {
665 if (0 != $written = fwrite ($fd, substr ($in, $write_cursor, $buff_size), $buff_size))
666 {
667 // log all communication data into global var
668 if (isset ($gateway_log))
669 $gateway_log .= preg_replace ('/^/m', '> ', substr ($in, $write_cursor, $written));
670
671 $write_cursor += $written;
672 }
673 else
674 $write_cursor = strlen ($in);
675
676 if ($write_cursor >= strlen ($in))
677 {
678 // close input fd
679 $write_left = array_diff ($write_left, array ($fd));
680 fclose ($fd);
681 }
682 }
683 foreach ($read_fd as $fd)
684 if ('' == $str = fread ($fd, $buff_size))
685 {
686 // close output fd
687 $read_left = array_diff ($read_left, array ($fd));
688 fclose ($fd);
689 }
690 else
691 {
692 // log all communication data into global var
693 if (isset ($gateway_log))
694 $gateway_log .= $str;
695
696 if ($fd == $pipes[1])
697 $out .= $str;
698 elseif ($fd == $pipes[2])
699 $errors .= $str;
700 }
701
702 $write_fd = $write_left;
703 $read_fd = $read_left;
704
705 // limit the size of gateway_log
706 if (isset ($gateway_log) && strlen ($gateway_log) > MAX_GW_LOGSIZE * 1.1)
707 $gateway_log = substr ($gateway_log, -MAX_GW_LOGSIZE);
708
709 }
710 // The global link to the resource needs to be destroyed here.
711 // PHP's proc_close implementation does nothing itself: it only returns
712 // the value saved by the resource destructor. If the resource was not
713 // destroyed (refcnt > 0), the return value is incorrect.
714 $script_child_res = NULL;
715
716 return proc_close ($child);
717 }
718
719 // returns the empty structure to be returned from getRunning8021QConfig
720 function constructRunning8021QConfig()
721 {
722 return array
723 (
724 'vlanlist' => array(), // unindexed list of integer VIDs
725 'portdata' => array(), // portconf structures indexed by portname
726 'portconfig' => array(), // config text lines indexed by portname.
727 'vlannames' => array(), // vlan names indexed by VID
728 );
729 }
730
731 function getRunning8021QConfig ($object_id)
732 {
733 $ret = queryDevice ($object_id, 'get8021q');
734 // Once there is no default VLAN in the parsed data, it means
735 // something else was parsed instead of config text.
736 if (!in_array (VLAN_DFL_ID, $ret['vlanlist']) || empty ($ret['portdata']))
737 throw new RTGatewayError ('communication with device failed');
738 return $ret;
739 }
740
741 function setDevice8021QConfig ($object_id, $pseudocode, $vlan_names)
742 {
743 // FIXME: this is a perfect place to log intended changes
744 // $object_id argument isn't used by default translating functions, but
745 // may come in handy for overloaded versions of these.
746 $commands = translateDeviceCommands ($object_id, $pseudocode, $vlan_names);
747 $breed = detectDeviceBreed ($object_id);
748 $output = queryTerminal ($object_id, $commands, FALSE);
749
750 // throw an exception if Juniper did not allow to enter config mode or to commit changes
751 if ($breed == 'jun10')
752 {
753 if (preg_match ('/>\s*configure exclusive\s*$[^#>]*?^error:/sm', $output))
754 throw new RTGatewayError ("Configuration is locked by other user");
755 elseif (preg_match ('/#\s*commit\s*$([^#]*?^error: .*?)$/sm', $output, $m))
756 throw new RTGatewayError ("Commit failed: ${m[1]}");
757 }
758 }
759
760 // if both $breed and $object_id are omitted, the breed could be auto-detected
761 // in case shortenIfName is called from within queryDevice
762 // (i.e. some function in deviceconfig.php)
763 function shortenIfName ($if_name, $breed = NULL, $object_id = NULL)
764 {
765 // This is a port name invented in snmp.php, do not translate it.
766 if (preg_match ('/^AC-in(-[12])?$/', $if_name))
767 return $if_name;
768
769 global $current_query_breed;
770 global $shorten_by_breed;
771 if (! isset ($breed))
772 {
773 if (isset ($object_id))
774 $breed = detectDeviceBreed ($object_id);
775 elseif (isset ($current_query_breed))
776 $breed = $current_query_breed;
777 }
778
779 if (isset ($breed) and isset ($shorten_by_breed[$breed]))
780 return call_user_func ($shorten_by_breed[$breed], $if_name);
781 else
782 return ios12ShortenIfName ($if_name);
783 }
784
785 function ios12ShortenIfName_real ($ifname)
786 {
787 $ifname = preg_replace ('@^FastEthernet(.+)$@', 'fa\\1', $ifname);
788 $ifname = preg_replace ('@^GigabitEthernet(.+)$@', 'gi\\1', $ifname);
789 $ifname = preg_replace ('@^TenGigabitEthernet(.+)$@', 'te\\1', $ifname);
790 $ifname = preg_replace ('@^port-channel(.+)$@i', 'po\\1', $ifname);
791 $ifname = strtolower ($ifname);
792 $ifname = preg_replace ('/^(fa|gi|te|po)\s+(\d.*)/', '$1$2', $ifname);
793 return $ifname;
794 }
795
796 function nxos4ShortenIfName ($ifname)
797 {
798 $ifname = preg_replace ('@^(Ethernet|Eth)(.+)$@', 'e\\2', $ifname);
799 $ifname = preg_replace ('@^port-channel(.+)$@i', 'po\\1', $ifname);
800 $ifname = preg_replace ('@^mgmt(.+)$@i', 'mgmt\\1', $ifname);
801 $ifname = strtolower ($ifname);
802 $ifname = preg_replace ('/^(e|po|mgmt)\s+(\d.*)/', '$1$2', $ifname);
803 return $ifname;
804 }
805
806 function vrp5xShortenIfName ($ifname)
807 {
808 if (preg_match ('@^eth-trunk(\d+)$@i', $ifname, $m))
809 return "Eth-Trunk${m[1]}";
810 $ifname = preg_replace ('@^MEth(.+)$@', 'me\\1', $ifname);
811 $ifname = preg_replace ('@^(?:Ethernet|Eth)(.+)$@', 'ether\\1', $ifname);
812 $ifname = preg_replace ('@^(?:GigabitEthernet|GE)(.+)$@', 'gi\\1', $ifname);
813 $ifname = preg_replace ('@^(?:XGigabitEthernet|XGE)(.+)$@', 'xg\\1', $ifname);
814 $ifname = strtolower ($ifname);
815 return $ifname;
816 }
817
818 function vrp85ShortenIfName ($ifname)
819 {
820 if (preg_match ('@^eth-trunk(\d+)$@i', $ifname, $m))
821 return "Eth-Trunk${m[1]}";
822 // VRP 8.5 has already shortened ifNames
823 $ifname = preg_replace ('@^MEth(.+)$@', 'me\\1', $ifname);
824 $ifname = strtolower ($ifname);
825 return $ifname;
826 }
827
828 function iosxr4ShortenIfName ($ifname)
829 {
830 $ifname = preg_replace ('@^Mg(?:mtEth)?\s*(.*)$@', 'mg\\1', $ifname);
831 $ifname = preg_replace ('@^FastEthernet\s*(.+)$@', 'fa\\1', $ifname);
832 $ifname = preg_replace ('@^GigabitEthernet\s*(.+)$@', 'gi\\1', $ifname);
833 $ifname = preg_replace ('@^TenGigE\s*(.*)$@', 'te\\1', $ifname);
834 $ifname = preg_replace ('@^BE\s*(\d+)$@', 'bundle-ether\\1', $ifname);
835 $ifname = preg_replace ('@^HundredGigE\s*(.+)$@i', 'hu\\1', $ifname);
836 $ifname = strtolower ($ifname);
837 return $ifname;
838 }
839
840 // this function should be kept as-is for compatibility.
841 // It is trying hard to complement every known breed.
842 function ios12ShortenIfName ($ifname)
843 {
844 if (preg_match ('@^eth-trunk(\d+)$@i', $ifname, $m))
845 return "Eth-Trunk${m[1]}";
846 $ifname = preg_replace ('@^(?:[Ee]thernet|Eth)(.+)$@', 'e\\1', $ifname);
847 $ifname = preg_replace ('@^FastEthernet(.+)$@', 'fa\\1', $ifname);
848 $ifname = preg_replace ('@^(?:GigabitEthernet|GE)\s*(.+)$@', 'gi\\1', $ifname);
849 $ifname = preg_replace ('@^TenGigabitEthernet(.+)$@', 'te\\1', $ifname);
850 $ifname = preg_replace ('@^port-channel(.+)$@i', 'po\\1', $ifname);
851 $ifname = preg_replace ('@^(?:XGigabitEthernet|XGE)(.+)$@', 'xg\\1', $ifname);
852 $ifname = preg_replace ('@^LongReachEthernet(.+)$@', 'lo\\1', $ifname);
853 $ifname = preg_replace ('@^Management(?:Ethernet)\s(.+)$@', 'ma\\1', $ifname);
854 $ifname = preg_replace ('@^Et(\d.*)$@', 'e\\1', $ifname);
855 $ifname = preg_replace ('@^TenGigE(.*)$@', 'te\\1', $ifname); // IOS XR4
856 $ifname = preg_replace ('@^Mg(?:mtEth)?(.*)$@', 'mg\\1', $ifname); // IOS XR4
857 $ifname = preg_replace ('@^BE(\d+)$@', 'bundle-ether\\1', $ifname); // IOS XR4
858 $ifname = strtolower ($ifname);
859 $ifname = preg_replace ('/^(e|fa|gi|te|po|xg|lo|ma)\s+(\d.*)/', '$1$2', $ifname);
860 return $ifname;
861 }