794705b5dd3676e86d971ed6654ee344667d2fd9
[racktables-incomplete-works] / wwwroot / inc / database.php
1 <?php
2
3 # This file is a part of RackTables, a datacenter and server room management
4 # framework. See accompanying file "COPYING" for the full copyright and
5 # licensing information.
6
7 /*
8 *
9 * This file is a library of database access functions for RackTables.
10 *
11 */
12
13 $SQLSchema = array
14 (
15 'object' => array
16 (
17 'table' => 'RackObject',
18 'columns' => array
19 (
20 'id' => 'id',
21 'name' => 'name',
22 'label' => 'label',
23 'asset_no' => 'asset_no',
24 'objtype_id' => 'objtype_id',
25 'rack_id' => '(SELECT rack_id FROM RackSpace WHERE object_id = RackObject.id ORDER BY rack_id ASC LIMIT 1)',
26 'rack_id_2' => "(SELECT parent_entity_id AS rack_id FROM EntityLink WHERE child_entity_type='object' AND child_entity_id = RackObject.id AND parent_entity_type = 'rack' ORDER BY rack_id ASC LIMIT 1)",
27 'container_id' => "(SELECT parent_entity_id FROM EntityLink WHERE child_entity_type='object' AND child_entity_id = RackObject.id AND parent_entity_type = 'object' ORDER BY parent_entity_id ASC LIMIT 1)",
28 'container_name' => '(SELECT name FROM RackObject WHERE id = container_id)',
29 'container_objtype_id' => '(SELECT objtype_id FROM RackObject WHERE id = container_id)',
30 'has_problems' => 'has_problems',
31 'comment' => 'comment',
32 'nports' => '(SELECT COUNT(*) FROM Port WHERE object_id = RackObject.id)',
33 '8021q_domain_id' => '(SELECT domain_id FROM VLANSwitch WHERE object_id = id LIMIT 1)',
34 '8021q_template_id' => '(SELECT template_id FROM VLANSwitch WHERE object_id = id LIMIT 1)',
35 ),
36 'keycolumn' => 'id',
37 'ordcolumns' => array ('RackObject.name'),
38 ),
39 'user' => array
40 (
41 'table' => 'UserAccount',
42 'columns' => array
43 (
44 'user_id' => 'user_id',
45 'user_name' => 'user_name',
46 'user_password_hash' => 'user_password_hash',
47 'user_realname' => 'user_realname',
48 ),
49 'keycolumn' => 'user_id',
50 'ordcolumns' => array ('UserAccount.user_name'),
51 ),
52 'ipv4net' => array
53 (
54 'table' => 'IPv4Network',
55 'columns' => array
56 (
57 'id' => 'id',
58 'ip_bin' => 'ip',
59 'mask' => 'mask',
60 'name' => 'name',
61 'comment' => 'comment',
62 '8021q' => '(SELECT GROUP_CONCAT(CONCAT(domain_id, "-", vlan_id) ORDER BY domain_id, vlan_id) FROM VLANIPv4 WHERE ipv4net_id = id)',
63 ),
64 'keycolumn' => 'id',
65 'ordcolumns' => array ('IPv4Network.ip', 'IPv4Network.mask'),
66 ),
67 'ipv6net' => array
68 (
69 'table' => 'IPv6Network',
70 'columns' => array
71 (
72 'id' => 'id',
73 'ip_bin' => 'ip',
74 'mask' => 'mask',
75 'name' => 'name',
76 'comment' => 'comment',
77 '8021q' => '(SELECT GROUP_CONCAT(CONCAT(domain_id, "-", vlan_id) ORDER BY domain_id, vlan_id) FROM VLANIPv6 WHERE ipv6net_id = id)',
78 ),
79 'keycolumn' => 'id',
80 'ordcolumns' => array ('IPv6Network.ip', 'IPv6Network.mask'),
81 ),
82 'file' => array
83 (
84 'table' => 'File',
85 'columns' => array
86 (
87 'id' => 'id',
88 'name' => 'name',
89 'type' => 'type',
90 'size' => 'size',
91 'ctime' => 'ctime',
92 'mtime' => 'mtime',
93 'atime' => 'atime',
94 'comment' => 'comment',
95 ),
96 'keycolumn' => 'id',
97 'ordcolumns' => array ('File.name'),
98 ),
99 'ipv4vs' => array
100 (
101 'table' => 'IPv4VS',
102 'columns' => array
103 (
104 'id' => 'id',
105 'vip_bin' => 'vip',
106 'vport' => 'vport',
107 'proto' => 'proto',
108 'name' => 'name',
109 'vsconfig' => 'vsconfig',
110 'rsconfig' => 'rsconfig',
111 'refcnt' => '(select count(vs_id) from IPv4LB where vs_id = id)',
112 //'vip' =>
113 //'dname' =>
114 ),
115 'keycolumn' => 'id',
116 'ordcolumns' => array ('IPv4VS.vip', 'IPv4VS.proto', 'IPv4VS.vport'),
117 ),
118 'ipv4rspool' => array
119 (
120 'table' => 'IPv4RSPool',
121 'columns' => array
122 (
123 'id' => 'id',
124 'name' => 'name',
125 'refcnt' => '(select count(rspool_id) from IPv4LB where rspool_id = id)',
126 'rscount' => '(select count(rspool_id) from IPv4RS where rspool_id = IPv4RSPool.id)',
127 'vsconfig' => 'vsconfig',
128 'rsconfig' => 'rsconfig',
129 ),
130 'keycolumn' => 'id',
131 'ordcolumns' => array ('IPv4RSPool.name', 'IPv4RSPool.id'),
132 ),
133 'rack' => array
134 (
135 'table' => 'Rack',
136 'columns' => array
137 (
138 'id' => 'id',
139 'name' => 'name',
140 'height' => 'height',
141 'asset_no' => 'asset_no',
142 'has_problems' => 'has_problems',
143 'comment' => 'comment',
144 'row_id' => 'row_id',
145 'row_name' => 'row_name',
146 'location_id' => 'location_id',
147 'location_name' => 'location_name',
148 ),
149 'keycolumn' => 'id',
150 'ordcolumns' => array ('location_name', 'row_name', 'sort_order', 'Rack.name'),
151 'pidcolumn' => 'row_id',
152 ),
153 'row' => array
154 (
155 'table' => 'Row',
156 'columns' => array
157 (
158 'id' => 'id',
159 'name' => 'name',
160 'location_id' => 'location_id',
161 'location_name' => 'location_name',
162 ),
163 'keycolumn' => 'id',
164 'ordcolumns' => array ('location_name', 'name'),
165 ),
166 'location' => array
167 (
168 'table' => 'Location',
169 'columns' => array
170 (
171 'id' => 'id',
172 'name' => 'name',
173 'has_problems' => 'has_problems',
174 'comment' => 'comment',
175 'parent_id' => 'parent_id',
176 'parent_name' => 'parent_name',
177 'refcnt' => "(SELECT COUNT(child_entity_id) FROM EntityLink EL WHERE EL.parent_entity_type = 'location' AND EL.parent_entity_id = Location.id)",
178 ),
179 'keycolumn' => 'id',
180 'ordcolumns' => array ('name'),
181 ),
182 'vst' => array
183 (
184 'table' => 'VLANSwitchTemplate',
185 'columns' => array
186 (
187 'id' => 'id',
188 'description' => 'description',
189 'mutex_rev' => 'mutex_rev',
190 'saved_by' => 'saved_by',
191 'switchc' => '(SELECT COUNT(object_id) FROM VLANSwitch WHERE template_id = id)',
192 'rulec' => '(SELECT COUNT(rule_no) FROM VLANSTRule WHERE vst_id = id)',
193 ),
194 'keycolumn' => 'id',
195 'ordcolumns' => array ('description'),
196 ),
197 );
198
199 $searchfunc = array
200 (
201 'object' => array
202 (
203 'by_port' => 'getPortSearchResults',
204 'by_attr' => 'getObjectAttrsSearchResults',
205 'by_iface' => 'getObjectIfacesSearchResults',
206 'by_nat' => 'getObjectNATSearchResults',
207 'by_cableid' => 'searchCableIDs',
208 ),
209 );
210
211 $tablemap_8021q = array
212 (
213 'desired' => array
214 (
215 'pvm' => 'PortVLANMode',
216 'pav' => 'PortAllowedVLAN',
217 'pnv' => 'PortNativeVLAN',
218 ),
219 'cached' => array
220 (
221 'pvm' => 'CachedPVM',
222 'pav' => 'CachedPAV',
223 'pnv' => 'CachedPNV',
224 ),
225 );
226
227 // VST roles
228 $port_role_options = array
229 (
230 'none' => 'none',
231 'access' => 'user: access only',
232 'trunk' => 'user: trunk only',
233 'anymode' => 'user: any mode',
234 'uplink' => 'system: uplink trunk',
235 'downlink' => 'system: downlink trunk',
236 );
237
238 $object_attribute_cache = array();
239
240 // Return list of locations directly under a specified location
241 function getLocations ($location_id)
242 {
243 $result = usePreparedSelectBlade
244 (
245 'SELECT id, name FROM Location WHERE parent_id = ? ORDER BY name',
246 array ($location_id)
247 );
248 return reduceSubarraysToColumn (reindexByID ($result->fetchAll (PDO::FETCH_ASSOC)), 'name');
249 }
250
251 // Return detailed information about one rack row.
252 function getRowInfo ($row_id)
253 {
254 $query =
255 "SELECT Row.id AS id, Row.name AS name, COUNT(Rack.id) AS count, " .
256 "IF(ISNULL(SUM(Rack.height)),0,SUM(Rack.height)) AS sum, " .
257 "Location.id AS location_id, Location.name AS location " .
258 "FROM Row LEFT JOIN Rack ON Rack.row_id = Row.id " .
259 "LEFT OUTER JOIN Location ON Row.location_id = Location.id " .
260 "WHERE Row.id = ? " .
261 "GROUP BY Row.id";
262 $result = usePreparedSelectBlade ($query, array ($row_id));
263 if ($row = $result->fetch (PDO::FETCH_ASSOC))
264 return $row;
265 else
266 throw new EntityNotFoundException ('rackrow', $row_id);
267 }
268
269 function getAllRows ()
270 {
271 $result = usePreparedSelectBlade ('SELECT id, name, location_id, location_name FROM Row ORDER BY location_name, name');
272 return reindexById ($result->fetchAll (PDO::FETCH_ASSOC));
273 }
274
275 // Return list of rows directly under a specified location
276 function getRows ($location_id)
277 {
278 $result = usePreparedSelectBlade
279 (
280 'SELECT R.id, R.name FROM Row R ' .
281 'INNER JOIN EntityLink EL ON ' .
282 "EL.parent_entity_type = 'location' " .
283 "AND EL.child_entity_type = 'row' " .
284 "AND EL.child_entity_id = R.id " .
285 'WHERE EL.parent_entity_id = ? ' .
286 'ORDER BY R.name',
287 array ($location_id)
288 );
289 return reduceSubarraysToColumn (reindexByID ($result->fetchAll (PDO::FETCH_ASSOC)), 'name');
290 }
291
292 function getRacks ($row_id)
293 {
294 $result = usePreparedSelectBlade
295 (
296 'SELECT id, name, asset_no, height, sort_order, comment, row_name FROM Rack WHERE row_id = ? ORDER BY sort_order',
297 array ($row_id)
298 );
299 return reindexById ($result->fetchAll (PDO::FETCH_ASSOC));
300 }
301
302 # Return rack and row details for those objects on the list, which have
303 # at least one rackspace atom allocated to them.
304 function getMountInfo ($object_ids)
305 {
306 if (! count ($object_ids))
307 return array();
308 # In theory number of involved racks can be equal or even greater, than the
309 # number of objects, but in practice it will often be tens times less. Hence
310 # the scope of the 1st pass is to tell IDs of all involved racks without
311 # fetching lots of duplicate data.
312 $result = usePreparedSelectBlade
313 (
314 'SELECT object_id, rack_id ' .
315 'FROM RackSpace ' .
316 'WHERE object_id IN(' . questionMarks (count ($object_ids)) . ') ' .
317 'GROUP BY object_id, rack_id ' .
318 'UNION ' .
319 'SELECT child_entity_id AS object_id, parent_entity_id AS rack_id ' .
320 'FROM EntityLink ' .
321 'WHERE child_entity_id IN(' . questionMarks (count ($object_ids)) . ') ' .
322 "AND parent_entity_type = 'rack' AND child_entity_type = 'object' " .
323 'ORDER BY rack_id ASC',
324 array_merge($object_ids, $object_ids)
325 );
326 $rackidlist = $objectlist = array();
327 foreach ($result as $row)
328 {
329 $objectlist[$row['object_id']][] = $row['rack_id'];
330 $rackidlist[] = $row['rack_id'];
331 }
332 unset ($result);
333 # short-cut to exit in case no object had rackspace allocated
334 if (! count ($rackidlist))
335 {
336 $ret = array();
337 foreach ($object_ids as $object_id)
338 $ret[$object_id] = array();
339 return $ret;
340 }
341 # Pass 2. Fetch shorter, but better extra data about the rows and racks,
342 # set displayed names for both.
343 $result = usePreparedSelectBlade
344 (
345 'SELECT Rack_.id as rack_id, Rack_.name AS rack_name, Rack_.label as rack_label, ' .
346 'parent_entity_id AS row_id, Row_.name AS row_name ' .
347 'FROM Object Rack_ ' .
348 "LEFT JOIN EntityLink ON (Rack_.id = child_entity_id AND parent_entity_type = 'row' AND child_entity_type = 'rack') " .
349 'LEFT JOIN Object Row_ ON parent_entity_id = Row_.id ' .
350 'WHERE Rack_.id IN(' . questionMarks (count ($rackidlist)) . ') ',
351 $rackidlist
352 );
353 $rackinfo = array();
354 foreach ($result as $row)
355 {
356 $rackinfo[$row['rack_id']] = array
357 (
358 'rack_id' => $row['rack_id'],
359 'row_id' => $row['row_id'],
360 );
361 if ('' != $row['rack_name'])
362 $rackinfo[$row['rack_id']]['rack_name'] = $row['rack_name'];
363 elseif ('' != $row['rack_label'])
364 $rackinfo[$row['rack_id']]['rack_name'] = $row['rack_label'];
365 else
366 $rackinfo[$row['rack_id']]['rack_name'] = 'rack#' . $row['rack_id'];
367 if ('' != $row['row_name'])
368 $rackinfo[$row['rack_id']]['row_name'] = $row['row_name'];
369 else
370 $rackinfo[$row['rack_id']]['row_name'] = 'row#' . $row['row_id'];
371 }
372 unset ($result);
373 # Pass 3. Combine retrieved data into returned array.
374 $ret = array();
375 foreach ($objectlist as $object_id => $racklist)
376 foreach ($racklist as $rack_id)
377 $ret[$object_id][] = $rackinfo[$rack_id];
378 return $ret;
379 }
380
381 // Return a simple object list w/o related information, so that the returned value
382 // can be directly used by printSelect(). An optional argument is the name of config
383 // option with constraint in RackCode.
384 function getNarrowObjectList ($varname = '')
385 {
386 $wideList = listCells ('object');
387 if (strlen ($varname) and strlen (getConfigVar ($varname)))
388 {
389 global $parseCache;
390 if (!isset ($parseCache[$varname]))
391 $parseCache[$varname] = spotPayload (getConfigVar ($varname), 'SYNT_EXPR');
392 if ($parseCache[$varname]['result'] != 'ACK')
393 return array();
394 $wideList = filterCellList ($wideList, $parseCache[$varname]['load']);
395 }
396 return formatEntityList ($wideList);
397 }
398
399 // For a given realm return a list of entity records, each with
400 // enough information for judgeCell() to execute.
401 function listCells ($realm, $parent_id = 0)
402 {
403 if (!$parent_id)
404 {
405 global $entityCache;
406 if (isset ($entityCache['complete'][$realm]))
407 return $entityCache['complete'][$realm];
408 }
409 global $SQLSchema;
410 if (!isset ($SQLSchema[$realm]))
411 throw new InvalidArgException ('realm', $realm);
412 $SQLinfo = $SQLSchema[$realm];
413 $qparams = array ();
414 $query = 'SELECT ';
415 foreach ($SQLinfo['columns'] as $alias => $expression)
416 // Automatically prepend table name to each single column, but leave all others intact.
417 $query .= ($alias == $expression ? "${SQLinfo['table']}.${alias}" : "${expression} as ${alias}") . ', ';
418 $query = trim($query, ', ');
419 $query .= " FROM ${SQLinfo['table']}";
420 if (isset ($SQLinfo['pidcolumn']) and $parent_id)
421 {
422 $query .= " WHERE ${SQLinfo['table']}.${SQLinfo['pidcolumn']} = ?";
423 $qparams[] = $parent_id;
424 }
425 $query .= " ORDER BY ";
426 foreach ($SQLinfo['ordcolumns'] as $oc)
427 $query .= "${oc}, ";
428 $query = trim($query, ', ');
429 $result = usePreparedSelectBlade ($query, $qparams);
430 $ret = array();
431 // Index returned result by the value of key column.
432 while ($row = $result->fetch (PDO::FETCH_ASSOC))
433 {
434 $entity_id = $row[$SQLinfo['keycolumn']];
435 $ret[$entity_id] = array ('realm' => $realm);
436 $ret[$entity_id]['etags'] = array();
437 foreach (array_keys ($SQLinfo['columns']) as $alias)
438 $ret[$entity_id][$alias] = $row[$alias];
439 // use the temporary rack_id_2 key and remove this key from the result array
440 if ($realm == 'object')
441 {
442 if (! isset ($ret[$entity_id]['rack_id']))
443 $ret[$entity_id]['rack_id'] = $ret[$entity_id]['rack_id_2'];
444 unset ($ret[$entity_id]['rack_id_2']);
445 }
446 }
447 unset($result);
448
449 // select tags and link them to previosly fetched entities
450 $query = 'SELECT entity_id, tag_id, user AS tag_user, UNIX_TIMESTAMP(date) AS tag_time FROM TagStorage WHERE entity_realm = ?';
451 $result = usePreparedSelectBlade ($query, array($realm));
452 global $taglist;
453 while ($row = $result->fetch (PDO::FETCH_ASSOC))
454 {
455 $tag_id = $row['tag_id'];
456 if (array_key_exists($row['entity_id'], $ret))
457 $ret[$row['entity_id']]['etags'][$tag_id] = array
458 (
459 'id' => $tag_id,
460 'tag' => $taglist[$tag_id]['tag'],
461 'parent_id' => $taglist[$tag_id]['parent_id'],
462 'user' => $row['tag_user'],
463 'time' => $row['tag_time'],
464 );
465 }
466 unset($result);
467 // Add necessary finish to the list before returning it. Maintain caches.
468 if (!$parent_id)
469 unset ($entityCache['partial'][$realm]);
470 if ($realm == 'object') // cache all attributes of all objects to speed up autotags calculation
471 cacheAllObjectsAttributes();
472 foreach ($ret as $entity_id => &$entity)
473 {
474 sortEntityTags ($entity); // changes ['etags'] and ['itags']
475 switch ($realm)
476 {
477 case 'object':
478 setDisplayedName ($entity); // set $entity['dname']
479 break;
480 case 'ipv4net':
481 $entity = array_merge ($entity, constructIPRange (ip4_int2bin ($entity['ip_bin']), $entity['mask']));
482 processIPNetVlans ($entity);
483 $entity['spare_ranges'] = array();
484 $entity['kidc'] = 0;
485 break;
486 case 'ipv6net':
487 $entity = array_merge ($entity, constructIPRange ($entity['ip_bin'], $entity['mask']));
488 processIPNetVlans ($entity);
489 $entity['spare_ranges'] = array();
490 $entity['kidc'] = 0;
491 break;
492 case 'ipv4vs':
493 $entity['vip'] = ip_format ($entity['vip_bin']);
494 setDisplayedName ($entity); // set $entity['dname']
495 break;
496 default:
497 break;
498 }
499 }
500 if ($realm == 'ipv4net' or $realm == 'ipv6net')
501 fillIPNetsCorrelation ($ret);
502
503 foreach (array_keys ($ret) as $entity_id)
504 {
505 $entity = &$ret[$entity_id];
506 $entity['atags'] = generateEntityAutoTags ($entity);
507 if (!$parent_id)
508 $entityCache['complete'][$realm][$entity_id] = $entity;
509 else
510 $entityCache['partial'][$realm][$entity_id] = $entity;
511 }
512
513 return $ret;
514 }
515
516 // Very much like listCells(), but return only one record requested
517 // throws an exception if entity not exists
518 function spotEntity ($realm, $id, $ignore_cache = FALSE)
519 {
520 global $entityCache;
521 if (! $ignore_cache)
522 {
523 if (isset ($entityCache['complete'][$realm]))
524 // Emphasize the absence of record, if listCells() has already been called.
525 if (isset ($entityCache['complete'][$realm][$id]))
526 return $entityCache['complete'][$realm][$id];
527 else
528 throw new EntityNotFoundException ($realm, $id);
529 elseif (isset ($entityCache['partial'][$realm][$id]))
530 return $entityCache['partial'][$realm][$id];
531 }
532 global $SQLSchema;
533 if (!isset ($SQLSchema[$realm]))
534 throw new InvalidArgException ('realm', $realm);
535 $SQLinfo = $SQLSchema[$realm];
536 $query = 'SELECT tag_id, TagStorage.user as tag_user, UNIX_TIMESTAMP(TagStorage.date) AS tag_time';
537 foreach ($SQLinfo['columns'] as $alias => $expression)
538 // Automatically prepend table name to each single column, but leave all others intact.
539 $query .= ', ' . ($alias == $expression ? "${SQLinfo['table']}.${alias}" : "${expression} as ${alias}");
540 $query .= " FROM ${SQLinfo['table']} LEFT JOIN TagStorage on entity_realm = ? and entity_id = ${SQLinfo['table']}.${SQLinfo['keycolumn']}";
541 $query .= " WHERE ${SQLinfo['table']}.${SQLinfo['keycolumn']} = ?";
542 $query .= " ORDER BY tag_id";
543 $result = usePreparedSelectBlade ($query, array ($realm, $id));
544 $ret = array();
545 global $taglist;
546 while ($row = $result->fetch (PDO::FETCH_ASSOC))
547 if (!isset ($ret['realm']))
548 {
549 $ret = array ('realm' => $realm);
550 foreach (array_keys ($SQLinfo['columns']) as $alias)
551 $ret[$alias] = $row[$alias];
552 // use the temporary rack_id_2 key and remove this key from the result array
553 if ($realm == 'object')
554 {
555 if (! isset ($ret['rack_id']))
556 $ret['rack_id'] = $ret['rack_id_2'];
557 unset ($ret['rack_id_2']);
558 }
559 $ret['etags'] = array();
560 if ($row['tag_id'] != NULL && isset ($taglist[$row['tag_id']]))
561 $ret['etags'][$row['tag_id']] = array
562 (
563 'id' => $row['tag_id'],
564 'tag' => $taglist[$row['tag_id']]['tag'],
565 'parent_id' => $taglist[$row['tag_id']]['parent_id'],
566 'user' => $row['tag_user'],
567 'time' => $row['tag_time'],
568 );
569 }
570 elseif (isset ($taglist[$row['tag_id']]))
571 $ret['etags'][$row['tag_id']] = array
572 (
573 'id' => $row['tag_id'],
574 'tag' => $taglist[$row['tag_id']]['tag'],
575 'parent_id' => $taglist[$row['tag_id']]['parent_id'],
576 'user' => $row['tag_user'],
577 'time' => $row['tag_time'],
578 );
579 unset ($result);
580 if (!isset ($ret['realm'])) // no rows were returned
581 throw new EntityNotFoundException ($realm, $id);
582 sortEntityTags ($ret); // changes ['etags'] and ['itags']
583 switch ($realm)
584 {
585 case 'object':
586 setDisplayedName ($ret); // set $ret['dname']
587 break;
588 case 'ipv4net':
589 processIPNetVlans ($ret);
590 $ret = array_merge ($ret, constructIPRange (ip4_int2bin ($ret['ip_bin']), $ret['mask']));
591 $ret['spare_ranges'] = array();
592 $ret['kidc'] = 0;
593 break;
594 case 'ipv6net':
595 processIPNetVlans ($ret);
596 $ret = array_merge ($ret, constructIPRange ($ret['ip_bin'], $ret['mask']));
597 $ret['spare_ranges'] = array();
598 $ret['kidc'] = 0;
599 break;
600 case 'ipv4vs':
601 $ret['vip'] = ip_format ($ret['vip_bin']);
602 setDisplayedName ($ret); // set $ret['dname']
603 break;
604 default:
605 break;
606 }
607
608 if ($realm == 'ipv4net')
609 {
610 $result = usePreparedSelectBlade ("
611 SELECT n2.id, n2.ip as ip_bin, n2.mask FROM
612 IPv4Network n1,
613 IPv4Network n2
614 WHERE
615 n1.id = ?
616 AND n2.ip BETWEEN n1.ip AND (n1.ip + (1 << (32 - n1.mask)) - 1)
617 AND n2.mask >= n1.mask
618 ORDER BY n2.ip, n2.mask
619 ", array ($id));
620 $nets = $result->fetchAll (PDO::FETCH_ASSOC);
621 foreach ($nets as &$net_row)
622 {
623 $net_row = array_merge ($net_row, constructIPRange (ip4_int2bin ($net_row['ip_bin']), $net_row['mask']));
624 $net_row['spare_ranges'] = array();
625 $net_row['kidc'] = 0;
626 }
627 fillIPNetsCorrelation ($nets);
628 if (is_array ($nets[0]) and $nets[0]['id'] == $id)
629 {
630 $ret['spare_ranges'] = $nets[0]['spare_ranges'];
631 $ret['kidc'] = $nets[0]['kidc'];
632 }
633 unset ($result);
634 }
635 elseif ($realm == 'ipv6net')
636 {
637 $result = usePreparedSelectBlade ("
638 SELECT n2.id, n2.ip as ip_bin, n2.mask FROM
639 IPv6Network n1,
640 IPv6Network n2
641 WHERE
642 n1.id = ?
643 AND n2.ip BETWEEN n1.ip AND n1.last_ip
644 AND n2.mask >= n1.mask
645 ORDER BY n2.ip, n2.mask
646 ", array ($id));
647 $nets = $result->fetchAll (PDO::FETCH_ASSOC);
648 foreach ($nets as &$net_row)
649 {
650 $net_row = array_merge ($net_row, constructIPRange ($net_row['ip_bin'], $net_row['mask']));
651 $net_row['spare_ranges'] = array();
652 $net_row['kidc'] = 0;
653 }
654 fillIPNetsCorrelation ($nets);
655 if (is_array ($nets[0]) and $nets[0]['id'] == $id)
656 {
657 $ret['spare_ranges'] = $nets[0]['spare_ranges'];
658 $ret['kidc'] = $nets[0]['kidc'];
659 }
660 unset ($result);
661 }
662 $ret['atags'] = generateEntityAutoTags ($ret);
663 $entityCache['partial'][$realm][$id] = $ret;
664 return $ret;
665 }
666
667 // This function can be used with array_walk().
668 function amplifyCell (&$record, $dummy = NULL)
669 {
670 switch ($record['realm'])
671 {
672 case 'object':
673 $record['ports'] = getObjectPortsAndLinks ($record['id']);
674 $record['ipv4'] = getObjectIPv4Allocations ($record['id']);
675 $record['ipv6'] = getObjectIPv6Allocations ($record['id']);
676 $record['nat4'] = getNATv4ForObject ($record['id']);
677 $record['files'] = getFilesOfEntity ($record['realm'], $record['id']);
678 break;
679 case 'file':
680 $record['links'] = getFileLinks ($record['id']);
681 break;
682 case 'location':
683 $record['locations'] = getLocations ($record['id']);
684 $record['rows'] = getRows ($record['id']);
685 break;
686 case 'row':
687 $record['racks'] = getRacks ($record['id']);
688 case 'rack':
689 // start with default rackspace
690 for ($i = $record['height']; $i > 0; $i--)
691 for ($locidx = 0; $locidx < 3; $locidx++)
692 $record[$i][$locidx]['state'] = 'F';
693 // load difference
694 $query =
695 "select unit_no, atom, state, object_id " .
696 "from RackSpace where rack_id = ? and " .
697 "unit_no between 1 and ? order by unit_no";
698 $result = usePreparedSelectBlade ($query, array ($record['id'], $record['height']));
699 global $loclist;
700 $mounted_objects = array();
701 $rows = $result->fetchAll (PDO::FETCH_ASSOC);
702 $record['isDeletable'] = (count ($rows)) ? FALSE : TRUE;
703 foreach ($rows as $row)
704 {
705 $record[$row['unit_no']][$loclist[$row['atom']]]['state'] = $row['state'];
706 $record[$row['unit_no']][$loclist[$row['atom']]]['object_id'] = $row['object_id'];
707 if ($row['state'] == 'T' and $row['object_id'] != NULL)
708 $mounted_objects[$row['object_id']] = TRUE;
709 }
710 $record['mountedObjects'] = array_keys ($mounted_objects);
711 unset ($result);
712 break;
713 case 'vst':
714 $record['rules'] = array();
715 $record['switches'] = array();
716 $result = usePreparedSelectBlade
717 (
718 'SELECT rule_no, port_pcre, port_role, wrt_vlans, description ' .
719 'FROM VLANSTRule WHERE vst_id = ? ORDER BY rule_no',
720 array ($record['id'])
721 );
722 while ($row = $result->fetch (PDO::FETCH_ASSOC))
723 $record['rules'][$row['rule_no']] = $row;
724 unset ($result);
725 $result = usePreparedSelectBlade ('SELECT object_id, domain_id FROM VLANSwitch WHERE template_id = ?', array ($record['id']));
726 while ($row = $result->fetch (PDO::FETCH_ASSOC))
727 $record['switches'][$row['object_id']] = $row;
728 break;
729 default:
730 }
731 }
732
733 // is called by spotEntity and listCells.
734 // replaces ['8021q'] text value in cell by an array with 'domain_id' and 'vlan_id' subkeys
735 // also sets ['vlanc'] cell key to the binded vlans count.
736 function processIPNetVlans (&$cell)
737 {
738 if (empty ($cell['8021q']))
739 $cell['8021q'] = array();
740 else
741 {
742 $ck_list = explode (',', $cell['8021q']);
743 $cell['8021q'] = array();
744 foreach ($ck_list as $vlan_ck)
745 {
746 list ($domain_id, $vlan_id) = decodeVLANCK ($vlan_ck);
747 $cell['8021q'][] = array ('domain_id' => $domain_id, 'vlan_id' => $vlan_id);
748 }
749 }
750 $cell['vlanc'] = count ($cell['8021q']);
751 }
752
753 function fetchPortList ($sql_where_clause, $query_params = array())
754 {
755 $query = <<<END
756 SELECT
757 Port.id,
758 Port.name,
759 Port.object_id,
760 Object.name AS object_name,
761 Port.l2address,
762 Port.label,
763 Port.reservation_comment,
764 Port.iif_id,
765 Port.type AS oif_id,
766 (SELECT PortInnerInterface.iif_name FROM PortInnerInterface WHERE PortInnerInterface.id = Port.iif_id) AS iif_name,
767 (SELECT Dictionary.dict_value FROM Dictionary WHERE Dictionary.dict_key = Port.type) AS oif_name,
768 IF(la.porta, la.cable, lb.cable) AS cableid,
769 IF(la.porta, pa.id, pb.id) AS remote_id,
770 IF(la.porta, pa.name, pb.name) AS remote_name,
771 IF(la.porta, pa.object_id, pb.object_id) AS remote_object_id,
772 IF(la.porta, oa.name, ob.name) AS remote_object_name,
773 (SELECT COUNT(*) FROM PortLog WHERE PortLog.port_id = Port.id) AS log_count,
774 PortLog.user,
775 UNIX_TIMESTAMP(PortLog.date) as time
776 FROM
777 Port
778 INNER JOIN Object ON Port.object_id = Object.id
779 LEFT JOIN Link AS la ON la.porta = Port.id
780 LEFT JOIN Port AS pa ON pa.id = la.portb
781 LEFT JOIN Object AS oa ON pa.object_id = oa.id
782 LEFT JOIN Link AS lb on lb.portb = Port.id
783 LEFT JOIN Port AS pb ON pb.id = lb.porta
784 LEFT JOIN Object AS ob ON pb.object_id = ob.id
785 LEFT JOIN PortLog ON PortLog.id = (SELECT id FROM PortLog WHERE PortLog.port_id = Port.id ORDER BY date DESC LIMIT 1)
786 WHERE
787 $sql_where_clause
788 END;
789
790 $result = usePreparedSelectBlade ($query, $query_params);
791 $ret = array();
792 while ($row = $result->fetch (PDO::FETCH_ASSOC))
793 {
794 $row['l2address'] = l2addressFromDatabase ($row['l2address']);
795 $row['linked'] = isset ($row['remote_id']) ? 1 : 0;
796
797 // last changed log
798 $row['last_log'] = array();
799 if ($row['log_count'])
800 {
801 $row['last_log']['user'] = $row['user'];
802 $row['last_log']['time'] = $row['time'];
803 }
804 unset ($row['user']);
805 unset ($row['time']);
806
807 $ret[] = $row;
808 }
809 return $ret;
810 }
811
812 function getObjectPortsAndLinks ($object_id)
813 {
814 $ret = fetchPortList ("Port.object_id = ?", array ($object_id));
815 return sortPortList ($ret, TRUE);
816 }
817
818 // Fetch the object type via SQL.
819 // spotEntity cannot be used because it references RackObject, which doesn't suit Racks, Rows, or Locations.
820 function getObjectType ($object_id)
821 {
822 $result = usePreparedSelectBlade ('SELECT objtype_id from Object WHERE id = ?', array ($object_id));
823 return $result->fetchColumn ();
824 }
825
826 // If the given name is used by any object other than the current object,
827 // raise an exception. Validation is bypassed for certain object types
828 // where duplicates are acceptable.
829 // NOTE: This could be enforced more strictly at the database level using triggers.
830 function checkObjectNameUniqueness ($name, $type_id, $object_id = 0)
831 {
832 // Some object types do not need unique names
833 // 1560 - Rack
834 // 1561 - Row
835 $dupes_allowed = array (1560, 1561);
836 if (in_array ($type_id, $dupes_allowed))
837 return;
838
839 $result = usePreparedSelectBlade
840 (
841 'SELECT COUNT(*) FROM Object WHERE name = ? AND id != ?',
842 array ($name, $object_id)
843 );
844 $row = $result->fetch (PDO::FETCH_NUM);
845 if ($row[0] != 0)
846 throw new InvalidRequestArgException ('name', $name, 'An object with that name already exists');
847 }
848
849 function commitAddObject ($new_name, $new_label, $new_type_id, $new_asset_no, $taglist = array())
850 {
851 checkObjectNameUniqueness ($new_name, $new_type_id);
852 usePreparedInsertBlade
853 (
854 'Object',
855 array
856 (
857 'name' => !strlen ($new_name) ? NULL : $new_name,
858 'label' => !strlen ($new_label) ? NULL : $new_label,
859 'objtype_id' => $new_type_id,
860 'asset_no' => !strlen ($new_asset_no) ? NULL : $new_asset_no,
861 )
862 );
863 $object_id = lastInsertID();
864 // Do AutoPorts magic
865 executeAutoPorts ($object_id, $new_type_id);
866 // Now tags...
867 produceTagsForNewRecord ('object', $taglist, $object_id);
868 recordObjectHistory ($object_id);
869 return $object_id;
870 }
871
872 function commitRenameObject ($object_id, $new_name)
873 {
874 $type_id = getObjectType ($object_id);
875 checkObjectNameUniqueness ($new_name, $type_id, $object_id);
876 usePreparedUpdateBlade
877 (
878 'Object',
879 array
880 (
881 'name' => !mb_strlen ($new_name) ? NULL : $new_name,
882 ),
883 array
884 (
885 'id' => $object_id
886 )
887 );
888 recordObjectHistory ($object_id);
889 }
890
891 function commitUpdateObject ($object_id, $new_name, $new_label, $new_has_problems, $new_asset_no, $new_comment)
892 {
893 $type_id = getObjectType ($object_id);
894 checkObjectNameUniqueness ($new_name, $type_id, $object_id);
895 usePreparedUpdateBlade
896 (
897 'Object',
898 array
899 (
900 'name' => !mb_strlen ($new_name) ? NULL : $new_name,
901 'label' => !mb_strlen ($new_label) ? NULL : $new_label,
902 'has_problems' => $new_has_problems,
903 'asset_no' => !mb_strlen ($new_asset_no) ? NULL : $new_asset_no,
904 'comment' => $new_comment == '' ? NULL : $new_comment,
905 ),
906 array
907 (
908 'id' => $object_id
909 )
910 );
911 recordObjectHistory ($object_id);
912 }
913
914 // used by getEntityRelatives for sorting
915 function compare_name ($a, $b)
916 {
917 return strnatcmp($a['name'], $b['name']);
918 }
919
920 // find either parents or children of a record
921 function getEntityRelatives ($type, $entity_type, $entity_id)
922 {
923 if ($type == 'parents')
924 {
925 // searching for parents
926 $sql =
927 'SELECT id, parent_entity_type AS entity_type, parent_entity_id AS entity_id FROM EntityLink ' .
928 'WHERE child_entity_type = ? AND child_entity_id = ?';
929 }
930 else
931 {
932 // searching for children
933 $sql =
934 'SELECT id, child_entity_type AS entity_type, child_entity_id AS entity_id FROM EntityLink ' .
935 'WHERE parent_entity_type = ? AND parent_entity_id = ?';
936 }
937 $result = usePreparedSelectBlade ($sql, array ($entity_type, $entity_id));
938 $rows = $result->fetchAll (PDO::FETCH_ASSOC);
939 $ret = array();
940 foreach ($rows as $row)
941 {
942 // get info of the relative (only objects supported now, others may be added later)
943 $relative = spotEntity ($row['entity_type'], $row['entity_id']);
944 switch ($row['entity_type'])
945 {
946 case 'object':
947 $page = 'object';
948 $id_name = 'object_id';
949 $name = $relative['dname'];
950 break;
951 case 'rack':
952 $page = 'rack';
953 $id_name = 'rack_id';
954 $name = $relative['name'];
955 break;
956 }
957
958 // name needs to have some value for hrefs to work
959 if (!strlen ($name))
960 $name = sprintf("[Unnamed %s]", formatEntityName($row['entity_type']));
961
962 $ret[$row['id']] = array(
963 'page' => $page,
964 'id_name' => $id_name,
965 'entity_type' => $row['entity_type'],
966 'entity_id' => $row['entity_id'],
967 'name' => $name
968 );
969 }
970 // sort by name
971 uasort($ret, 'compare_name');
972 return $ret;
973 }
974
975 # This function is recursive and returns only object IDs.
976 function getObjectContentsList ($object_id)
977 {
978 $ret = array();
979 $result = usePreparedSelectBlade
980 (
981 'SELECT child_entity_id FROM EntityLink ' .
982 'WHERE parent_entity_type = "object" AND child_entity_type = "object" AND parent_entity_id = ?',
983 array ($object_id)
984 );
985 # Free this result before the called copy builds its one.
986 $rows = $result->fetchAll (PDO::FETCH_ASSOC);
987 unset ($result);
988 foreach ($rows as $row)
989 {
990 if (in_array ($row['child_entity_id'], $ret))
991 throw new RackTablesError ("Cyclic dependency for object ${object_id}", RackTablesError::INTERNAL);
992 $ret[] = $row['child_entity_id'];
993 $ret = array_merge ($ret, call_user_func (__FUNCTION__, $row['child_entity_id']));
994 }
995 return $ret;
996 }
997
998 function commitLinkEntities ($parent_entity_type, $parent_entity_id, $child_entity_type, $child_entity_id)
999 {
1000 usePreparedInsertBlade
1001 (
1002 'EntityLink',
1003 array
1004 (
1005 'parent_entity_type' => $parent_entity_type,
1006 'parent_entity_id' => $parent_entity_id,
1007 'child_entity_type' => $child_entity_type,
1008 'child_entity_id' => $child_entity_id,
1009 )
1010 );
1011 }
1012
1013 function commitUpdateEntityLink
1014 (
1015 $old_parent_entity_type, $old_parent_entity_id, $old_child_entity_type, $old_child_entity_id,
1016 $new_parent_entity_type, $new_parent_entity_id, $new_child_entity_type, $new_child_entity_id
1017 )
1018 {
1019 usePreparedExecuteBlade
1020 (
1021 'UPDATE EntityLink SET parent_entity_type=?, parent_entity_id=?, child_entity_type=?, child_entity_id=? ' .
1022 'WHERE parent_entity_type=? AND parent_entity_id=? AND child_entity_type=? AND child_entity_id=?',
1023 array
1024 (
1025 $new_parent_entity_type, $new_parent_entity_id, $new_child_entity_type, $new_child_entity_id,
1026 $old_parent_entity_type, $old_parent_entity_id, $old_child_entity_type, $old_child_entity_id
1027 )
1028 );
1029 }
1030
1031 function commitUnlinkEntities ($parent_entity_type, $parent_entity_id, $child_entity_type, $child_entity_id)
1032 {
1033 usePreparedDeleteBlade
1034 (
1035 'EntityLink',
1036 array
1037 (
1038 'parent_entity_type' => $parent_entity_type,
1039 'parent_entity_id' => $parent_entity_id,
1040 'child_entity_type' => $child_entity_type,
1041 'child_entity_id' => $child_entity_id
1042 )
1043 );
1044 }
1045
1046 function commitUnlinkEntitiesByLinkID ($link_id)
1047 {
1048 usePreparedDeleteBlade ('EntityLink', array ('id' => $link_id));
1049 }
1050
1051 // The following functions return stats about VM-related info.
1052 // TODO: simplify the queries
1053 function getVMClusterSummary ()
1054 {
1055 $result = usePreparedSelectBlade
1056 (
1057 "SELECT O.id, O.name, " .
1058 "(SELECT COUNT(*) FROM EntityLink EL " .
1059 "LEFT JOIN Object O_H ON EL.child_entity_id = O_H.id " .
1060 "LEFT JOIN AttributeValue AV ON O_H.id = AV.object_id " .
1061 "WHERE EL.parent_entity_type = 'object' " .
1062 "AND EL.child_entity_type = 'object' " .
1063 "AND EL.parent_entity_id = O.id " .
1064 "AND O_H.objtype_id = 4 " .
1065 "AND AV.attr_id = 26 " .
1066 "AND AV.uint_value = 1501) AS hypervisors, " .
1067 "(SELECT COUNT(*) FROM EntityLink EL " .
1068 "LEFT JOIN Object O_VM ON EL.child_entity_id = O_VM.id " .
1069 "WHERE EL.parent_entity_type = 'object' " .
1070 "AND EL.child_entity_type = 'object' " .
1071 "AND EL.parent_entity_id = O.id " .
1072 "AND O_VM.objtype_id = 1504) AS VMs " .
1073 "FROM Object O " .
1074 "WHERE O.objtype_id = 1505 " .
1075 "ORDER BY O.name"
1076 );
1077 return $result->fetchAll (PDO::FETCH_ASSOC);
1078 }
1079
1080 function getVMResourcePoolSummary ()
1081 {
1082 $result = usePreparedSelectBlade
1083 (
1084 "SELECT O.id, O.name, " .
1085 "(SELECT O_C.id " .
1086 "FROM EntityLink EL " .
1087 "LEFT JOIN Object O_C ON EL.parent_entity_id = O_C.id " .
1088 "WHERE EL.child_entity_id = O.id " .
1089 "AND EL.parent_entity_type = 'object' " .
1090 "AND EL.child_entity_type = 'object' " .
1091 "AND O_C.objtype_id = 1505 LIMIT 1) AS cluster_id, " .
1092 "(SELECT O_C.name " .
1093 "FROM EntityLink EL " .
1094 "LEFT JOIN Object O_C ON EL.parent_entity_id = O_C.id " .
1095 "WHERE EL.child_entity_id = O.id " .
1096 "AND EL.parent_entity_type = 'object' " .
1097 "AND EL.child_entity_type = 'object' " .
1098 "AND O_C.objtype_id = 1505 LIMIT 1) AS cluster_name, " .
1099 "(SELECT COUNT(*) FROM EntityLink EL " .
1100 "LEFT JOIN Object O_VM ON EL.child_entity_id = O_VM.id " .
1101 "WHERE EL.parent_entity_type = 'object' " .
1102 "AND EL.child_entity_type = 'object' " .
1103 "AND EL.parent_entity_id = O.id " .
1104 "AND O_VM.objtype_id = 1504) AS VMs " .
1105 "FROM Object O " .
1106 "WHERE O.objtype_id = 1506 " .
1107 "ORDER BY O.name"
1108 );
1109 return $result->fetchAll (PDO::FETCH_ASSOC);
1110 }
1111
1112 function getVMHypervisorSummary ()
1113 {
1114 $result = usePreparedSelectBlade
1115 (
1116 "SELECT O.id, O.name, " .
1117 "(SELECT O_C.id " .
1118 "FROM EntityLink EL " .
1119 "LEFT JOIN Object O_C ON EL.parent_entity_id = O_C.id " .
1120 "WHERE EL.child_entity_id = O.id " .
1121 "AND EL.parent_entity_type = 'object' " .
1122 "AND EL.child_entity_type = 'object' " .
1123 "AND O_C.objtype_id = 1505 LIMIT 1) AS cluster_id, " .
1124 "(SELECT O_C.name " .
1125 "FROM EntityLink EL " .
1126 "LEFT JOIN Object O_C ON EL.parent_entity_id = O_C.id " .
1127 "WHERE EL.child_entity_id = O.id " .
1128 "AND EL.parent_entity_type = 'object' " .
1129 "AND EL.child_entity_type = 'object' " .
1130 "AND O_C.objtype_id = 1505 LIMIT 1) AS cluster_name, " .
1131 "(SELECT COUNT(*) FROM EntityLink EL " .
1132 "LEFT JOIN Object O_VM ON EL.child_entity_id = O_VM.id " .
1133 "WHERE EL.parent_entity_type = 'object' " .
1134 "AND EL.child_entity_type = 'object' " .
1135 "AND EL.parent_entity_id = O.id " .
1136 "AND O_VM.objtype_id = 1504) AS VMs " .
1137 "FROM Object O " .
1138 "LEFT JOIN AttributeValue AV ON O.id = AV.object_id " .
1139 "WHERE O.objtype_id = 4 " .
1140 "AND AV.attr_id = 26 " .
1141 "AND AV.uint_value = 1501 " .
1142 "ORDER BY O.name"
1143 );
1144 return $result->fetchAll (PDO::FETCH_ASSOC);
1145 }
1146
1147 function getVMSwitchSummary ()
1148 {
1149 $result = usePreparedSelectBlade
1150 (
1151 "SELECT O.id, O.name " .
1152 "FROM Object O " .
1153 "WHERE O.objtype_id = 1507 " .
1154 "ORDER BY O.name"
1155 );
1156 return $result->fetchAll (PDO::FETCH_ASSOC);
1157 }
1158
1159 // Remove file links related to the entity, but leave the entity and file(s) intact.
1160 function releaseFiles ($entity_realm, $entity_id)
1161 {
1162 usePreparedDeleteBlade ('FileLink', array ('entity_type' => $entity_realm, 'entity_id' => $entity_id));
1163 }
1164
1165 // There are times when you want to delete all traces of an object
1166 function commitDeleteObject ($object_id = 0)
1167 {
1168 // Reset most of stuff
1169 commitResetObject ($object_id);
1170 // Object itself
1171 usePreparedDeleteBlade ('Object', array ('id' => $object_id));
1172 // Dangling links
1173 usePreparedExecuteBlade
1174 (
1175 'DELETE FROM EntityLink WHERE ' .
1176 "(parent_entity_type IN ('rack', 'row', 'location') AND parent_entity_id = ?) OR " .
1177 "(child_entity_type IN ('rack', 'row', 'location') AND child_entity_id = ?)",
1178 array ($object_id, $object_id)
1179 );
1180 }
1181
1182 function commitResetObject ($object_id = 0)
1183 {
1184 releaseFiles ('object', $object_id);
1185 destroyTagsForEntity ('object', $object_id);
1186 usePreparedDeleteBlade ('IPv4LB', array ('object_id' => $object_id));
1187 usePreparedDeleteBlade ('IPv4Allocation', array ('object_id' => $object_id));
1188 usePreparedDeleteBlade ('IPv6Allocation', array ('object_id' => $object_id));
1189 usePreparedDeleteBlade ('IPv4NAT', array ('object_id' => $object_id));
1190 // Parent-child relationships
1191 usePreparedExecuteBlade
1192 (
1193 'DELETE FROM EntityLink WHERE ' .
1194 "(parent_entity_type = 'object' AND parent_entity_id = ?) OR (child_entity_type = 'object' AND child_entity_id = ?)",
1195 array ($object_id, $object_id)
1196 );
1197 // Rack space
1198 usePreparedExecuteBlade ('DELETE FROM Atom WHERE molecule_id IN (SELECT new_molecule_id FROM MountOperation WHERE object_id = ?)', array ($object_id));
1199 usePreparedExecuteBlade ('DELETE FROM Molecule WHERE id IN (SELECT new_molecule_id FROM MountOperation WHERE object_id = ?)', array ($object_id));
1200 usePreparedDeleteBlade ('MountOperation', array ('object_id' => $object_id));
1201 usePreparedDeleteBlade ('RackSpace', array ('object_id' => $object_id));
1202 // 802.1Q
1203 usePreparedDeleteBlade ('PortVLANMode', array ('object_id' => $object_id));
1204 usePreparedDeleteBlade ('PortNativeVLAN', array ('object_id' => $object_id));
1205 usePreparedDeleteBlade ('PortAllowedVLAN', array ('object_id' => $object_id));
1206 usePreparedDeleteBlade ('CachedPVM', array ('object_id' => $object_id));
1207 usePreparedDeleteBlade ('VLANSwitch', array ('object_id' => $object_id));
1208 // Ports & links
1209 usePreparedDeleteBlade ('Port', array ('object_id' => $object_id));
1210 // CN
1211 usePreparedUpdateBlade ('Object', array ('name' => NULL, 'label' => ''), array ('id' => $object_id));
1212 // FQDN
1213 commitUpdateAttrValue ($object_id, 3, "");
1214 // log history
1215 recordObjectHistory ($object_id);
1216 # Cacti graphs
1217 usePreparedDeleteBlade ('CactiGraph', array ('object_id' => $object_id));
1218 # Munin graphs
1219 usePreparedDeleteBlade ('MuninGraph', array ('object_id' => $object_id));
1220 }
1221
1222 function commitUpdateRack ($rack_id, $new_row_id, $new_name, $new_height, $new_has_problems, $new_asset_no, $new_comment)
1223 {
1224 // Can't shrink a rack if rows being deleted contain mounted objects
1225 $check_result = usePreparedSelectBlade ('SELECT COUNT(*) AS count FROM RackSpace WHERE rack_id = ? AND unit_no > ?', array ($rack_id, $new_height));
1226 $check_row = $check_result->fetch (PDO::FETCH_ASSOC);
1227 unset ($check_result);
1228 if ($check_row['count'] > 0)
1229 throw new InvalidArgException ('new_height', $new_height, 'Cannot shrink rack, objects are still mounted there');
1230
1231 // Determine if the row changed
1232 $old_rack = spotEntity ('rack', $rack_id);
1233 $old_row_id = $old_rack['row_id'];
1234 if ($old_row_id != $new_row_id)
1235 {
1236 // Move it to the specified row
1237 usePreparedUpdateBlade
1238 (
1239 'EntityLink',
1240 array ('parent_entity_id' => $new_row_id),
1241 array ('child_entity_type' => 'rack', 'child_entity_id' => $rack_id)
1242 );
1243
1244 // Set the sort_order attribute so it's placed at the end of the new row
1245 $rowInfo = getRowInfo ($new_row_id);
1246 usePreparedUpdateBlade
1247 (
1248 'AttributeValue',
1249 array ('uint_value' => $rowInfo['count']),
1250 array ('object_id' => $rack_id, 'attr_id' => 29)
1251 );
1252
1253 // Reset the sort order of the old row
1254 resetRackSortOrder ($old_row_id);
1255 }
1256
1257 // Update the height
1258 commitUpdateAttrValue ($rack_id, 27, $new_height);
1259
1260 // Update the rack
1261 commitUpdateObject ($rack_id, $new_name, NULL, $new_has_problems, $new_asset_no, $new_comment);
1262 }
1263
1264 // Used when sort order is manually changed, and when a rack is moved or deleted
1265 // Input is expected to be a pre-sorted array of rack IDs
1266 function updateRackSortOrder ($racks)
1267 {
1268 for ($i = 0; $i<count($racks); $i++)
1269 {
1270 usePreparedUpdateBlade
1271 (
1272 'AttributeValue',
1273 array ('uint_value' => $i+1),
1274 array ('object_id' => $racks[$i], 'attr_id' => 29)
1275 );
1276 }
1277 }
1278
1279 function resetRackSortOrder ($row_id)
1280 {
1281 // Re-order the row's racks
1282 $racks = getRacks($row_id);
1283 $rack_ids = array ();
1284 foreach ($racks as $rack_id => $rackDetails)
1285 $rack_ids[] = $rack_id;
1286 updateRackSortOrder ($rack_ids);
1287 }
1288
1289 // This function accepts rack data returned by amplifyCell(), validates and applies changes
1290 // supplied in $_REQUEST and returns resulting array. Only those changes are examined, which
1291 // correspond to current rack ID.
1292 // 1st arg is rackdata, 2nd arg is unchecked state, 3rd arg is checked state.
1293 // If 4th arg is present, object_id fields will be updated accordingly to the new state.
1294 // The function returns TRUE if the DB was successfully changed, FALSE otherwise
1295 function processGridForm (&$rackData, $unchecked_state, $checked_state, $object_id = 0)
1296 {
1297 global $loclist, $dbxlink;
1298 $rack_id = $rackData['id'];
1299 $rack_name = $rackData['name'];
1300 $rackchanged = FALSE;
1301 $dbxlink->beginTransaction();
1302 for ($unit_no = $rackData['height']; $unit_no > 0; $unit_no--)
1303 {
1304 for ($locidx = 0; $locidx < 3; $locidx++)
1305 {
1306 if ($rackData[$unit_no][$locidx]['enabled'] != TRUE)
1307 continue;
1308 // detect a change
1309 $state = $rackData[$unit_no][$locidx]['state'];
1310 if (isset ($_REQUEST["atom_${rack_id}_${unit_no}_${locidx}"]) and $_REQUEST["atom_${rack_id}_${unit_no}_${locidx}"] == 'on')
1311 $newstate = $checked_state;
1312 else
1313 $newstate = $unchecked_state;
1314 if ($state == $newstate)
1315 continue;
1316 $rackchanged = TRUE;
1317 // and validate
1318 $atom = $loclist[$locidx];
1319 // The only changes allowed are those introduced by checkbox grid.
1320 if
1321 (
1322 !($state == $checked_state && $newstate == $unchecked_state) &&
1323 !($state == $unchecked_state && $newstate == $checked_state)
1324 )
1325 {
1326 showError ("${rack_name}: Rack ID ${rack_id}, unit ${unit_no}, 'atom ${atom}', cannot change state from '${state}' to '${newstate}'");
1327 return FALSE;
1328 }
1329 // Here we avoid using ON DUPLICATE KEY UPDATE by first performing DELETE
1330 // anyway and then looking for probable need of INSERT.
1331 usePreparedDeleteBlade ('RackSpace', array ('rack_id' => $rack_id, 'unit_no' => $unit_no, 'atom' => $atom));
1332 if ($newstate != 'F')
1333 usePreparedInsertBlade ('RackSpace', array ('rack_id' => $rack_id, 'unit_no' => $unit_no, 'atom' => $atom, 'state' => $newstate));
1334 if ($newstate == 'T' and $object_id != 0)
1335 {
1336 // At this point we already have a record in RackSpace.
1337 usePreparedUpdateBlade
1338 (
1339 'RackSpace',
1340 array ('object_id' => $object_id),
1341 array
1342 (
1343 'rack_id' => $rack_id,
1344 'unit_no' => $unit_no,
1345 'atom' => $atom,
1346 )
1347 );
1348 $rackData[$unit_no][$locidx]['object_id'] = $object_id;
1349 }
1350 }
1351 }
1352 if ($rackchanged)
1353 {
1354 usePreparedDeleteBlade ('RackThumbnail', array ('rack_id' => $rack_id));
1355 $dbxlink->commit();
1356 return TRUE;
1357 }
1358 $dbxlink->rollBack();
1359 return FALSE;
1360 }
1361
1362 // This function builds a list of rack-unit-atom records, which are assigned to
1363 // the requested object.
1364 function getMoleculeForObject ($object_id)
1365 {
1366 $result = usePreparedSelectBlade
1367 (
1368 'SELECT rack_id, unit_no, atom FROM RackSpace ' .
1369 'WHERE state = "T" AND object_id = ? ORDER BY rack_id, unit_no, atom',
1370 array ($object_id)
1371 );
1372 return $result->fetchAll (PDO::FETCH_ASSOC);
1373 }
1374
1375 // This function builds a list of rack-unit-atom records for requested molecule.
1376 function getMolecule ($mid = 0)
1377 {
1378 $result = usePreparedSelectBlade ('SELECT rack_id, unit_no, atom FROM Atom WHERE molecule_id = ?', array ($mid));
1379 return $result->fetchAll (PDO::FETCH_ASSOC);
1380 }
1381
1382 // returns exactly what is's named after
1383 function lastInsertID ()
1384 {
1385 $result = usePreparedSelectBlade ('select last_insert_id()');
1386 $row = $result->fetch (PDO::FETCH_NUM);
1387 return $row[0];
1388 }
1389
1390 // This function creates a new record in Molecule and number of linked
1391 // R-U-A records in Atom.
1392 function createMolecule ($molData)
1393 {
1394 usePreparedExecuteBlade ('INSERT INTO Molecule VALUES()');
1395 $molecule_id = lastInsertID();
1396 foreach ($molData as $rua)
1397 usePreparedInsertBlade
1398 (
1399 'Atom',
1400 array
1401 (
1402 'molecule_id' => $molecule_id,
1403 'rack_id' => $rua['rack_id'],
1404 'unit_no' => $rua['unit_no'],
1405 'atom' => $rua['atom'],
1406 )
1407 );
1408 return $molecule_id;
1409 }
1410
1411 // History logger. This function assumes certain table naming convention and
1412 // column design:
1413 // - History table must have the same row set (w/o keys) plus one row named
1414 // 'ctime' of type 'timestamp'.
1415 function recordObjectHistory ($object_id)
1416 {
1417 global $remote_username;
1418 usePreparedExecuteBlade
1419 (
1420 "INSERT INTO ObjectHistory SELECT *, CURRENT_TIMESTAMP(), ? FROM Object WHERE id=?",
1421 array ($remote_username, $object_id)
1422 );
1423 }
1424
1425 function getRackspaceHistory ()
1426 {
1427 $result = usePreparedSelectBlade
1428 (
1429 "SELECT id as mo_id, object_id as ro_id, ctime, comment, user_name FROM " .
1430 "MountOperation ORDER BY ctime DESC"
1431 );
1432 return $result->fetchAll (PDO::FETCH_ASSOC);
1433 }
1434
1435 // This function is used in renderRackspaceHistory()
1436 function getOperationMolecules ($op_id = 0)
1437 {
1438 $result = usePreparedSelectBlade ('SELECT old_molecule_id, new_molecule_id FROM MountOperation WHERE id = ?', array ($op_id));
1439 // We expect one row.
1440 $row = $result->fetch (PDO::FETCH_ASSOC);
1441 return array ($row['old_molecule_id'], $row['new_molecule_id']);
1442 }
1443
1444 function getResidentRacksData ($object_id = 0, $fetch_rackdata = TRUE)
1445 {
1446 $result = usePreparedSelectBlade
1447 (
1448 // Include racks that the object is directly mounted in
1449 "SELECT rack_id FROM RackSpace WHERE object_id = ? " .
1450 "UNION " .
1451 // Include racks that it's parent is mounted in
1452 "SELECT RS.rack_id FROM RackSpace RS INNER JOIN EntityLink EL ON RS.object_id = EL.parent_entity_id AND EL.parent_entity_type = 'object' WHERE EL.child_entity_id = ? AND EL.child_entity_type = 'object' " .
1453 "UNION " .
1454 // and racks that it is 'Zero-U' mounted in
1455 "SELECT parent_entity_id AS rack_id FROM EntityLink WHERE parent_entity_type = 'rack' AND child_entity_type = 'object' AND child_entity_id = ? " .
1456 'ORDER BY rack_id', array ($object_id, $object_id, $object_id)
1457 );
1458 $rows = $result->fetchAll (PDO::FETCH_NUM);
1459 unset ($result);
1460
1461 $ret = array();
1462 foreach ($rows as $row)
1463 if (! isset ($ret[$row[0]]))
1464 {
1465 if (!$fetch_rackdata)
1466 $rackData = $row[0];
1467 else
1468 {
1469 $rackData = spotEntity ('rack', $row[0]);
1470 amplifyCell ($rackData);
1471 }
1472 $ret[$row[0]] = $rackData;
1473 }
1474 return $ret;
1475 }
1476
1477 function commitAddPort ($object_id = 0, $port_name, $port_type_id, $port_label, $port_l2address)
1478 {
1479 $db_l2address = l2addressForDatabase ($port_l2address);
1480 global $dbxlink;
1481 $dbxlink->exec ('LOCK TABLES Port WRITE');
1482 if (alreadyUsedL2Address ($db_l2address, $object_id))
1483 {
1484 $dbxlink->exec ('UNLOCK TABLES');
1485 throw new InvalidRequestArgException ('port_l2address', $port_l2address, 'address belongs to another object');
1486 }
1487 $matches = array();
1488 switch (1)
1489 {
1490 case preg_match ('/^([[:digit:]]+)-([[:digit:]]+)$/', $port_type_id, $matches):
1491 $iif_id = $matches[1];
1492 $oif_id = $matches[2];
1493 break;
1494 case preg_match ('/^([[:digit:]]+)$/', $port_type_id, $matches):
1495 $iif_id = 1;
1496 $oif_id = $matches[1];
1497 break;
1498 default:
1499 $dbxlink->exec ('UNLOCK TABLES');
1500 throw new InvalidArgException ('port_type_id', $port_type_id, 'format error');
1501 }
1502 usePreparedInsertBlade
1503 (
1504 'Port',
1505 array
1506 (
1507 'name' => $port_name,
1508 'object_id' => $object_id,
1509 'label' => $port_label,
1510 'iif_id' => $iif_id,
1511 'type' => $oif_id,
1512 'l2address' => ($db_l2address === '') ? NULL : $db_l2address,
1513 )
1514 );
1515 $dbxlink->exec ('UNLOCK TABLES');
1516 return lastInsertID();
1517 }
1518
1519 function getPortReservationComment ($port_id)
1520 {
1521 $result = usePreparedSelectBlade ('SELECT reservation_comment FROM Port WHERE id = ?', array ($port_id));
1522 return $result->fetchColumn();
1523 }
1524
1525 // The fifth argument may be either explicit 'NULL' or some (already quoted by the upper layer)
1526 // string value. In case it is omitted, we just assign it its current value.
1527 // It would be nice to simplify this semantics later.
1528 function commitUpdatePort ($object_id, $port_id, $port_name, $port_type_id, $port_label, $port_l2address, $port_reservation_comment)
1529 {
1530 $db_l2address = l2addressForDatabase ($port_l2address);
1531 global $dbxlink;
1532 $dbxlink->exec ('LOCK TABLES Port WRITE');
1533 if (alreadyUsedL2Address ($db_l2address, $object_id))
1534 {
1535 $dbxlink->exec ('UNLOCK TABLES');
1536 // FIXME: it is more correct to throw InvalidArgException here
1537 // and convert it to InvalidRequestArgException at upper level,
1538 // when there is a mean to do that.
1539 throw new InvalidRequestArgException ('port_l2address', $db_l2address, 'address belongs to another object');
1540 }
1541 $prev_comment = getPortReservationComment ($port_id);
1542 $reservation_comment = mb_strlen ($port_reservation_comment) ? $port_reservation_comment : NULL;
1543 usePreparedUpdateBlade
1544 (
1545 'Port',
1546 array
1547 (
1548 'name' => $port_name,
1549 'type' => $port_type_id,
1550 'label' => $port_label,
1551 'reservation_comment' => $reservation_comment,
1552 'l2address' => ($db_l2address === '') ? NULL : $db_l2address,
1553 ),
1554 array
1555 (
1556 'id' => $port_id,
1557 'object_id' => $object_id
1558 )
1559 );
1560 $dbxlink->exec ('UNLOCK TABLES');
1561 if ($prev_comment !== $reservation_comment)
1562 addPortLogEntry ($port_id, sprintf ("Reservation changed from '%s' to '%s'", $prev_comment, $reservation_comment));
1563 }
1564
1565 function commitUpdatePortComment ($port_id, $port_reservation_comment)
1566 {
1567 global $dbxlink;
1568 $dbxlink->exec ('LOCK TABLES Port WRITE');
1569 $prev_comment = getPortReservationComment ($port_id);
1570 $reservation_comment = mb_strlen ($port_reservation_comment) ? $port_reservation_comment : NULL;
1571 usePreparedUpdateBlade
1572 (
1573 'Port',
1574 array
1575 (
1576 'reservation_comment' => $reservation_comment,
1577 ),
1578 array
1579 (
1580 'id' => $port_id,
1581 )
1582 );
1583 $dbxlink->exec ('UNLOCK TABLES');
1584 if ($prev_comment !== $reservation_comment)
1585 addPortLogEntry ($port_id, sprintf ("Reservation changed from '%s' to '%s'", $prev_comment, $reservation_comment));
1586 }
1587
1588 function commitUpdatePortOIF ($port_id, $port_type_id)
1589 {
1590 usePreparedUpdateBlade
1591 (
1592 'Port',
1593 array ('type' => $port_type_id),
1594 array ('id' => $port_id)
1595 );
1596 }
1597
1598 function getAllIPv4Allocations ()
1599 {
1600 $result = usePreparedSelectBlade
1601 (
1602 "select object_id as object_id, ".
1603 "Object.name as object_name, ".
1604 "IPv4Allocation.name as name, ".
1605 "IPv4Allocation.type as type, ".
1606 "INET_NTOA(ip) as ip ".
1607 "from IPv4Allocation join Object on id=object_id "
1608 );
1609 return $result->fetchAll (PDO::FETCH_ASSOC);
1610 }
1611
1612 function linkPorts ($porta, $portb, $cable = NULL)
1613 {
1614 if ($porta == $portb)
1615 throw new InvalidArgException ('porta/portb', $porta, "Ports can't be the same");
1616
1617 global $dbxlink;
1618 $dbxlink->exec ('LOCK TABLES Link WRITE');
1619 $result = usePreparedSelectBlade
1620 (
1621 'SELECT COUNT(*) FROM Link WHERE porta IN (?,?) OR portb IN (?,?)',
1622 array ($porta, $portb, $porta, $portb)
1623 );
1624 if ($result->fetchColumn () != 0)
1625 {
1626 $dbxlink->exec ('UNLOCK TABLES');
1627 return "Port ${porta} or ${portb} is already linked";
1628 }
1629 unset ($result);
1630 if ($porta > $portb)
1631 {
1632 $tmp = $porta;
1633 $porta = $portb;
1634 $portb = $tmp;
1635 }
1636 usePreparedInsertBlade
1637 (
1638 'Link',
1639 array
1640 (
1641 'porta' => $porta,
1642 'portb' => $portb,
1643 'cable' => mb_strlen ($cable) ? $cable : NULL
1644 )
1645 );
1646 $dbxlink->exec ('UNLOCK TABLES');
1647 usePreparedExecuteBlade
1648 (
1649 'UPDATE Port SET reservation_comment=NULL WHERE id IN(?, ?)',
1650 array ($porta, $portb)
1651 );
1652
1653 // log new links
1654 $result = usePreparedSelectBlade
1655 (
1656 "SELECT Port.id, Port.name as port_name, Object.name as obj_name FROM Port " .
1657 "INNER JOIN Object ON Port.object_id = Object.id WHERE Port.id IN (?, ?)",
1658 array ($porta, $portb)
1659 );
1660 $rows = $result->fetchAll (PDO::FETCH_ASSOC);
1661 unset ($result);
1662 foreach ($rows as $row)
1663 {
1664 $pair_id = ($row['id'] == $porta ? $portb : $porta);
1665 addPortLogEntry ($pair_id, sprintf ("linked to %s %s", $row['obj_name'], $row['port_name']));
1666 }
1667 }
1668
1669 function commitUpdatePortLink ($port_id, $cable = NULL)
1670 {
1671 return usePreparedUpdateBlade
1672 (
1673 'Link',
1674 array ('cable' => mb_strlen ($cable) ? $cable : NULL),
1675 array ('porta' => $port_id, 'portb' => $port_id),
1676 'OR'
1677 );
1678 }
1679
1680 function commitUnlinkPort ($port_id)
1681 {
1682 // fetch and log existing link
1683 $result = usePreparedSelectBlade
1684 (
1685 "SELECT pa.id AS id_a, pa.name AS port_name_a, oa.name AS obj_name_a, " .
1686 "pb.id AS id_b, pb.name AS port_name_b, ob.name AS obj_name_b " .
1687 "FROM " .
1688 "Link INNER JOIN Port pa ON pa.id = Link.porta " .
1689 "INNER JOIN Port pb ON pb.id = Link.portb " .
1690 "INNER JOIN RackObject oa ON pa.object_id = oa.id " .
1691 "INNER JOIN RackObject ob ON pb.object_id = ob.id " .
1692 "WHERE " .
1693 "Link.porta = ? OR Link.portb = ?",
1694 array ($port_id, $port_id)
1695 );
1696 $rows = $result->fetchAll (PDO::FETCH_ASSOC);
1697 unset ($result);
1698 foreach ($rows as $row)
1699 {
1700 addPortLogEntry ($row['id_a'], sprintf ("unlinked from %s %s", $row['obj_name_b'], $row['port_name_b']));
1701 addPortLogEntry ($row['id_b'], sprintf ("unlinked from %s %s", $row['obj_name_a'], $row['port_name_a']));
1702 }
1703
1704 // remove existing link
1705 usePreparedDeleteBlade ('Link', array ('porta' => $port_id, 'portb' => $port_id), 'OR');
1706 }
1707
1708 function addPortLogEntry ($port_id, $message)
1709 {
1710 global $disable_logging;
1711 if (isset ($disable_logging) && $disable_logging)
1712 return;
1713 global $remote_username;
1714 usePreparedExecuteBlade
1715 (
1716 "INSERT INTO PortLog (port_id, user, date, message) VALUES (?, ?, NOW(), ?)",
1717 array ($port_id, $remote_username, $message)
1718 );
1719 }
1720
1721 function addIPLogEntry ($ip_bin, $message)
1722 {
1723 switch (strlen ($ip_bin))
1724 {
1725 case 4: return addIPv4LogEntry ($ip_bin, $message);
1726 case 16: return addIPv6LogEntry ($ip_bin, $message);
1727 default: throw new InvalidArgException ('ip_bin', $ip_bin, "Invalid binary IP");
1728 }
1729 }
1730
1731 function addIPv4LogEntry ($ip_bin, $message)
1732 {
1733 global $disable_logging;
1734 if (isset ($disable_logging) && $disable_logging)
1735 return;
1736 global $remote_username;
1737 usePreparedExecuteBlade
1738 (
1739 "INSERT INTO IPv4Log (ip, date, user, message) VALUES (?, NOW(), ?, ?)",
1740 array (ip4_bin2db ($ip_bin), $remote_username, $message)
1741 );
1742 }
1743
1744 function addIPv6LogEntry ($ip_bin, $message)
1745 {
1746 global $disable_logging;
1747 if (isset ($disable_logging) && $disable_logging)
1748 return;
1749 global $remote_username;
1750 usePreparedExecuteBlade
1751 (
1752 "INSERT INTO IPv6Log (ip, date, user, message) VALUES (?, NOW(), ?, ?)",
1753 array ($ip_bin, $remote_username, $message)
1754 );
1755 }
1756
1757 function fetchIPLogEntry ($ip_bin)
1758 {
1759 switch (strlen ($ip_bin))
1760 {
1761 case 4: return fetchIPv4LogEntry ($ip_bin);
1762 case 16: return fetchIPv6LogEntry ($ip_bin);
1763 default: throw new InvalidArgException ('ip_bin', $ip_bin, "Invalid binary IP");
1764 }
1765 }
1766
1767 function fetchIPv4LogEntry ($ip_bin)
1768 {
1769 $result = usePreparedSelectBlade
1770 (
1771 "SELECT date, user, message FROM IPv4Log WHERE ip = ? ORDER BY date ASC",
1772 array (ip4_bin2db ($ip_bin))
1773 );
1774 return $result->fetchAll (PDO::FETCH_ASSOC);
1775 }
1776
1777 function fetchIPv6LogEntry ($ip_bin)
1778 {
1779 $result = usePreparedSelectBlade
1780 (
1781 "SELECT date, user, message FROM IPv6Log WHERE ip = ? ORDER BY date ASC",
1782 array ($ip_bin)
1783 );
1784 return $result->fetchAll (PDO::FETCH_ASSOC);
1785 }
1786
1787 // wrapper around getObjectIPv4AllocationList and getObjectIPv6AllocationList
1788 function getObjectIPAllocationList ($object_id)
1789 {
1790 return
1791 getObjectIPv4AllocationList ($object_id) +
1792 getObjectIPv6AllocationList ($object_id);
1793 }
1794
1795 // Returns all IPv4 addresses allocated to object, but does not attach detailed info about address
1796 // Used instead of getObjectIPv4Allocations if you need perfomance but 'addrinfo' value
1797 function getObjectIPv4AllocationList ($object_id)
1798 {
1799 $ret = array();
1800 $result = usePreparedSelectBlade
1801 (
1802 'SELECT name AS osif, type, ip FROM IPv4Allocation ' .
1803 'WHERE object_id = ?',
1804 array ($object_id)
1805 );
1806 while ($row = $result->fetch (PDO::FETCH_ASSOC))
1807 $ret[ip4_int2bin ($row['ip'])] = array ('osif' => $row['osif'], 'type' => $row['type']);
1808 return $ret;
1809 }
1810
1811 // Returns all IPv6 addresses allocated to object, but does not attach detailed info about address
1812 // Used instead of getObjectIPv6Allocations if you need perfomance but 'addrinfo' value
1813 function getObjectIPv6AllocationList ($object_id)
1814 {
1815 $ret = array();
1816 $result = usePreparedSelectBlade
1817 (
1818 'SELECT name AS osif, type, ip AS ip FROM IPv6Allocation ' .
1819 'WHERE object_id = ?',
1820 array ($object_id)
1821 );
1822 while ($row = $result->fetch (PDO::FETCH_ASSOC))
1823 $ret[$row['ip']] = array ('osif' => $row['osif'], 'type' => $row['type']);
1824 return $ret;
1825 }
1826
1827 // Return all IP addresses allocated to the object sorted by allocation name.
1828 // Attach detailed info about address to each alocation records.
1829 // Index result by binary ip
1830 function getObjectIPAllocations ($object_id)
1831 {
1832 return amplifyAllocationList (getObjectIPAllocationList ($object_id));
1833 }
1834 function getObjectIPv4Allocations ($object_id)
1835 {
1836 return amplifyAllocationList (getObjectIPv4AllocationList ($object_id));
1837 }
1838 function getObjectIPv6Allocations ($object_id)
1839 {
1840 return amplifyAllocationList (getObjectIPv6AllocationList ($object_id));
1841 }
1842
1843 function amplifyAllocationList ($alloc_list)
1844 {
1845 $ret = array();
1846 $sorted = array();
1847 foreach ($alloc_list as $ip_bin => $alloc)
1848 $sorted[$alloc['osif']][$ip_bin] = $alloc;
1849 foreach (sortPortList ($sorted) as $osif => $subarray)
1850 foreach ($subarray as $ip_bin => $alloc)
1851 {
1852 $alloc['addrinfo'] = getIPAddress ($ip_bin);
1853 $ret[$ip_bin] = $alloc;
1854 }
1855 return $ret;
1856 }
1857
1858 function scanIPSpace ($pairlist)
1859 {
1860 $v4_pairs = array();
1861 $v6_pairs = array();
1862 foreach ($pairlist as $pair)
1863 {
1864 if (strlen ($pair['first']) == 4)
1865 $v4_pairs[] = $pair;
1866 elseif (strlen ($pair['first']) == 16)
1867 $v6_pairs[] = $pair;
1868 }
1869 return
1870 scanIPv4Space ($v4_pairs) +
1871 scanIPv6Space ($v6_pairs);
1872 }
1873
1874 // Check the range requested for meaningful IPv4 records, build them
1875 // into a list and return. Return an empty list if nothing matched.
1876 // Both arguments are expected in 4-byte binary string form. The resulting list
1877 // is keyed by 4-byte binary IPs, items aren't sorted.
1878 // LATER: accept a list of pairs and build WHERE sub-expression accordingly
1879 function scanIPv4Space ($pairlist)
1880 {
1881 $ret = array();
1882 if (!count ($pairlist)) // this is normal for a network completely divided into smaller parts
1883 return $ret;
1884 // FIXME: this is a copy-and-paste prototype
1885 $or = '';
1886 $whereexpr1 = '(';
1887 $whereexpr2 = '(';
1888 $whereexpr3 = '(';
1889 $whereexpr4 = '(';
1890 $whereexpr5a = '(';
1891 $whereexpr5b = '(';
1892 $whereexpr6 = '(';
1893 $qparams = array();
1894 $qparams_bin = array();
1895 foreach ($pairlist as $tmp)
1896 {
1897 $whereexpr1 .= $or . "ip between ? and ?";
1898 $whereexpr2 .= $or . "ip between ? and ?";
1899 $whereexpr3 .= $or . "vip between ? and ?";
1900 $whereexpr4 .= $or . "rsip between ? and ?";
1901 $whereexpr5a .= $or . "remoteip between ? and ?";
1902 $whereexpr5b .= $or . "localip between ? and ?";
1903 $whereexpr6 .= $or . "l.ip between ? and ?";
1904 $or = ' or ';
1905 $qparams[] = ip4_bin2db ($tmp['first']);
1906 $qparams[] = ip4_bin2db ($tmp['last']);
1907 $qparams_bin[] = $tmp['first'];
1908 $qparams_bin[] = $tmp['last'];
1909 }
1910 $whereexpr1 .= ')';
1911 $whereexpr2 .= ')';
1912 $whereexpr3 .= ')';
1913 $whereexpr4 .= ')';
1914 $whereexpr5a .= ')';
1915 $whereexpr5b .= ')';
1916 $whereexpr6 .= ')';
1917
1918 // 1. collect labels and reservations
1919 $query = "select ip, name, comment, reserved from IPv4Address ".
1920 "where ${whereexpr1} and (reserved = 'yes' or name != '' or comment != '')";
1921 $result = usePreparedSelectBlade ($query, $qparams);
1922 while ($row = $result->fetch (PDO::FETCH_ASSOC))
1923 {
1924 $ip_bin = ip4_int2bin ($row['ip']);
1925 if (!isset ($ret[$ip_bin]))
1926 $ret[$ip_bin] = constructIPAddress ($ip_bin);
1927 $ret[$ip_bin]['name'] = $row['name'];
1928 $ret[$ip_bin]['comment'] = $row['comment'];
1929 $ret[$ip_bin]['reserved'] = $row['reserved'];
1930 }
1931 unset ($result);
1932
1933 // 2. check for allocations
1934 $query =
1935 "select ip, object_id, name, type " .
1936 "from IPv4Allocation where ${whereexpr2} order by type";
1937 $result = usePreparedSelectBlade ($query, $qparams);
1938 // release DBX early to avoid issues with nested spotEntity() calls
1939 $allRows = $result->fetchAll (PDO::FETCH_ASSOC);
1940 unset ($result);
1941 foreach ($allRows as $row)
1942 {
1943 $ip_bin = ip4_int2bin ($row['ip']);
1944 if (!isset ($ret[$ip_bin]))
1945 $ret[$ip_bin] = constructIPAddress ($ip_bin);
1946 $oinfo = spotEntity ('object', $row['object_id']);
1947 $ret[$ip_bin]['allocs'][] = array
1948 (
1949 'type' => $row['type'],
1950 'name' => $row['name'],
1951 'object_id' => $row['object_id'],
1952 'object_name' => $oinfo['dname'],
1953 );
1954 }
1955
1956 // 3. look for virtual services
1957 $query = "select id, vip from IPv4VS where ${whereexpr3}";
1958 $result = usePreparedSelectBlade ($query, $qparams_bin);
1959 $allRows = $result->fetchAll (PDO::FETCH_ASSOC);
1960 unset ($result);
1961 foreach ($allRows as $row)
1962 {
1963 $ip_bin = $row['vip'];
1964 if (!isset ($ret[$ip_bin]))
1965 $ret[$ip_bin] = constructIPAddress ($ip_bin);
1966 $ret[$ip_bin]['vslist'][] = $row['id'];
1967 }
1968
1969 // 4. don't forget about real servers along with pools
1970 $query = "select rsip, rspool_id from IPv4RS where ${whereexpr4}";
1971 $result = usePreparedSelectBlade ($query, $qparams_bin);
1972 while ($row = $result->fetch (PDO::FETCH_ASSOC))
1973 {
1974 $ip_bin = $row['rsip'];
1975 if (!isset ($ret[$ip_bin]))
1976 $ret[$ip_bin] = constructIPAddress ($ip_bin);
1977 $ret[$ip_bin]['rsplist'][] = $row['rspool_id'];
1978 }
1979 unset ($result);
1980
1981 // 5. add NAT rules, remote ip
1982 $query =
1983 "select " .
1984 "proto, " .
1985 "localip, " .
1986 "localport, " .
1987 "remoteip, " .
1988 "remoteport, " .
1989 "description " .
1990 "from IPv4NAT " .
1991 "where ${whereexpr5a} " .
1992 "order by localip, localport, remoteip, remoteport, proto";
1993 $result = usePreparedSelectBlade ($query, $qparams);
1994 while ($row = $result->fetch (PDO::FETCH_ASSOC))
1995 {
1996 $ip_bin_local = ip4_int2bin ($row['localip']);
1997 $ip_bin_remote = ip4_int2bin ($row['remoteip']);
1998 $row['localip_bin'] = $ip_bin_local;
1999 $row['remoteip_bin'] = $ip_bin_remote;
2000 $row['localip'] = ip_format ($ip_bin_local);
2001 $row['remoteip'] = ip_format ($ip_bin_remote);
2002 if (!isset ($ret[$ip_bin_remote]))
2003 $ret[$ip_bin_remote] = constructIPAddress ($ip_bin_remote);
2004 $ret[$ip_bin_remote]['inpf'][] = $row;
2005 }
2006 unset ($result);
2007 // 5. add NAT rules, local ip
2008 $query =
2009 "select " .
2010 "proto, " .
2011 "localip, " .
2012 "localport, " .
2013 "remoteip, " .
2014 "remoteport, " .
2015 "description " .
2016 "from IPv4NAT " .
2017 "where ${whereexpr5b} " .
2018 "order by localip, localport, remoteip, remoteport, proto";
2019 $result = usePreparedSelectBlade ($query, $qparams);
2020 while ($row = $result->fetch (PDO::FETCH_ASSOC))
2021 {
2022 $ip_bin_local = ip4_int2bin ($row['localip']);
2023 $ip_bin_remote = ip4_int2bin ($row['remoteip']);
2024 $row['localip_bin'] = $ip_bin_local;
2025 $row['remoteip_bin'] = $ip_bin_remote;
2026 $row['localip'] = ip_format ($ip_bin_local);
2027 $row['remoteip'] = ip_format ($ip_bin_remote);
2028 if (!isset ($ret[$ip_bin_local]))
2029 $ret[$ip_bin_local] = constructIPAddress ($ip_bin_local);
2030 $ret[$ip_bin_local]['outpf'][] = $row;
2031 }
2032 unset ($result);
2033 // 6. collect last log message
2034 $query = "select l.ip, l.user, UNIX_TIMESTAMP(l.date) AS time from IPv4Log l INNER JOIN " .
2035 " (SELECT MAX(id) as id FROM IPv4Log GROUP BY ip) v USING (id) WHERE ${whereexpr6}";
2036 $result = usePreparedSelectBlade ($query, $qparams);
2037 while ($row = $result->fetch (PDO::FETCH_ASSOC))
2038 {
2039 $ip_bin = ip4_int2bin ($row['ip']);
2040 if (isset ($ret[$ip_bin]))
2041 $ret[$ip_bin]['last_log'] = array
2042 (
2043 'user' => $row['user'],
2044 'time' => $row['time'],
2045 );
2046 }
2047 unset ($result);
2048
2049 return $ret;
2050 }
2051
2052 // Check the range requested for meaningful IPv6 records, build them
2053 // into a list and return. Return an empty list if nothing matched.
2054 // Both arguments are expected as 16-byte binary IPs. The resulting list
2055 // is keyed by 16-byte bynary IPs, items aren't sorted.
2056 function scanIPv6Space ($pairlist)
2057 {
2058 $ret = array();
2059 if (!count ($pairlist)) // this is normal for a network completely divided into smaller parts
2060 return $ret;
2061
2062 $or = '';
2063 $whereexpr1 = '(';
2064 $whereexpr2 = '(';
2065 $whereexpr3 = '(';
2066 $whereexpr4 = '(';
2067 $whereexpr6 = '(';
2068 $qparams = array();
2069 foreach ($pairlist as $tmp)
2070 {
2071 $whereexpr1 .= $or . "ip between ? and ?";
2072 $whereexpr2 .= $or . "ip between ? and ?";
2073 $whereexpr3 .= $or . "vip between ? and ?";
2074 $whereexpr4 .= $or . "rsip between ? and ?";
2075 $whereexpr6 .= $or . "l.ip between ? and ?";
2076 $or = ' or ';
2077 $qparams[] = $tmp['first'];
2078 $qparams[] = $tmp['last'];
2079 }
2080 $whereexpr1 .= ')';
2081 $whereexpr2 .= ')';
2082 $whereexpr3 .= ')';
2083 $whereexpr4 .= ')';
2084 $whereexpr6 .= ')';
2085
2086 // 1. collect labels and reservations
2087 $query = "select ip, name, comment, reserved from IPv6Address ".
2088 "where ${whereexpr1} and (reserved = 'yes' or name != '' or comment != '')";
2089 $result = usePreparedSelectBlade ($query, $qparams);
2090 while ($row = $result->fetch (PDO::FETCH_ASSOC))
2091 {
2092 $ip_bin = $row['ip'];
2093 if (!isset ($ret[$ip_bin]))
2094 $ret[$ip_bin] = constructIPAddress ($ip_bin);
2095 $ret[$ip_bin]['name'] = $row['name'];
2096 $ret[$ip_bin]['comment'] = $row['comment'];
2097 $ret[$ip_bin]['reserved'] = $row['reserved'];
2098 }
2099 unset ($result);
2100
2101 // 2. check for allocations
2102 $query =
2103 "select ip, object_id, name, type " .
2104 "from IPv6Allocation where ${whereexpr2} order by type";
2105 $result = usePreparedSelectBlade ($query, $qparams);
2106 // release DBX early to avoid issues with nested spotEntity() calls
2107 $allRows = $result->fetchAll (PDO::FETCH_ASSOC);
2108 unset ($result);
2109 foreach ($allRows as $row)
2110 {
2111 $ip_bin = $row['ip'];
2112 if (!isset ($ret[$ip_bin]))
2113 $ret[$ip_bin] = constructIPAddress ($ip_bin);
2114 $oinfo = spotEntity ('object', $row['object_id']);
2115 $ret[$ip_bin]['allocs'][] = array
2116 (
2117 'type' => $row['type'],
2118 'name' => $row['name'],
2119 'object_id' => $row['object_id'],
2120 'object_name' => $oinfo['dname'],
2121 );
2122 }
2123
2124 // 3. look for virtual services
2125 $query = "select id, vip from IPv4VS where ${whereexpr3}";
2126 $result = usePreparedSelectBlade ($query, $qparams);
2127 $allRows = $result->fetchAll (PDO::FETCH_ASSOC);
2128 unset ($result);
2129 foreach ($allRows as $row)
2130 {
2131 $ip_bin = $row['vip'];
2132 if (!isset ($ret[$ip_bin]))
2133 $ret[$ip_bin] = constructIPAddress ($ip_bin);
2134 $ret[$ip_bin]['vslist'][] = $row['id'];
2135 }
2136
2137 // 4. don't forget about real servers along with pools
2138 $query = "select rsip, rspool_id from IPv4RS where ${whereexpr4}";
2139 $result = usePreparedSelectBlade ($query, $qparams);
2140 while ($row = $result->fetch (PDO::FETCH_ASSOC))
2141 {
2142 $ip_bin = $row['rsip'];
2143 if (!isset ($ret[$ip_bin]))
2144 $ret[$ip_bin] = constructIPAddress ($ip_bin);
2145 $ret[$ip_bin]['rsplist'][] = $row['rspool_id'];
2146 }
2147 unset ($result);
2148
2149 // 6. collect last log message
2150 $query = "select l.ip, l.user, UNIX_TIMESTAMP(l.date) AS time from IPv6Log l INNER JOIN " .
2151 " (SELECT MAX(id) as id FROM IPv6Log GROUP BY ip) v USING (id) WHERE ${whereexpr6}";
2152 $result = usePreparedSelectBlade ($query, $qparams);
2153 while ($row = $result->fetch (PDO::FETCH_ASSOC))
2154 {
2155 $ip_bin = $row['ip'];
2156 if (isset ($ret[$ip_bin]))
2157 $ret[$ip_bin]['last_log'] = array
2158 (
2159 'user' => $row['user'],
2160 'time' => $row['time'],
2161 );
2162 }
2163 unset ($result);
2164 return $ret;
2165 }
2166
2167 function bindIPToObject ($ip_bin, $object_id = 0, $name = '', $type = '')
2168 {
2169 switch (strlen ($ip_bin))
2170 {
2171 case 4:
2172 $db_ip = ip4_bin2db ($ip_bin);
2173 $table = 'IPv4Allocation';
2174 $table2 = 'IPv4Address';
2175 break;
2176 case 16:
2177 $db_ip = $ip_bin;
2178 $table = 'IPv6Allocation';
2179 $table2 = 'IPv6Address';
2180 break;
2181 default: throw new InvalidArgException ('ip_bin', $ip_bin, "Invalid binary IP");
2182 }
2183
2184 // release IP reservation and/or comment if configured
2185 $release = getConfigVar ('IPV4_AUTO_RELEASE');
2186 if ($release >= 2)
2187 usePreparedExecuteBlade ("DELETE FROM $table2 WHERE ip = ?", array ($db_ip));
2188 elseif ($release >= 1)
2189 usePreparedExecuteBlade ("UPDATE $table2 SET reserved = 'no' WHERE ip = ?", array ($db_ip));
2190
2191 usePreparedInsertBlade
2192 (
2193 $table,
2194 array ('ip' => $db_ip, 'object_id' => $object_id, 'name' => $name, 'type' => $type)
2195 );
2196 // store history line
2197 $cell = spotEntity ('object', $object_id);
2198 setDisplayedName ($cell);
2199 addIPLogEntry ($ip_bin, "Binded with ${cell['dname']}, ifname=$name");
2200 }
2201
2202 function bindIPv4ToObject ($ip_bin, $object_id = 0, $name = '', $type = '')
2203 {
2204 if (strlen ($ip_bin) != 4)
2205 throw new InvalidArgException ('ip_bin', $ip_bin, "Invalid binary IP");
2206 return bindIPToObject ($ip_bin, $object_id, $name, $type);
2207 }
2208
2209 function bindIPv6ToObject ($ip_bin, $object_id = 0, $name = '', $type = '')
2210 {
2211 if (strlen ($ip_bin) != 16)
2212 throw new InvalidArgException ('ip_bin', $ip_bin, "Invalid binary IP");
2213 return bindIPToObject ($ip_bin, $object_id, $name, $type);
2214 }
2215
2216 // Universal v4/v6 wrapper around getIPv4AddressNetworkId and getIPv6AddressNetworkId.
2217 // Return the id of the smallest IP network containing the given IP address
2218 // or NULL, if nothing was found. When finding the covering network for
2219 // another network, it is important to filter out matched records with longer
2220 // masks (they aren't going to be the right pick).
2221 function getIPAddressNetworkId ($ip_bin, $masklen = NULL)
2222 {
2223 switch (strlen ($ip_bin))
2224 {
2225 case 4: return getIPv4AddressNetworkId ($ip_bin, isset ($masklen) ? $masklen : 32);
2226 case 16: return getIPv6AddressNetworkId ($ip_bin, isset ($masklen) ? $masklen : 128);
2227 default: throw new InvalidArgException ('ip_bin', $ip_bin, "Invalid binary IP");
2228 }
2229 }
2230
2231 // Returns ipv4net or ipv6net entity, or NULL if no spanning network found.
2232 // Throws an exception if $ip_bin is not valid binary address;
2233 function spotNetworkByIP ($ip_bin, $masklen = NULL)
2234 {
2235 $net_id = getIPAddressNetworkId ($ip_bin, $masklen);
2236 if (! $net_id)
2237 return NULL;
2238 switch (strlen ($ip_bin))
2239 {
2240 case 4: return spotEntity ('ipv4net', $net_id);
2241 case 16: return spotEntity ('ipv6net', $net_id);
2242 default: throw new InvalidArgException ('ip_bin', $ip_bin, "Invalid binary IP");
2243 }
2244 }
2245
2246 // Return the id of the smallest IPv4 network containing the given IPv4 address
2247 // or NULL, if nothing was found. When finding the covering network for
2248 // another network, it is important to filter out matched records with longer
2249 // masks (they aren't going to be the right pick).
2250 function getIPv4AddressNetworkId ($ip_bin, $masklen = 32)
2251 {
2252 if ($row = callHook ('fetchIPv4AddressNetworkRow', $ip_bin, $masklen))
2253 return $row['id'];
2254 return NULL;
2255 }
2256
2257 function fetchIPAddressNetworkRow ($ip_bin, $masklen = NULL)
2258 {
2259 switch (strlen ($ip_bin))
2260 {
2261 case 4:
2262 return callHook ('fetchIPv4AddressNetworkRow', $ip_bin, isset ($masklen) ? $masklen : 32);
2263 case 16:
2264 return callHook ('fetchIPv6AddressNetworkRow', $ip_bin, isset ($masklen) ? $masklen : 128);
2265 default:
2266 throw new InvalidArgException ('ip_bin', $ip_bin, "Invalid binary address");
2267 }
2268 }
2269
2270 function fetchIPv4AddressNetworkRow ($ip_bin, $masklen = 32)
2271 {
2272 $query = 'select * from IPv4Network where ' .
2273 "? & (4294967295 >> (32 - mask)) << (32 - mask) = ip " .
2274 "and mask < ? " .
2275 'order by mask desc limit 1';
2276 $result = usePreparedSelectBlade ($query, array (ip4_bin2db ($ip_bin), $masklen));
2277 if ($row = $result->fetch (PDO::FETCH_ASSOC))
2278 return $row;
2279 return NULL;
2280 }
2281
2282 // Return the id of the smallest IPv6 network containing the given IPv6 address
2283 // ($ip is an instance of IPv4Address class) or NULL, if nothing was found.
2284 function getIPv6AddressNetworkId ($ip_bin, $masklen = 128)
2285 {
2286 if ($row = callHook ('fetchIPv6AddressNetworkRow', $ip_bin, $masklen))
2287 return $row['id'];
2288 return NULL;
2289 }
2290
2291 function fetchIPv6AddressNetworkRow ($ip_bin, $masklen = 128)
2292 {
2293 $query = 'select * from IPv6Network where ip <= ? AND last_ip >= ? and mask < ? order by mask desc limit 1';
2294 $result = usePreparedSelectBlade ($query, array ($ip_bin, $ip_bin, $masklen));
2295 if ($row = $result->fetch (PDO::FETCH_ASSOC))
2296 return $row;
2297 return NULL;
2298 }
2299
2300 // This function is actually used not only to update, but also to create records,
2301 // that's why ON DUPLICATE KEY UPDATE was replaced by DELETE-INSERT pair
2302 // (MySQL 4.0 workaround).
2303 function updateAddress ($ip_bin, $name = '', $reserved = 'no', $comment)
2304 {
2305 switch (strlen ($ip_bin))
2306 {
2307 case 4:
2308 $table = 'IPv4Address';
2309 $db_ip = ip4_bin2db ($ip_bin);
2310 break;
2311 case 16:
2312 $table = 'IPv6Address';
2313 $db_ip = $ip_bin;
2314 break;
2315 default: throw new InvalidArgException ('ip_bin', $ip_bin, "Invalid binary IP");
2316 }
2317
2318 // compute update log message
2319 $result = usePreparedSelectBlade ("SELECT name, comment FROM $table WHERE ip = ?", array ($db_ip));
2320 $old_name = '';
2321 $old_comment = '';
2322 if ($row = $result->fetch (PDO::FETCH_ASSOC))
2323 {
2324 $old_name = $row['name'];
2325 $old_comment = $row['comment'];
2326 }
2327
2328 // If the 'comment' argument was specified when this function was called, use it.
2329 // If not, retain the old value.
2330 $comment = (func_num_args () == 4 ) ? $comment : $old_comment;
2331
2332 unset ($result);
2333 $messages = array ();
2334 if ($name != $old_name)
2335 {
2336 if ($name == '')
2337 $messages[] = "name '$old_name' removed";
2338 elseif ($old_name == '')
2339 $messages[] = "name set to '$name'";
2340 else
2341 $messages[] = "name changed from '$old_name' to '$name'";
2342 }
2343 if ($comment != $old_comment)
2344 {
2345 if ($comment == '')
2346 $messages[] = "comment '$old_comment' removed";
2347 elseif ($old_name == '')
2348 $messages[] = "comment set to '$comment'";
2349 else
2350 $messages[] = "comment changed from '$old_comment' to '$comment'";
2351 }
2352
2353 usePreparedDeleteBlade ($table, array ('ip' => $db_ip));
2354 // INSERT may appear not necessary.
2355 if ($name != '' or $comment != '' or $reserved != 'no')
2356 usePreparedInsertBlade
2357 (
2358 $table,
2359 array ('name' => $name, 'comment' => $comment, 'reserved' => $reserved, 'ip' => $db_ip)
2360 );
2361 // store history line
2362 if ($messages)
2363 addIPLogEntry ($ip_bin, ucfirst (implode (', ', $messages)));
2364 }
2365
2366 function updateV4Address ($ip_bin, $name = '', $reserved = 'no', $comment = '')
2367 {
2368 if (strlen ($ip_bin) != 4)
2369 throw new InvalidArgException ('ip_bin', $ip_bin, "Invalid binary IP");
2370 return updateAddress ($ip_bin, $name, $reserved, $comment);
2371 }
2372
2373 function updateV6Address ($ip_bin, $name = '', $reserved = 'no', $comment = '')
2374 {
2375 if (strlen ($ip_bin) != 16)
2376 throw new InvalidArgException ('ip_bin', $ip_bin, "Invalid binary IP");
2377 return updateAddress ($ip_bin, $name, $reserved, $comment);
2378 }
2379
2380 function updateIPBond ($ip_bin, $object_id=0, $name='', $type='')
2381 {
2382 switch (strlen ($ip_bin))
2383 {
2384 case 4: return updateIPv4Bond ($ip_bin, $object_id, $name, $type);
2385 case 16: return updateIPv6Bond ($ip_bin, $object_id, $name, $type);
2386 default: throw new InvalidArgException ('ip_bin', $ip_bin, "Invalid binary IP");
2387 }
2388 }
2389
2390 function updateIPv4Bond ($ip_bin, $object_id=0, $name='', $type='')
2391 {
2392 usePreparedUpdateBlade
2393 (
2394 'IPv4Allocation',
2395 array
2396 (
2397 'name' => $name,
2398 'type' => $type,
2399 ),
2400 array
2401 (
2402 'ip' => ip4_bin2db ($ip_bin),
2403 'object_id' => $object_id,
2404 )
2405 );
2406 }
2407
2408 function updateIPv6Bond ($ip_bin, $object_id=0, $name='', $type='')
2409 {
2410 usePreparedUpdateBlade
2411 (
2412 'IPv6Allocation',
2413 array
2414 (
2415 'name' => $name,
2416 'type' => $type,
2417 ),
2418 array
2419 (
2420 'ip' => $ip_bin,
2421 'object_id' => $object_id,
2422 )
2423 );
2424 }
2425
2426
2427 function unbindIPFromObject ($ip_bin, $object_id)
2428 {
2429 switch (strlen ($ip_bin))
2430 {
2431 case 4:
2432 $table = 'IPv4Allocation';
2433 $db_ip = ip4_bin2db ($ip_bin);
2434 break;
2435 case 16:
2436 $table = 'IPv6Allocation';
2437 $db_ip = $ip_bin;
2438 break;
2439 default: throw new InvalidArgException ('ip_bin', $ip_bin, "Invalid binary IP");
2440 }
2441
2442 $n_deleted = usePreparedDeleteBlade
2443 (
2444 $table,
2445 array ('ip' => $db_ip, 'object_id' => $object_id)
2446 );
2447 if ($n_deleted)
2448 {
2449 // store history line
2450 $cell = spotEntity ('object', $object_id);
2451 setDisplayedName ($cell);
2452 addIPLogEntry ($ip_bin, "Removed from ${cell['dname']}");
2453 }
2454 }
2455
2456 function unbindIPv4FromObject ($ip_bin, $object_id)
2457 {
2458 if (strlen ($ip_bin) != 4)
2459 throw new InvalidArgException ('ip_bin', $ip_bin, "Invalid binary IP");
2460 return unbindIPFromObject ($ip_bin, $object_id);
2461 }
2462
2463 function unbindIPv6FromObject ($ip_bin, $object_id)
2464 {
2465 if (strlen ($ip_bin) != 16)
2466 throw new InvalidArgException ('ip_bin', $ip_bin, "Invalid binary IP");
2467 return unbindIPFromObject ($ip_bin, $object_id);
2468 }
2469
2470 function getIPv4PrefixSearchResult ($terms)
2471 {
2472 $byname = getSearchResultByField
2473 (
2474 'IPv4Network',
2475 array ('id'),
2476 'name',
2477 $terms,
2478 'ip'
2479 );
2480 $ret = array();
2481 foreach ($byname as $row)
2482 $ret[$row['id']] = spotEntity ('ipv4net', $row['id']);
2483 return $ret;
2484 }
2485
2486 function getIPv6PrefixSearchResult ($terms)
2487 {
2488 $byname = getSearchResultByField
2489 (
2490 'IPv6Network',
2491 array ('id'),
2492 'name',
2493 $terms,
2494 'ip'
2495 );
2496 $ret = array();
2497 foreach ($byname as $row)
2498 $ret[$row['id']] = spotEntity ('ipv6net', $row['id']);
2499 return $ret;
2500 }
2501
2502 function getIPv4AddressSearchResult ($terms)
2503 {
2504 $query = "select ip, name from IPv4Address where ";
2505 $or = '';
2506 $qparams = array();
2507 foreach (explode (' ', $terms) as $term)
2508 {
2509 $query .= $or . "name like ?";
2510 $or = ' or ';
2511 $qparams[] = "%${term}%";
2512 }
2513 $result = usePreparedSelectBlade ($query, $qparams);
2514 $ret = array();
2515 while ($row = $result->fetch (PDO::FETCH_ASSOC))
2516 {
2517 $ip_bin = ip4_int2bin ($row['ip']);
2518 $row['ip'] = $ip_bin;
2519 $ret[$ip_bin] = $row;
2520 }
2521 return $ret;
2522 }
2523
2524 function getIPv6AddressSearchResult ($terms)
2525 {
2526 $query = "select ip, name from IPv6Address where ";
2527 $or = '';
2528 $qparams = array();
2529 foreach (explode (' ', $terms) as $term)
2530 {
2531 $query .= $or . "name like ?";
2532 $or = ' or ';
2533 $qparams[] = "%${term}%";
2534 }
2535 $result = usePreparedSelectBlade ($query, $qparams);
2536 return reindexById ($result->fetchAll (PDO::FETCH_ASSOC), 'ip');
2537 }
2538
2539 function getIPv4RSPoolSearchResult ($terms)
2540 {
2541 $byname = getSearchResultByField
2542 (
2543 'IPv4RSPool',
2544 array ('id'),
2545 'name',
2546 $terms,
2547 'name'
2548 );
2549 $ret = array();
2550 foreach ($byname as $row)
2551 $ret[$row['id']] = spotEntity ('ipv4rspool', $row['id']);
2552 return $ret;
2553 }
2554
2555 function getIPv4VServiceSearchResult ($terms)
2556 {
2557 $byname = getSearchResultByField
2558 (
2559 'IPv4VS',
2560 array ('id'),
2561 'name',
2562 $terms,
2563 'vip'
2564 );
2565 $ret = array();
2566 foreach ($byname as $row)
2567 $ret[$row['id']] = spotEntity ('ipv4vs', $row['id']);
2568 return $ret;
2569 }
2570
2571 function getAccountSearchResult ($terms)
2572 {
2573 $byUsername = getSearchResultByField
2574 (
2575 'UserAccount',
2576 array ('user_id', 'user_name', 'user_realname'),
2577 'user_name',
2578 $terms,
2579 'user_name'
2580 );
2581 $byRealname = getSearchResultByField
2582 (
2583 'UserAccount',
2584 array ('user_id', 'user_name', 'user_realname'),
2585 'user_realname',
2586 $terms,
2587 'user_name'
2588 );
2589 // Merge it together, if duplicates persist, byUsername wins
2590 $ret = array();
2591 foreach (array ($byRealname, $byUsername) as $array)
2592 foreach ($array as $user)
2593 {
2594 $user['realm'] = 'user';
2595 $user['id'] = $user['user_id'];
2596 $ret[$user['user_id']] = $user;
2597 }
2598 return $ret;
2599 }
2600
2601 function getFileSearchResult ($terms)
2602 {
2603 $byName = getSearchResultByField
2604 (
2605 'File',
2606 array ('id'),
2607 'name',
2608 $terms,
2609 'name'
2610 );
2611 $byComment = getSearchResultByField
2612 (
2613 'File',
2614 array ('id'),
2615 'comment',
2616 $terms,
2617 'name'
2618 );
2619 // Filter out dupes.
2620 foreach ($byName as $res1)
2621 foreach (array_keys ($byComment) as $key2)
2622 if ($res1['id'] == $byComment[$key2]['id'])
2623 {
2624 unset ($byComment[$key2]);
2625 continue 2;
2626 }
2627 $ret = array();
2628 foreach (array_merge ($byName, $byComment) as $row)
2629 $ret[$row['id']] = spotEntity ('file', $row['id']);
2630 return $ret;
2631 }
2632
2633 function getRackSearchResult ($terms)
2634 {
2635 $byName = getSearchResultByField
2636 (
2637 'Rack',
2638 array ('id'),
2639 'name',
2640 $terms,
2641 'name'
2642 );
2643 $byComment = getSearchResultByField
2644 (
2645 'Rack',
2646 array ('id'),
2647 'comment',
2648 $terms,
2649 'name'
2650 );
2651 $byAssetNo = getSearchResultByField
2652 (
2653 'Rack',
2654 array ('id'),
2655 'asset_no',
2656 $terms,
2657 'name'
2658 );
2659 $bySticker = getStickerSearchResults ('Rack', $terms);
2660 // Filter out dupes.
2661 foreach ($byName as $res1)
2662 {
2663 foreach (array_keys ($byComment) as $key2)
2664 if ($res1['id'] == $byComment[$key2]['id'])
2665 unset ($byComment[$key2]);
2666 foreach (array_keys ($byAssetNo) as $key3)
2667 if ($res1['id'] == $byAssetNo[$key3]['id'])
2668 unset ($byAssetNo[$key3]);
2669 foreach (array_keys ($bySticker) as $key4)
2670 if ($res1['id'] == $bySticker[$key4]['id'])
2671 unset ($bySticker[$key4]);
2672 }
2673 $ret = array();
2674 foreach (array_merge ($byName, $byComment, $byAssetNo, $bySticker) as $row)
2675 $ret[$row['id']] = spotEntity ('rack', $row['id']);
2676 return $ret;
2677 }
2678
2679 function getLocationSearchResult ($terms)
2680 {
2681 $byName = getSearchResultByField
2682 (
2683 'Location',
2684 array ('id'),
2685 'name',
2686 $terms,
2687 'name'
2688 );
2689 $byComment = getSearchResultByField
2690 (
2691 'Location',
2692 array ('id'),
2693 'comment',
2694 $terms,
2695 'name'
2696 );
2697 $bySticker = getStickerSearchResults ('Location', $terms);
2698 // Filter out dupes.
2699 foreach ($byName as $res1)
2700 {
2701 foreach (array_keys ($byComment) as $key2)
2702 if ($res1['id'] == $byComment[$key2]['id'])
2703 unset ($byComment[$key2]);
2704 foreach (array_keys ($bySticker) as $key3)
2705 if ($res1['id'] == $bySticker[$key3]['id'])
2706 unset ($bySticker[$key3]);
2707 }
2708 $ret = array();
2709 foreach (array_merge ($byName, $byComment, $bySticker) as $location)
2710 $ret[$location['id']] = spotEntity ('location', $location['id']);
2711 return $ret;
2712 }
2713
2714 function getVLANSearchResult ($terms)
2715 {
2716 $ret = array();
2717 $byDescr = getSearchResultByField
2718 (
2719 'VLANDescription',
2720 array ('domain_id', 'vlan_id'),
2721 'vlan_descr',
2722 $terms
2723 );
2724 foreach ($byDescr as $row)
2725 {
2726 $vlan_ck = $row['domain_id'] . '-' . $row['vlan_id'];
2727 $row['id'] = $vlan_ck;
2728 $ret[$vlan_ck] = $row;
2729 }
2730 return $ret;
2731 }
2732
2733 function getSearchResultByField ($tablename, $retcolumns, $scancolumn, $terms, $ordercolumn = '', $exactness = 0)
2734 {
2735 $query = 'SELECT ' . implode (', ', $retcolumns) . " FROM ${tablename} WHERE ";
2736 $qparams = array();
2737 $pfx = '';
2738 foreach (explode (' ', $terms) as $term)
2739 {
2740 switch ($exactness)
2741 {
2742 case 3:
2743 $query .= $pfx . "${scancolumn} REGEXP ?";
2744 $qparams[] = $term;
2745 break;
2746 case 2: // does this work as expected?
2747 $query .= $pfx . "BINARY ${scancolumn} = ?";
2748 $qparams[] = $term;
2749 break;
2750 case 1:
2751 $query .= $pfx . "${scancolumn} = ?";
2752 $qparams[] = $term;
2753 break;
2754 default:
2755 $query .= $pfx . "${scancolumn} LIKE ?";
2756 $qparams[] = "%${term}%";
2757 break;
2758 }
2759 $pfx = ' OR ';
2760 }
2761 if ($ordercolumn != '')
2762 $query .= " ORDER BY ${ordercolumn}";
2763 $result = usePreparedSelectBlade ($query, $qparams);
2764 return $result->fetchAll (PDO::FETCH_ASSOC);
2765 }
2766
2767 function getObjectSearchResults ($what)
2768 {
2769 $ret = array();
2770 global $searchfunc;
2771 foreach ($searchfunc['object'] as $method => $func)
2772 foreach ($func ($what) as $objRecord)
2773 {
2774 $ret[$objRecord['id']]['id'] = $objRecord['id'];
2775 $ret[$objRecord['id']][$method] = $objRecord[$method];
2776 }
2777 foreach (getStickerSearchResults ('RackObject', $what) as $objRecord)
2778 {
2779 $ret[$objRecord['id']]['id'] = $objRecord['id'];
2780 $ret[$objRecord['id']]['by_sticker'] = $objRecord['by_sticker'];
2781 }
2782 return $ret;
2783 }
2784
2785 function getObjectAttrsSearchResults ($what)
2786 {
2787 $ret = array();
2788 foreach (array ('name', 'label', 'asset_no') as $column)
2789 {
2790 $tmp = getSearchResultByField
2791 (
2792 'RackObject',
2793 array ('id'),
2794 $column,
2795 $what,
2796 $column
2797 );
2798 foreach ($tmp as $row)
2799 {
2800 $ret[$row['id']]['id'] = $row['id'];
2801 $ret[$row['id']]['by_attr'][] = $column;
2802 }
2803 }
2804 return $ret;
2805 }
2806
2807 // Search stickers and return a list of pairs "object_id-attribute_id",
2808 // which matched. A partilar object_id could be returned more than once, if it has
2809 // multiple matching stickers. Search is only performed on "string" attributes.
2810 function getStickerSearchResults ($tablename, $what)
2811 {
2812 $result = usePreparedSelectBlade
2813 (
2814 'SELECT object_id, attr_id FROM AttributeValue AV ' .
2815 "INNER JOIN ${tablename} O ON AV.object_id = O.id " .
2816 'WHERE string_value LIKE ? ORDER BY object_id',
2817 array ("%${what}%")
2818 );
2819
2820 $map = getAttrMap();
2821 $ret = array();
2822 while ($row = $result->fetch (PDO::FETCH_ASSOC))
2823 {
2824 if ($map[$row['attr_id']]['type'] == 'string')
2825 {
2826 $ret[$row['object_id']]['id'] = $row['object_id'];
2827 $ret[$row['object_id']]['by_sticker'][] = $row['attr_id'];
2828 }
2829 }
2830 return $ret;
2831 }
2832
2833 // search in port "reservation comment", "label" and "L2 address" columns
2834 function getPortSearchResults ($what)
2835 {
2836 $ret = array();
2837 $ports = getSearchResultByField
2838 (
2839 'Port',
2840 array ('object_id', 'id', 'reservation_comment'),
2841 'reservation_comment',
2842 $what,
2843 'object_id',
2844 0
2845 );
2846 foreach ($ports as $port)
2847 {
2848 $ret[$port['object_id']]['id'] = $port['object_id'];
2849 $ret[$port['object_id']]['by_port'][$port['id']] = $port['reservation_comment'];
2850 }
2851 $ports = getSearchResultByField
2852 (
2853 'Port',
2854 array ('object_id', 'id', 'label'),
2855 'label',
2856 $what,
2857 'object_id',
2858 0
2859 );
2860 foreach ($ports as $port)
2861 {
2862 $ret[$port['object_id']]['id'] = $port['object_id'];
2863 $ret[$port['object_id']]['by_port'][$port['id']] = $port['label'];
2864 }
2865 try
2866 {
2867 $db_l2address = l2addressForDatabase ($what);
2868 }
2869 catch (InvalidArgException $e)
2870 {
2871 return $ret;
2872 }
2873 $ports = getSearchResultByField
2874 (
2875 'Port',
2876 array ('object_id', 'id', 'l2address'),
2877 'l2address',
2878 $db_l2address,
2879 'object_id',
2880 2
2881 );
2882 foreach ($ports as $port)
2883 {
2884 $ret[$port['object_id']]['id'] = $port['object_id'];
2885 $ret[$port['object_id']]['by_port'][$port['id']] = $port['l2address'];
2886 }
2887 return $ret;
2888 }
2889
2890 // search in IPv4 allocations
2891 function getObjectIfacesSearchResults ($what)
2892 {
2893 $ret = array();
2894 $ifaces4 = getSearchResultByField
2895 (
2896 'IPv4Allocation',
2897 array ('object_id', 'name'),
2898 'name',
2899 $what,
2900 'object_id'
2901 );
2902 $ifaces6 = getSearchResultByField
2903 (
2904 'IPv6Allocation',
2905 array ('object_id', 'name'),
2906 'name',
2907 $what,
2908 'object_id'
2909 );
2910 foreach (array_merge ($ifaces4, $ifaces6) as $row)
2911 {
2912 $ret[$row['object_id']]['id'] = $row['object_id'];
2913 $ret[$row['object_id']]['by_iface'][] = $row['name'];
2914 }
2915 return $ret;
2916 }
2917
2918 function getObjectNATSearchResults ($what)
2919 {
2920 $ret = array();
2921 $ifaces = getSearchResultByField
2922 (
2923 'IPv4NAT',
2924 array ('object_id', 'description'),
2925 'description',
2926 $what,
2927 'object_id'
2928 );
2929 foreach ($ifaces as $row)
2930 {
2931 $ret[$row['object_id']]['id'] = $row['object_id'];
2932 $ret[$row['object_id']]['by_nat'][] = $row['description'];
2933 }
2934 return $ret;
2935 }
2936
2937 function searchCableIDs ($what)
2938 {
2939 $ret = array();
2940 $result = usePreparedSelectBlade
2941 (
2942 'SELECT object_id, cable ' .
2943 'FROM Link INNER JOIN Port ON porta = Port.id OR portb = Port.id ' .
2944 'WHERE cable LIKE ? ORDER BY object_id',
2945 array ("%${what}%")
2946 );
2947 while ($row = $result->fetch (PDO::FETCH_ASSOC))
2948 {
2949 $ret[$row['object_id']]['id'] = $row['object_id'];
2950 $ret[$row['object_id']]['by_cableid'][] = $row['cable'];
2951 }
2952 return $ret;
2953 }
2954
2955 // This function returns either port ID or NULL for specified arguments.
2956 function getPortIDs ($object_id, $port_name)
2957 {
2958 $result = usePreparedSelectBlade ('SELECT id FROM Port WHERE object_id = ? AND name = ?', array ($object_id, $port_name));
2959 return reduceSubarraysToColumn ($result->fetchAll (PDO::FETCH_ASSOC), 'id');
2960 }
2961
2962 // Search in "FQDN" attribute only, and return object ID, when there is exactly
2963 // one result found (and NULL in any other case).
2964 function searchByMgmtHostname ($string)
2965 {
2966 $result = usePreparedSelectBlade ('SELECT object_id FROM AttributeValue WHERE attr_id = 3 AND string_value = ? LIMIT 2', array ($string));
2967 $rows = $result->fetchAll (PDO::FETCH_NUM);
2968 if (count ($rows) == 1)
2969 return $rows[0][0];
2970 unset ($result);
2971
2972 // second attempt: search for FQDN part, separated by dot.
2973 $result = usePreparedSelectBlade ('SELECT object_id FROM AttributeValue WHERE attr_id = 3 AND string_value LIKE ? LIMIT 2', array ("$string.%"));
2974 $rows = $result->fetchAll (PDO::FETCH_NUM);
2975 if (count ($rows) != 1)
2976 return NULL;
2977 return $rows[0][0];
2978 }
2979
2980 // returns an array of object ids
2981 function searchByAttrValue ($attr_id, $value)
2982 {
2983 $map = getAttrMap();
2984 if (! isset ($map[$attr_id]))
2985 throw new InvalidArgException ('attr_id', $attr_id, "No such attribute");
2986
2987 $field = NULL;
2988 switch ($map[$attr_id]['type'])
2989 {
2990 case 'string':
2991 $field = 'string_value';
2992 break;
2993 case 'float':
2994 $field = 'float_value';
2995 break;
2996 case 'uint':
2997 case 'dict':
2998 case 'date':
2999 $field = 'uint_value';
3000 break;
3001 default:
3002 throw new InvalidArgException ('type', $map[$attr_id]['type']);
3003 }
3004
3005 $result = usePreparedSelectBlade ("
3006 SELECT object_id FROM AttributeValue
3007 WHERE
3008 attr_id = ?
3009 AND $field = ?
3010 ", array ($attr_id, $value)
3011 );
3012 return $result->fetchAll (PDO::FETCH_COLUMN, 0);
3013 }
3014
3015
3016 // returns user_id
3017 // throws an exception if error occured
3018 function commitCreateUserAccount ($username, $realname, $password)
3019 {
3020 usePreparedInsertBlade
3021 (
3022 'UserAccount',
3023 array
3024 (
3025 'user_name' => $username,
3026 'user_realname' => $realname == '' ? NULL : $realname,
3027 'user_password_hash' => $password,
3028 )
3029 );
3030 return lastInsertID();
3031 }
3032
3033 function commitUpdateUserAccount ($id, $new_username, $new_realname, $new_password)
3034 {
3035 usePreparedUpdateBlade
3036 (
3037 'UserAccount',
3038 array
3039 (
3040 'user_name' => $new_username,
3041 'user_realname' => $new_realname == '' ? NULL : $new_realname,
3042 'user_password_hash' => $new_password,
3043 ),
3044 array ('user_id' => $id)
3045 );
3046 }
3047
3048 // This function returns an array of all port type pairs from PortCompat table.
3049 function getPortOIFCompat ($ignore_cache = FALSE)
3050 {
3051 static $cache = NULL;
3052 if (! $ignore_cache && isset ($cache))
3053 return $cache;
3054
3055 $query =
3056 "select type1, type2, d1.dict_value as type1name, d2.dict_value as type2name from " .
3057 "PortCompat as pc inner join Dictionary as d1 on pc.type1 = d1.dict_key " .
3058 "inner join Dictionary as d2 on pc.type2 = d2.dict_key " .
3059 'ORDER BY type1name, type2name';
3060 $result = usePreparedSelectBlade ($query);
3061 $cache = $result->fetchAll (PDO::FETCH_ASSOC);
3062 return $cache;
3063 }
3064
3065 // Returns an array of all object type pairs from the ObjectParentCompat table.
3066 function getObjectParentCompat ()
3067 {
3068 $result = usePreparedSelectBlade
3069 (
3070 'SELECT parent_objtype_id, child_objtype_id, d1.dict_value AS parent_name, d2.dict_value AS child_name, ' .
3071 '(SELECT COUNT(*) FROM EntityLink EL ' .
3072 "LEFT JOIN Object PO ON (EL.parent_entity_id = PO.id AND EL.parent_entity_type = 'object') " .
3073 "LEFT JOIN Object CO ON (EL.child_entity_id = CO.id AND EL.child_entity_type = 'object') " .
3074 'WHERE PO.objtype_id = parent_objtype_id AND CO.objtype_id = child_objtype_id) AS count ' .
3075 'FROM ObjectParentCompat AS pc INNER JOIN Dictionary AS d1 ON pc.parent_objtype_id = d1.dict_key ' .
3076 'INNER JOIN Dictionary AS d2 ON pc.child_objtype_id = d2.dict_key ' .
3077 'ORDER BY parent_name, child_name'
3078 );
3079 return $result->fetchAll (PDO::FETCH_ASSOC);
3080 }
3081
3082 // Used to determine if a type of object may have a parent or not
3083 function objectTypeMayHaveParent ($objtype_id)
3084 {
3085 $result = usePreparedSelectBlade ('SELECT COUNT(*) FROM ObjectParentCompat WHERE child_objtype_id = ?', array ($objtype_id));
3086 $row = $result->fetch (PDO::FETCH_NUM);
3087 if ($row[0] > 0)
3088 return TRUE;
3089 return FALSE;
3090 }
3091
3092 // Add a pair to the ObjectParentCompat table.
3093 function commitSupplementOPC ($parent_objtype_id, $child_objtype_id)
3094 {
3095 if ($parent_objtype_id <= 0)
3096 throw new InvalidArgException ('parent_objtype_id', $parent_objtype_id);
3097 if ($child_objtype_id <= 0)
3098 throw new InvalidArgException ('child_objtype_id', $child_objtype_id);
3099 usePreparedInsertBlade
3100 (
3101 'ObjectParentCompat',
3102 array ('parent_objtype_id' => $parent_objtype_id, 'child_objtype_id' => $child_objtype_id)
3103 );
3104 }
3105
3106 // Remove a pair from the ObjectParentCompat table.
3107 function commitReduceOPC ($parent_objtype_id, $child_objtype_id)
3108 {
3109 usePreparedDeleteBlade ('ObjectParentCompat', array ('parent_objtype_id' => $parent_objtype_id, 'child_objtype_id' => $child_objtype_id));
3110 }
3111
3112 function getDictStats ()
3113 {
3114 $result = usePreparedSelectBlade
3115 (
3116 "select Chapter.id as chapter_no, Chapter.name as chapter_name, count(dict_key) as wc from " .
3117 "Chapter left join Dictionary on Chapter.id = Dictionary.chapter_id group by Chapter.id"
3118 );
3119 $tc = $tw = $uc = $uw = 0;
3120 while ($row = $result->fetch (PDO::FETCH_ASSOC))
3121 {
3122 $tc++;
3123 $tw += $row['wc'];;
3124 if ($row['chapter_no'] < 10000)
3125 continue;
3126 $uc++;
3127 $uw += $row['wc'];;
3128 }
3129 unset ($result);
3130 $result = usePreparedSelectBlade
3131 (
3132 "select count(object_id) as attrc from RackObject as ro left join " .
3133 "AttributeValue as av on ro.id = av.object_id group by ro.id"
3134 );
3135 $to = $ta = $so = 0;
3136 while ($row = $result->fetch (PDO::FETCH_ASSOC))
3137 {
3138 $to++;
3139 if ($row['attrc'] != 0)
3140 {
3141 $so++;
3142 $ta += $row['attrc'];
3143 }
3144 }
3145 unset ($result);
3146 $ret = array();
3147 $ret['Total chapters in dictionary'] = $tc;
3148 $ret['Total words in dictionary'] = $tw;
3149 $ret['User chapters'] = $uc;
3150 $ret['Words in user chapters'] = $uw;
3151 $ret['Total objects'] = $to;
3152 $ret['Objects with stickers'] = $so;
3153 $ret['Total stickers attached'] = $ta;
3154 return $ret;
3155 }
3156
3157 function getIPv4Stats ()
3158 {
3159 $ret = array();
3160 $subject = array();
3161 $subject[] = array ('q' => 'select count(id) from IPv4Network', 'txt' => 'Networks');
3162 $subject[] = array ('q' => 'select count(ip) from IPv4Address', 'txt' => 'Addresses commented/reserved');
3163 $subject[] = array ('q' => 'select count(ip) from IPv4Allocation', 'txt' => 'Addresses allocated');
3164 $subject[] = array ('q' => 'select count(*) from IPv4NAT', 'txt' => 'NAT rules');
3165 $subject[] = array ('q' => 'select count(id) from IPv4VS', 'txt' => 'Virtual services');
3166 $subject[] = array ('q' => 'select count(id) from IPv4RSPool', 'txt' => 'Real server pools');
3167 $subject[] = array ('q' => 'select count(id) from IPv4RS', 'txt' => 'Real servers');
3168 $subject[] = array ('q' => 'select count(distinct object_id) from IPv4LB', 'txt' => 'Load balancers');
3169
3170 foreach ($subject as $item)
3171 {
3172 $result = usePreparedSelectBlade ($item['q']);
3173 $row = $result->fetch (PDO::FETCH_NUM);
3174 $ret[$item['txt']] = $row[0];
3175 unset ($result);
3176 }
3177 return $ret;
3178 }
3179
3180 function getIPv6Stats ()
3181 {
3182 $ret = array();
3183 $subject = array();
3184 $subject[] = array ('q' => 'select count(id) from IPv6Network', 'txt' => 'Networks');
3185 $subject[] = array ('q' => 'select count(ip) from IPv6Address', 'txt' => 'Addresses commented/reserved');
3186 $subject[] = array ('q' => 'select count(ip) from IPv6Allocation', 'txt' => 'Addresses allocated');
3187
3188 foreach ($subject as $item)
3189 {
3190 $result = usePreparedSelectBlade ($item['q']);
3191 $row = $result->fetch (PDO::FETCH_NUM);
3192 $ret[$item['txt']] = $row[0];
3193 unset ($result);
3194 }
3195 return $ret;
3196 }
3197
3198 function getRackspaceStats ()
3199 {
3200 $ret = array();