r4429 Adding a new custom script by Göran Törnqvist: "I thought I share my daily
[racktables-contribs] / local_portlinker.php
CommitLineData
91b12714
DO
1<?php
2//
3// This extension for RackTables is used to mass connect ports (usefull for connecting back ends of patch panels)
4//
5// Version 2.0
6//
7// Created by Jeroen Benda
8// 04-2010
9// http://racktablescode.j-tools.net
10//
11// Please do not remove the credentials if you alter this file.
12//
13
14/*
15Port linker allows you to connect the back ends of a patch panel to each other. That means you have created 2 ports per port on your
16patch panel. One for the actual port and one for the cable coming out at the back.
17The port type of the cable coming out at the back has to be in the array $portLinkerPortTypes to work (see below). So what needs to be done is
18the setup of these backend cable types. So far I came up with 3 different cable types that I use
19- utpLink
20- fiberLinkSingleMode
21- fiberLinkMultiMode
22There is no real need for the difference between the last 2 but from the network team wanted this difference since the backend is different
23
24After you've done that you need to set up a naming convention for your patch panels (if you have not already done so). With any object in
25RackTables, the common name has to be unique. This extension uses the common name to identify which panels have to be connected so it cannot
26be left blank. For example our naming convention is <rackname>-<rownumber where panel is mounted>.
27
28You can use the Port Generator extension (can be found at racktablescode.j-tools.net) to generate the ports for a patch panel, both front and
29back. Afterwards you can connect the end cables with this Port Linker.
30
31To tell this extension which ends to connect you use the label field. If none of the link ports have been connected yet and there are link
32ports defined, the port linker tab will show up.
33
34Different checks are done before the link to connect the ports is actually shown. You have to make sure that the number of ports match,
35the types match etc.
36
37So now for some examples:
38Let's say you have panel S1-42 with 24 ports and a panel N1-37 with 24 ports that need to be connected. You should create the ports on both
39panels (or generate them) so you have a front port and a cable port per port on your patch panel:
40S1-42 eth1
41S1-42 eth1link
42S1-42 eth2
43S1-42 eth2link
44....
45N1-37 eth1
46N1-37 eth1link
47N2-37 eth2
48N2-37 eth2link
49.....
50You than tell the port linker which panels to link. You do this by putting in the label field the destination of the patch panel. In this example
51the label for S1-42 will be N1-37 AND the label for N1-37 will be S1-42.
52Common name Label
53S1-42 N1-37
54N1-37 S1-42
55
56If you select the tab port linker, you get an overview of the ports
57that will be linked and a link to confirm that you actually want to connect them. If you connect on the link, they will be linked.
58
59A little more difficult example:
60Let's say you have a panel S1-42 with 24 ports and 2 panels, N1-37 and N2-37 both with 12 ports (a server rack patch panel going to 2 network
61racks). The labels will be as followed:
62Common name Label
63N1-37 S1-42
64N2-37 S1-42
65S1-42 N1-37 N2-37
66
67Notice that the label for S1-42 has 2 names seperated by a space. This means that the ports of S1-42 will be split evenly. Because there are
682 destinations, the number of ports will be divided by 2. Half the number of ports (12) will go to one and the other will go to other. In this
69example, the FIRST half of ports will go to N1-37 and the SECOND half of ports will go to N2-37
70For the network racks all ports will go to S1-42 so no need for anything else in the label.
71When you click on port linker now, you will get a list that looks like this:
72
73S1-42 eth1link N1-37 eth1link
74S1-42 eth2link N1-37 eth2link
75...
76S1-42 eth13link N2-37 eth1link
77...
78S1-42 eth24link N2-37 eth12link
79
80Finally an even more complex (but normal) example:
81You have 2 server racks S1 and S2 which both have a patch panel at the top. Half of the 24 ports go to one network N1 and the other go to another
82network rack N2. In this example, the network racks also have 24 ports patch panels and on each row their receive patch panels from 2 server racks.
83Let's say that in this case the panel in the network racks at row 37 connects to S1 and S2, the first 12 ports of the patch panel in each network
84rack go to S1 and the second 12 to S2.
85This will result in the following labels:
86Common name Label
87S1-42 N1-37 N2-37
88S2-42 N1-37 N2-37
89N1-37 S1-42 S2-42
90N2-37 S1-42 S2-42
91
92The label is always written from the perspective of the patch panel. In this example the ports for S1 go to N1 and N2 in that order. The same is
93true for the ports of S2 so the labels are the same. In the network racks you see the same thing from the other side. Even though the labels are
94the same, this will not result in a connecting conflict because port linker looks from both ends to make sure the cables are connected properly.
95If we just look at ports 1 and 13 in this example you get:
96
97S1-42 eth1link N1-37 eth1link
98S1-42 eth13link N2-37 eth1link
99S2-42 eth1link N1-37 eth13link
100S2-42 eth13link N2-37 eth13link
101
102*/
103
104//
105// This php file depends on 2 variables that have to be set before this file is included:
106//
107// $portLinkerPortTypes contains port types that can be connected (these are the types that represent the cables at the back end
108// of the patch panel)
109// $portLinkerObjectTypes contains object types that can be connected (this is usually the patch panel and maybe a self created ODF panel)
110//
111
112//
113// Set up the RackTables variables that create the tab
114// The tab only shows up if there are ports defined and no ports already linked
115//
116// The functions referred to in the handlers and trigger are all in this php file
117//
118$tab['object']['portlinker'] = 'Port linker';
119$trigger['object']['portlinker'] = 'localtrigger_PortLinker';
120$tabhandler['object']['portlinker'] = 'localfunc_PortLinker';
121$ophandler['object']['default']['linknow'] = 'execute_PortLinker';
122
123//
124// Check whether the variables are set or otherwise set the default values.
125// This is for this extension rather useless because you need extra port types that represent the cables at the back of the patch panel
126//
127global $portLinkerPortTypes;
128if (!isset($portLinkerPortTypes)) {
129 $portLinkerPortTypes = array();
130}
131global $portLinkerObjectTypes;
132if (!isset($portLinkerObjectTypes)) {
133 $portLinkerObjectTypes = array(9);
134}
135
136function determinePortSplit ($object_label,$portCount) {
137 $labels = explode(" ",$object_label);
138 $splits = array();
139 if (count($labels)==0) {
140 } else if (count($labels)==1) {
141 $splits[$labels[0]] = array();
142 $splits[$labels[0]]['start'] = 1;
143 $splits[$labels[0]]['count'] = $portCount;
144 $splits[$labels[0]]['end'] = $portCount;
145 } else {
146 $sum = 0;
147 foreach($labels as $aLabel) {
148 $splits[$aLabel] = array();
149 $splits[$aLabel]['start'] = $sum+1;
150 $splits[$aLabel]['count'] = floor($portCount/count($labels));
151 $splits[$aLabel]['end'] = $sum+floor($portCount/count($labels));
152 $sum += $splits[$aLabel]['count'];
153 }
154 if ($sum<$portCount) {
155 $temp = array_keys($splits);
156 end($temp);
157 $splits[current($temp)]['count'] += $portCount-$sum;
158 $splits[current($temp)]['end'] = $portCount;
159 }
160 }
161 return $splits;
162}
163
164//
165// This function returns the number of ports that have a type in the array $portLinkerPortTypes
166//
167function countPorts ($object_id) {
168 global $portLinkerPortTypes;
169 $record = getObjectPortsAndLinks($object_id);
170 $count = 0;
171 foreach ($record as $aPort) {
172 if (in_array($aPort['type_id'],$portLinkerPortTypes)) {
173 $count++;
174 }
175 }
176 return $count;
177}
178
179//
180// This function returns the integer found in a string. It keeps the first digits found.
181// Example: eth3link => 3
182// fbr15link => 15
183// fbr1link2 => 1
184// 12a => 12
185//
186function str2int ($xIn) {
187 $theNumber = "";
188 $allowedToAdd = true;
189 $inNumber = false;
190 $i = 0;
191 while ($i<strlen($xIn)) {
192 if ($allowedToAdd) {
193 if (is_numeric($xIn{$i})) {
194 $inNumber = true;
195 $theNumber .= $xIn{$i};
196 } else {
197 if ($inNumber) {
198 $inNumber = false;
199 $allowedToAdd = false;
200 }
201 }
202 }
203 $i++;
204 }
205 return intval($theNumber);
206}
207
208//
209// This function returns an array of all available ports of a type that is listed in the array $portLinkerPortTypes
210// of an object where the port number (as defined by parsing the name through str2int) is between $portStart and ($portStart+$portCount)
211//
212function getPorts ($object_id, $portCount, $portStart) {
213 global $portLinkerPortTypes;
214 $record = getObjectPortsAndLinks($object_id);
215 $foundPorts = array();
216 foreach ($record as $aPort) {
217 if (in_array($aPort['type_id'],$portLinkerPortTypes) && strlen($aPort['reservation_comment'])==0 && $aPort['remote_id']==0) {
218 $num = str2int($aPort['name']);
219 if ($num>=$portStart && $num<$portStart+$portCount) {
220 $foundPorts[$num] = array();
221 $foundPorts[$num]['id'] = $aPort['id'];
222 $foundPorts[$num]['name'] = $aPort['name'];
223 }
224 }
225 }
226 ksort($foundPorts);
227 return $foundPorts;
228}
229
230//
231// This is the function that actually executes the linking of the ports
232// It uses determine_PortLinker function to see if it is allowed to link
233//
234$msgcode['execute_PortLinker']['OK'] = 0;
235$msgcode['execute_PortLinker']['ERR'] = 100;
236function execute_PortLinker () {
237 global $localSplit, $remoteSplit;
238 $errorText = determine_PortLinker();
239 $object = spotEntity ('object', $_REQUEST['object_id']);
240 if (strlen($errorText)==0) {
241 $count = 0;
242 // loop through all remote objects as defined
243 foreach ($localSplit as $aKey=>$aValue) {
244 $numAdd = $remoteSplit[$aKey][$object['name']]['start']-$aValue['start'];
245 foreach ($aValue['ports'] as $aPortNum=>$aPort) {
246 linkPorts($aPort['id'],$aValue['remote_ports'][$aPortNum+$numAdd]['id']);
247 $count++;
248 }
249 }
250 }
251 if (strlen($errorText)==0) {
252 return buildRedirectURL (__FUNCTION__, 'OK', array("{$count} ports successfully linked"));
253 } else {
254 return buildRedirectURL (__FUNCTION__, 'ERR', array($errorText));
255 }
256}
257
258function determine_PortLinker() {
259 global $localSplit, $remoteSplit, $portLinkerObjectTypes;
260 $errorText = "";
261 assertUIntArg ('object_id', __FUNCTION__);
262 $object = spotEntity ('object', $_REQUEST['object_id']);
263 $linkok = localpretrigger_PortLinker();
264 if ($linkok==2) {
265 if (in_array($object['objtype_id'],$portLinkerObjectTypes)) {
266 $localPortCount = countPorts($object['id']);
267 $remotePortCount = array();
268 $remoteObject = array();
269 $remoteSplit = array();
270 $localSplit = determinePortSplit($object['label'],$localPortCount);
271 $current = 1;
272 foreach ($localSplit as $aKey=>$aValue) {
273 if (strlen($errorText)==0) {
274 $q = "SELECT id FROM RackObject WHERE name='{$aKey}' ";
275 $result = useSelectBlade ($q, __FUNCTION__);
276 if ($result==NULL) { print_r($dbxlink->errorInfo()); die(); }
277 if ($row = $result->fetch (PDO::FETCH_NUM)) {
278 $remotePortCount[$aKey] = countPorts($row[0]);
279 $remoteObject[$aKey] = spotEntity('object',$row[0]);
280 $remoteSplit[$aKey] = determinePortSplit($remoteObject[$aKey]['label'],$remotePortCount[$aKey]);
281 } else {
282 $errorText = "Could not find object <b>{$aKey}</b>";
283 }
284 }
285 }
286 if (strlen($errorText)==0) {
287 foreach ($localSplit as $aKey=>$aValue) {
288 if (strlen($errorText)==0 && !isset($remoteSplit[$aKey][$object['name']]['count'])) {
289 $errorText = "Object <b>{$aKey}</b> does not list this object in the label field as a remote panel";
290 }
291 if (strlen($errorText)==0 && $remoteSplit[$aKey][$object['name']]['count']!=$aValue['count']) {
292 $errorText = "Port count does not match for object <b>{$aKey}</b>";
293 }
294 }
295 if (strlen($errorText)==0) {
296 foreach ($localSplit as $aKey=>$aValue) {
297 if (strlen($errorText)==0) {
298 $localSplit[$aKey]['ports'] = getPorts($object['id'],$aValue['count'],$aValue['start']);
299 if (count($localSplit[$aKey]['ports'])!=$aValue['count']) {
300 $errorText = "Not all ports available on this object";
301 }
302 }
303 if (strlen($errorText)==0) {
304 $localSplit[$aKey]['remote_ports'] = getPorts($remoteObject[$aKey]['id'],$remoteSplit[$aKey][$object['name']]['count'],
305 $remoteSplit[$aKey][$object['name']]['start']);
306 if (count($localSplit[$aKey]['ports'])!=$remoteSplit[$aKey][$object['name']]['count']) {
307 $errorText = "Not all ports available on this object";
308 }
309 }
310 }
311 }
312 }
313 } else {
314 $errorText = "Object type should be PatchPanel or ODFPanel";
315 }
316 } else {
317 switch ($linkok) {
318 case "-1" : $errorText = "There are no ports configured yet, so nothing to link to."; break;
319 case "0" : $errorText = "Some link ports are already linked to another port."; break;
320 case "1" : $errorText = "No ports found that end in link."; break;
321 default : $errorText = "Unknown error.";
322 }
323 }
324 return $errorText;
325}
326
327function localfunc_PortLinker()
328{
329 global $localSplit, $remoteSplit;
330 startPortlet("Port linker");
331 print "<center>";
332 $errorText = determine_PortLinker();
333 $object = spotEntity ('object', $_REQUEST['object_id']);
334 if (strlen($errorText)>0) {
335 print "Trying to link this object to : {$object['label']}<br><br>\n";
336 print $errorText;
337 } else {
338 echo "<a href='".
339 makeHrefProcess(array('op'=>'linknow','page'=>'object','tab'=>'default','object_id'=>$_REQUEST['object_id'])).
340 "'>Connect the following ports now:</a><p>\n";
341
342 print "<table cellspacing=0 cellpadding='5' align='center' class='widetable'>";
343 print "<tr><th>Object name</th><th>Port name</th><th>&nbsp;&nbsp;&nbsp;</th><th>Remote object name</th><th>Remote port name</th></tr>\n";
344 foreach ($localSplit as $aKey=>$aValue) {
345 $numAdd = $remoteSplit[$aKey][$object['name']]['start']-$aValue['start'];
346 foreach ($aValue['ports'] as $aPortNum=>$aPort) {
347 print "<tr><td>{$object['name']}</td><td>{$aPort['name']}</td><td>&nbsp;</td><td>{$aKey}</td><td>";
348 print $aValue['remote_ports'][$aPortNum+$numAdd]['name']."</td></tr>\n";
349 }
350 }
351 print "</table>";
352 }
353 print "</center>";
354 finishPortlet();
355}
356
357function localpretrigger_PortLinker() {
358 global $portLinkerPortTypes;
359 $record = getObjectPortsAndLinks ($_REQUEST['object_id']);
360 if (count($record)>0) {
361 $linkok = 1;
362 foreach ($record as $aPort) {
363 if (in_array($aPort['type_id'],$portLinkerPortTypes)) {
364 if ($linkok==1) {
365 $linkok = 2;
366 }
367 if (strlen($aPort['remote_id'])>0) {
368 $linkok = 0;
369 }
370 }
371 }
372 } else {
373 $linkok = -1;
374 }
375 return $linkok;
376}
377
378function localtrigger_PortLinker()
379{
380 global $portLinkerObjectTypes;
381 assertUIntArg ('object_id', __FUNCTION__);
382 $object = spotEntity ('object', $_REQUEST['object_id']);
383 if (in_array($object['objtype_id'],$portLinkerObjectTypes)) {
384 $linkok = localpretrigger_PortLinker();
385 } else {
386 $linkok = 0;
387 }
388 if ($linkok==2)
389 return 1;
390 else
391 {
392 return '';
393 }
394}
395?>