Like most configuration management systems, salt allows users to describe server configurations in an abstract fashion (i.e. with code or pseudo-code) without requiring any knowledge of how those configurations get applied to a particluar operating system.
For example the following expression in salt will result in the apache package being installed on any server it is applied to, whether that server uses yum, apt or pacman.
apache: pkg: - installed
However, if you want to bring a new or custom operating system under salt management, you will need to let salt know how to manage the various artifacts (such as packages and services) for that system.
This article shows you how.
The first step is to get your operating system name to be picked up by grains (a component of salt which enumerates static information about your system).
#salt -v salt-206.mydomain.local grains.item os Executing job with jid 20120903195900028504 ------------------------------------------- salt-206.mydomain.local: Unknown Linux
With a salt state as described above, attempting to apply this state with the state.highstate command should result in the following exception:
#salt -v salt-206.mydomain.local state.highstate Executing job with jid 20120903200519438968 ------------------------------------------- salt-206.mydomain.local: ---------- State: - pkg Name: apache Function: installed Result: False Comment: An exception occured in this state: Traceback (most recent call last): File "/opt/pkg/salt-0.10.2-1/lib/python2.6/site-packages/salt/state.py", line 821, in call *cdata['args'], **cdata['kwargs']) File "/opt/pkg/salt-0.10.2-1/lib/python2.6/site-packages/salt/states/pkg.py", line 60, in installed cver = __salt__['pkg.version'](name) KeyError: 'pkg.version'
This is because salt is unable to determine which salt module to use to manage packages on this unknown operating system. Let’s start to rectify this by placing an appropriate package manager module in our salt module directory. I’ve installed salt under /opt/pkg/salt so my module directory is /opt/pkg/salt/lib/python2.6/site-packages/salt, yours will probably be different. You can locate your salt install directory with:
#python Python 2.6.4 (r264:75706, Jul 12 2010, 23:06:45) [GCC 4.0.3] on linux2 Type "help", "copyright", "credits" or "license" for more information. >>> import salt >>> print salt.__file__ <module 'salt' from '/opt/pkg/salt-0.10.2-1/lib/python2.6/site-packages/salt/__init__.pyc'>
Change into this directory and then into the modules subdirectory. Hopefully whatever package manager your operating system is using is similar to one that exists already. If not you will need to read one of the existing modules such as apt.py or yumpkg.py and implement the functions described in these modules. In our case (and I would hope, in most cases) we do have a module which is very similar (apt.py for us) and so can take a copy. You can give your new module any name you like, here we’ll call it fooapt.py.
#pwd /opt/pkg/salt/lib/python2.6/site-packages/salt/modules #cp apt.py fooapt.py
Now we need to edit fooapt.py and search for the __virtual__() function which should be very near the top of the module. This function will return ‘pkg’ for grains values that identify your custom operating system. Obviously os is a common grain to key upon but in our case we are going to use the lsb_distrib_codename key as I keep my operating system identifiers in /etc/lsb-release and this is how grains enumerates them.
After my changes my fooapt.py looks like:
def __virtual__(): ''' Confirm this module is on a Foo based system ''' return 'pkg' if __grains__['lsb_distrib_codename'] == 'foo' else False
Now when we run our state.highstate command:
#salt -v salt-206.mydomain.local state.highstate Executing job with jid 20120903202013258385 ------------------------------------------- salt-206.mydomain.local: ---------- State: - pkg Name: apache Function: installed Result: True Comment: Package apache installed Changes: apache: {'new': '2.2.17', 'old': ''}
Hooray, that’s better.
We generally need to repeat this process for each abstracted resource that we want to include in our salt state files. The next most obvious one is the service resource.
Let’s expand our state file to include a service definition:
apache: pkg: - installed service: - running - require: - pkg: apache
Once again, running state.highstate should fail:
Executing job with jid 20120903205201606727 ------------------------------------------- salt-206.mydomain.local: ---------- State: - service Name: apache Function: running Result: False Comment: An exception occured in this state: Traceback (most recent call last): File "/opt/pkg/salt-0.10.2-1/lib/python2.6/site-packages/salt/state.py", line 823, in call ret = self.states[cdata['full']](*cdata['args']) File "/opt/pkg/salt-0.10.2-1/lib/python2.6/site-packages/salt/states/service.py", line 255, in running changes = {name: __salt__['service.start'](name)} File "/opt/pkg/salt-0.10.2-1/lib/python2.6/site-packages/salt/modules/service.py", line 50, in start cmd = os.path.join(grainmap[__grains__['os']], KeyError: 'Unknown Linux'
Again, Debian is the closest service manager to our custom OS so we’ll copy this module also:
#pwd /opt/pkg/salt/lib/python2.6/site-packages/salt/modules #cp debian_service.py foo_service.py
And edit the __virtual__() module function:
def __virtual__(): ''' Only work on Foo ''' if __grains__['lsb_distrib_codename'] == 'Foo': return 'service' return False
Slightly irritatingly we still get an exception when we run state.highstate:
#salt -v salt-206.mydomain.local state.highstate Executing job with jid 20120903212940065249 ------------------------------------------- salt-206.mydomain.local: ---------- State: - service Name: apache Function: running Result: False Comment: An exception occured in this state: Traceback (most recent call last): File "/opt/pkg/salt-0.10.2-1/lib/python2.6/site-packages/salt/state.py", line 823, in call ret = self.states[cdata['full']](*cdata['args']) File "/opt/pkg/salt-0.10.2-1/lib/python2.6/site-packages/salt/states/service.py", line 255, in running changes = {name: __salt__['service.start'](name)} File "/opt/pkg/salt-0.10.2-1/lib/python2.6/site-packages/salt/modules/service.py", line 51, in start cmd = os.path.join(grainmap[__grains__['os']], KeyError: 'Unknown Linux'
Once again our undefined os grain is nipping at our ankles. Here we need to update the list of operating systems for which salt will avoid using the default service.py module. To do this, edit service.py and find its __virtual__() function. Within this is a list called disable. Add our ‘Unknown Linux’ to the end of this list:
def __virtual__(): ''' Only work on systems which default to systemd ''' # Disable on these platforms, specific service modules exist: disable = [ 'RedHat', 'CentOS', 'Scientific', 'Fedora', 'Gentoo', 'Ubuntu', 'Debian', 'Unknown Linux', #<<<<<< Here's our new entry ] if __grains__['os'] in disable: return False # Disable on all non-Linux OSes as well if __grains__['kernel'] != 'Linux': return False return 'service'
Now try state.highstate again: #salt -v salt-206.mydomain.local state.highstate Executing job with jid 20120903213444867910 ——————————————- salt-206.mydomain.local: ———- State: – service Name: apache Function: running Result: True Comment: Started Service apache Changes: apache: True
Hooray!
This method is, however, a little irrtating as, up until this point, we’ve been adding modules without touching the salt core code. With our update to the disable list, we now have something that we have to maintain between salt releases. Pity.
Still, we now can manage packages and services on our new operating system. We can follow a similar pattern with each new resource type we wish to manage.
We work with our clients to de-risk and accelerate their business goals realisation. Our approach is based on tailoring our services to fit your needs leveraging our portfolio of strategy, execution, innovation and service delivery offerings to help you reach your objectives
We’re always on the lookout for exceptional talent and people who share our values. Even as we continue to grow, we maintain a family environment with respect and teamwork core to our culture.
Companies can start deploying containerised workloads to Kubernetes In days not months, by leveraging Automation Logic’s Kubernetes Platform Accelerator.
Largest Irish-Founded Tech Employer Surpasses 3000 Employees Signing 15th and 16th Acquisition Deals Version 1, a leading digital transformation partner, is to acquire Automation Logic – for an undisclosed sum as part of its ambitious growth strategy for 2023 and beyond. The Automation Logic deal is subject to clearance by the National Security and Investment […]
Automation Logic were recognised as one of the UK’s best Workplaces again for the 5th year running! We also placed 29th in best workplaces for women. This highlights the great work that we are doing as a business, including all the wonderful work being put in by the employee-led DE&I groups. This award means so […]