demo: remove support for versions 0.16.x~0.19.x
[racktables-contribs] / facter / facter.php
CommitLineData
38ba29a5
AD
1<?php
2/*
8b63b6a6 3 * Facter (Puppet) plugin for racktables 0.20.1 (and probably newer)
1806a4df
DO
4*
5* This file is both a web GUI and REST webservice for auto create and update machines based on facter (puppet) files.
6*
8b63b6a6
DK
7* REST example:
8* curl -F "userfile=@/root/facter.txt" -u username:password "http://racktables/index.php?module=redirect&page=depot&tab=facter&op=Update"
1806a4df
DO
9*
10* Usage instructions:
8b63b6a6
DK
11* Add to plugins folder along with manifest file
12* To get VMs to auto add you have to create a facter function to return a list like:
13* export FACTER_VMs=$(virsh list | awk '$3 == "running" {printf $2","}' | sed -e 's/,$//');
14* Whatever you use for VMs it should return a list like: vms => vm1,vm2
15*
1806a4df
DO
16*
17* Author: Torstein Hansen <huleboer@users.sourceforge.net>, sponsored by eniro.no
18*
19* This script is based on yaml_import for racktables by Tommy Botten Jensen
20*
21* 2011-08-25 modified by Neil Scholten <neil.scholten@gamigo.com>
22* - adjusted path for racktables > 0.19.1
23* - modified .yaml parsing to match to 'facter -py' format
24* - modified interface-type detection to use virtual port on VMs
25* - modified OS detection to match more better default sets (Testcase: CentOS).
8b63b6a6
DK
26*
27* 2012-12-13 modified by Daniel Kasen <djtech@gmail.com>
28* - added generic looping to easially add fields
29* - Corrected issues with VM's breaking script
30* - reverted .yaml parsing due to strings in facter not parsing right for mem. options
31* - added error checking to ignore unusable lines in manifest file
32* - fixed ip additions for 20.1
33* - added VM auto adding to Parent
1806a4df 34*
1ae6267d 35* 2015-02-10 modified by Gjermund Jensvoll <gjerjens@gmail.com>
36* - RackTables >= 0.20.8 compatability (Fixing error "Argument 'port_type_id' of value NULL is invalid (format error)")
37*
1806a4df 38*/
38ba29a5
AD
39
40
41// Depot Tab for objects.
42$tab['depot']['facter'] = 'Facter';
43$tabhandler['depot']['facter'] = 'ViewHTML';
44$ophandler['depot']['facter']['Update'] = 'Update';
45
38ba29a5
AD
46// The ophandler to insert objects (if any)
47function Update()
48{
1806a4df
DO
49 // Read uploaded file
50 $lines = file($_FILES['userfile']['tmp_name']);
51
52 // add file contents to facter array
53 foreach ($lines as $line_num => $line)
54 {
8b63b6a6 55 $tmpfacter=explode("=>",$line,2);
1806a4df
DO
56 $facter[trim($tmpfacter[0])]=str_replace('"', '',trim($tmpfacter[1]));
57 }
58
59 // Fix fqdn since all fields have \n inn them
60 $facter['fqdn']=str_replace("\n","", $facter['fqdn']);
61
62 // Check if it's an existing machine
5d5dbda0
DO
63 // 2011-08-31 <neil.scholten@gamigo.com>
64 // * expanded query to try to match via facter Serialnumber to be able to
65 // match unnamed HW assets. Serial is more precise and less likely to be changed.
66 if (
67 array_key_exists('serialnumber', $facter) &&
68 strlen($facter['serialnumber']) > 0 &&
69 $facter['serialnumber'] != 'Not Specified' ) {
70 $query = "select id from RackObject where name = \"$facter[fqdn]\" OR asset_no = \"$facter[serialnumber]\" LIMIT 1";
71 } else {
72 $query = "select id from RackObject where name = \"$facter[fqdn]\" LIMIT 1";
73 }
1806a4df
DO
74 unset($result);
75 $result = usePreparedSelectBlade ($query);
76 $resultarray = $result->fetchAll (PDO::FETCH_ASSOC);
77 if($resultarray) {
78 $id=$resultarray[0]['id'];
79 }
1806a4df
DO
80 // If it's a new machine
81 if (! isset($id))
82 {
83 // Check to see if it's a physical machine and get the correct id for Server
5d5dbda0 84 if ($facter['is_virtual']=="false")
1806a4df
DO
85 {
86 // Find server id
87 $query = "select dict_key from Dictionary where dict_value='Server' LIMIT 1";
88 unset($result);
89 $result = usePreparedSelectBlade ($query);
90 $resultarray = $result->fetchAll ();
91 if($resultarray)
92 {
93 $virtual=$resultarray[0]['dict_key'];
94 }
95 }
96 // Check to see if it's a virtual machine and get the correct id for VM
97 else
98 {
99 // Find virtual id
100 $query = "select dict_key from Dictionary where dict_value='VM' LIMIT 1";
101 unset($result);
102 $result = usePreparedSelectBlade ($query);
103 $resultarray = $result->fetchAll (PDO::FETCH_ASSOC);
104 if($resultarray)
105 {
106 $virtual=$resultarray[0]['dict_key'];
107 }
108 }
109 // Add the new machine
8b63b6a6
DK
110 $newmachine=commitAddObject($facter['fqdn'],"",$virtual,$value = "");
111 $type_id = getObjectTypeID($newmachine);
1806a4df
DO
112 }
113 // If it's an existing machine
114 else
115 {
116 // Just set some fields I use later down for updating
117 $newmachine=$id;
118 $machineupdate=1;
8b63b6a6 119 $type_id = getObjectTypeID($newmachine);
1806a4df
DO
120 }
121
122
5d5dbda0
DO
123 // 2011-08-31 <neil.scholten@gamigo.com>
124 // * Update (unique) name of object.
125 if (
126 array_key_exists('serialnumber', $facter) &&
127 strlen($facter['serialnumber']) > 0 &&
128 $facter['serialnumber'] != 'Not Specified' ) {
5d5dbda0
DO
129 unset($result);
130 $query = "select * from RackObject where asset_no = \"$facter[serialnumber]\" LIMIT 1";
131 $result = usePreparedSelectBlade ($query);
132 $resultarray = $result->fetchAll (PDO::FETCH_ASSOC);
133 if($resultarray) {
134 $id = $resultarray[0]['id'];
135 $label = $resultarray[0]['label'];
136 // Update FQDN
137 commitUpdateObject($id, $facter['fqdn'], $label, 'no', $facter['serialnumber'], 'Facter Import::Update Common Name');
138 }
139 }
140
1806a4df 141 // Find HW type id
5d5dbda0
DO
142 // 2011-08-31 <neil.scholten@gamigo.com>
143 // * adjust format to match default Dictionary Sets
8b63b6a6
DK
144 if ($facter['is_virtual']=="false")
145 {
146 $iHWTemp = preg_match('([a-zA-Z]{1,})', $facter['manufacturer'], $matches);
147 $sManufacturer = $matches[0];
148 $sHW = preg_replace('(\ )', '\1%GPASS%', $facter['productname']);
149 $sHWType = $sManufacturer.' '.$sHW;
150 $query = "select id from Attribute where name='HW type' LIMIT 1";
151 unset($result);
152 $result = usePreparedSelectBlade ($query);
153 $resultarray = $result->fetchAll (PDO::FETCH_ASSOC);
154 if($resultarray) {
155 $id=$resultarray[0]['id'];
156 // Update HW type
157 $hw_dict_key = getdict($sHWType, $chapter=11 );
158 commitUpdateAttrValue ($object_id = $newmachine, $attr_id = $id, $value = $hw_dict_key);
159 }
160 //Also Check if HYPERVISOR
161 if (isset($facter['is_hypervisor']))
162 {
163 $query = "select id from Attribute where name REGEXP '^ *Hypervisor Type$' LIMIT 1";
164 unset($result);
165 $result = usePreparedSelectBlade ($query);
166 $resultarray = $result->fetchAll (PDO::FETCH_ASSOC);
167 if($resultarray) {
168 $id=$resultarray[0]['id'];
169 // Update Hypervisor type
170 $hypervisor_type = $facter['is_hypervisor'];
171 $hypervisor_type_dict_key = getdict($hw=$hypervisor_type, $chapter=10005);
172 commitUpdateAttrValue ($object_id = $newmachine, $attr_id = $id, $value = $hypervisor_type_dict_key);
173 }
174 //Set Value to Yes
175 $query = "select id from Attribute where name REGEXP '^ *Hypervisor' LIMIT 1";
176 unset($result);
177 $result = usePreparedSelectBlade ($query);
178 $resultarray = $result->fetchAll (PDO::FETCH_ASSOC);
179 if($resultarray) {
180 $id=$resultarray[0]['id'];
181 // Update Hypervisor type
182 $hypervisor = "Yes";
183 $hypervisor_dict_key = getdict($hypervisor, $chapter=29);
184 commitUpdateAttrValue ($object_id = $newmachine, $attr_id = $id, $value = $hypervisor_dict_key);
185 }
186 //Find Running VMs
187 $vms = explode(',',$facter['vms']);
188 $vm_count = count($vms);
189 for ($i = 0; $i < $vm_count; $i++) {
190 //addToParent
191 addVmToParent ($vms[$i], $newmachine);
192 }
193 } else {
194 $query = "select id from Attribute where name REGEXP '^ *Hypervisor' LIMIT 1";
195 unset($result);
196 $result = usePreparedSelectBlade ($query);
197 $resultarray = $result->fetchAll (PDO::FETCH_ASSOC);
198 if($resultarray) {
199 $id=$resultarray[0]['id'];
200 // Update Hypervisor type
201 $hypervisor = "No";
202 $hypervisor_dict_key = getdict($hypervisor, $chapter=29);
203 commitUpdateAttrValue ($object_id = $newmachine, $attr_id = $id, $value = "");
204 }
205 }
1806a4df 206 }
8b63b6a6 207
1806a4df 208 // Find SW type id (OS)
8b63b6a6 209 $query = "select id from Attribute where name REGEXP '^ *SW type$' LIMIT 1";
1806a4df
DO
210 unset($result);
211 $result = usePreparedSelectBlade ($query);
212 $resultarray = $result->fetchAll (PDO::FETCH_ASSOC);
213 if($resultarray) {
214 $id=$resultarray[0]['id'];
215 // Update SW type (OS)
8b63b6a6 216 $osrelease = $facter['operatingsystem'] . '%GSKIP%' . $facter['operatingsystem'] . ' V' . $facter['operatingsystemrelease'];
1806a4df
DO
217 $os_dict_key = getdict($hw=$osrelease, $chapter=13);
218 commitUpdateAttrValue ($object_id = $newmachine, $attr_id = $id, $value = $os_dict_key);
219 }
220
8b63b6a6 221 //Generic to read in from file
bc6d25f4 222 $manifest_file = fopen("../plugins/manifest", "r") or die("Could not open manifest, make sure it is in the websrv root and called manifest \n");
8b63b6a6
DK
223 while ( ! feof ($manifest_file)) {
224 $tmp_line = fgets($manifest_file);
225 if (!empty($tmp_line) && !preg_match("/\/\//",$tmp_line)) {
226 @list($Fact, $Attr, $Chapter) = array_map('trim', (explode(',', $tmp_line, 3)));
227 //check for multi-facter names
228 if(strstr($Fact, '.')) {
229 @list($Fact1, $Fact2) = array_map('trim', (explode('.', $Fact)));
230 $value = $facter[$Fact1] .' '. $facter[$Fact2];
231 if(!isset($facter[$Fact1]) || !isset($facter[$Fact2])) {
232 echo "WARNING: $Fact1 or $Fact2 does not exist in Facter for this object \n";
233 continue;
234 }
235 } else {
236 if(!isset($facter[$Fact])) {
237 echo "WARNING: $Fact does not exist in Facter for this object \n";
238 continue;
239 } else {
240 $value = $facter[$Fact];
241 }
242 }
243 $query = "select id from Attribute where name REGEXP '^ *$Attr' LIMIT 1";
244 unset($result);
245 $result = usePreparedSelectBlade ($query);
246 $resultarray = $result->fetchAll (PDO::FETCH_ASSOC);
247 $id=$resultarray[0]['id'];
248 if (!valid($type_id, $id)) {
249 echo "WARNING: Not a valid Mapping for $Fact to $Attr for objectType $type_id \n";
250 }
251 else if($resultarray) {
252 if(!empty($Chapter)) {
253 $name = $value;
254 $name_dict_key = getdict($hw=$name, $chapter=$Chapter);
255 commitUpdateAttrValue ($object_id = $newmachine, $attr_id = $id, $value = $name_dict_key);
256 } else if (preg_match("/[0-9]{1,2}\/[0-9]{1,2}\/[0-9]{4}/",$value) || preg_match("/[0-9]{1,2}\-[0-9]{1,2}\-[0-9]{4}/",$value) || preg_match("/[0-9]{4}\-[0-9]{1,2}\-[0-9]{1,2}/",$value)) {
257 //handle dates
258 commitUpdateAttrValue ($newmachine, $id, strtotime($value) );
259 } else {
260 commitUpdateAttrValue ($newmachine, $id, $value );
261 }
262 }
263 }
1806a4df 264 }
8b63b6a6 265 fclose($manifest_file);
1806a4df
DO
266
267 // Add network interfaces
268
269 // Create an array with interfaces
270 $nics = explode(',',$facter['interfaces']);
271
8b63b6a6 272 // Go through all interfaces and add IP and MAC
1806a4df
DO
273 $count = count($nics);
274 for ($i = 0; $i < $count; $i++) {
275 // Remove newline from the field
276 $nics[$i]=str_replace("\n","", $nics[$i]);
277
278 // We generally don't monitor sit interfaces.
279 // We don't do this for lo interfaces, too
5d5dbda0
DO
280 // 2011-08-31 <neil.scholten@gamigo.com>
281 // * Only Document real interfaces, dont do bridges, bonds, vlan-interfaces
282 // when they have no IP defined.
8b63b6a6 283 if ( preg_match('(_|^(bond|lo|sit|vnet|virbr|veth|peth))',$nics[$i]) != 0 ) {
5d5dbda0
DO
284 // do nothing
285 } else {
286 // Get IP
287 if (isset($facter['ipaddress_' . $nics[$i]]))
288 $ip = $facter['ipaddress_' . $nics[$i]];
289
290 // Get MAC
291 if (isset($facter['macaddress_' . $nics[$i]]))
292 $mac = $facter['macaddress_' . $nics[$i]];
293
294 //check if VM or not
295 if ($facter['is_virtual']=="false")
296 {
297 // Find 1000Base-T id
1ae6267d 298 $query = "select id from PortOuterInterface where oif_name REGEXP '^ *1000Base-T$' LIMIT 1";
1806a4df 299 }
5d5dbda0
DO
300 else
301 {
302 // Find virtual port id
1ae6267d 303 $query = "select id from PortOuterInterface where oif_name REGEXP '^ *virtual port$' LIMIT 1";
5d5dbda0
DO
304 }
305 unset($result);
306 $result = usePreparedSelectBlade ($query);
307 $resultarray = $result->fetchAll (PDO::FETCH_ASSOC);
308 if($resultarray) {
1ae6267d 309 $nictypeid=$resultarray[0]['id'];
5d5dbda0
DO
310 }
311
312 // Remove newline from ip
313 $ip=str_replace("\n","", $ip);
314
315 // Check to se if the interface has an ip assigned
316 $query = "SELECT object_id FROM IPv4Allocation where object_id=$newmachine and name=\"$nics[$i]\"";
317 unset($result);
318 $result = usePreparedSelectBlade ($query);
319 $resultarray = $result->fetchAll (PDO::FETCH_ASSOC);
320
321 if($resultarray) {
322 unset($id);
323 $ipcheck=$resultarray;
324 }
5d5dbda0
DO
325 // Check if it's been configured a port already
326 $query = "SELECT id,iif_id FROM Port where object_id=$newmachine and name=\"$nics[$i]\"";
327 unset($result);
328 $result = usePreparedSelectBlade ($query);
329 $resultarray = $result->fetchAll (PDO::FETCH_ASSOC);
330
331 if($resultarray) {
332 $portid = $resultarray[0]['id'];
333 unset($id);
334 $portcheck=$resultarray;
335 }
5d5dbda0
DO
336 // Add/update port
337 // 2011-08-31 <neil.scholten@gamigo.com>
338 // * Don't touch already existing complex ports
339 if ( $resultarray[0]['type'] != 9 ) {
340 if ( count($portcheck) == 1 ) {
341 commitUpdatePort($newmachine,$portid, $nics[$i], $nictypeid, "Ethernet port", "$mac", NULL);
342 }
343 else
344 {
345 commitAddPort($object_id = $newmachine, $nics[$i], $nictypeid,'Ethernet port',"$mac");
346 }
347 } else {
348 //We've got a complex port, don't touch it, it raises an error with 'Database error: foreign key violation'
349 }
5d5dbda0
DO
350 if (count($ipcheck) == 1 ) {
351 if( $ip ) {
8b63b6a6 352 updateAddress(ip_parse($ip) , $newmachine, $nics[$i],'regular');
5d5dbda0 353 }
1806a4df 354 }
5d5dbda0
DO
355 else
356 {
357 if( $ip ) {
8b63b6a6 358 bindIpToObject(ip_parse($ip), $newmachine, $nics[$i],'regular');
5d5dbda0
DO
359 }
360 }
5d5dbda0
DO
361 unset($portcheck);
362 unset($ipcheck);
363 unset($ip);
364 unset($mac);
1806a4df 365 }
1806a4df 366 }
bc6d25f4
DK
367 //uncomment to start using auto tags
368 //addTagToObject($facter, $newmachine);
8b63b6a6 369 return buildRedirectURL ();
38ba29a5
AD
370}
371
372// Display the import page.
373function ViewHTML()
374{
1806a4df 375 startPortlet();
1806a4df
DO
376 echo "<table with=90% align=center border=0 cellpadding=5 cellspacing=0 align=center class=cooltable><tr valign=top>";
377 echo "<form method=post enctype=\"multipart/form-data\" action='index.php?module=redirect&page=depot&tab=facter&op=Update'>";
378 echo "<input type=\"hidden\" name=\"MAX_FILE_SIZE\" value=\"30000\" />";
379 echo "Upload a facter file: <input name=\"userfile\" type=\"file\" /><br />";
380 echo "<input type=\"submit\" value=\"Upload File\" />";
1806a4df
DO
381 echo "</td></tr></table></td></tr>";
382 echo "</form>";
383 echo "</table>";
384 finishPortlet();
38ba29a5
AD
385}
386
387function getdict ($hw,$chapter) {
1806a4df
DO
388 try {
389 global $dbxlink;
390 $query = "select dict_key from Dictionary where chapter_id='$chapter' AND dict_value ='$hw' LIMIT 1";
391 $result = usePreparedSelectBlade ($query);
392 $array = $result->fetchAll (PDO::FETCH_ASSOC);
393
394 if($array) {
395 return $array[0]['dict_key'];
396 }
397 else {
398 // Chapter ID for hardware is 11.
399 $dbxlink->exec("INSERT INTO Dictionary (chapter_id,dict_value) VALUES ('$chapter','$hw')");
400
401 $squery = "select dict_key from Dictionary where dict_value ='$hw' AND chapter_ID ='$chapter' LIMIT 1";
402 $sresult = usePreparedSelectBlade ($squery);
403 $sarray = $sresult->fetchAll (PDO::FETCH_ASSOC);
404
405 if($sarray) {
406 return $sarray[0]['dict_key'];
407 }
408
409 else {
410 // If it still has not returned, we are up shit creek.
411 return 0;
412 }
413 }
414 $dbxlink = null;
415 }
416 catch(PDOException $e)
417 {
418 echo $e->getMessage();
419 }
38ba29a5 420}
8b63b6a6 421
bc6d25f4 422//new check to make sure the object type allows the string
8b63b6a6
DK
423function valid ($type_id, $id) {
424 //Check to see if this combination exists in the AttributeMap
425 $valid = "SELECT * from AttributeMap WHERE objtype_id = '$type_id' AND attr_id = '$id' LIMIT 1";
426 unset($result);
427 $result = usePreparedSelectBlade ($valid);
428 $resultarray = $result->fetchAll (PDO::FETCH_ASSOC);
429 $exists = $resultarray[0]['objtype_id'];
430 if (!empty($exists))
431 return 1;
432 else
433 return 0;
434}
435
436function getObjectTypeID ($newmachine) {
437 $objtype = "SELECT objtype_id from RackObject WHERE id = $newmachine LIMIT 1";
438 unset($result);
439 $result = usePreparedSelectBlade ($objtype);
440 $resultarray = $result->fetchAll (PDO::FETCH_ASSOC);
441 return $resultarray[0]['objtype_id'];
442}
443
444//Find Parent of VM Object
445function addVmToParent ($vms, $newmachine) {
446 $search_for_child = "select id from RackObject WHERE name REGEXP '^ *$vms\\\.'";
447 unset($result);
448 $result = usePreparedSelectBlade ($search_for_child);
449 $resultarray = $result->fetchAll (PDO::FETCH_ASSOC);
450 $child = $resultarray[0]['id'];
451 if (!empty($child)){
452 //make sure the association doesn't exist already or deal with it
453 $current_container = "SELECT parent_entity_id from EntityLink WHERE child_entity_id = $child";
454 unset($result);
455 $result = usePreparedSelectBlade ($current_container);
456 $resultarray = $result->fetchAll (PDO::FETCH_ASSOC);
457 $current_parent = $resultarray[0]['parent_entity_id'];
458 if ( ($current_parent != $newmachine ) && !empty($current_parent)){
459 commitUpdateEntityLink('object',$current_parent,'object',$child,'object',$newmachine,'object',$child);
460 } else if (empty($current_parent)) {
461 commitLinkEntities('object', $newmachine,'object',$child);
462 }
463 } else {
464 echo "WARNING: The $vms VM does not exist for this Parent \n";
465 }
466}
bc6d25f4
DK
467
468//Auto Tagging
469//Must enable this function in Update function and ensure "$facter[KEY]" exists
470function addTagToObject ($facter, $newmachine) {
471 $tags = array($facter['machinetype'], $facter['domain']);
472 $count_tags = count($tags);
473 for ($i = 0; $i < $count_tags; $i++) {
474 rebuildTagChainForEntity ('object', $newmachine, array (getTagByName($tags[$i])));
475 }
476}
38ba29a5 477?>