Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Module for admin-provided munki conditions #850

Merged
merged 13 commits into from
Nov 22, 2017
17 changes: 17 additions & 0 deletions app/modules/munki_conditions/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
Munki Conditions Module
=======================

Provides the results of [munki conditions](https://github.com/munki/munki/wiki/Conditional-Items), allowing admins to easily generate their own reportable datapoints without creating custom MunkiReport modules. Condition scripts are even easier to create using [munki-facts](https://github.com/munki/munki-facts).

Admin-Provided Condition scripts are found in `/usr/local/munki/conditions`.
The results of these scripts are stored in the 'Conditions' dictionary in `/Library/Managed Installs/ManagedInstallReport.plist`.

Both keys and values are truncated to 65,535 characters.

The following information is stored in the munki_conditions table:

* serial_number
* condition_key
* condition_value

This module is mostly based on work by clburlison, erikng, bochoven and more.
7 changes: 7 additions & 0 deletions app/modules/munki_conditions/locales/en.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
{
"client_tab": "Munki Conditions",
"key": "Key",
"listing": "Munki Conditions",
"reporttitle": "Munki Conditions",
"value": "Value"
}
79 changes: 79 additions & 0 deletions app/modules/munki_conditions/munki_conditions_controller.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
<?php
/**
* munki_conditions status module class
*
* @package munkireport
* @author nperkins487
*
**/
class munki_conditions_controller extends Module_controller
{

protected $module_path;
protected $view_path;


/*** Protect methods with auth! ****/
public function __construct()
{
// Store module path
$this->module_path = dirname(__FILE__);
$this->view_path = dirname(__FILE__) . '/views/';
}
/**
* Default method
*
* @author
**/
public function index()
{
echo "You've loaded the munki_conditions module!";
}

/**
* undocumented function summary
*
* Undocumented function long description
*
* @param type var Description
* @return {11:return type}
*/
public function listing($value = '')
{
if (! $this->authorized()) {
redirect('auth/login');
}
$data['page'] = 'clients';
$data['scripts'] = array("clients/client_list.js");
$obj = new View();
$obj->view('munki_conditions_listing', $data, $this->view_path);
}

/**
* Get munki conditions for serial_number
*
* @param string $serial serial number
* @author clburlison
**/
public function get_data($serial = '')
{

$out = array();
$temp = array();
if (! $this->authorized()) {
$out['error'] = 'Not authorized';
} else {
$munki_conditions = new munki_conditions_model;
foreach ($munki_conditions->retrieve_records($serial) as $conditions) {
$temp[] = $conditions->rs;
}
foreach ($temp as $value) {
$out[$value['condition_key']] = $value['condition_value'];
}
}

$obj = new View();
$obj->view('json', array('msg' => $out));
}

} // END class default_module
57 changes: 57 additions & 0 deletions app/modules/munki_conditions/munki_conditions_model.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
<?php
class munki_conditions_model extends Model
{

public function __construct($serial = '')
{
parent::__construct('id', 'munki_conditions'); //primary key, tablename
$this->rs['id'] = 0;
$this->rs['serial_number'] = $serial;
$this->rs['condition_key'] = '';
$this->rt['condition_key'] = 'TEXT';
$this->rs['condition_value'] = '';
$this->rt['condition_value'] = 'TEXT';

// Schema version, increment when creating a db migration
$this->schema_version = 0;

// Add indexes
$this->idx[] = array('serial_number');

// Create table if it does not exist
$this->create_table();

if ($serial) {
$this->retrieve_record($serial);

$this->serial = $serial;
}
}

/**
* Process data sent by postflight
*
* @param string data
* @author erikng
**/
public function process($plist)
{
require_once(APP_PATH . 'lib/CFPropertyList/CFPropertyList.php');
$parser = new CFPropertyList();
$parser->parse($plist);

$plist = $parser->toArray();

$this->deleteWhere('serial_number=?', $this->serial_number);
$item = array_pop($plist);

reset($item);
while (list($key, $val) = each($item)) {
$this->condition_key = $key;
$this->condition_value = $val;

$this->id = '';
$this->save();
}
}
}
12 changes: 12 additions & 0 deletions app/modules/munki_conditions/provides.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
<?php

return array(
'client_tabs' => array(
'munki_conditions' => array('view' => 'client_munki_conditions_tab', 'i18n' => 'munki_conditions.client_tab'),
),
'listings' => array(
'munki_conditions' => array('view' => 'munki_conditions_listing', 'i18n' => 'munki_conditions.listing'),
),
'reports' => array(),
'widgets' => array()
);
26 changes: 26 additions & 0 deletions app/modules/munki_conditions/scripts/install.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
#!/bin/bash

MODULE_NAME="munki_conditions"
MODULESCRIPT="munki_conditions.py"
MODULE_CACHE_FILE="munki_conditions.plist"

CTL="${BASEURL}index.php?/module/${MODULE_NAME}/"

# Get the scripts in the proper directories
"${CURL[@]}" "${CTL}get_script/${MODULESCRIPT}" -o "${MUNKIPATH}postflight.d/${MODULESCRIPT}"

# Check exit status of curl
if [ $? = 0 ]; then
# Make executable
chmod a+x "${MUNKIPATH}postflight.d/${MODULESCRIPT}"

# Set preference to include this file in the postflight check
setreportpref $MODULE_NAME "${POSTFLIGHT_CACHEPATH}${MODULE_CACHE_FILE}"

else
echo "Failed to download all required components!"
rm -f "${MUNKIPATH}postflight.d/${MODULESCRIPT}"

# Signal that we had an error
ERR=1
fi
68 changes: 68 additions & 0 deletions app/modules/munki_conditions/scripts/munki_conditions.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
#!/usr/bin/python
"""
munki_conditions for munkireport
"""

import os
import json
import plistlib
import CoreFoundation
from datetime import datetime

class DateTimeEncoder(json.JSONEncoder):
def default(self, o):
if isinstance(o, datetime):
return o.isoformat()

return json.JSONEncoder.default(self, o)

def gather_conditions(path):
"""
Read condition keys and values from the ManagedInstallReport and process them into a
dict of strings less than 256 characters.
"""
try:
plist = plistlib.readPlist(path)
except:
print 'Could not find ManagedInstallReport.plist'
return {}

conditions = {}

if plist.get('Conditions'):
for key, value in plist['Conditions'].iteritems():
key = key[:65535]
if isinstance(value, list) and len(value) > 0:
conditions[key] = json.dumps(value)[:65535]
else:
conditions[key] = str(value)[:65535]
else:
print 'No conditions found.'

return conditions


def main():
"""Main"""

# Create cache dir if it does not exist
cache_dir = '%s/cache' % os.path.dirname(os.path.realpath(__file__))
if not os.path.exists(cache_dir):
os.makedirs(cache_dir)

managed_install_dir = CoreFoundation.CFPreferencesCopyAppValue( "ManagedInstallDir", "ManagedInstalls")
managed_install_dir = managed_install_dir or '/Library/Managed Installs'
managed_install_report = managed_install_dir + '/ManagedInstallReport.plist'

if not os.path.exists(managed_install_report):
print "%s is missing." % managed_install_report
conditions_report = {}
else:
conditions_report = gather_conditions(managed_install_report)

# Write munkiinfo report to cache
output_plist = os.path.join(cache_dir, 'munki_conditions.plist')
plistlib.writePlist(conditions_report, output_plist)

if __name__ == '__main__':
main()
4 changes: 4 additions & 0 deletions app/modules/munki_conditions/scripts/uninstall.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
#!/bin/bash

rm -f "${MUNKIPATH}postflight.d/munki_conditions.py"
rm -f "${MUNKIPATH}postflight.d/cache/munki_conditions.plist"
39 changes: 39 additions & 0 deletions app/modules/munki_conditions/views/client_munki_conditions_tab.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
<h2 data-i18n="munki_conditions.client_tab"></h2>

<div id="munki_conditions-msg" data-i18n="listing.loading" class="col-lg-12 text-center"></div>

<div id="munki_conditions-view" class="row hide">
<div class="col-md-7">
<table id="munki_conditions-table" class="table table-striped">
<tr>
<th data-i18n="munki_conditions.key"></th>
<th data-i18n="munki_conditions.value"></th>
</tr>
</table>
</div>
</div>

<script>
$(document).on('appReady', function(e, lang) {

// Get munki_conditions data
$.getJSON( appUrl + '/module/munki_conditions/get_data/' + serialNumber, function( data ) {
if( Object.keys(data).length === 0 ){
$('#munki_conditions-msg').text(i18n.t('No Munki Condition Data Found!'));
}
else{
// Hide
$('#munki_conditions-msg').text('');
$('#munki_conditions-view').removeClass('hide');

// Add data
for (var key in data) {
var element = '<tr><th>' + key + '</th><td>' + data[key] + '</td></tr>'
$('#munki_conditions-table tbody').append(element)
}
}

});
});

</script>
Loading