Release of grains plugin to racktables-contribs
[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
23217dfb
DO
126//------------------------
127//Version 1.2
128//Revised by Jorge Sanchez
129//04-2011
130//------------------------
131//Changes
132//------------------------
133//Only one change has been added to this file. The $result variable is no longer equal to useSelectBlade($q,__FUNCTION__); but to usePreparedSelectBlade($q);
134//This is due to the changes since 0.17.x to 0.18.7 to the rendering of objects
91b12714 135//
23217dfb 136
91b12714
DO
137global $portLinkerPortTypes;
138if (!isset($portLinkerPortTypes)) {
23217dfb 139 $portLinkerPortTypes = array(50015);
91b12714 140}
23217dfb
DO
141
142
91b12714
DO
143global $portLinkerObjectTypes;
144if (!isset($portLinkerObjectTypes)) {
145 $portLinkerObjectTypes = array(9);
146}
147
23217dfb 148
91b12714
DO
149function determinePortSplit ($object_label,$portCount) {
150 $labels = explode(" ",$object_label);
151 $splits = array();
152 if (count($labels)==0) {
153 } else if (count($labels)==1) {
154 $splits[$labels[0]] = array();
155 $splits[$labels[0]]['start'] = 1;
156 $splits[$labels[0]]['count'] = $portCount;
157 $splits[$labels[0]]['end'] = $portCount;
158 } else {
159 $sum = 0;
160 foreach($labels as $aLabel) {
161 $splits[$aLabel] = array();
162 $splits[$aLabel]['start'] = $sum+1;
163 $splits[$aLabel]['count'] = floor($portCount/count($labels));
164 $splits[$aLabel]['end'] = $sum+floor($portCount/count($labels));
165 $sum += $splits[$aLabel]['count'];
166 }
167 if ($sum<$portCount) {
168 $temp = array_keys($splits);
169 end($temp);
170 $splits[current($temp)]['count'] += $portCount-$sum;
171 $splits[current($temp)]['end'] = $portCount;
172 }
173 }
174 return $splits;
175}
176
177//
178// This function returns the number of ports that have a type in the array $portLinkerPortTypes
179//
180function countPorts ($object_id) {
181 global $portLinkerPortTypes;
182 $record = getObjectPortsAndLinks($object_id);
183 $count = 0;
184 foreach ($record as $aPort) {
23217dfb 185 if (in_array($aPort['oif_id'],$portLinkerPortTypes)) {
91b12714
DO
186 $count++;
187 }
188 }
189 return $count;
190}
191
192//
193// This function returns the integer found in a string. It keeps the first digits found.
194// Example: eth3link => 3
195// fbr15link => 15
196// fbr1link2 => 1
197// 12a => 12
198//
199function str2int ($xIn) {
200 $theNumber = "";
201 $allowedToAdd = true;
202 $inNumber = false;
203 $i = 0;
204 while ($i<strlen($xIn)) {
205 if ($allowedToAdd) {
206 if (is_numeric($xIn{$i})) {
207 $inNumber = true;
208 $theNumber .= $xIn{$i};
209 } else {
210 if ($inNumber) {
211 $inNumber = false;
212 $allowedToAdd = false;
213 }
214 }
215 }
216 $i++;
217 }
218 return intval($theNumber);
219}
220
221//
222// This function returns an array of all available ports of a type that is listed in the array $portLinkerPortTypes
223// of an object where the port number (as defined by parsing the name through str2int) is between $portStart and ($portStart+$portCount)
224//
225function getPorts ($object_id, $portCount, $portStart) {
226 global $portLinkerPortTypes;
227 $record = getObjectPortsAndLinks($object_id);
228 $foundPorts = array();
229 foreach ($record as $aPort) {
23217dfb 230 if (in_array($aPort['oif_id'],$portLinkerPortTypes) && strlen($aPort['reservation_comment'])==0 && $aPort['remote_id']==0) {
91b12714
DO
231 $num = str2int($aPort['name']);
232 if ($num>=$portStart && $num<$portStart+$portCount) {
233 $foundPorts[$num] = array();
234 $foundPorts[$num]['id'] = $aPort['id'];
235 $foundPorts[$num]['name'] = $aPort['name'];
236 }
237 }
238 }
239 ksort($foundPorts);
240 return $foundPorts;
241}
242
243//
244// This is the function that actually executes the linking of the ports
245// It uses determine_PortLinker function to see if it is allowed to link
246//
247$msgcode['execute_PortLinker']['OK'] = 0;
248$msgcode['execute_PortLinker']['ERR'] = 100;
249function execute_PortLinker () {
250 global $localSplit, $remoteSplit;
251 $errorText = determine_PortLinker();
252 $object = spotEntity ('object', $_REQUEST['object_id']);
253 if (strlen($errorText)==0) {
254 $count = 0;
255 // loop through all remote objects as defined
256 foreach ($localSplit as $aKey=>$aValue) {
257 $numAdd = $remoteSplit[$aKey][$object['name']]['start']-$aValue['start'];
258 foreach ($aValue['ports'] as $aPortNum=>$aPort) {
259 linkPorts($aPort['id'],$aValue['remote_ports'][$aPortNum+$numAdd]['id']);
260 $count++;
261 }
262 }
263 }
264 if (strlen($errorText)==0) {
265 return buildRedirectURL (__FUNCTION__, 'OK', array("{$count} ports successfully linked"));
266 } else {
267 return buildRedirectURL (__FUNCTION__, 'ERR', array($errorText));
268 }
269}
270
271function determine_PortLinker() {
272 global $localSplit, $remoteSplit, $portLinkerObjectTypes;
273 $errorText = "";
274 assertUIntArg ('object_id', __FUNCTION__);
275 $object = spotEntity ('object', $_REQUEST['object_id']);
276 $linkok = localpretrigger_PortLinker();
277 if ($linkok==2) {
278 if (in_array($object['objtype_id'],$portLinkerObjectTypes)) {
279 $localPortCount = countPorts($object['id']);
280 $remotePortCount = array();
281 $remoteObject = array();
282 $remoteSplit = array();
283 $localSplit = determinePortSplit($object['label'],$localPortCount);
284 $current = 1;
285 foreach ($localSplit as $aKey=>$aValue) {
286 if (strlen($errorText)==0) {
287 $q = "SELECT id FROM RackObject WHERE name='{$aKey}' ";
23217dfb 288 $result = usePreparedSelectBlade ($q);
91b12714
DO
289 if ($result==NULL) { print_r($dbxlink->errorInfo()); die(); }
290 if ($row = $result->fetch (PDO::FETCH_NUM)) {
291 $remotePortCount[$aKey] = countPorts($row[0]);
292 $remoteObject[$aKey] = spotEntity('object',$row[0]);
293 $remoteSplit[$aKey] = determinePortSplit($remoteObject[$aKey]['label'],$remotePortCount[$aKey]);
294 } else {
295 $errorText = "Could not find object <b>{$aKey}</b>";
296 }
297 }
298 }
299 if (strlen($errorText)==0) {
300 foreach ($localSplit as $aKey=>$aValue) {
301 if (strlen($errorText)==0 && !isset($remoteSplit[$aKey][$object['name']]['count'])) {
302 $errorText = "Object <b>{$aKey}</b> does not list this object in the label field as a remote panel";
303 }
304 if (strlen($errorText)==0 && $remoteSplit[$aKey][$object['name']]['count']!=$aValue['count']) {
305 $errorText = "Port count does not match for object <b>{$aKey}</b>";
306 }
307 }
308 if (strlen($errorText)==0) {
309 foreach ($localSplit as $aKey=>$aValue) {
310 if (strlen($errorText)==0) {
311 $localSplit[$aKey]['ports'] = getPorts($object['id'],$aValue['count'],$aValue['start']);
312 if (count($localSplit[$aKey]['ports'])!=$aValue['count']) {
313 $errorText = "Not all ports available on this object";
314 }
315 }
316 if (strlen($errorText)==0) {
317 $localSplit[$aKey]['remote_ports'] = getPorts($remoteObject[$aKey]['id'],$remoteSplit[$aKey][$object['name']]['count'],
318 $remoteSplit[$aKey][$object['name']]['start']);
319 if (count($localSplit[$aKey]['ports'])!=$remoteSplit[$aKey][$object['name']]['count']) {
320 $errorText = "Not all ports available on this object";
321 }
322 }
323 }
324 }
325 }
326 } else {
327 $errorText = "Object type should be PatchPanel or ODFPanel";
328 }
329 } else {
330 switch ($linkok) {
331 case "-1" : $errorText = "There are no ports configured yet, so nothing to link to."; break;
332 case "0" : $errorText = "Some link ports are already linked to another port."; break;
333 case "1" : $errorText = "No ports found that end in link."; break;
334 default : $errorText = "Unknown error.";
335 }
336 }
337 return $errorText;
338}
339
340function localfunc_PortLinker()
341{
342 global $localSplit, $remoteSplit;
343 startPortlet("Port linker");
344 print "<center>";
345 $errorText = determine_PortLinker();
346 $object = spotEntity ('object', $_REQUEST['object_id']);
347 if (strlen($errorText)>0) {
348 print "Trying to link this object to : {$object['label']}<br><br>\n";
349 print $errorText;
350 } else {
351 echo "<a href='".
352 makeHrefProcess(array('op'=>'linknow','page'=>'object','tab'=>'default','object_id'=>$_REQUEST['object_id'])).
353 "'>Connect the following ports now:</a><p>\n";
354
355 print "<table cellspacing=0 cellpadding='5' align='center' class='widetable'>";
356 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";
357 foreach ($localSplit as $aKey=>$aValue) {
358 $numAdd = $remoteSplit[$aKey][$object['name']]['start']-$aValue['start'];
359 foreach ($aValue['ports'] as $aPortNum=>$aPort) {
360 print "<tr><td>{$object['name']}</td><td>{$aPort['name']}</td><td>&nbsp;</td><td>{$aKey}</td><td>";
361 print $aValue['remote_ports'][$aPortNum+$numAdd]['name']."</td></tr>\n";
362 }
363 }
364 print "</table>";
365 }
366 print "</center>";
367 finishPortlet();
368}
369
370function localpretrigger_PortLinker() {
371 global $portLinkerPortTypes;
372 $record = getObjectPortsAndLinks ($_REQUEST['object_id']);
373 if (count($record)>0) {
374 $linkok = 1;
375 foreach ($record as $aPort) {
23217dfb 376 if (in_array($aPort['oif_id'],$portLinkerPortTypes)) {
91b12714
DO
377 if ($linkok==1) {
378 $linkok = 2;
379 }
380 if (strlen($aPort['remote_id'])>0) {
381 $linkok = 0;
382 }
383 }
384 }
385 } else {
386 $linkok = -1;
387 }
388 return $linkok;
389}
390
23217dfb 391
91b12714
DO
392function localtrigger_PortLinker()
393{
394 global $portLinkerObjectTypes;
395 assertUIntArg ('object_id', __FUNCTION__);
396 $object = spotEntity ('object', $_REQUEST['object_id']);
397 if (in_array($object['objtype_id'],$portLinkerObjectTypes)) {
398 $linkok = localpretrigger_PortLinker();
399 } else {
400 $linkok = 0;
401 }
402 if ($linkok==2)
403 return 1;
404 else
405 {
406 return '';
407 }
408}
23217dfb
DO
409
410function test_debug()
411{
412global $portLinkerObjectTypes;
413print $portLinkerObjectTypes;
414}
91b12714 415?>