knorby / facterpy

Python library to provide a cached and dictionary-like interface to Puppet's facter utility
BSD 2-Clause "Simplified" License
8 stars 4 forks source link

Use facter's `--json` #10

Open daenney opened 9 years ago

daenney commented 9 years ago

Facter can output JSON if you give it the --json argument on the command line. All it takes is:

json.loads(subprocess.check_output(['facter', '--json']))

You can easily turn it into a named tuple too:

>>> y = json.loads(subprocess.check_output(['facter', '--json']), object_hook=lambda d: namedtuple('Factset', d.keys())(*d.values()))

>>> dir(y)
['__add__', '__class__', '__contains__', '__delattr__', '__dict__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__getitem__', '__getnewargs__', '__getslice__', '__getstate__', '__gt__', '__hash__', '__init__', '__iter__', '__le__', '__len__', '__lt__', '__module__', '__mul__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__rmul__', '__setattr__', '__sizeof__', '__slots__', '__str__', '__subclasshook__', '_asdict', '_fields', '_make', '_replace', 'architecture', 'count', 'domain', 'facterversion', 'fqdn', 'gid', 'hardwareisa', 'hardwaremodel', 'hostname', 'id', 'index', 'interfaces', 'ipaddress', 'ipaddress6', 'ipaddress6_en0', 'ipaddress_en0', 'ipaddress_lo0', 'is_virtual', 'kernel', 'kernelmajversion', 'kernelrelease', 'kernelversion', 'macaddress', 'macaddress_awdl0', 'macaddress_bridge0', 'macaddress_en0', 'macaddress_en1', 'macaddress_en2', 'macaddress_p2p0', 'macosx_buildversion', 'macosx_productname', 'macosx_productversion', 'macosx_productversion_major', 'macosx_productversion_minor', 'memoryfree', 'memoryfree_mb', 'memorysize', 'memorysize_mb', 'mtu_awdl0', 'mtu_bridge0', 'mtu_en0', 'mtu_en1', 'mtu_en2', 'mtu_gif0', 'mtu_lo0', 'mtu_p2p0', 'mtu_stf0', 'netmask', 'netmask_en0', 'netmask_lo0', 'network_en0', 'network_lo0', 'operatingsystem', 'operatingsystemmajrelease', 'operatingsystemrelease', 'os', 'osfamily', 'path', 'processorcount', 'processors', 'productname', 'ps', 'puppetversion', 'rubyplatform', 'rubysitedir', 'rubyversion', 'sp_boot_mode', 'sp_boot_rom_version', 'sp_boot_volume', 'sp_cpu_type', 'sp_current_processor_speed', 'sp_kernel_version', 'sp_l2_cache_core', 'sp_l3_cache', 'sp_local_host_name', 'sp_machine_model', 'sp_machine_name', 'sp_number_processors', 'sp_os_version', 'sp_packages', 'sp_physical_memory', 'sp_platform_uuid', 'sp_secure_vm', 'sp_serial_number', 'sp_smc_version_system', 'sp_uptime', 'sp_user_name', 'swapencrypted', 'swapfree', 'swapfree_mb', 'swapsize', 'swapsize_mb', 'system_uptime', 'timezone', 'uptime', 'uptime_days', 'uptime_hours', 'uptime_seconds', 'virtual']

>>> y.memorysize
u'16.00 GB'

This will go haywire if a Fact has a - in a key name as that won't fly in Python but that's easily rectified.

The namedtuple has two advantages:

knorby commented 9 years ago

@daenney oh, cool. That's definitely new, as I wouldn't have written the library if it was available when I did (I'm not an active puppet user currently). Do you happened to know what version that was added in?

I don't really think of this case as a good one for namedtuples, as puppet-defined facts are not predefined. I do like the idea of defining properties for common values, and perhaps some better handling for certain types (e.g. size values). I'm not sure I see the value in guaranteeing immutability.

daenney commented 9 years ago

The value in immutability is that people can't mess with the values of the Facts after the fact. Facts represent the current state of your system as determined by Facter. If people can go in and mess with that data they aren't really of any value to make any kind of decision with. But yes, I ran into a multitude of issues with the tuples because the keys aren't guaranteed to be able to convert to things that are suitable as attribute names for Python.

As far as --json goes, it has been around since Facter 1.6, so summer 2011.

knorby commented 9 years ago

I looked through facter commit history, and it looks like past versions didn't include the option when the json gem wasn't installed (likely how I missed it), and I'm uncertain exactly how that's changed aside from it always being displayed. I will continue to want fallback handling, which is some of the point to this library in the first place.

I don't think there is a point to the sort of immutability you are describing in python, because it doesn't really buy you anything. Preventing unintended modifications is one thing, which this library already does, but there isn't a point really in guarding against bad actors, as there is almost always a workaround, including with namedtuples.

mohsenBanan commented 3 years ago

import json import subprocess

import collections

y = json.loads(subprocess.check_output(['facter', '--json']), object_hook=lambda d: collections.namedtuple('Factset', d.keys(), rename=True)(*d.values()))

print(y.networking.primary)

This works for me and rename=True makes use of namedtuple reliable.