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