r4252 database.php: add closeCursor call to linkPorts()
[racktables] / wwwroot / inc / deviceconfig.php
1 <?php
2
3 // Read provided output of "show cdp neighbors detail" command and
4 // return a list of records with (translated) local port name,
5 // remote device name and (translated) remote port name.
6 function ios12ReadCDPStatus ($input)
7 {
8 $ret = array();
9 foreach (explode ("\n", $input) as $line)
10 {
11 $matches = array();
12 switch (TRUE)
13 {
14 case preg_match ('/^Device ID:\s*([A-Za-z0-9][A-Za-z0-9\.\-]*)/', $line, $matches):
15 case preg_match ('/^System Name:\s*([A-Za-z0-9][A-Za-z0-9\.\-]*)/', $line, $matches):
16 $ret['current']['device'] = $matches[1];
17 break;
18 case preg_match ('/^Interface: (.+), ?Port ID \(outgoing port\): (.+)$/', $line, $matches):
19 if (array_key_exists ('device', $ret['current']))
20 $ret[ios12ShortenIfName ($matches[1])][] = array
21 (
22 'device' => $ret['current']['device'],
23 'port' => ios12ShortenIfName ($matches[2]),
24 );
25 unset ($ret['current']);
26 break;
27 default:
28 }
29 }
30 unset ($ret['current']);
31 return $ret;
32 }
33
34 function ios12ReadLLDPStatus ($input)
35 {
36 $ret = array();
37 $got_header = FALSE;
38 foreach (explode ("\n", $input) as $line)
39 {
40 if (preg_match ("/^Device ID/", $line))
41 $got_header = TRUE;
42
43 if (!$got_header)
44 continue;
45
46 $matches = preg_split ('/\s+/', $line);
47
48 switch (count ($matches))
49 {
50 case 5:
51 list ($remote_name, $local_port, $ttl, $caps, $remote_port) = $matches;
52 $local_port = ios12ShortenIfName ($local_port);
53 $remote_port = ios12ShortenIfName ($remote_port);
54 $ret[$local_port][] = array
55 (
56 'device' => $remote_name,
57 'port' => $remote_port,
58 );
59 break;
60 default:
61 }
62 }
63 return $ret;
64 }
65
66 function xos12ReadLLDPStatus ($input)
67 {
68 $ret = array();
69 foreach (explode ("\n", $input) as $line)
70 {
71 $matches = array();
72 switch (TRUE)
73 {
74 case preg_match ('/^LLDP Port ([[:digit:]]+) detected \d+ neighbor$/', $line, $matches):
75 $ret['current']['local_port'] = ios12ShortenIfName ($matches[1]);
76 break;
77 case preg_match ('/^ Port ID : "(.+)"$/', $line, $matches):
78 $ret['current']['remote_port'] = ios12ShortenIfName ($matches[1]);
79 break;
80 case preg_match ('/^ - System Name: "(.+)"$/', $line, $matches):
81 if
82 (
83 array_key_exists ('current', $ret) and
84 array_key_exists ('local_port', $ret['current']) and
85 array_key_exists ('remote_port', $ret['current'])
86 )
87 $ret[$ret['current']['local_port']][] = array
88 (
89 'device' => $matches[1],
90 'port' => $ret['current']['remote_port'],
91 );
92 unset ($ret['current']);
93 default:
94 }
95 }
96 unset ($ret['current']);
97 return $ret;
98 }
99
100 function vrp53ReadLLDPStatus ($input)
101 {
102 $ret = array();
103 foreach (explode ("\n", $input) as $line)
104 {
105 $matches = array();
106 switch (TRUE)
107 {
108 case preg_match ('/^(.+) has \d+ neighbors:$/', $line, $matches):
109 $ret['current']['local_port'] = ios12ShortenIfName ($matches[1]);
110 break;
111 case preg_match ('/^(PortIdSubtype|PortId): ([^ ]+)/', $line, $matches):
112 $ret['current'][$matches[1]] = $matches[2];
113 break;
114 case preg_match ('/^SysName: (.+)$/', $line, $matches):
115 if
116 (
117 array_key_exists ('current', $ret) and
118 array_key_exists ('PortIdSubtype', $ret['current']) and
119 ($ret['current']['PortIdSubtype'] == 'interfaceAlias' or $ret['current']['PortIdSubtype'] == 'interfaceName') and
120 array_key_exists ('PortId', $ret['current']) and
121 array_key_exists ('local_port', $ret['current'])
122 )
123 $ret[$ret['current']['local_port']][] = array
124 (
125 'device' => $matches[1],
126 'port' => ios12ShortenIfName ($ret['current']['PortId']),
127 );
128 unset ($ret['current']);
129 break;
130 default:
131 }
132 }
133 unset ($ret['current']);
134 return $ret;
135 }
136
137 function vrp55ReadLLDPStatus ($input)
138 {
139 $ret = array();
140 foreach (explode ("\n", $input) as $line)
141 {
142 $matches = array();
143 switch (TRUE)
144 {
145 case preg_match ('/^(.+) has \d+ neighbors:$/', $line, $matches):
146 $ret['current']['local_port'] = ios12ShortenIfName ($matches[1]);
147 break;
148 case preg_match ('/^Port ID type :([^ ]+)/', $line, $matches):
149 $ret['current']['PortIdSubtype'] = $matches[1];
150 break;
151 case preg_match ('/^Port ID :(.+)$/', $line, $matches):
152 $ret['current']['PortId'] = $matches[1];
153 break;
154 case preg_match ('/^System name :(.+)$/', $line, $matches):
155 if
156 (
157 array_key_exists ('current', $ret) and
158 array_key_exists ('PortIdSubtype', $ret['current']) and
159 ($ret['current']['PortIdSubtype'] == 'interfaceAlias' or $ret['current']['PortIdSubtype'] == 'interfaceName') and
160 array_key_exists ('PortId', $ret['current']) and
161 array_key_exists ('local_port', $ret['current'])
162 )
163 $ret[$ret['current']['local_port']][] = array
164 (
165 'device' => $matches[1],
166 'port' => ios12ShortenIfName ($ret['current']['PortId']),
167 );
168 unset ($ret['current']);
169 break;
170 default:
171 }
172 }
173 unset ($ret['current']);
174 return $ret;
175 }
176
177 function vrp53ReadHNDPStatus ($input)
178 {
179 $ret = array();
180 foreach (explode ("\n", $input) as $line)
181 {
182 $matches = array();
183 switch (TRUE)
184 {
185 case preg_match ('/^ Interface: (.+)$/', $line, $matches):
186 $ret['current']['local_port'] = ios12ShortenIfName ($matches[1]);
187 break;
188 case preg_match ('/^ Port Name : (.+)$/', $line, $matches):
189 $ret['current']['remote_port'] = ios12ShortenIfName ($matches[1]);
190 break;
191 case preg_match ('/^ Device Name : (.+)$/', $line, $matches):
192 if
193 (
194 array_key_exists ('current', $ret) and
195 array_key_exists ('local_port', $ret['current']) and
196 array_key_exists ('remote_port', $ret['current'])
197 )
198 $ret[$ret['current']['local_port']][] = array
199 (
200 'device' => $matches[1],
201 'port' => $ret['current']['remote_port'],
202 );
203 unset ($ret['current']);
204 break;
205 default:
206 }
207 }
208 unset ($ret['current']);
209 return $ret;
210 }
211
212 function ios12ReadVLANConfig ($input)
213 {
214 $ret = array
215 (
216 'vlanlist' => array(),
217 'portdata' => array(),
218 );
219 $procfunc = 'ios12ScanTopLevel';
220 foreach (explode ("\n", $input) as $line)
221 $procfunc = $procfunc ($ret, $line);
222 return $ret;
223 }
224
225 function ios12ScanTopLevel (&$work, $line)
226 {
227 $matches = array();
228 switch (TRUE)
229 {
230 case (preg_match ('@^interface ((Ethernet|FastEthernet|GigabitEthernet|TenGigabitEthernet|Port-channel)[[:digit:]]+(/[[:digit:]]+)*)$@', $line, $matches)):
231 $work['current'] = array ('port_name' => ios12ShortenIfName ($matches[1]));
232 $work['current']['config'][] = array ('type' => 'line-header', 'line' => $line);
233 return 'ios12PickSwitchportCommand'; // switch to interface block reading
234 case (preg_match ('/^VLAN Name Status Ports$/', $line, $matches)):
235 return 'ios12PickVLANCommand';
236 default:
237 return __FUNCTION__; // continue scan
238 }
239 }
240
241 function ios12PickSwitchportCommand (&$work, $line)
242 {
243 if ($line[0] != ' ') // end of interface section
244 {
245 // save work, if it makes sense
246 switch ($work['current']['mode'])
247 {
248 case 'access':
249 if (!array_key_exists ('access vlan', $work['current']))
250 $work['current']['access vlan'] = 1;
251 $work['portdata'][$work['current']['port_name']] = array
252 (
253 'mode' => 'access',
254 'allowed' => array ($work['current']['access vlan']),
255 'native' => $work['current']['access vlan'],
256 );
257 break;
258 case 'trunk':
259 if (!array_key_exists ('trunk native vlan', $work['current']))
260 $work['current']['trunk native vlan'] = 1;
261 if (!array_key_exists ('trunk allowed vlan', $work['current']))
262 $work['current']['trunk allowed vlan'] = range (VLAN_MIN_ID, VLAN_MAX_ID);
263 // Having configured VLAN as "native" doesn't mean anything
264 // as long as it's not listed on the "allowed" line.
265 $effective_native = in_array
266 (
267 $work['current']['trunk native vlan'],
268 $work['current']['trunk allowed vlan']
269 ) ? $work['current']['trunk native vlan'] : 0;
270 $work['portdata'][$work['current']['port_name']] = array
271 (
272 'mode' => 'trunk',
273 'allowed' => $work['current']['trunk allowed vlan'],
274 'native' => $effective_native,
275 );
276 break;
277 case 'SKIP':
278 break;
279 case 'IP':
280 default:
281 // dot1q-tunnel, dynamic, private-vlan or even none --
282 // show in returned config and let user decide, if they
283 // want to fix device config or work around these ports
284 // by means of VST.
285 $work['portdata'][$work['current']['port_name']] = array
286 (
287 'mode' => 'none',
288 'allowed' => array(),
289 'native' => 0,
290 );
291 break;
292 }
293 if (isset ($work['portdata'][$work['current']['port_name']]))
294 $work['portdata'][$work['current']['port_name']]['config'] = $work['current']['config'];
295 unset ($work['current']);
296 return 'ios12ScanTopLevel';
297 }
298 // not yet
299 $matches = array();
300 $line_class = 'line-8021q';
301 switch (TRUE)
302 {
303 case (preg_match ('@^ switchport mode (.+)$@', $line, $matches)):
304 $work['current']['mode'] = $matches[1];
305 break;
306 case (preg_match ('@^ switchport access vlan (.+)$@', $line, $matches)):
307 $work['current']['access vlan'] = $matches[1];
308 break;
309 case (preg_match ('@^ switchport trunk native vlan (.+)$@', $line, $matches)):
310 $work['current']['trunk native vlan'] = $matches[1];
311 break;
312 case (preg_match ('@^ switchport trunk allowed vlan add (.+)$@', $line, $matches)):
313 $work['current']['trunk allowed vlan'] = array_merge
314 (
315 $work['current']['trunk allowed vlan'],
316 iosParseVLANString ($matches[1])
317 );
318 break;
319 case (preg_match ('@^ switchport trunk allowed vlan (.+)$@', $line, $matches)):
320 $work['current']['trunk allowed vlan'] = iosParseVLANString ($matches[1]);
321 break;
322 case preg_match ('@^ channel-group @', $line):
323 // port-channel subinterface config follows that of the master interface
324 $work['current']['mode'] = 'SKIP';
325 break;
326 case preg_match ('@^ ip address @', $line):
327 // L3 interface does no switchport functions
328 $work['current']['mode'] = 'IP';
329 break;
330 default: // suppress warning on irrelevant config clause
331 $line_class = 'line-other';
332 }
333 $work['current']['config'][] = array ('type' => $line_class, 'line' => $line);
334 return __FUNCTION__;
335 }
336
337 function ios12PickVLANCommand (&$work, $line)
338 {
339 $matches = array();
340 switch (TRUE)
341 {
342 case ($line == '---- -------------------------------- --------- -------------------------------'):
343 // ignore the rest of VLAN table header;
344 break;
345 case (preg_match ('@! END OF VLAN LIST$@', $line)):
346 return 'ios12ScanTopLevel';
347 case (preg_match ('@^([[:digit:]]+) {1,4}.{32} active @', $line, $matches)):
348 if (!array_key_exists ($matches[1], $work['vlanlist']))
349 $work['vlanlist'][] = $matches[1];
350 break;
351 default:
352 }
353 return __FUNCTION__;
354 }
355
356 // Another finite automata to read a dialect of Foundry configuration.
357 function fdry5ReadVLANConfig ($input)
358 {
359 $ret = array
360 (
361 'vlanlist' => array(),
362 'portdata' => array(),
363 );
364 $procfunc = 'fdry5ScanTopLevel';
365 foreach (explode ("\n", $input) as $line)
366 $procfunc = $procfunc ($ret, $line);
367 return $ret;
368 }
369
370 function fdry5ScanTopLevel (&$work, $line)
371 {
372 $matches = array();
373 switch (TRUE)
374 {
375 case (preg_match ('@^vlan ([[:digit:]]+)( name .+)? (by port)$@', $line, $matches)):
376 if (!array_key_exists ($matches[1], $work['vlanlist']))
377 $work['vlanlist'][] = $matches[1];
378 $work['current'] = array ('vlan_id' => $matches[1]);
379 return 'fdry5PickVLANSubcommand';
380 case (preg_match ('@^interface ethernet ([[:digit:]]+/[[:digit:]]+/[[:digit:]]+)$@', $line, $matches)):
381 $work['current'] = array ('port_name' => 'e' . $matches[1]);
382 return 'fdry5PickInterfaceSubcommand';
383 default:
384 return __FUNCTION__;
385 }
386 }
387
388 function fdry5PickVLANSubcommand (&$work, $line)
389 {
390 if ($line[0] != ' ') // end of VLAN section
391 {
392 unset ($work['current']);
393 return 'fdry5ScanTopLevel';
394 }
395 // not yet
396 $matches = array();
397 switch (TRUE)
398 {
399 case (preg_match ('@^ tagged (.+)$@', $line, $matches)):
400 // add current VLAN to 'allowed' list of each mentioned port
401 foreach (fdry5ParsePortString ($matches[1]) as $port_name)
402 if (array_key_exists ($port_name, $work['portdata']))
403 $work['portdata'][$port_name]['allowed'][] = $work['current']['vlan_id'];
404 else
405 $work['portdata'][$port_name] = array
406 (
407 'mode' => 'trunk',
408 'allowed' => array ($work['current']['vlan_id']),
409 'native' => 0, // can be updated later
410 );
411 $work['portdata'][$port_name]['mode'] = 'trunk';
412 break;
413 case (preg_match ('@^ untagged (.+)$@', $line, $matches)):
414 // replace 'native' column of each mentioned port with current VLAN ID
415 foreach (fdry5ParsePortString ($matches[1]) as $port_name)
416 {
417 if (array_key_exists ($port_name, $work['portdata']))
418 {
419 $work['portdata'][$port_name]['native'] = $work['current']['vlan_id'];
420 $work['portdata'][$port_name]['allowed'][] = $work['current']['vlan_id'];
421 }
422 else
423 $work['portdata'][$port_name] = array
424 (
425 'mode' => 'access',
426 'allowed' => array ($work['current']['vlan_id']),
427 'native' => $work['current']['vlan_id'],
428 );
429 // Untagged ports are initially assumed to be access ports, and
430 // when this assumption is right, this is the final port mode state.
431 // When the port is dual-mode one, this is detected and justified
432 // later in "interface" section of config text.
433 $work['portdata'][$port_name]['mode'] = 'access';
434 }
435 break;
436 default: // nom-nom
437 }
438 return __FUNCTION__;
439 }
440
441 function fdry5PickInterfaceSubcommand (&$work, $line)
442 {
443 if ($line[0] != ' ') // end of interface section
444 {
445 if (array_key_exists ('dual-mode', $work['current']))
446 {
447 if (array_key_exists ($work['current']['port_name'], $work['portdata']))
448 // update existing record
449 $work['portdata'][$work['current']['port_name']]['native'] = $work['current']['dual-mode'];
450 else
451 // add new
452 $work['portdata'][$work['current']['port_name']] = array
453 (
454 'allowed' => array ($work['current']['dual-mode']),
455 'native' => $work['current']['dual-mode'],
456 );
457 // a dual-mode port is always considered a trunk port
458 // (but not in the IronWare's meaning of "trunk") regardless of
459 // number of assigned tagged VLANs
460 $work['portdata'][$work['current']['port_name']]['mode'] = 'trunk';
461 }
462 unset ($work['current']);
463 return 'fdry5ScanTopLevel';
464 }
465 $matches = array();
466 switch (TRUE)
467 {
468 case (preg_match ('@^ dual-mode( +[[:digit:]]+ *)?$@', $line, $matches)):
469 // default VLAN ID for dual-mode command is 1
470 $work['current']['dual-mode'] = strlen (trim ($matches[1])) ? trim ($matches[1]) : 1;
471 break;
472 // FIXME: trunk/link-aggregate/ip address pulls port from 802.1Q field
473 default: // nom-nom
474 }
475 return __FUNCTION__;
476 }
477
478 function fdry5ParsePortString ($string)
479 {
480 $ret = array();
481 $tokens = explode (' ', trim ($string));
482 while (count ($tokens))
483 {
484 $letters = array_shift ($tokens); // "ethe", "to"
485 $numbers = array_shift ($tokens); // "x", "x/x", "x/x/x"
486 switch ($letters)
487 {
488 case 'ethe':
489 if ($prev_numbers != NULL)
490 $ret[] = 'e' . $prev_numbers;
491 $prev_numbers = $numbers;
492 break;
493 case 'to':
494 $ret = array_merge ($ret, fdry5GenPortRange ($prev_numbers, $numbers));
495 $prev_numbers = NULL; // no action on next token
496 break;
497 default: // ???
498 return array();
499 }
500 }
501 // flush delayed item
502 if ($prev_numbers != NULL)
503 $ret[] = 'e' . $prev_numbers;
504 return $ret;
505 }
506
507 // Take two indices in form "x", "x/x" or "x/x/x" and return the range of
508 // ports spanning from the first to the last. The switch software makes it
509 // easier to perform, because "ethe x/x/x to y/y/y" ranges never cross
510 // unit/slot boundary (every index except the last remains constant).
511 function fdry5GenPortRange ($from, $to)
512 {
513 $matches = array();
514 if (1 !== preg_match ('@^([[:digit:]]+/)?([[:digit:]]+/)?([[:digit:]]+)$@', $from, $matches))
515 return array();
516 $prefix = 'e' . $matches[1] . $matches[2];
517 $from_idx = $matches[3];
518 if (1 !== preg_match ('@^([[:digit:]]+/)?([[:digit:]]+/)?([[:digit:]]+)$@', $to, $matches))
519 return array();
520 $to_idx = $matches[3];
521 for ($i = $from_idx; $i <= $to_idx; $i++)
522 $ret[] = $prefix . $i;
523 return $ret;
524 }
525
526 // an implementation for Huawei syntax
527 function vrp53ReadVLANConfig ($input)
528 {
529 $ret = array
530 (
531 'vlanlist' => array(),
532 'portdata' => array(),
533 );
534 $procfunc = 'vrp53ScanTopLevel';
535 foreach (explode ("\n", $input) as $line)
536 $procfunc = $procfunc ($ret, $line);
537 return $ret;
538 }
539
540 function vrp53ScanTopLevel (&$work, $line)
541 {
542 $matches = array();
543 switch (TRUE)
544 {
545 case (preg_match ('@^ vlan batch (.+)$@', $line, $matches)):
546 foreach (vrp53ParseVLANString ($matches[1]) as $vlan_id)
547 $work['vlanlist'][] = $vlan_id;
548 return __FUNCTION__;
549 case (preg_match ('@^interface ((GigabitEthernet|XGigabitEthernet|Eth-Trunk)([[:digit:]]+(/[[:digit:]]+)*))$@', $line, $matches)):
550 $matches[1] = preg_replace ('@^GigabitEthernet(.+)$@', 'gi\\1', $matches[1]);
551 $matches[1] = preg_replace ('@^XGigabitEthernet(.+)$@', 'xg\\1', $matches[1]);
552 $matches[1] = preg_replace ('@^Eth-Trunk(.+)$@', 'et\\1', $matches[1]);
553 $work['current'] = array ('port_name' => $matches[1]);
554 $work['current']['config'][] = array ('type' => 'line-header', 'line' => $line);
555 return 'vrp53PickInterfaceSubcommand';
556 default:
557 return __FUNCTION__;
558 }
559 }
560
561 function vrp53ParseVLANString ($string)
562 {
563 $string = preg_replace ('/ to /', '-', $string);
564 $string = preg_replace ('/ /', ',', $string);
565 return iosParseVLANString ($string);
566 }
567
568 function vrp53PickInterfaceSubcommand (&$work, $line)
569 {
570 if ($line[0] == '#') // end of interface section
571 {
572 // Configuration Guide - Ethernet 3.3.4:
573 // "By default, the interface type is hybrid."
574 if (!array_key_exists ('link-type', $work['current']))
575 $work['current']['link-type'] = 'hybrid';
576 if (!array_key_exists ('allowed', $work['current']))
577 $work['current']['allowed'] = array();
578 if (!array_key_exists ('native', $work['current']))
579 $work['current']['native'] = 0;
580 switch ($work['current']['link-type'])
581 {
582 case 'access':
583 // VRP does not assign access ports to VLAN1 by default,
584 // leaving them blocked.
585 $work['portdata'][$work['current']['port_name']] =
586 $work['current']['native'] ? array
587 (
588 'allowed' => $work['current']['allowed'],
589 'native' => $work['current']['native'],
590 'mode' => 'access',
591 ) : array
592 (
593 'mode' => 'none',
594 'allowed' => array(),
595 'native' => 0,
596 );
597 break;
598 case 'trunk':
599 $work['portdata'][$work['current']['port_name']] = array
600 (
601 'allowed' => $work['current']['allowed'],
602 'native' => 0,
603 'mode' => 'trunk',
604 );
605 break;
606 case 'hybrid':
607 $work['portdata'][$work['current']['port_name']] = array
608 (
609 'allowed' => $work['current']['allowed'],
610 'native' => $work['current']['native'],
611 'mode' => 'trunk',
612 );
613 break;
614 default: // dot1q-tunnel ?
615 }
616 if (isset ($work['portdata'][$work['current']['port_name']]))
617 $work['portdata'][$work['current']['port_name']]['config'] = $work['current']['config'];
618 unset ($work['current']);
619 return 'vrp53ScanTopLevel';
620 }
621 $matches = array();
622 $line_class = 'line-8021q';
623 switch (TRUE)
624 {
625 case (preg_match ('@^ port default vlan ([[:digit:]]+)$@', $line, $matches)):
626 $work['current']['native'] = $matches[1];
627 if (!array_key_exists ('allowed', $work['current']))
628 $work['current']['allowed'] = array();
629 if (!in_array ($matches[1], $work['current']['allowed']))
630 $work['current']['allowed'][] = $matches[1];
631 break;
632 case (preg_match ('@^ port link-type (.+)$@', $line, $matches)):
633 $work['current']['link-type'] = $matches[1];
634 break;
635 case (preg_match ('@^ port trunk allow-pass vlan (.+)$@', $line, $matches)):
636 if (!array_key_exists ('allowed', $work['current']))
637 $work['current']['allowed'] = array();
638 foreach (vrp53ParseVLANString ($matches[1]) as $vlan_id)
639 if (!in_array ($vlan_id, $work['current']['allowed']))
640 $work['current']['allowed'][] = $vlan_id;
641 break;
642 // TODO: make sure, that a port with "eth-trunk" clause always ends up in "none" mode
643 default: // nom-nom
644 $line_class = 'line-other';
645 }
646 $work['current']['config'][] = array('type' => $line_class, 'line' => $line);
647 return __FUNCTION__;
648 }
649
650 function vrp55Read8021QConfig ($input)
651 {
652 $ret = array
653 (
654 'vlanlist' => array (1), // VRP 5.50 hides VLAN1 from config text
655 'portdata' => array(),
656 );
657 foreach (explode ("\n", $input) as $line)
658 {
659 $matches = array();
660 // top level
661 if (!array_key_exists ('current', $ret))
662 {
663 switch (TRUE)
664 {
665 case (preg_match ('@^ vlan batch (.+)$@', $line, $matches)):
666 foreach (vrp53ParseVLANString ($matches[1]) as $vlan_id)
667 $ret['vlanlist'][] = $vlan_id;
668 break;
669 case (preg_match ('@^interface ((GigabitEthernet|XGigabitEthernet|Eth-Trunk)([[:digit:]]+(/[[:digit:]]+)*))$@', $line, $matches)):
670 $matches[1] = preg_replace ('@^GigabitEthernet(.+)$@', 'gi\\1', $matches[1]);
671 $matches[1] = preg_replace ('@^XGigabitEthernet(.+)$@', 'xg\\1', $matches[1]);
672 $ret['current'] = array ('port_name' => $matches[1]);
673 $ret['current']['config'][] = array ('type' => 'line-header', 'line' => $line);
674 break;
675 }
676 continue;
677 }
678 // inside an interface block
679 $line_class = 'line-8021q';
680 switch (TRUE)
681 {
682 case preg_match ('/^ port (link-type )?hybrid /', $line):
683 throw new RTGatewayError ("unsupported configuration: ${line}");
684 case preg_match ('/^ port link-type (.+)$/', $line, $matches):
685 $ret['current']['link-type'] = $matches[1];
686 break;
687 // Native VLAN is configured differently for each link-type case, but
688 // VRP is known to filter off clauses, which don't make sense for
689 // current link-type. This way any interface section should contain
690 // only one kind of "set native" clause (but if this constraint breaks,
691 // we get a problem).
692 case preg_match ('/^ port (default|trunk pvid) vlan ([[:digit:]]+)$/', $line, $matches):
693 $ret['current']['native'] = $matches[2];
694 if (!array_key_exists ('allowed', $ret['current']))
695 $ret['current']['allowed'] = array();
696 if (!in_array ($ret['current']['native'], $ret['current']['allowed']))
697 $ret['current']['allowed'][] = $ret['current']['native'];
698 break;
699 case preg_match ('/^ port trunk allow-pass vlan (.+)$/', $line, $matches):
700 if (!array_key_exists ('allowed', $ret['current']))
701 $ret['current']['allowed'] = array();
702 foreach (vrp53ParseVLANString ($matches[1]) as $vlan_id)
703 if (!in_array ($vlan_id, $ret['current']['allowed']))
704 $ret['current']['allowed'][] = $vlan_id;
705 break;
706 case $line == ' undo portswitch':
707 case preg_match ('/^ ip address /', $line):
708 $ret['current']['link-type'] = 'IP';
709 break;
710 case preg_match ('/^ eth-trunk /', $line):
711 $ret['current']['link-type'] = 'SKIP';
712 break;
713 case substr ($line, 0, 1) == '#': // end of interface section
714 if (!array_key_exists ('link-type', $ret['current']))
715 throw new RTGatewayError ('unsupported configuration: link-type is neither trunk nor access for ' . $ret['current']['port_name']);
716 if (!array_key_exists ('allowed', $ret['current']))
717 $ret['current']['allowed'] = array();
718 if (!array_key_exists ('native', $ret['current']))
719 $ret['current']['native'] = 0;
720 switch ($ret['current']['link-type'])
721 {
722 case 'access':
723 // In VRP 5.50 an access port has default VLAN ID == 1
724 $ret['portdata'][$ret['current']['port_name']] =
725 $ret['current']['native'] ? array
726 (
727 'mode' => 'access',
728 'allowed' => $ret['current']['allowed'],
729 'native' => $ret['current']['native'],
730 ) : array
731 (
732 'mode' => 'access',
733 'allowed' => array (VLAN_DFL_ID),
734 'native' => VLAN_DFL_ID,
735 );
736 break;
737 case 'trunk':
738 $ret['portdata'][$ret['current']['port_name']] = array
739 (
740 'mode' => 'trunk',
741 'allowed' => $ret['current']['allowed'],
742 'native' => $ret['current']['native'],
743 );
744 break;
745 case 'IP':
746 $ret['portdata'][$ret['current']['port_name']] = array
747 (
748 'mode' => 'none',
749 'allowed' => array(),
750 'native' => 0,
751 );
752 break;
753 case 'SKIP':
754 default: // dot1q-tunnel ?
755 }
756 if (isset ($ret['portdata'][$ret['current']['port_name']]))
757 $ret['portdata'][$ret['current']['port_name']]['config'] = $ret['current']['config'];
758 unset ($ret['current']);
759 continue 2;
760 default: // nom-nom
761 $line_class = 'line-other';
762 }
763 $ret['current']['config'][] = array ('type' => $line_class, 'line' => $line);
764 }
765 return $ret;
766 }
767
768 function nxos4Read8021QConfig ($input)
769 {
770 $ret = array
771 (
772 'vlanlist' => array(),
773 'portdata' => array(),
774 );
775 $procfunc = 'nxos4ScanTopLevel';
776 foreach (explode ("\n", $input) as $line)
777 $procfunc = $procfunc ($ret, $line);
778 return $ret;
779 }
780
781 function nxos4ScanTopLevel (&$work, $line)
782 {
783 $matches = array();
784 switch (TRUE)
785 {
786 case (preg_match ('@^interface ((Ethernet|Port-channel)[[:digit:]]+(/[[:digit:]]+)*)$@i', $line, $matches)):
787 $matches[1] = preg_replace ('@^Ethernet(.+)$@i', 'e\\1', $matches[1]);
788 $matches[1] = preg_replace ('@^Port-channel(.+)$@i', 'po\\1', $matches[1]);
789 $work['current'] = array ('port_name' => $matches[1]);
790 $work['current']['config'][] = array ('type' => 'line-header', 'line' => $line);
791 return 'nxos4PickSwitchportCommand';
792 case (preg_match ('@^vlan (.+)$@', $line, $matches)):
793 foreach (iosParseVLANString ($matches[1]) as $vlan_id)
794 $work['vlanlist'][] = $vlan_id;
795 return __FUNCTION__;
796 default:
797 return __FUNCTION__; // continue scan
798 }
799 }
800
801 function nxos4PickSwitchportCommand (&$work, $line)
802 {
803 if ($line == '') // end of interface section
804 {
805 // fill in defaults
806 if (!array_key_exists ('mode', $work['current']))
807 $work['current']['mode'] = 'access';
808 // save work, if it makes sense
809 switch ($work['current']['mode'])
810 {
811 case 'access':
812 if (!array_key_exists ('access vlan', $work['current']))
813 $work['current']['access vlan'] = 1;
814 $work['portdata'][$work['current']['port_name']] = array
815 (
816 'mode' => 'access',
817 'allowed' => array ($work['current']['access vlan']),
818 'native' => $work['current']['access vlan'],
819 );
820 break;
821 case 'trunk':
822 if (!array_key_exists ('trunk native vlan', $work['current']))
823 $work['current']['trunk native vlan'] = 1;
824 // FIXME: NX-OS reserves VLANs 3968 through 4047 plus 4094 for itself
825 if (!array_key_exists ('trunk allowed vlan', $work['current']))
826 $work['current']['trunk allowed vlan'] = range (VLAN_MIN_ID, VLAN_MAX_ID);
827 // Having configured VLAN as "native" doesn't mean anything
828 // as long as it's not listed on the "allowed" line.
829 $effective_native = in_array
830 (
831 $work['current']['trunk native vlan'],
832 $work['current']['trunk allowed vlan']
833 ) ? $work['current']['trunk native vlan'] : 0;
834 $work['portdata'][$work['current']['port_name']] = array
835 (
836 'mode' => 'trunk',
837 'allowed' => $work['current']['trunk allowed vlan'],
838 'native' => $effective_native,
839 );
840 break;
841 case 'SKIP':
842 case 'fex-fabric': // associated port-channel
843 break;
844 default:
845 // dot1q-tunnel, dynamic, private-vlan
846 $work['portdata'][$work['current']['port_name']] = array
847 (
848 'mode' => 'none',
849 'allowed' => array(),
850 'native' => 0,
851 );
852 // unset (routed), dot1q-tunnel, dynamic, private-vlan --- skip these
853 }
854 if (isset ($work['portdata'][$work['current']['port_name']]))
855 $work['portdata'][$work['current']['port_name']]['config'] = $work['current']['config'];
856 unset ($work['current']);
857 return 'nxos4ScanTopLevel';
858 }
859 // not yet
860 $matches = array();
861 $line_class = 'line-8021q';
862 switch (TRUE)
863 {
864 case (preg_match ('@^ switchport mode (.+)$@', $line, $matches)):
865 $work['current']['mode'] = $matches[1];
866 break;
867 case (preg_match ('@^ switchport access vlan (.+)$@', $line, $matches)):
868 $work['current']['access vlan'] = $matches[1];
869 break;
870 case (preg_match ('@^ switchport trunk native vlan (.+)$@', $line, $matches)):
871 $work['current']['trunk native vlan'] = $matches[1];
872 break;
873 case (preg_match ('@^ switchport trunk allowed vlan add (.+)$@', $line, $matches)):
874 $work['current']['trunk allowed vlan'] = array_merge
875 (
876 $work['current']['trunk allowed vlan'],
877 iosParseVLANString ($matches[1])
878 );
879 break;
880 case (preg_match ('@^ switchport trunk allowed vlan (.+)$@', $line, $matches)):
881 $work['current']['trunk allowed vlan'] = iosParseVLANString ($matches[1]);
882 break;
883 case preg_match ('/^ +channel-group /', $line):
884 $work['current']['mode'] = 'SKIP';
885 break;
886 default: // suppress warning on irrelevant config clause
887 $line_class = 'line-other';
888 }
889 $work['current']['config'][] = array ('type' => $line_class, 'line' => $line);
890 return __FUNCTION__;
891 }
892
893 // Get a list of VLAN management pseudo-commands and return a text
894 // of real vendor-specific commands, which implement the work.
895 // This work is done in two rounds:
896 // 1. For "add allowed" and "rem allowed" commands detect continuous
897 // sequences of VLAN IDs and replace them with ranges of form "A-B",
898 // where B>A.
899 // 2. Iterate over the resulting list and produce real CLI commands.
900 function ios12TranslatePushQueue ($queue)
901 {
902 $ret = '';
903 foreach ($queue as $cmd)
904 switch ($cmd['opcode'])
905 {
906 case 'create VLAN':
907 $ret .= "vlan ${cmd['arg1']}\nexit\n";
908 break;
909 case 'destroy VLAN':
910 $ret .= "no vlan ${cmd['arg1']}\n";
911 break;
912 case 'add allowed':
913 case 'rem allowed':
914 $clause = $cmd['opcode'] == 'add allowed' ? 'add' : 'remove';
915 $ret .= "interface ${cmd['port']}\n";
916 foreach (listToRanges ($cmd['vlans']) as $range)
917 $ret .= "switchport trunk allowed vlan ${clause} " .
918 ($range['from'] == $range['to'] ? $range['to'] : "${range['from']}-${range['to']}") .
919 "\n";
920 $ret .= "exit\n";
921 break;
922 case 'set native':
923 $ret .= "interface ${cmd['arg1']}\nswitchport trunk native vlan ${cmd['arg2']}\nexit\n";
924 break;
925 case 'unset native':
926 $ret .= "interface ${cmd['arg1']}\nno switchport trunk native vlan ${cmd['arg2']}\nexit\n";
927 break;
928 case 'set access':
929 $ret .= "interface ${cmd['arg1']}\nswitchport access vlan ${cmd['arg2']}\nexit\n";
930 break;
931 case 'unset access':
932 $ret .= "interface ${cmd['arg1']}\nno switchport access vlan\nexit\n";
933 break;
934 case 'set mode':
935 $ret .= "interface ${cmd['arg1']}\nswitchport mode ${cmd['arg2']}\n";
936 if ($cmd['arg2'] == 'trunk')
937 $ret .= "no switchport trunk native vlan\nswitchport trunk allowed vlan none\n";
938 $ret .= "exit\n";
939 break;
940 case 'begin configuration':
941 $ret .= "configure terminal\n";
942 break;
943 case 'end configuration':
944 $ret .= "end\n";
945 break;
946 case 'save configuration':
947 $ret .= "copy running-config startup-config\n\n";
948 break;
949 default:
950 throw new InvalidArgException ('opcode', $cmd['opcode']);
951 }
952 return $ret;
953 }
954
955 function fdry5TranslatePushQueue ($queue)
956 {
957 $ret = '';
958 foreach ($queue as $cmd)
959 switch ($cmd['opcode'])
960 {
961 case 'create VLAN':
962 $ret .= "vlan ${cmd['arg1']}\nexit\n";
963 break;
964 case 'destroy VLAN':
965 $ret .= "no vlan ${cmd['arg1']}\n";
966 break;
967 case 'add allowed':
968 foreach ($cmd['vlans'] as $vlan_id)
969 $ret .= "vlan ${vlan_id}\ntagged ${cmd['port']}\nexit\n";
970 break;
971 case 'rem allowed':
972 foreach ($cmd['vlans'] as $vlan_id)
973 $ret .= "vlan ${vlan_id}\nno tagged ${cmd['port']}\nexit\n";
974 break;
975 case 'set native':
976 $ret .= "interface ${cmd['arg1']}\ndual-mode ${cmd['arg2']}\nexit\n";
977 break;
978 case 'unset native':
979 $ret .= "interface ${cmd['arg1']}\nno dual-mode ${cmd['arg2']}\nexit\n";
980 break;
981 case 'set access':
982 $ret .= "vlan ${cmd['arg2']}\nuntagged ${cmd['arg1']}\nexit\n";
983 break;
984 case 'unset access':
985 $ret .= "vlan ${cmd['arg2']}\nno untagged ${cmd['arg1']}\nexit\n";
986 break;
987 case 'set mode': // NOP
988 break;
989 case 'begin configuration':
990 $ret .= "conf t\n";
991 break;
992 case 'end configuration':
993 $ret .= "end\n";
994 break;
995 case 'save configuration':
996 $ret .= "write memory\n";
997 break;
998 default:
999 throw new InvalidArgException ('opcode', $cmd['opcode']);
1000 }
1001 return $ret;
1002 }
1003
1004 function vrp53TranslatePushQueue ($queue)
1005 {
1006 $ret = '';
1007 foreach ($queue as $cmd)
1008 switch ($cmd['opcode'])
1009 {
1010 case 'create VLAN':
1011 $ret .= "vlan ${cmd['arg1']}\nquit\n";
1012 break;
1013 case 'destroy VLAN':
1014 $ret .= "undo vlan ${cmd['arg1']}\n";
1015 break;
1016 case 'add allowed':
1017 case 'rem allowed':
1018 $clause = $cmd['opcode'] == 'add allowed' ? '' : 'undo ';
1019 $ret .= "interface ${cmd['port']}\n";
1020 foreach (listToRanges ($cmd['vlans']) as $range)
1021 $ret .= "${clause}port trunk allow-pass vlan " .
1022 ($range['from'] == $range['to'] ? $range['to'] : "${range['from']} to ${range['to']}") .
1023 "\n";
1024 $ret .= "quit\n";
1025 break;
1026 case 'set native':
1027 case 'set access':
1028 $ret .= "interface ${cmd['arg1']}\nport default vlan ${cmd['arg2']}\nquit\n";
1029 break;
1030 case 'unset native':
1031 case 'unset access':
1032 $ret .= "interface ${cmd['arg1']}\nundo port default vlan\nquit\n";
1033 break;
1034 case 'set mode':
1035 $modemap = array ('access' => 'access', 'trunk' => 'hybrid');
1036 $ret .= "interface ${cmd['arg1']}\nport link-type " . $modemap[$cmd['arg2']] . "\n";
1037 if ($cmd['arg2'] == 'hybrid')
1038 $ret .= "undo port default vlan\nundo port trunk allow-pass vlan all\n";
1039 $ret .= "quit\n";
1040 break;
1041 case 'begin configuration':
1042 $ret .= "system-view\n";
1043 break;
1044 case 'end configuration':
1045 $ret .= "return\n";
1046 break;
1047 case 'save configuration':
1048 $ret .= "save\nY\n";
1049 break;
1050 default:
1051 throw new InvalidArgException ('opcode', $cmd['opcode']);
1052 }
1053 return $ret;
1054 }
1055
1056 function vrp55TranslatePushQueue ($queue)
1057 {
1058 $ret = '';
1059 foreach ($queue as $cmd)
1060 switch ($cmd['opcode'])
1061 {
1062 case 'create VLAN':
1063 if ($cmd['arg1'] != 1)
1064 $ret .= "vlan ${cmd['arg1']}\nquit\n";
1065 break;
1066 case 'destroy VLAN':
1067 if ($cmd['arg1'] != 1)
1068 $ret .= "undo vlan ${cmd['arg1']}\n";
1069 break;
1070 case 'add allowed':
1071 case 'rem allowed':
1072 $undo = $cmd['opcode'] == 'add allowed' ? '' : 'undo ';
1073 $ret .= "interface ${cmd['port']}\n";
1074 foreach (listToRanges ($cmd['vlans']) as $range)
1075 $ret .= "${undo}port trunk allow-pass vlan " .
1076 ($range['from'] == $range['to'] ? $range['to'] : "${range['from']} to ${range['to']}") .
1077 "\n";
1078 $ret .= "quit\n";
1079 break;
1080 case 'set native':
1081 $ret .= "interface ${cmd['arg1']}\nport trunk pvid vlan ${cmd['arg2']}\nquit\n";
1082 break;
1083 case 'set access':
1084 $ret .= "interface ${cmd['arg1']}\nport default vlan ${cmd['arg2']}\nquit\n";
1085 break;
1086 case 'unset native':
1087 $ret .= "interface ${cmd['arg1']}\nundo port trunk pvid vlan\nquit\n";
1088 break;
1089 case 'unset access':
1090 $ret .= "interface ${cmd['arg1']}\nundo port default vlan\nquit\n";
1091 break;
1092 case 'set mode':
1093 // VRP 5.50's meaning of "trunk" is much like the one of IOS
1094 // (unlike the way VRP 5.30 defines "trunk" and "hybrid"),
1095 // but it is necessary to undo configured VLANs on a port
1096 // for mode change command to succeed.
1097 $undo = array
1098 (
1099 'access' => "undo port trunk allow-pass vlan all\n" .
1100 "port trunk allow-pass vlan 1\n" .
1101 "undo port trunk pvid vlan\n",
1102 'trunk' => "undo port default vlan\n",
1103 );
1104 $ret .= "interface ${cmd['arg1']}\n" . $undo[$cmd['arg2']];
1105 $ret .= "port link-type ${cmd['arg2']}\nquit\n";
1106 break;
1107 case 'begin configuration':
1108 $ret .= "system-view\n";
1109 break;
1110 case 'end configuration':
1111 $ret .= "return\n";
1112 break;
1113 case 'save configuration':
1114 $ret .= "save\nY\n";
1115 break;
1116 default:
1117 throw new InvalidArgException ('opcode', $cmd['opcode']);
1118 }
1119 return $ret;
1120 }
1121
1122 function xos12TranslatePushQueue ($queue)
1123 {
1124 $ret = '';
1125 foreach ($queue as $cmd)
1126 switch ($cmd['opcode'])
1127 {
1128 case 'create VLAN':
1129 $ret .= "create vlan VLAN${cmd['arg1']}\n";
1130 $ret .= "configure vlan VLAN${cmd['arg1']} tag ${cmd['arg1']}\n";
1131 break;
1132 case 'destroy VLAN':
1133 $ret .= "delete vlan VLAN${cmd['arg1']}\n";
1134 break;
1135 case 'add allowed':
1136 foreach ($cmd['vlans'] as $vlan_id)
1137 {
1138 $vlan_name = $vlan_id == 1 ? 'Default' : "VLAN${vlan_id}";
1139 $ret .= "configure vlan ${vlan_name} add ports ${cmd['port']} tagged\n";
1140 }
1141 break;
1142 case 'rem allowed':
1143 foreach ($cmd['vlans'] as $vlan_id)
1144 {
1145 $vlan_name = $vlan_id == 1 ? 'Default' : "VLAN${vlan_id}";
1146 $ret .= "configure vlan ${vlan_name} delete ports ${cmd['port']}\n";
1147 }
1148 break;
1149 case 'set native':
1150 $vlan_name = $cmd['arg2'] == 1 ? 'Default' : "VLAN${cmd['arg2']}";
1151 $ret .= "configure vlan ${vlan_name} delete ports ${cmd['arg1']}\n";
1152 $ret .= "configure vlan ${vlan_name} add ports ${cmd['arg1']} untagged\n";
1153 break;
1154 case 'unset native':
1155 $vlan_name = $cmd['arg2'] == 1 ? 'Default' : "VLAN${cmd['arg2']}";
1156 $ret .= "configure vlan ${vlan_name} delete ports ${cmd['arg1']}\n";
1157 $ret .= "configure vlan ${vlan_name} add ports ${cmd['arg1']} tagged\n";
1158 break;
1159 case 'set access':
1160 $vlan_name = $cmd['arg2'] == 1 ? 'Default' : "VLAN${cmd['arg2']}";
1161 $ret .= "configure vlan ${vlan_name} add ports ${cmd['arg1']} untagged\n";
1162 break;
1163 case 'unset access':
1164 $vlan_name = $cmd['arg2'] == 1 ? 'Default' : "VLAN${cmd['arg2']}";
1165 $ret .= "configure vlan ${vlan_name} delete ports ${cmd['arg1']}\n";
1166 break;
1167 case 'set mode':
1168 case 'begin configuration':
1169 case 'end configuration':
1170 break; // NOP
1171 case 'save configuration':
1172 $ret .= "save configuration\ny\n";
1173 break;
1174 default:
1175 throw new InvalidArgException ('opcode', $cmd['opcode']);
1176 }
1177 return $ret;
1178 }
1179
1180 function xos12Read8021QConfig ($input)
1181 {
1182 $ret = array
1183 (
1184 'vlanlist' => array (1),
1185 'portdata' => array(),
1186 );
1187 foreach (explode ("\n", $input) as $line)
1188 {
1189 $matches = array();
1190 switch (TRUE)
1191 {
1192 case (preg_match ('/^create vlan "([[:alnum:]]+)"$/', $line, $matches)):
1193 if (!preg_match ('/^VLAN[[:digit:]]+$/', $matches[1]))
1194 throw new RTGatewayError ('unsupported VLAN name ' . $matches[1]);
1195 break;
1196 case (preg_match ('/^configure vlan ([[:alnum:]]+) tag ([[:digit:]]+)$/', $line, $matches)):
1197 if (strtolower ($matches[1]) == 'default')
1198 throw new RTGatewayError ('default VLAN tag must be 1');
1199 if ($matches[1] != 'VLAN' . $matches[2])
1200 throw new RTGatewayError ("VLAN name ${matches[1]} does not match its tag ${matches[2]}");
1201 $ret['vlanlist'][] = $matches[2];
1202 break;
1203 case (preg_match ('/^configure vlan ([[:alnum:]]+) add ports (.+) (tagged|untagged) */', $line, $matches)):
1204 $submatch = array();
1205 if ($matches[1] == 'Default')
1206 $matches[1] = 'VLAN1';
1207 if (!preg_match ('/^VLAN([[:digit:]]+)$/', $matches[1], $submatch))
1208 throw new RTGatewayError ('unsupported VLAN name ' . $matches[1]);
1209 $vlan_id = $submatch[1];
1210 foreach (iosParseVLANString ($matches[2]) as $port_name)
1211 {
1212 if (!array_key_exists ($port_name, $ret['portdata']))
1213 $ret['portdata'][$port_name] = array
1214 (
1215 'mode' => 'trunk',
1216 'allowed' => array(),
1217 'native' => 0,
1218 );
1219 $ret['portdata'][$port_name]['allowed'][] = $vlan_id;
1220 if ($matches[3] == 'untagged')
1221 $ret['portdata'][$port_name]['native'] = $vlan_id;
1222 }
1223 break;
1224 default:
1225 }
1226 }
1227 return $ret;
1228 }
1229
1230 function ciscoReadInterfaceStatus ($text)
1231 {
1232 $result = array();
1233 $state = 'headerSearch';
1234 foreach (explode ("\n", $text) as $line)
1235 {
1236 switch ($state)
1237 {
1238 case 'headerSearch':
1239 if (preg_match('/^Port\s+Name\s+Status/', $line))
1240 {
1241 $name_field_borders = getColumnCoordinates($line, 'Name');
1242 if (isset ($name_field_borders['from']))
1243 $state = 'readPort';
1244 }
1245 break;
1246 case 'readPort':
1247 $portname = ios12ShortenIfName (trim (substr ($line, 0, $name_field_borders['from'])));
1248 $rest = trim (substr ($line, $name_field_borders['from'] + $name_field_borders['length'] + 1));
1249 $field_list = preg_split('/\s+/', $rest);
1250 if (count ($field_list) < 4)
1251 break;
1252 list ($status_raw, $vlan, $duplex, $speed, $type) = $field_list;
1253 if ($status_raw == 'connected' || $status_raw == 'up')
1254 $status = 'up';
1255 elseif ($status_raw == 'notconnect' || $status_raw == 'down')
1256 $status = 'down';
1257 else
1258 $status = 'disabled';
1259 $result[$portname] = array
1260 (
1261 'status' => $status,
1262 'speed' => $speed,
1263 'duplex' => $duplex,
1264 );
1265 break;
1266 }
1267 }
1268 return $result;
1269 }
1270
1271 function vrpReadInterfaceStatus ($text)
1272 {
1273 $result = array();
1274 $state = 'headerSearch';
1275 foreach (explode ("\n", $text) as $line)
1276 {
1277 switch ($state)
1278 {
1279 case 'headerSearch':
1280 if (preg_match('/^Interface\s+Phy\w*\s+Protocol/i', $line))
1281 $state = 'readPort';
1282 break;
1283 case 'readPort':
1284 if (preg_match('/[\$><\]]/', $line))
1285 break 2;
1286 $field_list = preg_split('/\s+/', $line);
1287 if (count ($field_list) < 7)
1288 break;
1289 list ($portname, $status_raw) = $field_list;
1290 $portname = ios12ShortenIfName ($portname);
1291
1292 if ($status_raw == 'up' || $status_raw == 'down')
1293 $status = $status_raw;
1294 else
1295 $status = 'disabled';
1296 $result[$portname] = array
1297 (
1298 'status' => $status,
1299 );
1300 break;
1301 }
1302 }
1303 return $result;
1304 }
1305
1306 function maclist_sort ($a, $b)
1307 {
1308 if ($a['vid'] == $b['vid'])
1309 return 0;
1310 return ($a['vid'] < $b['vid']) ? -1 : 1;
1311 }
1312
1313 function ios12ReadMacList ($text)
1314 {
1315 $result = array();
1316 $state = 'headerSearch';
1317 foreach (explode ("\n", $text) as $line)
1318 {
1319 switch ($state)
1320 {
1321 case 'headerSearch':
1322 if (preg_match('/Vlan\s+Mac Address\s+Type.*Ports?\s*$/i', $line))
1323 $state = 'readPort';
1324 break;
1325 case 'readPort':
1326 if (! preg_match ('/(\d+)\s+([a-f0-9]{4}\.[a-f0-9]{4}\.[a-f0-9]{4})\s.*?(\S+)$/', trim ($line), $matches))
1327 break;
1328 $portname = ios12ShortenIfName ($matches[3]);
1329 $result[$portname][] = array
1330 (
1331 'mac' => $matches[2],
1332 'vid' => $matches[1],
1333 );
1334 break;
1335 }
1336 }
1337 foreach ($result as $portname => &$maclist)
1338 usort ($maclist, 'maclist_sort');
1339 return $result;
1340 }
1341
1342 function nxos4ReadMacList ($text)
1343 {
1344 $result = array();
1345 $state = 'headerSearch';
1346 foreach (explode ("\n", $text) as $line)
1347 {
1348 switch ($state)
1349 {
1350 case 'headerSearch':
1351 if (preg_match('/VLAN\s+MAC Address\s+Type\s+age\s+Secure\s+NTFY\s+Ports/i', $line))
1352 $state = 'readPort';
1353 break;
1354 case 'readPort':
1355 if (! preg_match ('/(\d+)\s+([a-f0-9]{4}\.[a-f0-9]{4}\.[a-f0-9]{4})\s.*?(\S+)$/', trim ($line), $matches))
1356 break;
1357 $portname = ios12ShortenIfName ($matches[3]);
1358 $result[$portname][] = array
1359 (
1360 'mac' => $matches[2],
1361 'vid' => $matches[1],
1362 );
1363 break;
1364 }
1365 }
1366 foreach ($result as $portname => &$maclist)
1367 usort ($maclist, 'maclist_sort');
1368 return $result;
1369 }
1370
1371 function vrp53ReadMacList ($text)
1372 {
1373 $result = array();
1374 $state = 'headerSearch';
1375 foreach (explode ("\n", $text) as $line)
1376 {
1377 switch ($state)
1378 {
1379 case 'headerSearch':
1380 if (preg_match('/MAC Address\s+VLAN\/VSI\s+Port/i', $line))
1381 $state = 'readPort';
1382 break;
1383 case 'readPort':
1384 if (! preg_match ('/([a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{4})\s+(\d+)\s+(\S+)/', trim ($line), $matches))
1385 break;
1386 $portname = ios12ShortenIfName ($matches[3]);
1387 $result[$portname][] = array
1388 (
1389 'mac' => str_replace ('-', '.', $matches[1]),
1390 'vid' => $matches[2],
1391 );
1392 break;
1393 }
1394 }
1395 foreach ($result as $portname => &$maclist)
1396 usort ($maclist, 'maclist_sort');
1397 return $result;
1398 }
1399
1400 function vrp55ReadMacList ($text)
1401 {
1402 $result = array();
1403 $state = 'headerSearch';
1404 foreach (explode ("\n", $text) as $line)
1405 {
1406 switch ($state)
1407 {
1408 case 'headerSearch':
1409 if (preg_match('/MAC Address\s+VLAN\/\S*\s+PEVLAN\s+CEVLAN\s+Port/i', $line))
1410 $state = 'readPort';
1411 break;
1412 case 'readPort':
1413 if (! preg_match ('/([a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{4})\s+(\d+)(?:\s+\S+){2}\s+(\S+)/', trim ($line), $matches))
1414 break;
1415 $portname = ios12ShortenIfName ($matches[3]);
1416 $result[$portname][] = array
1417 (
1418 'mac' => str_replace ('-', '.', $matches[1]),
1419 'vid' => $matches[2],
1420 );
1421 break;
1422 }
1423 }
1424 foreach ($result as $portname => &$maclist)
1425 usort ($maclist, 'maclist_sort');
1426 return $result;
1427 }
1428
1429 ?>