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 142define ('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); 230for ($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 235for ($i = 589; $i <= 637; $i++) 236 $breed_by_hwcode[$i] = 'dlink'; 237 238function 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 251function assertDeviceBreed ($object_id) 252{ 253 if ('' == $breed = detectDeviceBreed ($object_id)) 254 throw new RTGatewayError ('Cannot determine device breed'); 255 return $breed; 256} 257 258function validBreedFunction ($breed, $command) 259{ 260 global $breedfunc; 261 return array_key_exists ("${breed}-${command}-main", $breedfunc); 262} 263 264function 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 272function 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 324function 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. 350function 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. 464function 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 598function 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 720function 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 731function 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 741function 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) 763function 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 785function 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 796function 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 806function 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 818function 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 828function 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. 842function 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} 862