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