SLB config generation bugfix
[racktables] / wwwroot / inc / remote.php
CommitLineData
7cb57a03
AA
1<?php
2
cddbb9fd
DO
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
1319f245
DO
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.
9d3ef879
DO
10$breedfunc = array
11(
12 'ios12-getcdpstatus-main' => 'ios12ReadCDPStatus',
13 'ios12-getlldpstatus-main' => 'ios12ReadLLDPStatus',
14 'ios12-get8021q-main' => 'ios12ReadVLANConfig',
15 'ios12-get8021q-top' => 'ios12ScanTopLevel',
16 'ios12-get8021q-readport' => 'ios12PickSwitchportCommand',
17 'ios12-get8021q-readvlan' => 'ios12PickVLANCommand',
18 'ios12-getportstatus-main' => 'ciscoReadInterfaceStatus',
19 'ios12-getmaclist-main' => 'ios12ReadMacList',
20 'ios12-xlatepushq-main' => 'ios12TranslatePushQueue',
21 'ios12-getallconf-main' => 'ios12SpotConfigText',
22 'fdry5-get8021q-main' => 'fdry5ReadVLANConfig',
23 'fdry5-get8021q-top' => 'fdry5ScanTopLevel',
24 'fdry5-get8021q-readvlan' => 'fdry5PickVLANSubcommand',
25 'fdry5-get8021q-readport' => 'fdry5PickInterfaceSubcommand',
26 'fdry5-xlatepushq-main' => 'fdry5TranslatePushQueue',
27 'fdry5-getallconf-main' => 'fdry5SpotConfigText',
28 'vrp53-getlldpstatus-main' => 'vrp5xReadLLDPStatus',
29 'vrp53-get8021q-main' => 'vrp53ReadVLANConfig',
30 'vrp53-get8021q-top' => 'vrp53ScanTopLevel',
31 'vrp53-get8021q-readport' => 'vrp53PickInterfaceSubcommand',
32 'vrp53-getportstatus-main' => 'vrpReadInterfaceStatus',
33 'vrp53-getmaclist-main' => 'vrp53ReadMacList',
34 'vrp53-xlatepushq-main' => 'vrp53TranslatePushQueue',
35 'vrp53-getallconf-main' => 'vrp5xSpotConfigText',
36 'vrp55-getlldpstatus-main' => 'vrp5xReadLLDPStatus',
37 'vrp55-get8021q-main' => 'vrp55Read8021QConfig',
38 'vrp55-getportstatus-main' => 'vrpReadInterfaceStatus',
39 'vrp55-getmaclist-main' => 'vrp55ReadMacList',
40 'vrp55-xlatepushq-main' => 'vrp55TranslatePushQueue',
41 'vrp55-getallconf-main' => 'vrp5xSpotConfigText',
42 'nxos4-getcdpstatus-main' => 'ios12ReadCDPStatus',
43 'nxos4-getlldpstatus-main' => 'nxos4ReadLLDPStatus',
44 'nxos4-get8021q-main' => 'nxos4Read8021QConfig',
45 'nxos4-get8021q-top' => 'nxos4ScanTopLevel',
46 'nxos4-get8021q-readport' => 'nxos4PickSwitchportCommand',
47 'nxos4-get8021q-readvlan' => 'nxos4PickVLANCommand',
48 'nxos4-getportstatus-main' => 'ciscoReadInterfaceStatus',
49 'nxos4-getmaclist-main' => 'nxos4ReadMacList',
50 'nxos4-xlatepushq-main' => 'nxos4TranslatePushQueue',
51 'nxos4-getallconf-main' => 'nxos4SpotConfigText',
52 'dlink-get8021q-main' => 'dlinkReadVLANConfig',
53 'dlink-get8021q-top' => 'dlinkScanTopLevel',
54 'dlink-get8021q-pickvlan' => 'dlinkPickVLANCommand',
55 'dlink-getportstatus-main' => 'dlinkReadInterfaceStatus',
56 'dlink-getmaclist-main' => 'dlinkReadMacList',
57 'dlink-xlatepushq-main' => 'dlinkTranslatePushQueue',
58 'linux-get8021q-main' => 'linuxReadVLANConfig',
59 'linux-getportstatus-main' => 'linuxReadInterfaceStatus',
60 'linux-getmaclist-main' => 'linuxReadMacList',
61 'linux-xlatepushq-main' => 'linuxTranslatePushQueue',
62 'xos12-getlldpstatus-main' => 'xos12ReadLLDPStatus',
63 'xos12-get8021q-main' => 'xos12Read8021QConfig',
64 'xos12-xlatepushq-main' => 'xos12TranslatePushQueue',
65 'xos12-getallconf-main' => 'xos12SpotConfigText',
66 'jun10-get8021q-main' => 'jun10Read8021QConfig',
67 'jun10-xlatepushq-main' => 'jun10TranslatePushQueue',
68 'jun10-getallconf-main' => 'jun10SpotConfigText',
69 'ftos8-xlatepushq-main' => 'ftos8TranslatePushQueue',
70 'ftos8-getlldpstatus-main' => 'ftos8ReadLLDPStatus',
71 'ftos8-getmaclist-main' => 'ftos8ReadMacList',
72 'ftos8-getportstatus-main' => 'ftos8ReadInterfaceStatus',
73 'ftos8-get8021q-main' => 'ftos8Read8021QConfig',
74 'ftos8-getallconf-main' => 'ftos8SpotConfigText',
75 'air12-xlatepushq-main' => 'air12TranslatePushQueue',
76 'air12-getallconf-main' => 'ios12SpotConfigText',
77 'eos4-getallconf-main' => 'eos4SpotConfigText',
78 'eos4-getmaclist-main' => 'eos4ReadMacList',
79 'eos4-getportstatus-main' => 'eos4ReadInterfaceStatus',
80 'eos4-getlldpstatus-main' => 'eos4ReadLLDPStatus',
81 'eos4-get8021q-main' => 'eos4Read8021QConfig',
82 'eos4-xlatepushq-main' => 'eos4TranslatePushQueue',
83 'ros11-getallconf-main' => 'ros11SpotConfigText',
84 'ros11-xlatepushq-main' => 'ros11TranslatePushQueue',
85 'ros11-getlldpstatus-main' => 'ros11ReadLLDPStatus',
86 'ros11-getportstatus-main' => 'ros11ReadInterfaceStatus',
87 'ros11-getmaclist-main' => 'ros11ReadMacList',
88 'ros11-get8021q-main' => 'ros11Read8021QConfig',
89 'ros11-get8021q-scantop' => 'ros11Read8021QScanTop',
90 'ros11-get8021q-vlandb' => 'ros11Read8021QVLANDatabase',
91 'ros11-get8021q-readports' => 'ros11Read8021QPorts',
1319f245
DO
92 'iosxr4-xlatepushq-main' => 'iosxr4TranslatePushQueue',
93 'iosxr4-getallconf-main' => 'iosxr4SpotConfigText',
9d3ef879
DO
94 'ucs-xlatepushq-main' => 'ucsTranslatePushQueue',
95 'ucs-getinventory-main' => 'ucsReadInventory',
96);
97
98function detectDeviceBreed ($object_id)
99{
100 $breed_by_swcode = array
101 (
102 251 => 'ios12',
103 252 => 'ios12',
104 254 => 'ios12',
105 963 => 'nxos4', // NX-OS 4.0
106 964 => 'nxos4', // NX-OS 4.1
107 1365 => 'nxos4', // NX-OS 4.2
108 1410 => 'nxos4', // NX-OS 5.0, seems compatible
109 1411 => 'nxos4', // NX-OS 5.1
110 1643 => 'nxos4', // NX-OS 6.0
111 1352 => 'xos12',
112 1360 => 'vrp53',
113 1361 => 'vrp55',
114 1369 => 'vrp55', // VRP versions 5.5 and 5.7 seem to be compatible
115 1363 => 'fdry5',
116 1367 => 'jun10', # 10S
117 1597 => 'jun10', # 10R
118 1598 => 'jun10', # 11R
119 1599 => 'jun10', # 12R
120 1594 => 'ftos8',
121 1673 => 'air12', # AIR IOS 12.3
122 1674 => 'air12', # AIR IOS 12.4
123 1675 => 'eos4',
124 1759 => 'iosxr4', # Cisco IOS XR 4.2
125 1786 => 'ros11', # Marvell ROS 1.1
126 242 => 'linux',
127 243 => 'linux',
128 1331 => 'linux',
129 1332 => 'linux',
130 1333 => 'linux',
131 1334 => 'linux',
132 1395 => 'linux',
133 1396 => 'linux',
134 );
135 for ($i = 225; $i <= 235; $i++)
136 $breed_by_swcode[$i] = 'linux';
137 for ($i = 418; $i <= 436; $i++)
138 $breed_by_swcode[$i] = 'linux';
139 for ($i = 1417; $i <= 1422; $i++)
140 $breed_by_swcode[$i] = 'linux';
141 $breed_by_hwcode = array();
142 for ($i = 589; $i <= 637; $i++)
143 $breed_by_hwcode[$i] = 'dlink';
144 $breed_by_mgmtcode = array (1788 => 'ucs');
145 foreach (getAttrValues ($object_id) as $record)
146 if ($record['id'] == 4 and array_key_exists ($record['key'], $breed_by_swcode))
147 return $breed_by_swcode[$record['key']];
148 elseif ($record['id'] == 2 and array_key_exists ($record['key'], $breed_by_hwcode))
149 return $breed_by_hwcode[$record['key']];
150 elseif ($record['id'] == 30 and array_key_exists ($record['key'], $breed_by_mgmtcode))
151 return $breed_by_mgmtcode[$record['key']];
152 return '';
153}
154
2a46177c
DO
155function assertDeviceBreed ($object_id)
156{
157 if ('' == $breed = detectDeviceBreed ($object_id))
158 throw new RTGatewayError ('Cannot determine device breed');
159 return $breed;
160}
161
9d3ef879
DO
162function validBreedFunction ($breed, $command)
163{
164 global $breedfunc;
165 return array_key_exists ("${breed}-${command}-main", $breedfunc);
166}
167
168function assertBreedFunction ($breed, $command)
169{
170 global $breedfunc;
171 if (! validBreedFunction ($breed, $command))
2a46177c
DO
172 throw new RTGatewayError ("unsupported command '${command}' for breed '${breed}'");
173 return $breedfunc["${breed}-${command}-main"];
9d3ef879
DO
174}
175
7cb57a03
AA
176function queryDevice ($object_id, $command)
177{
2a46177c
DO
178 $breed = assertDeviceBreed ($object_id);
179 $funcname = assertBreedFunction ($breed, $command);
7cb57a03 180 require_once 'deviceconfig.php';
2a46177c
DO
181 if (! is_callable ($funcname))
182 throw new RTGatewayError ("undeclared function '${funcname}'");
7cb57a03 183 $query = translateDeviceCommands ($object_id, array (array ('opcode' => $command)));
2a46177c 184 return $command == 'xlatepushq' ? $query : $funcname (queryTerminal ($object_id, $query, FALSE));
7cb57a03
AA
185}
186
187function translateDeviceCommands ($object_id, $crq, $vlan_names = NULL)
188{
2a46177c
DO
189 $breed = assertDeviceBreed ($object_id);
190 $funcname = assertBreedFunction ($breed, 'xlatepushq');
7cb57a03 191 require_once 'deviceconfig.php';
2a46177c
DO
192 if (! is_callable ($funcname))
193 throw new RTGatewayError ("undeclared function '${funcname}'");
194 return $funcname ($object_id, $crq, $vlan_names);
7cb57a03
AA
195}
196
197// This function returns a text output received from the device
198// You can override connection settings by implement a callback named 'terminal_settings'.
199// Errors are thrown as exceptions if not $tolerate_remote_errors, and shown as warnings otherwise.
200function queryTerminal ($object_id, $commands, $tolerate_remote_errors = TRUE)
201{
202 $objectInfo = spotEntity ('object', $object_id);
203 $endpoints = findAllEndpoints ($object_id, $objectInfo['name']);
204 if (count ($endpoints) == 0)
205 throw new RTGatewayError ('no management address set');
206 if (count ($endpoints) > 1)
207 throw new RTGatewayError ('cannot pick management address');
208
5d2988fb 209 // telnet prompt and mode specification
7cb57a03
AA
210 switch ($breed = detectDeviceBreed ($object_id))
211 {
77b7e03c
DO
212 case 'ios12':
213 case 'fdry5':
7a0e9964 214 case 'ftos8':
a3447df9 215 $protocol = 'netcat'; // default is netcat mode
349394c1 216 $prompt = '^(Login|Username|Password): $|^\S+[>#]$'; // set the prompt in case user would like to specify telnet protocol
7cb57a03 217 break;
d4a957e0
DO
218 case 'air12':
219 $protocol = 'telnet'; # Aironet IOS is broken
220 $prompt = '^(Username|Password): $|^\S+[>#]$';
221 break;
7cb57a03
AA
222 case 'vrp53':
223 case 'vrp55':
5d2988fb 224 $protocol = 'telnet';
338465a8 225 $prompt = '^\[[^[\]]+\]$|^<[^<>]+>$|^(Username|Password):$|(?:\[Y\/N\]|\(Y\/N\)\[[YN]\]):?$';
7cb57a03
AA
226 break;
227 case 'nxos4':
5d2988fb 228 $protocol = 'telnet';
f6943818 229 $prompt = '(^([Ll]ogin|[Pp]assword):|[>#]) $';
7cb57a03
AA
230 break;
231 case 'xos12':
5d2988fb 232 $protocol = 'telnet';
18997a72 233 $prompt = ': $|\.\d+ # $|\?\s*\([Yy]\/[Nn]\)\s*$';
7cb57a03
AA
234 break;
235 case 'jun10':
5d2988fb 236 $protocol = 'telnet';
7cb57a03
AA
237 $prompt = '^login: $|^Password:$|^\S+@\S+[>#] $';
238 break;
85703049
DO
239 case 'eos4':
240 $protocol = 'telnet'; # strict RFC854 implementation, netcat won't work
241 $prompt = '^(\xf2?login|Username|Password): $|^\S+[>#]$';
242 break;
919bb8b6
DO
243 case 'ros11':
244 $protocol = 'netcat'; # see ftos8 case
245 $prompt = '^(User Name|\rPassword):$|^\r?\S+# $';
246 break;
7a0e9964
AA
247 case 'iosxr4':
248 $protocol = 'telnet';
249 $prompt = '^\r?(Login|Username|Password): $|^\r?\S+[>#]$';
250 break;
be91a564
JS
251 case 'ucs':
252 $protocol = 'ucssdk';
253 break;
5d2988fb
AA
254 default:
255 $protocol = 'netcat';
256 $prompt = NULL;
7cb57a03
AA
257 }
258
259 // set the default settings before calling user-defined callback
260 $settings = array
261 (
262 'hostname' => $endpoints[0],
a3447df9 263 'protocol' => $protocol,
7cb57a03
AA
264 'port' => NULL,
265 'prompt' => $prompt,
266 'username' => NULL,
267 'password' => NULL,
ada8a703 268 'timeout' => 15,
7cb57a03 269 'connect_timeout' => 2,
a9edde6c 270 'prompt_delay' => 0.001, # 1ms
7cb57a03
AA
271 'sudo_user' => NULL,
272 'identity_file' => NULL,
273 );
274 if (is_callable ('terminal_settings'))
275 call_user_func ('terminal_settings', $objectInfo, array (&$settings)); // override settings
276
01b0c68f
AA
277 if (! isset ($settings['port']) and $settings['protocol'] == 'netcat')
278 $settings['port'] = 23;
279
7cb57a03
AA
280 $params = array ( $settings['hostname'] );
281 $params_from_settings = array();
282 switch ($settings['protocol'])
283 {
284 case 'telnet':
01b0c68f
AA
285 case 'netcat':
286 // prepend command list with vendor-specific disabling pager command
7cb57a03
AA
287 switch ($breed)
288 {
289 case 'ios12':
290 $commands = "terminal length 0\n" . $commands;
291 break;
292 case 'nxos4':
52bf6129 293 case 'air12':
0a2d7ff1 294 case 'ftos8':
7cb57a03
AA
295 $commands = "terminal length 0\nterminal no monitor\n" . $commands;
296 break;
297 case 'xos12':
298 $commands = "disable clipaging\n" . $commands;
299 break;
300 case 'vrp55':
301 $commands = "screen-length 0 temporary\n" . $commands;
302 break;
303 case 'fdry5':
304 $commands = "skip-page-display\n" . $commands;
305 break;
306 case 'jun10':
184ec25d 307 $commands = "set cli screen-length 0\n" . $commands;
7cb57a03 308 break;
85703049
DO
309 case 'eos4':
310 $commands = "enable\nno terminal monitor\nterminal length 0\n" . $commands;
311 break;
919bb8b6
DO
312 case 'ros11':
313 $commands = "terminal datadump\n" . $commands;
314 $commands .= "\n\n"; # temporary workaround for telnet server
315 break;
7a0e9964
AA
316 case 'iosxr4':
317 $commands = "terminal length 0\nterminal monitor disable\n" . $commands;
318 break;
fc3a6bd0
IE
319 case 'dlink':
320 $commands = "disable clipaging\n" . $commands;
321 break;
7cb57a03
AA
322 }
323 // prepend telnet commands by credentials
324 if (isset ($settings['password']))
325 $commands = $settings['password'] . "\n" . $commands;
326 if (isset ($settings['username']))
327 $commands = $settings['username'] . "\n" . $commands;
01b0c68f
AA
328 // command-line options are specific to client: telnet or netcat
329 switch ($settings['protocol'])
330 {
331 case 'telnet':
332 $params_from_settings['port'] = 'port';
333 $params_from_settings['prompt'] = 'prompt';
334 $params_from_settings['connect-timeout'] = 'connect_timeout';
335 $params_from_settings['timeout'] = 'timeout';
336 $params_from_settings['prompt-delay'] = 'prompt_delay';
337 break;
338 case 'netcat':
56930ac4 339 $params_from_settings['p'] = 'port';
01b0c68f 340 $params_from_settings['w'] = 'timeout';
56930ac4 341 $params_from_settings['b'] = 'ncbin';
01b0c68f
AA
342 break;
343 }
7cb57a03
AA
344 break;
345 case 'ssh':
346 $params_from_settings['port'] = 'port';
424604b4 347 $params_from_settings['proto'] = 'proto';
7cb57a03
AA
348 $params_from_settings['username'] = 'username';
349 $params_from_settings['i'] = 'identity_file';
350 $params_from_settings['sudo-user'] = 'sudo_user';
351 $params_from_settings['connect-timeout'] = 'connect_timeout';
352 break;
be91a564
JS
353 case 'ucssdk': # remote XML through a Python backend
354 $params = array(); # reset
355 # UCS in its current implementation besides the terminal_settings() provides
356 # an additional username/password feed through the HTML from. Whenever the
357 # user provides the credentials through the form, use these instead of the
358 # credentials [supposedly] set by terminal_settings().
359 if ($script_mode != TRUE && ! isCheckSet ('use_terminal_settings'))
360 {
361 genericAssertion ('ucs_login', 'string');
362 genericAssertion ('ucs_password', 'string');
363 $settings['username'] = $_REQUEST['ucs_login'];
364 $settings['password'] = $_REQUEST['ucs_password'];
365 }
366 foreach (array ('hostname', 'username', 'password') as $item)
367 if (empty ($settings[$item]))
368 throw new RTGatewayError ("${item} not available, check terminal_settings()");
369 $commands = "login ${settings['hostname']} ${settings['username']} ${settings['password']}\n" . $commands;
370 break;
7cb57a03 371 default:
c0b84dfb 372 throw RTGatewayError ("Invalid terminal protocol '${settings['protocol']}' specified");
7cb57a03
AA
373 }
374 foreach ($params_from_settings as $param_name => $setting_name)
375 if (isset ($settings[$setting_name]))
01b0c68f
AA
376 if (is_int ($param_name))
377 $params[] = $settings[$setting_name];
378 else
379 $params[$param_name] = $settings[$setting_name];
7cb57a03
AA
380
381 $ret_code = callScript ($settings['protocol'], $params, $commands, $out, $errors);
01b0c68f 382 if ($settings['protocol'] != 'ssh' || ! $tolerate_remote_errors)
7cb57a03
AA
383 {
384 if (! empty ($errors))
e75026f8 385 throw new RTGatewayError ("${settings['protocol']} error: " . rtrim ($errors));
7cb57a03
AA
386 elseif ($ret_code !== 0)
387 throw new RTGatewayError ("${settings['protocol']} error: result code $ret_code");
388 }
ba2d4431 389 elseif (! empty ($errors)) // ssh and tolerate and non-empty $errors
7cb57a03
AA
390 foreach (explode ("\n", $errors) as $line)
391 if (strlen ($line))
392 showWarning ("${settings['protocol']} ${settings['hostname']}: $line");
393 return strtr($out, array("\r" => "")); // cut ^M symbols
394}
395
396function callScript ($gwname, $params, $in, &$out, &$errors)
397{
398 global $racktables_gwdir, $local_gwdir;
399 if (isset ($local_gwdir) && file_exists ("$local_gwdir/$gwname"))
400 $dir = $local_gwdir;
401 elseif (isset ($racktables_gwdir) && file_exists ("$racktables_gwdir/$gwname"))
402 $dir = $racktables_gwdir;
403 if (! isset ($dir))
c0b84dfb 404 throw new RTGatewayError ("Could not find the gateway file called '$gwname'");
7cb57a03
AA
405
406 $cmd_line = "./$gwname";
407 foreach ($params as $key => $value)
408 {
409 if (! isset ($value))
410 continue;
411 if (preg_match ('/^\d+$/', $key))
412 $cmd_line .= " " . escapeshellarg ($value);
413 else
414 {
415 if (strlen ($key) == 1)
416 $cmd_line .= " " . escapeshellarg ("-$key") . " " . escapeshellarg ($value);
417 else
418 $cmd_line .= " " . escapeshellarg("--$key=$value");
419 }
420 }
424604b4 421
7cb57a03 422 $pipes = array();
dec748f6 423 $child = proc_open
7cb57a03
AA
424 (
425 $cmd_line,
426 array (
427 0 => array ('pipe', 'r'),
428 1 => array ('pipe', 'w'),
429 2 => array ('pipe', 'w'),
430 ),
431 $pipes,
432 $dir
433 );
434 if (! is_resource ($child))
c0b84dfb 435 throw new RTGatewayError ("cant execute $dir/$gwname");
15da1b37
AA
436
437 $buff_size = 4096;
438 $write_left = array ($pipes[0]);
439 $read_left = array ($pipes[1], $pipes[2]);
440 $write_fd = $write_left;
441 $read_fd = $read_left;
442 $except_fd = array();
e9222a8a
AA
443 $out = '';
444 $errors = '';
15da1b37
AA
445 while ((! empty ($read_fd) || ! empty ($write_fd)) && stream_select ($read_fd, $write_fd, $except_fd, NULL))
446 {
447 foreach ($write_fd as $fd)
448 {
449 $written = fwrite ($fd, $in, $buff_size);
450 $in = substr ($in, $written);
451 if ($written == 0 || empty ($in))
452 {
453 // close input fd
454 $write_left = array_diff ($write_left, array ($fd));
455 fclose ($fd);
456 }
457 }
458 foreach ($read_fd as $fd)
459 {
460 $str = fread ($fd, $buff_size);
461 if (strlen ($str) == 0)
462 {
463 // close output fd
464 $read_left = array_diff ($read_left, array ($fd));
465 fclose ($fd);
466 }
467 else
468 {
469 if ($fd == $pipes[1])
470 $out .= $str;
471 elseif ($fd == $pipes[2])
472 $errors .= $str;
473 }
474 }
475
476 $write_fd = $write_left;
477 $read_fd = $read_left;
478 }
479
7da04382 480 return proc_close ($child);
7cb57a03
AA
481}
482
bc93236a
AA
483function getRunning8021QConfig ($object_id)
484{
485 $ret = queryDevice ($object_id, 'get8021q');
486 // Once there is no default VLAN in the parsed data, it means
487 // something else was parsed instead of config text.
1c0a61e2 488 if (!in_array (VLAN_DFL_ID, $ret['vlanlist']) || empty ($ret['portdata']))
bc93236a
AA
489 throw new RTGatewayError ('communication with device failed');
490 return $ret;
491}
492
493function setDevice8021QConfig ($object_id, $pseudocode, $vlan_names)
494{
495 // FIXME: this is a perfect place to log intended changes
496 // $object_id argument isn't used by default translating functions, but
497 // may come in handy for overloaded versions of these.
9f1da7de 498 $commands = translateDeviceCommands ($object_id, $pseudocode, $vlan_names);
dc68fd36
AA
499 $breed = detectDeviceBreed ($object_id);
500 $output = queryTerminal ($object_id, $commands, FALSE);
dec748f6 501
dc68fd36
AA
502 // throw an exception if Juniper did not allow to enter config mode or to commit changes
503 if ($breed == 'jun10')
504 {
505 if (preg_match ('/>\s*configure exclusive\s*$[^#>]*?^error:/sm', $output))
506 throw new RTGatewayError ("Configuration is locked by other user");
507 elseif (preg_match ('/#\s*commit\s*$([^#]*?^error: .*?)$/sm', $output, $m))
508 throw new RTGatewayError ("Commit failed: ${m[1]}");
509 }
bc93236a
AA
510}
511
7cb57a03 512?>