linkmgmt needs at least RT 0.20.11
[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
2b232eaa 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
2b232eaa 20*
1806a4df
DO
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).
2b232eaa 26*
8b63b6a6
DK
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
2b232eaa
MK
144 $update_hw_type = true;
145 if (isset($_GET['update_hw_type']) && $_GET['update_hw_type'] == 'false')
146 {
147 $update_hw_type = false
148 }
149 if ($facter['is_virtual']=="false" && $update_hw_type)
150 {
8b63b6a6
DK
151 $iHWTemp = preg_match('([a-zA-Z]{1,})', $facter['manufacturer'], $matches);
152 $sManufacturer = $matches[0];
153 $sHW = preg_replace('(\ )', '\1%GPASS%', $facter['productname']);
154 $sHWType = $sManufacturer.' '.$sHW;
155 $query = "select id from Attribute where name='HW type' LIMIT 1";
156 unset($result);
157 $result = usePreparedSelectBlade ($query);
158 $resultarray = $result->fetchAll (PDO::FETCH_ASSOC);
159 if($resultarray) {
160 $id=$resultarray[0]['id'];
161 // Update HW type
162 $hw_dict_key = getdict($sHWType, $chapter=11 );
163 commitUpdateAttrValue ($object_id = $newmachine, $attr_id = $id, $value = $hw_dict_key);
164 }
165 //Also Check if HYPERVISOR
166 if (isset($facter['is_hypervisor']))
167 {
168 $query = "select id from Attribute where name REGEXP '^ *Hypervisor Type$' LIMIT 1";
169 unset($result);
170 $result = usePreparedSelectBlade ($query);
171 $resultarray = $result->fetchAll (PDO::FETCH_ASSOC);
172 if($resultarray) {
173 $id=$resultarray[0]['id'];
2b232eaa 174 // Update Hypervisor type
8b63b6a6
DK
175 $hypervisor_type = $facter['is_hypervisor'];
176 $hypervisor_type_dict_key = getdict($hw=$hypervisor_type, $chapter=10005);
177 commitUpdateAttrValue ($object_id = $newmachine, $attr_id = $id, $value = $hypervisor_type_dict_key);
178 }
179 //Set Value to Yes
180 $query = "select id from Attribute where name REGEXP '^ *Hypervisor' LIMIT 1";
181 unset($result);
182 $result = usePreparedSelectBlade ($query);
183 $resultarray = $result->fetchAll (PDO::FETCH_ASSOC);
184 if($resultarray) {
185 $id=$resultarray[0]['id'];
2b232eaa 186 // Update Hypervisor type
8b63b6a6
DK
187 $hypervisor = "Yes";
188 $hypervisor_dict_key = getdict($hypervisor, $chapter=29);
189 commitUpdateAttrValue ($object_id = $newmachine, $attr_id = $id, $value = $hypervisor_dict_key);
190 }
191 //Find Running VMs
192 $vms = explode(',',$facter['vms']);
193 $vm_count = count($vms);
194 for ($i = 0; $i < $vm_count; $i++) {
195 //addToParent
196 addVmToParent ($vms[$i], $newmachine);
2b232eaa 197 }
8b63b6a6
DK
198 } else {
199 $query = "select id from Attribute where name REGEXP '^ *Hypervisor' LIMIT 1";
200 unset($result);
201 $result = usePreparedSelectBlade ($query);
202 $resultarray = $result->fetchAll (PDO::FETCH_ASSOC);
203 if($resultarray) {
204 $id=$resultarray[0]['id'];
205 // Update Hypervisor type
206 $hypervisor = "No";
207 $hypervisor_dict_key = getdict($hypervisor, $chapter=29);
208 commitUpdateAttrValue ($object_id = $newmachine, $attr_id = $id, $value = "");
209 }
210 }
1806a4df 211 }
2b232eaa 212
1806a4df 213 // Find SW type id (OS)
8b63b6a6 214 $query = "select id from Attribute where name REGEXP '^ *SW type$' LIMIT 1";
1806a4df
DO
215 unset($result);
216 $result = usePreparedSelectBlade ($query);
217 $resultarray = $result->fetchAll (PDO::FETCH_ASSOC);
218 if($resultarray) {
219 $id=$resultarray[0]['id'];
220 // Update SW type (OS)
2b232eaa
MK
221 // Ubuntu LTS - https://wiki.ubuntu.com/LTS
222 // Ubuntu LTS can be detected by an even OS Major and an OS minor of '04'.
223 // Example Ubuntu 'operatingsystemrelease' values: '16.10', '15.04'.
224 $os_release_lts = "";
225 $facter_osrelease = explode(".", $facter['operatingsystemrelease'])
226 if ($facter['operatingsystem'] == 'Ubuntu' && $facter_osrelease[0] % 2 == 0 && $facter_osrelease[1] == '04') {
227 $os_release_lts = " LTS";
228 }
229 $osrelease = $facter['operatingsystem'] . '%GSKIP%' . $facter['operatingsystem'] . ' ' . $facter['operatingsystemrelease'] . $os_release_lts;
1806a4df
DO
230 $os_dict_key = getdict($hw=$osrelease, $chapter=13);
231 commitUpdateAttrValue ($object_id = $newmachine, $attr_id = $id, $value = $os_dict_key);
232 }
233
2b232eaa
MK
234 //Generic to read in from file
235 global $racktables_plugins_dir;
236 $manifest_file = fopen($racktables_plugins_dir . "/manifest", "r") or die("Could not open manifest, make sure it is in the websrv root and called manifest \n");
8b63b6a6
DK
237 while ( ! feof ($manifest_file)) {
238 $tmp_line = fgets($manifest_file);
239 if (!empty($tmp_line) && !preg_match("/\/\//",$tmp_line)) {
240 @list($Fact, $Attr, $Chapter) = array_map('trim', (explode(',', $tmp_line, 3)));
241 //check for multi-facter names
242 if(strstr($Fact, '.')) {
243 @list($Fact1, $Fact2) = array_map('trim', (explode('.', $Fact)));
244 $value = $facter[$Fact1] .' '. $facter[$Fact2];
245 if(!isset($facter[$Fact1]) || !isset($facter[$Fact2])) {
246 echo "WARNING: $Fact1 or $Fact2 does not exist in Facter for this object \n";
247 continue;
248 }
249 } else {
250 if(!isset($facter[$Fact])) {
251 echo "WARNING: $Fact does not exist in Facter for this object \n";
252 continue;
253 } else {
254 $value = $facter[$Fact];
255 }
256 }
257 $query = "select id from Attribute where name REGEXP '^ *$Attr' LIMIT 1";
258 unset($result);
259 $result = usePreparedSelectBlade ($query);
260 $resultarray = $result->fetchAll (PDO::FETCH_ASSOC);
261 $id=$resultarray[0]['id'];
262 if (!valid($type_id, $id)) {
263 echo "WARNING: Not a valid Mapping for $Fact to $Attr for objectType $type_id \n";
264 }
265 else if($resultarray) {
266 if(!empty($Chapter)) {
267 $name = $value;
268 $name_dict_key = getdict($hw=$name, $chapter=$Chapter);
269 commitUpdateAttrValue ($object_id = $newmachine, $attr_id = $id, $value = $name_dict_key);
270 } 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)) {
271 //handle dates
272 commitUpdateAttrValue ($newmachine, $id, strtotime($value) );
273 } else {
274 commitUpdateAttrValue ($newmachine, $id, $value );
2b232eaa 275 }
8b63b6a6
DK
276 }
277 }
1806a4df 278 }
8b63b6a6 279 fclose($manifest_file);
1806a4df
DO
280
281 // Add network interfaces
282
283 // Create an array with interfaces
284 $nics = explode(',',$facter['interfaces']);
285
8b63b6a6 286 // Go through all interfaces and add IP and MAC
1806a4df
DO
287 $count = count($nics);
288 for ($i = 0; $i < $count; $i++) {
289 // Remove newline from the field
290 $nics[$i]=str_replace("\n","", $nics[$i]);
291
292 // We generally don't monitor sit interfaces.
293 // We don't do this for lo interfaces, too
5d5dbda0
DO
294 // 2011-08-31 <neil.scholten@gamigo.com>
295 // * Only Document real interfaces, dont do bridges, bonds, vlan-interfaces
296 // when they have no IP defined.
2b232eaa 297 if ( preg_match('/(_|^(docker|lo|sit|vnet|virbr|veth|peth))/',$nics[$i]) != 0 ) {
5d5dbda0
DO
298 // do nothing
299 } else {
300 // Get IP
301 if (isset($facter['ipaddress_' . $nics[$i]]))
302 $ip = $facter['ipaddress_' . $nics[$i]];
2b232eaa 303
5d5dbda0
DO
304 // Get MAC
305 if (isset($facter['macaddress_' . $nics[$i]]))
306 $mac = $facter['macaddress_' . $nics[$i]];
2b232eaa 307
5d5dbda0
DO
308 //check if VM or not
309 if ($facter['is_virtual']=="false")
310 {
311 // Find 1000Base-T id
1ae6267d 312 $query = "select id from PortOuterInterface where oif_name REGEXP '^ *1000Base-T$' LIMIT 1";
1806a4df 313 }
5d5dbda0
DO
314 else
315 {
316 // Find virtual port id
1ae6267d 317 $query = "select id from PortOuterInterface where oif_name REGEXP '^ *virtual port$' LIMIT 1";
5d5dbda0
DO
318 }
319 unset($result);
320 $result = usePreparedSelectBlade ($query);
321 $resultarray = $result->fetchAll (PDO::FETCH_ASSOC);
322 if($resultarray) {
1ae6267d 323 $nictypeid=$resultarray[0]['id'];
5d5dbda0 324 }
2b232eaa 325
5d5dbda0
DO
326 // Remove newline from ip
327 $ip=str_replace("\n","", $ip);
2b232eaa 328
5d5dbda0
DO
329 // Check to se if the interface has an ip assigned
330 $query = "SELECT object_id FROM IPv4Allocation where object_id=$newmachine and name=\"$nics[$i]\"";
331 unset($result);
332 $result = usePreparedSelectBlade ($query);
333 $resultarray = $result->fetchAll (PDO::FETCH_ASSOC);
2b232eaa 334
5d5dbda0
DO
335 if($resultarray) {
336 unset($id);
337 $ipcheck=$resultarray;
338 }
5d5dbda0
DO
339 // Check if it's been configured a port already
340 $query = "SELECT id,iif_id FROM Port where object_id=$newmachine and name=\"$nics[$i]\"";
341 unset($result);
342 $result = usePreparedSelectBlade ($query);
343 $resultarray = $result->fetchAll (PDO::FETCH_ASSOC);
2b232eaa 344
5d5dbda0
DO
345 if($resultarray) {
346 $portid = $resultarray[0]['id'];
347 unset($id);
348 $portcheck=$resultarray;
349 }
5d5dbda0
DO
350 // Add/update port
351 // 2011-08-31 <neil.scholten@gamigo.com>
352 // * Don't touch already existing complex ports
353 if ( $resultarray[0]['type'] != 9 ) {
354 if ( count($portcheck) == 1 ) {
355 commitUpdatePort($newmachine,$portid, $nics[$i], $nictypeid, "Ethernet port", "$mac", NULL);
356 }
357 else
358 {
359 commitAddPort($object_id = $newmachine, $nics[$i], $nictypeid,'Ethernet port',"$mac");
360 }
361 } else {
362 //We've got a complex port, don't touch it, it raises an error with 'Database error: foreign key violation'
363 }
5d5dbda0
DO
364 if (count($ipcheck) == 1 ) {
365 if( $ip ) {
8b63b6a6 366 updateAddress(ip_parse($ip) , $newmachine, $nics[$i],'regular');
5d5dbda0 367 }
1806a4df 368 }
5d5dbda0
DO
369 else
370 {
371 if( $ip ) {
8b63b6a6 372 bindIpToObject(ip_parse($ip), $newmachine, $nics[$i],'regular');
5d5dbda0
DO
373 }
374 }
5d5dbda0
DO
375 unset($portcheck);
376 unset($ipcheck);
377 unset($ip);
378 unset($mac);
1806a4df 379 }
1806a4df 380 }
bc6d25f4
DK
381 //uncomment to start using auto tags
382 //addTagToObject($facter, $newmachine);
8b63b6a6 383 return buildRedirectURL ();
38ba29a5
AD
384}
385
386// Display the import page.
387function ViewHTML()
388{
1806a4df 389 startPortlet();
1806a4df
DO
390 echo "<table with=90% align=center border=0 cellpadding=5 cellspacing=0 align=center class=cooltable><tr valign=top>";
391 echo "<form method=post enctype=\"multipart/form-data\" action='index.php?module=redirect&page=depot&tab=facter&op=Update'>";
392 echo "<input type=\"hidden\" name=\"MAX_FILE_SIZE\" value=\"30000\" />";
393 echo "Upload a facter file: <input name=\"userfile\" type=\"file\" /><br />";
394 echo "<input type=\"submit\" value=\"Upload File\" />";
1806a4df
DO
395 echo "</td></tr></table></td></tr>";
396 echo "</form>";
397 echo "</table>";
398 finishPortlet();
38ba29a5
AD
399}
400
401function getdict ($hw,$chapter) {
1806a4df
DO
402 try {
403 global $dbxlink;
404 $query = "select dict_key from Dictionary where chapter_id='$chapter' AND dict_value ='$hw' LIMIT 1";
405 $result = usePreparedSelectBlade ($query);
406 $array = $result->fetchAll (PDO::FETCH_ASSOC);
407
408 if($array) {
409 return $array[0]['dict_key'];
410 }
411 else {
412 // Chapter ID for hardware is 11.
413 $dbxlink->exec("INSERT INTO Dictionary (chapter_id,dict_value) VALUES ('$chapter','$hw')");
414
415 $squery = "select dict_key from Dictionary where dict_value ='$hw' AND chapter_ID ='$chapter' LIMIT 1";
416 $sresult = usePreparedSelectBlade ($squery);
417 $sarray = $sresult->fetchAll (PDO::FETCH_ASSOC);
418
419 if($sarray) {
420 return $sarray[0]['dict_key'];
421 }
422
423 else {
424 // If it still has not returned, we are up shit creek.
425 return 0;
426 }
427 }
428 $dbxlink = null;
429 }
430 catch(PDOException $e)
431 {
432 echo $e->getMessage();
433 }
38ba29a5 434}
8b63b6a6 435
bc6d25f4 436//new check to make sure the object type allows the string
8b63b6a6
DK
437function valid ($type_id, $id) {
438 //Check to see if this combination exists in the AttributeMap
439 $valid = "SELECT * from AttributeMap WHERE objtype_id = '$type_id' AND attr_id = '$id' LIMIT 1";
440 unset($result);
441 $result = usePreparedSelectBlade ($valid);
442 $resultarray = $result->fetchAll (PDO::FETCH_ASSOC);
443 $exists = $resultarray[0]['objtype_id'];
444 if (!empty($exists))
445 return 1;
446 else
447 return 0;
448}
449
450function getObjectTypeID ($newmachine) {
451 $objtype = "SELECT objtype_id from RackObject WHERE id = $newmachine LIMIT 1";
452 unset($result);
453 $result = usePreparedSelectBlade ($objtype);
454 $resultarray = $result->fetchAll (PDO::FETCH_ASSOC);
455 return $resultarray[0]['objtype_id'];
456}
457
458//Find Parent of VM Object
459function addVmToParent ($vms, $newmachine) {
460 $search_for_child = "select id from RackObject WHERE name REGEXP '^ *$vms\\\.'";
461 unset($result);
462 $result = usePreparedSelectBlade ($search_for_child);
463 $resultarray = $result->fetchAll (PDO::FETCH_ASSOC);
464 $child = $resultarray[0]['id'];
465 if (!empty($child)){
466 //make sure the association doesn't exist already or deal with it
467 $current_container = "SELECT parent_entity_id from EntityLink WHERE child_entity_id = $child";
468 unset($result);
469 $result = usePreparedSelectBlade ($current_container);
470 $resultarray = $result->fetchAll (PDO::FETCH_ASSOC);
471 $current_parent = $resultarray[0]['parent_entity_id'];
472 if ( ($current_parent != $newmachine ) && !empty($current_parent)){
473 commitUpdateEntityLink('object',$current_parent,'object',$child,'object',$newmachine,'object',$child);
474 } else if (empty($current_parent)) {
475 commitLinkEntities('object', $newmachine,'object',$child);
476 }
477 } else {
478 echo "WARNING: The $vms VM does not exist for this Parent \n";
479 }
480}
bc6d25f4
DK
481
482//Auto Tagging
483//Must enable this function in Update function and ensure "$facter[KEY]" exists
484function addTagToObject ($facter, $newmachine) {
485 $tags = array($facter['machinetype'], $facter['domain']);
486 $count_tags = count($tags);
487 for ($i = 0; $i < $count_tags; $i++) {
488 rebuildTagChainForEntity ('object', $newmachine, array (getTagByName($tags[$i])));
489 }
490}
38ba29a5 491?>