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