<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>Blog Archive on t0rrant's tech blog</title><link>https://implement.pt/blog/</link><description>Recent content in Blog Archive on t0rrant's tech blog</description><generator>Hugo -- gohugo.io</generator><language>en</language><lastBuildDate>Wed, 04 Nov 2020 12:27:00 +0100</lastBuildDate><atom:link href="https://implement.pt/blog/index.xml" rel="self" type="application/rss+xml"/><item><title>OpenStack - the beauty of the Beast - part 1</title><link>https://implement.pt/2020/11/openstack-the-beauty-of-the-beast-pt1/</link><pubDate>Wed, 04 Nov 2020 12:27:00 +0100</pubDate><guid>https://implement.pt/2020/11/openstack-the-beauty-of-the-beast-pt1/</guid><summary type="html">&lt;div class="document">
&lt;p>An attempt at an in-depth look into OpenStack and its various components&lt;/p>
&lt;/div></summary><content type="html">&lt;div class="document">
&lt;div class="section" id="introduction">
&lt;h2>Introduction&lt;/h2>
&lt;p>Almost two years ago I embarked on a challenge which led me through the depths of OpenStack and the hardship of managing
a multi-region OpenStack deployment. OpenStack is a &lt;strong>beast&lt;/strong>, really, it can get enormous even with just &amp;quot;basic&amp;quot; cloud
features. For example, if I want to launch a Virtual Machine (VM) in the cloud and have it display a static webpage via
HTTP it will, at least, need the following from the cloud provider:&lt;/p>
&lt;ol class="arabic simple">
&lt;li>a way to identify a cloud user&lt;/li>
&lt;li>a way to store images to boot the VM&lt;/li>
&lt;li>hypervisor nodes to launch the VM in&lt;/li>
&lt;li>some kind of scheduler to select to which hypervisor the VM should be allocated to&lt;/li>
&lt;li>network virtualization on top of the physical network layer, so our user network is separated from other users&lt;/li>
&lt;li>some way to access the VM from the exterior&lt;/li>
&lt;li>a way for all of the services to communicate with each other&lt;/li>
&lt;li>a place to store the state of the cloud at any given moment&lt;/li>
&lt;/ol>
&lt;p>That said, there is &lt;strong>a lot&lt;/strong> of service interaction, and they are amazingly independent services that can do their part
on their own, and in the end it works (mostly) as expected and it is somewhat straightforward to use.&lt;/p>
&lt;p>We can use OpenStack for managing VMs (nova), containers (magnum) or bare metal (ironic) hosts. In this first part of
presenting my OpenStack experience we will focusing on VMs and will be covering:&lt;/p>
&lt;ol class="arabic simple">
&lt;li>OpenStack overall architecture&lt;/li>
&lt;li>Just enough service dependencies to get a working OpenStack cluster&lt;/li>
&lt;li>Common difficulties (especially when coming from interacting with older releases)&lt;/li>
&lt;li>Basic usage (user perspective)&lt;/li>
&lt;/ol>
&lt;p>In a future second (or even third) part we will cover other topics like operating the cloud, dealing with migrations,
planning for high availability, and debugging networking issues (insert ominous sounds here).&lt;/p>
&lt;/div>
&lt;div class="section" id="architecture">
&lt;h2>Architecture&lt;/h2>
&lt;p>Let us start from a user-centric perspective. As we can see in &lt;a class="reference external" href="#fig-openstack-overview">Figure 1&lt;/a> the user interacts
with each service independently. However, in order to do that there needs no be some kind of authentication, and it
should also exist some kind of service discovery. We can depict a possible interaction as follows:&lt;/p>
&lt;ol class="arabic simple">
&lt;li>Cloud User communicates with the Identity service, authenticating against it and receiving a session token after a
successful authentication.&lt;/li>
&lt;li>Cloud User communicates with the Identity service using the previously received token and asks for all of the known
public service endpoints.&lt;/li>
&lt;li>Cloud User communicates with the desired service through its public endpoint, using the same token it received
previously.&lt;/li>
&lt;/ol>
&lt;div class="figure align-center" id="fig-openstack-overview">
&lt;a class="reference external image-reference" href="./img/Openstack_overview.png">&lt;img alt="OpenStack service interaction from the user perspective" src="./img/Openstack_overview.png" />&lt;/a>
&lt;p class="caption">OpenStack service interaction from the user perspective&lt;/p>
&lt;/div>
&lt;p>So we can see that the Identity service is a keystone for OpenStack's workflow, and is no surprise that it is called
Keystone ;-)&lt;/p>
&lt;p>Although there are obvious dependencies for using each service, the beauty of OpenStack is that a failure of one service
does not mean a complete failure of another service. We may, however, lack some functionality of the service, for
example, if your identity plane crashes spectacularly you may not be able to launch new VMs but your VMs' will still be
up and running, and the network is also reachable.&lt;/p>
&lt;p>This way of interacting is the usual CLI interaction path, you can, however, not see any of this and use the Dashboard
service, also known as Horizon, so that you can see in a web application all of the available services and all of the
available features from those services, acting as a proxy for the previous interaction.&lt;/p>
&lt;/div>
&lt;div class="section" id="services">
&lt;h2>Services&lt;/h2>
&lt;p>In this section we will see every service, one by one, in more detail.&lt;/p>
&lt;ol class="arabic simple">
&lt;li>&lt;a class="reference internal" href="#identity">Identity&lt;/a>&lt;/li>
&lt;li>&lt;a class="reference internal" href="#image">Image&lt;/a>&lt;/li>
&lt;li>&lt;a class="reference internal" href="#block-storage">Block Storage&lt;/a>&lt;/li>
&lt;li>&lt;a class="reference internal" href="#dns">DNS&lt;/a>&lt;/li>
&lt;li>&lt;a class="reference internal" href="#network">Network&lt;/a>&lt;/li>
&lt;li>&lt;a class="reference internal" href="#compute">Compute&lt;/a>&lt;/li>
&lt;li>&lt;a class="reference internal" href="#dashboard">Dashboard&lt;/a>&lt;/li>
&lt;li>&lt;a class="reference internal" href="#service-communication">Service Communication&lt;/a>&lt;/li>
&lt;li>&lt;a class="reference internal" href="#service-state">Service State&lt;/a>&lt;/li>
&lt;/ol>
&lt;div class="section" id="identity">
&lt;h3>Identity&lt;/h3>
&lt;p>&lt;em>Keystone is an OpenStack service that provides API client authentication, service discovery, and distributed&lt;/em>
&lt;em>multi-tenant authorization by implementing&lt;/em>
&lt;a class="reference external" href="https://docs.openstack.org/api-ref/identity/index.html">OpenStack’s Identity API&lt;/a>. &lt;a class="footnote-reference" href="#keystonedocs" id="id1">[1]&lt;/a>&lt;/p>
&lt;p>The central piece of every OpenStack cloud, it is responsible for keeping records of users, generating authentication
tokens and other access mechanisms like API keys, maintaining a registry of available services for the cloud, and ensure
individual service authentication and access control for different users to different projects (aka tenants).&lt;/p>
&lt;/div>
&lt;div class="section" id="image">
&lt;h3>Image&lt;/h3>
&lt;p>&lt;em>The Image service (glance) project provides a service where users can upload and discover data assets that are meant&lt;/em>
&lt;em>to be used with other services. This currently includes images and metadata definitions.&lt;/em> &lt;a class="footnote-reference" href="#glancedocs" id="id2">[2]&lt;/a>&lt;/p>
&lt;p>This service is responsible for maintaining images that will be used to launch VM instances. They can be public, usually
made available by administrators, private or shared, so one tenant can upload an image and share it with other tenants.&lt;/p>
&lt;/div>
&lt;div class="section" id="block-storage">
&lt;h3>Block Storage&lt;/h3>
&lt;p>&lt;em>Cinder is the OpenStack Block Storage service for providing volumes to Nova virtual machines, Ironic bare metal hosts,&lt;/em>
&lt;em>containers and more.&lt;/em> &lt;a class="footnote-reference" href="#cinderdocs" id="id3">[3]&lt;/a>&lt;/p>
&lt;p>This services is responsible to store volumes created from cinder images so we can boot our VMs with persistent root
storage, persistent volumes to attach to instances for additional storage, or even create encrypted volumes using
another OpenStack service responsible for secrets management we will not cover here (barbican).&lt;/p>
&lt;/div>
&lt;div class="section" id="dns">
&lt;h3>DNS&lt;/h3>
&lt;p>&lt;em>Designate is a multi-tenant DNSaaS service for OpenStack. It provides a REST API with integrated Keystone&lt;/em>
&lt;em>authentication. It can be configured to auto-generate records based on Nova and Neutron actions. Designate supports a&lt;/em>
&lt;em>variety of DNS servers including Bind9 and PowerDNS 4.&lt;/em> &lt;a class="footnote-reference" href="#designatedocs" id="id4">[4]&lt;/a>&lt;/p>
&lt;p>With DNSaaS incorporated in an OpenStack cloud deployment you can have every instance you launch to have valid DNS
records for their interfaces, and records for floating IPs you create, making it simple to manage DNS for your projects.&lt;/p>
&lt;/div>
&lt;div class="section" id="network">
&lt;h3>Network&lt;/h3>
&lt;p>&lt;em>Neutron is an OpenStack project to provide &amp;quot;network connectivity as a service&amp;quot; between interface devices (e.g., vNICs)&lt;/em>
&lt;em>managed by other OpenStack services (e.g., nova). It implements the&lt;/em>
&lt;a class="reference external" href="https://docs.openstack.org/api-ref/network/">OpenStack Networking API&lt;/a>. &lt;a class="footnote-reference" href="#neutrondocs" id="id5">[5]&lt;/a>&lt;/p>
&lt;p>This service is what gives network connectivity to and from your VM instances. It enables users to create Routers,
making it possible to create multiple networks within your projects, connect your routers to the outside network,
assigning public IPs to your VM instances, announcing Border Gateway Protocol (BGP) routes among other network
operations, like creating firewall rules outside Operating System (OS) space, vital to your services operations.&lt;/p>
&lt;/div>
&lt;div class="section" id="compute">
&lt;h3>Compute&lt;/h3>
&lt;p>&lt;em>Nova is the OpenStack project that provides a way to provision compute instances (aka virtual servers). Nova supports&lt;/em>
&lt;em>creating virtual machines, baremetal servers (through the use of ironic), and has limited support for system&lt;/em>
&lt;em>containers. Nova runs as a set of daemons on top of existing Linux servers to provide that service.&lt;/em> &lt;a class="footnote-reference" href="#novadocs" id="id6">[6]&lt;/a>&lt;/p>
&lt;p>This is the service responsible for allocating VMs to hypervisors, and it depends on &lt;em>keystone&lt;/em>, &lt;em>glance&lt;/em> and &lt;em>neutron&lt;/em>
components to operate on a basic level.&lt;/p>
&lt;/div>
&lt;div class="section" id="dashboard">
&lt;h3>Dashboard&lt;/h3>
&lt;p>&lt;em>Horizon is the canonical implementation of OpenStack’s Dashboard, which provides a web based user interface to
*OpenStack services including Nova, Swift, Keystone, etc.&lt;/em> &lt;a class="footnote-reference" href="#horizondocs" id="id7">[7]&lt;/a>&lt;/p>
&lt;p>It is basically a way for users to interact with the OpenStack cloud in a simple graphical way, avoiding to perform
complex operations through the OpenStack API or the OpenStack CLI. It is also an easy way to look at resources available
to individual projects, and resource details all in one place.&lt;/p>
&lt;/div>
&lt;div class="section" id="service-communication">
&lt;h3>Service Communication&lt;/h3>
&lt;p>All of the above services have to communicate with each other, or even between different agents for the same service.
This is achieved through message queueing and at this time it supports the following backends:&lt;/p>
&lt;ul class="simple">
&lt;li>RabbitMQ&lt;/li>
&lt;li>Qpid&lt;/li>
&lt;li>ZeroMQ&lt;/li>
&lt;/ul>
&lt;p>&lt;em>OpenStack does not support message-level confidence, such as message signing. Consequently, you must secure and&lt;/em>
&lt;em>authenticate the message transport itself. For high-availability (HA) configurations, you must perform queue-to-queue&lt;/em>
&lt;em>authentication and encryption.&lt;/em> &lt;a class="footnote-reference" href="#messagingdocs" id="id8">[8]&lt;/a>&lt;/p>
&lt;/div>
&lt;div class="section" id="service-state">
&lt;h3>Service State&lt;/h3>
&lt;p>State for the above services has to be kept in an atomic, consistent, isolated and durable way. So we should use a
database backend which is compliant to this model. OpenStack considers using either PostgreSQL or MySQL as the database
backend, I personally like MySQL (or MariaDB) as it provides high availability options out of the box.&lt;/p>
&lt;p>&lt;em>The choice of database server is an important consideration in the security of an OpenStack deployment. Multiple&lt;/em>
&lt;em>factors should be considered when deciding on a database server&lt;/em>. &lt;a class="footnote-reference" href="#databasedocs" id="id9">[9]&lt;/a>&lt;/p>
&lt;/div>
&lt;div class="section" id="openstack-landscape">
&lt;h3>Openstack Landscape&lt;/h3>
&lt;p>After this fast run-down of services we can look at another representation of an OpenStack cloud, as we can see in
&lt;a class="reference external" href="#fig-openstack-landscape">Figure 2&lt;/a>.&lt;/p>
&lt;div class="figure align-center" id="fig-openstack-landscape">
&lt;a class="reference external image-reference" href="./img/Openstack_landscape.png">&lt;img alt="OpenStack service interaction from the system's perspective" src="./img/Openstack_landscape.png" />&lt;/a>
&lt;p class="caption">OpenStack service architecture&lt;/p>
&lt;/div>
&lt;p>Here we can see three layers:&lt;/p>
&lt;ul class="simple">
&lt;li>External Layer&lt;/li>
&lt;li>Service Layer&lt;/li>
&lt;li>Physical Layer&lt;/li>
&lt;/ul>
&lt;p>In the External Layer we have our cloud users, which interact with the OpenStack cloud through either the Horizon
Dashboard or through its REST API, either directly or through the openstackclient wrapper. These interactions usually
start with asking the Keystone service for a token, and using that token to interact with the other cloud services. The
service discovery is also made via the Keystone service, as every service must register with the Keystone endpoint.&lt;/p>
&lt;p>We could consider the Dashboard to be part of the service layer, although I like to see it more like the wrapper to the
REST API that it is, a web front-end, so we will leave it in a limbo between the External and the Service layer&lt;/p>
&lt;p>In the Service Layer we have several distinct Planes where specific operations occur.&lt;/p>
&lt;p>In the Control Plane we have all of our services' controllers (neutron, designate, nova) or standalone services
(keystone, cinder, glance). These are responsible for, respectively, either delegate actions to other service agents
(i.e: delegate VM creation to specific compute nodes), or take direct actions themselves (i.e: upload a cloud image to
the storage backend).&lt;/p>
&lt;p>In the Compute Plane we have the compute nodes, which receive VM allocation requests in the nova agents, and create
those VMs using the appropriate hypervisor.&lt;/p>
&lt;p>The Network Plane is probably the most complex, and is responsible for plugging and unplugging &lt;em>ports, create networks
or subnets, and provide IP addressing. These plug-ins and agents differ depending on the vendor and technologies used in
the particular cloud. OpenStack Networking ships with plug-ins and agents for Cisco virtual and physical switches, NEC
OpenFlow products, Open vSwitch, Linux bridging, and the VMware NSX product&lt;/em> &lt;a class="footnote-reference" href="#neutronserviceoverview" id="id10">[10]&lt;/a> , via neutron
agents and plug-ins. It can also be responsible for announcing BGP routes (via a specific neutron agent), and manage DNS
zones and records (via a designate agent).&lt;/p>
&lt;p>The Storage Plane is where cloud images (glance) and volumes (cinder) are effectively stored. Openstack supports a
&lt;a class="reference external" href="https://docs.openstack.org/cinder/ussuri/drivers.html">wide variety&lt;/a> or storage backends, although in their
documentation they say the most common are &lt;a class="reference external" href="https://ceph.io/">Ceph RBD&lt;/a>, &lt;a class="reference external" href="https://linux-nfs.org/">NFS&lt;/a> or
&lt;a class="reference external" href="https://wiki.gentoo.org/wiki/LVM">Local Disk (LVM)&lt;/a>.&lt;/p>
&lt;p>The Control, Compute and Network Planes interact through two channels. A messaging queue (usually RabbitMQ) is used for
services to publish and collect messages in specific service queues, where operations to perform and their results are
sent to. A database (usually MySQL or PostgreSQL) is used to store the state of each service, describing the state of
the whole OpenStack cloud state. Obviously these channels are &lt;strong>very&lt;/strong> important for a smooth operation, sometimes being
an operational bottleneck, and so they should be separate services and ideally clustered.&lt;/p>
&lt;p>Finally, the Physical Layer is where the services actual run be it VMs, bare metal machines or containers. In
&lt;a class="reference external" href="#fig-openstack-landscape">Figure 2&lt;/a> we can see a correspondence between the Service Layer and the Physical Layer
through their colours (sorry for not choosing colorblind safe colours):&lt;/p>
&lt;ul class="simple">
&lt;li>Control Plane runs in Controller Nodes&lt;/li>
&lt;li>Compute Plane runs in Compute Nodes&lt;/li>
&lt;li>Network Plane runs in Network Nodes&lt;/li>
&lt;li>Storage Plane runs in Ceph Cluster&lt;/li>
&lt;li>State Storage runs in MariaDB Cluster&lt;/li>
&lt;li>Message Queue runs in RabbitMQ Cluster&lt;/li>
&lt;/ul>
&lt;p>And with all of these services configured properly we have a minimal working OpenStack Cloud, neat!&lt;/p>
&lt;p>For a broader view of what OpenStack can provide, we can look at &lt;a class="reference external" href="#fig-openstack-monster">Figure 3&lt;/a> for a list of all
of the projects that exist until the day of this post.&lt;/p>
&lt;div class="figure align-center" id="fig-openstack-monster">
&lt;a class="reference external image-reference" href="./img/map-of-OpenStack-projects.png">&lt;img alt="Map of OpenStack services" src="./img/map-of-OpenStack-projects.png" />&lt;/a>
&lt;p class="caption">Map of OpenStack projects &lt;a class="footnote-reference" href="#openstackintro" id="id12">[11]&lt;/a>&lt;/p>
&lt;/div>
&lt;/div>
&lt;/div>
&lt;div class="section" id="conclusion">
&lt;h2>Conclusion&lt;/h2>
&lt;p>In this first post we took a look at different OpenStack components, namely the bare minimum to have a functional cloud:&lt;/p>
&lt;ul class="simple">
&lt;li>manage users, projects and their access tokens (Identity)&lt;/li>
&lt;li>manage images to boot VM instances from&lt;/li>
&lt;li>manage volumes to store arbitrary data, attaching and detaching from VM instances&lt;/li>
&lt;li>manage network components such as routers, networks, subnets, DNS&lt;/li>
&lt;li>launch VM instances from an available image or volume&lt;/li>
&lt;/ul>
&lt;p>This article and it referred resources should give you everything you need to be able to deploy a minimal and functional
OpenStack cluster.&lt;/p>
&lt;p>Feel free to leave comments below, any improvement to this and other articles is always welcome.&lt;/p>
&lt;p>Thanks for reading!&lt;/p>
&lt;/div>
&lt;div class="section" id="references">
&lt;h2>References&lt;/h2>
&lt;table class="docutils footnote" frame="void" id="keystonedocs" rules="none">
&lt;colgroup>&lt;col class="label" />&lt;col />&lt;/colgroup>
&lt;tbody valign="top">
&lt;tr>&lt;td class="label">&lt;a class="fn-backref" href="#id1">[1]&lt;/a>&lt;/td>&lt;td>--, Keystone, the OpenStack Identity Service - OpenStack Docs &lt;a class="reference external" href="https://docs.openstack.org/keystone/latest/">[link]&lt;/a>&lt;/td>&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;table class="docutils footnote" frame="void" id="glancedocs" rules="none">
&lt;colgroup>&lt;col class="label" />&lt;col />&lt;/colgroup>
&lt;tbody valign="top">
&lt;tr>&lt;td class="label">&lt;a class="fn-backref" href="#id2">[2]&lt;/a>&lt;/td>&lt;td>--, Welcome to Glance's documentation! - OpenStack Docs &lt;a class="reference external" href="https://docs.openstack.org/glance/latest/">[link]&lt;/a>&lt;/td>&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;table class="docutils footnote" frame="void" id="cinderdocs" rules="none">
&lt;colgroup>&lt;col class="label" />&lt;col />&lt;/colgroup>
&lt;tbody valign="top">
&lt;tr>&lt;td class="label">&lt;a class="fn-backref" href="#id3">[3]&lt;/a>&lt;/td>&lt;td>--, OpenStack Block Storage (Cinder) documentation - OpenStack Docs &lt;a class="reference external" href="https://docs.openstack.org/cinder/latest/">[link]&lt;/a>&lt;/td>&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;table class="docutils footnote" frame="void" id="designatedocs" rules="none">
&lt;colgroup>&lt;col class="label" />&lt;col />&lt;/colgroup>
&lt;tbody valign="top">
&lt;tr>&lt;td class="label">&lt;a class="fn-backref" href="#id4">[4]&lt;/a>&lt;/td>&lt;td>--, Designate, a DNSaaS component for OpenStack - OpenStack Docs &lt;a class="reference external" href="https://docs.openstack.org/designate/latest/">[link]&lt;/a>&lt;/td>&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;table class="docutils footnote" frame="void" id="neutrondocs" rules="none">
&lt;colgroup>&lt;col class="label" />&lt;col />&lt;/colgroup>
&lt;tbody valign="top">
&lt;tr>&lt;td class="label">&lt;a class="fn-backref" href="#id5">[5]&lt;/a>&lt;/td>&lt;td>--, Welcome to Neutron’s documentation! - OpenStack Docs &lt;a class="reference external" href="https://docs.openstack.org/neutron/latest/">[link]&lt;/a>&lt;/td>&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;table class="docutils footnote" frame="void" id="novadocs" rules="none">
&lt;colgroup>&lt;col class="label" />&lt;col />&lt;/colgroup>
&lt;tbody valign="top">
&lt;tr>&lt;td class="label">&lt;a class="fn-backref" href="#id6">[6]&lt;/a>&lt;/td>&lt;td>--, OpenStack Compute (nova) - OpenStack Docs &lt;a class="reference external" href="https://docs.openstack.org/nova/latest/">[link]&lt;/a>&lt;/td>&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;table class="docutils footnote" frame="void" id="horizondocs" rules="none">
&lt;colgroup>&lt;col class="label" />&lt;col />&lt;/colgroup>
&lt;tbody valign="top">
&lt;tr>&lt;td class="label">&lt;a class="fn-backref" href="#id7">[7]&lt;/a>&lt;/td>&lt;td>--, Horizon: The OpenStack Dashboard Project - OpenStack Docs &lt;a class="reference external" href="https://docs.openstack.org/horizon/latest/">[link]&lt;/a>&lt;/td>&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;table class="docutils footnote" frame="void" id="messagingdocs" rules="none">
&lt;colgroup>&lt;col class="label" />&lt;col />&lt;/colgroup>
&lt;tbody valign="top">
&lt;tr>&lt;td class="label">&lt;a class="fn-backref" href="#id8">[8]&lt;/a>&lt;/td>&lt;td>--, Message queuing - OpenStack Docs &lt;a class="reference external" href="https://docs.openstack.org/security-guide/messaging.html">[link]&lt;/a>&lt;/td>&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;table class="docutils footnote" frame="void" id="databasedocs" rules="none">
&lt;colgroup>&lt;col class="label" />&lt;col />&lt;/colgroup>
&lt;tbody valign="top">
&lt;tr>&lt;td class="label">&lt;a class="fn-backref" href="#id9">[9]&lt;/a>&lt;/td>&lt;td>--, Databases - OpenStack Docs &lt;a class="reference external" href="https://docs.openstack.org/security-guide/databases.html">[link]&lt;/a>&lt;/td>&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;table class="docutils footnote" frame="void" id="neutronserviceoverview" rules="none">
&lt;colgroup>&lt;col class="label" />&lt;col />&lt;/colgroup>
&lt;tbody valign="top">
&lt;tr>&lt;td class="label">&lt;a class="fn-backref" href="#id10">[10]&lt;/a>&lt;/td>&lt;td>--, Networking service overview - Openstack Docs &lt;a class="reference external" href="https://docs.openstack.org/neutron/latest/install/common/get-started-networking.html">[link]&lt;/a>&lt;/td>&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;table class="docutils footnote" frame="void" id="openstackintro" rules="none">
&lt;colgroup>&lt;col class="label" />&lt;col />&lt;/colgroup>
&lt;tbody valign="top">
&lt;tr>&lt;td class="label">&lt;a class="fn-backref" href="#id12">[11]&lt;/a>&lt;/td>&lt;td>--, Introduction, Openstack Docs &lt;a class="reference external" href="https://docs.openstack.org/contributors/common/introduction.html">[link]&lt;/a>&lt;/td>&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;/div>
&lt;/div></content><category>Cloud Computing</category><category>linux</category><category>devops</category><category>cloud computing</category><category>inspec</category><category>compliance</category><category>integration</category><category>testing</category></item><item><title>Integration and Compliance with InSpec</title><link>https://implement.pt/2020/10/integration-and-compliance-with-inspec/</link><pubDate>Sun, 11 Oct 2020 13:37:00 +0100</pubDate><guid>https://implement.pt/2020/10/integration-and-compliance-with-inspec/</guid><summary type="html">&lt;div class="document">
&lt;p>A hands-on tutorial on how to audit and test for integration using Chef InSpec&lt;/p>
&lt;/div></summary><content type="html">&lt;div class="document">
&lt;div class="section" id="introduction">
&lt;h2>Introduction&lt;/h2>
&lt;p>&lt;em>Chef InSpec is an open-source framework for testing and auditing your applications and infrastructure. Chef InSpec&lt;/em>
&lt;em>works by comparing the actual state of your system with the desired state that you express in easy-to-read and&lt;/em>
&lt;em>easy-to-write Chef InSpec code. Chef InSpec detects violations and displays findings in the form of a report, but puts&lt;/em>
&lt;em>you in control of remediation.&lt;/em> &lt;a class="footnote-reference" href="#chefinspec" id="id1">[1]&lt;/a>&lt;/p>
&lt;p>After we got past that well marketed description on what InSpec is, let us be more specific on what you can do with it.&lt;/p>
&lt;p>When developing Chef Cookbooks you usually have a default InSpec test file created for you which is also defined in the
&lt;em>kitchen.yml&lt;/em> manifest, i.e:&lt;/p>
&lt;p>&lt;div class="highlight">&lt;pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">&lt;code class="language-shell" data-lang="shell">t0rrant@testing:~$ chef generate cookbook mycookbook
....
Your cookbook is ready. Type &lt;span style="color:#e6db74">`&lt;/span>cd mycookbook&lt;span style="color:#e6db74">`&lt;/span> to enter it.
....
Why not start by writing an InSpec test? Tests &lt;span style="color:#66d9ef">for&lt;/span> the default recipe are stored at:
test/integration/default/default_test.rb
...
t0rrant@testing:~$ cd mycookbook/&lt;/code>&lt;/pre>&lt;/div>
&lt;div class="highlight">&lt;div style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">
&lt;table style="border-spacing:0;padding:0;margin:0;border:0;width:auto;overflow:auto;display:block;">&lt;tr>&lt;td style="vertical-align:top;padding:0;margin:0;border:0;">
&lt;pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">&lt;code>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 1
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 2
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 3
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 4
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 5
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 6
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 7
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 8
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 9
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">10
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">11
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">12
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">13
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">14
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">15
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">16
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">17
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td style="vertical-align:top;padding:0;margin:0;border:0;;width:100%">
&lt;pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">&lt;code class="language-ruby" data-lang="ruby">&lt;span style="color:#75715e"># test/integration/default/default_test.rb&lt;/span>
&lt;span style="color:#75715e"># InSpec test for recipe mycookbook::default&lt;/span>
&lt;span style="color:#75715e"># The InSpec reference, with examples and extensive documentation, can be&lt;/span>
&lt;span style="color:#75715e"># found at https://www.inspec.io/docs/reference/resources/&lt;/span>
&lt;span style="color:#66d9ef">unless&lt;/span> os&lt;span style="color:#f92672">.&lt;/span>windows?
&lt;span style="color:#75715e"># This is an example test, replace with your own test.&lt;/span>
describe user(&lt;span style="color:#e6db74">&amp;#39;root&amp;#39;&lt;/span>), &lt;span style="color:#e6db74">:skip&lt;/span> &lt;span style="color:#66d9ef">do&lt;/span>
it { should exist }
&lt;span style="color:#66d9ef">end&lt;/span>
&lt;span style="color:#66d9ef">end&lt;/span>
&lt;span style="color:#75715e"># This is an example test, replace it with your own test.&lt;/span>
describe port(&lt;span style="color:#ae81ff">80&lt;/span>), &lt;span style="color:#e6db74">:skip&lt;/span> &lt;span style="color:#66d9ef">do&lt;/span>
it { should_not be_listening }
&lt;span style="color:#66d9ef">end&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>
&lt;div class="highlight">&lt;div style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">
&lt;table style="border-spacing:0;padding:0;margin:0;border:0;width:auto;overflow:auto;display:block;">&lt;tr>&lt;td style="vertical-align:top;padding:0;margin:0;border:0;">
&lt;pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">&lt;code>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 1
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 2
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 3
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 4
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 5
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 6
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 7
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 8
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 9
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">10
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">11
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">12
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">13
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">14
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td style="vertical-align:top;padding:0;margin:0;border:0;;width:100%">
&lt;pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">&lt;code class="language-yaml" data-lang="yaml">&lt;span style="color:#75715e"># kitchen.yml&lt;/span>
...
&lt;span style="color:#f92672">verifier&lt;/span>:
&lt;span style="color:#f92672">name&lt;/span>: &lt;span style="color:#ae81ff">inspec&lt;/span>
...
&lt;span style="color:#f92672">suites&lt;/span>:
- &lt;span style="color:#f92672">name&lt;/span>: &lt;span style="color:#ae81ff">default&lt;/span>
&lt;span style="color:#f92672">verifier&lt;/span>:
&lt;span style="color:#f92672">inspec_tests&lt;/span>:
- &lt;span style="color:#ae81ff">test/integration/default&lt;/span>
&lt;span style="color:#f92672">attributes&lt;/span>:
&lt;span style="color:#ae81ff">t0rrant@testing:~/mycookbook$&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;/p>
&lt;p>So you can see that it is fairly simple to start smashing keys and add more tests for your cookbook.&lt;/p>
&lt;p>Now imagine that besides that specific cookbook you have a running system, in production, with some of the same
integration requirements. Let us assume the following requirement:&lt;/p>
&lt;ul class="simple">
&lt;li>the machine should be serving MySQL from port 3306&lt;/li>
&lt;/ul>
&lt;p>according to those requirements we could define some rules for the system:&lt;/p>
&lt;p>&lt;em>For the sake of simplicity, we assume the target system is running a debian-based Linux distribution&lt;/em>&lt;/p>
&lt;ul class="simple">
&lt;li>the &lt;strong>package&lt;/strong> &lt;strong>mariadb-server&lt;/strong> should be installed&lt;/li>
&lt;li>the &lt;strong>user&lt;/strong> &lt;strong>mysql&lt;/strong> must exist&lt;/li>
&lt;li>the &lt;strong>mysqld&lt;/strong> &lt;strong>process&lt;/strong> should be listening on &lt;strong>port&lt;/strong> &lt;strong>3306&lt;/strong>&lt;/li>
&lt;/ul>
&lt;p>This can be easily described as:&lt;/p>
&lt;div class="highlight">&lt;div style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">
&lt;table style="border-spacing:0;padding:0;margin:0;border:0;width:auto;overflow:auto;display:block;">&lt;tr>&lt;td style="vertical-align:top;padding:0;margin:0;border:0;">
&lt;pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">&lt;code>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 1
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 2
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 3
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 4
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 5
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 6
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 7
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 8
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 9
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">10
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">11
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">12
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td style="vertical-align:top;padding:0;margin:0;border:0;;width:100%">
&lt;pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">&lt;code class="language-ruby" data-lang="ruby">describe package(&lt;span style="color:#e6db74">&amp;#39;mariadb-server&amp;#39;&lt;/span>) &lt;span style="color:#66d9ef">do&lt;/span>
it { should be_installed }
&lt;span style="color:#66d9ef">end&lt;/span>
describe user(&lt;span style="color:#e6db74">&amp;#39;mysql&amp;#39;&lt;/span>) &lt;span style="color:#66d9ef">do&lt;/span>
it { should exist }
&lt;span style="color:#66d9ef">end&lt;/span>
describe port(&lt;span style="color:#ae81ff">3306&lt;/span>) &lt;span style="color:#66d9ef">do&lt;/span>
it { should be_listening }
its(&lt;span style="color:#e6db74">&amp;#39;processes&amp;#39;&lt;/span>) { should &lt;span style="color:#66d9ef">include&lt;/span> &lt;span style="color:#e6db74">&amp;#39;mysqld&amp;#39;&lt;/span> }
&lt;span style="color:#66d9ef">end&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>
&lt;p>The result of having that evaluated successfully should be something like:&lt;/p>
&lt;div class="highlight">&lt;pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">&lt;code class="language-shell" data-lang="shell">Package mariadb-server
✔ is expected to be installed
User mysql
✔ is expected to exist
Port &lt;span style="color:#ae81ff">3306&lt;/span>
✔ is expected to be listening
✔ processes is expected to include &lt;span style="color:#e6db74">&amp;#34;mysqld&amp;#34;&lt;/span>&lt;/code>&lt;/pre>&lt;/div>
&lt;p>Cool, but wait... what are we actually testing here? Configuration, not functionality. These tests do not tell us if the
MySQL server is actually working or that the SSH server is properly accepting Public Keys and not Passwords.&lt;/p>
&lt;p>This shows that the system is compliant for our specific rules, but what we probably want is also a way to test for
functionality.&lt;/p>
&lt;p>As a first step let us organize these tests and create a profile for them. Let us call it &lt;cite>database&lt;/cite>.&lt;/p>
&lt;/div>
&lt;div class="section" id="creating-inspec-profiles">
&lt;h2>Creating InSpec Profiles&lt;/h2>
&lt;p>&lt;em>Chef InSpec supports the creation of complex test and compliance profiles, which organize controls to support&lt;/em>
&lt;em>dependency management and code reuse. Each profile is a standalone structure with its own distribution and execution&lt;/em>
&lt;em>flow.&lt;/em> &lt;a class="footnote-reference" href="#inspecprofiles" id="id2">[2]&lt;/a>&lt;/p>
&lt;p>Here we will only cover the basic aspects of InSpec profiles, you should check the docs &lt;a class="footnote-reference" href="#inspecprofiles" id="id3">[2]&lt;/a> for more
information.&lt;/p>
&lt;div class="section" id="profile-structure">
&lt;h3>Profile Structure&lt;/h3>
&lt;p>A profile can be seen as a directory with the following structure:&lt;/p>
&lt;div class="highlight">&lt;pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">&lt;code class="language-shell" data-lang="shell">t0rrant@testing:~/inspec/profile$ tree
.
├── controls
│   ├── control1.rb
│   └── control2.rb
├── files
│   └── example_input.yml
├── inspec.yml
└── README.md&lt;/code>&lt;/pre>&lt;/div>
&lt;p>The &lt;cite>README.md&lt;/cite> file should contain information about the profile, I usually leave some examples of a successful
execution on the profile.&lt;/p>
&lt;p>The &lt;cite>inspec.yml&lt;/cite> file has general information about the profile, including inputs (we will get to that later), the
profile name, supported platforms, the profile's version, &lt;a class="reference external" href="https://docs.chef.io/inspec/profiles/#inspecyml">among others&lt;/a>.&lt;/p>
&lt;p>The &lt;cite>controls&lt;/cite> directory contains the InSpec tests which will be applied when executing the profile.&lt;/p>
&lt;p>The &lt;cite>files&lt;/cite> directory can contain additional files, accessible by the profile, or just arbitrary files useful outside
of the testing scope, and it is optional.&lt;/p>
&lt;p>We can also have a &lt;cite>libraries&lt;/cite> directory in which we can have InSpec resources extensions and helper functions, and is
also optional.&lt;/p>
&lt;/div>
&lt;div class="section" id="database-profile">
&lt;h3>Database Profile&lt;/h3>
&lt;p>Let us create the previously mentioned structure with two control files (one for compliance and another for
integration testing), the main &lt;cite>inspec.yml&lt;/cite> file describing our profile and an &lt;cite>input.yml&lt;/cite> (which we will use to pass
custom values to variables we will be using in the integration testing).&lt;/p>
&lt;div class="highlight">&lt;pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">&lt;code class="language-shell" data-lang="shell">t0rrant@testing:~/database$ tree
.
├── controls
│   ├── compliance.rb
│   └── integration.rb
├── files
│   └── input.yml
├── inspec.yml
└── README.md&lt;/code>&lt;/pre>&lt;/div>
&lt;p>First the &lt;cite>inspec.yml&lt;/cite> file:&lt;/p>
&lt;div class="highlight">&lt;div style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">
&lt;table style="border-spacing:0;padding:0;margin:0;border:0;width:auto;overflow:auto;display:block;">&lt;tr>&lt;td style="vertical-align:top;padding:0;margin:0;border:0;">
&lt;pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">&lt;code>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 1
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 2
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 3
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 4
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 5
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 6
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 7
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 8
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 9
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">10
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">11
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">12
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">13
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">14
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">15
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">16
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">17
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">18
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">19
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">20
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">21
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">22
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">23
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">24
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">25
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">26
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">27
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">28
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">29
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">30
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td style="vertical-align:top;padding:0;margin:0;border:0;;width:100%">
&lt;pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">&lt;code class="language-yaml" data-lang="yaml">&lt;span style="color:#75715e"># inspec.yml&lt;/span>
&lt;span style="color:#f92672">name&lt;/span>: &lt;span style="color:#ae81ff">database&lt;/span>
&lt;span style="color:#f92672">title&lt;/span>: &lt;span style="color:#ae81ff">Basic Database&lt;/span>
&lt;span style="color:#f92672">maintainer&lt;/span>: &lt;span style="color:#ae81ff">Manuel Torrinha&lt;/span>
&lt;span style="color:#f92672">copyright&lt;/span>: &lt;span style="color:#ae81ff">Manuel torrinha&lt;/span>
&lt;span style="color:#f92672">copyright_email&lt;/span>: &lt;span style="color:#ae81ff">torrinha@implement.pt&lt;/span>
&lt;span style="color:#f92672">license&lt;/span>: &lt;span style="color:#ae81ff">MIT&lt;/span>
&lt;span style="color:#f92672">summary&lt;/span>: &lt;span style="color:#ae81ff">Verify that mariadb Server is configured and working properly&lt;/span>
&lt;span style="color:#f92672">version&lt;/span>: &lt;span style="color:#ae81ff">1.0.0&lt;/span>
&lt;span style="color:#f92672">supports&lt;/span>:
- &lt;span style="color:#f92672">platform-family&lt;/span>: &lt;span style="color:#ae81ff">linux&lt;/span>
&lt;span style="color:#f92672">inspec_version&lt;/span>: &lt;span style="color:#e6db74">&amp;#34;~&amp;gt; 4.0&amp;#34;&lt;/span>
&lt;span style="color:#f92672">inputs&lt;/span>:
- &lt;span style="color:#f92672">name&lt;/span>: &lt;span style="color:#ae81ff">db_host&lt;/span>
&lt;span style="color:#f92672">description&lt;/span>: &lt;span style="color:#ae81ff">Hostname or IP address for accessing the database&lt;/span>
&lt;span style="color:#f92672">type&lt;/span>: &lt;span style="color:#ae81ff">string&lt;/span>
&lt;span style="color:#f92672">value&lt;/span>: &lt;span style="color:#e6db74">&amp;#39;127.0.0.1&amp;#39;&lt;/span>
- &lt;span style="color:#f92672">name&lt;/span>: &lt;span style="color:#ae81ff">db_port&lt;/span>
&lt;span style="color:#f92672">description&lt;/span>: &lt;span style="color:#ae81ff">Port on which the database should be listening&lt;/span>
&lt;span style="color:#f92672">type&lt;/span>: &lt;span style="color:#ae81ff">numeric&lt;/span>
&lt;span style="color:#f92672">value&lt;/span>: &lt;span style="color:#e6db74">&amp;#39;3306&amp;#39;&lt;/span>
- &lt;span style="color:#f92672">name&lt;/span>: &lt;span style="color:#ae81ff">db_user&lt;/span>
&lt;span style="color:#f92672">description&lt;/span>: &lt;span style="color:#ae81ff">Username used to access the database&lt;/span>
&lt;span style="color:#f92672">type&lt;/span>: &lt;span style="color:#ae81ff">string&lt;/span>
&lt;span style="color:#f92672">required&lt;/span>: &lt;span style="color:#66d9ef">true&lt;/span>
- &lt;span style="color:#f92672">name&lt;/span>: &lt;span style="color:#ae81ff">db_password&lt;/span>
&lt;span style="color:#f92672">description&lt;/span>: &lt;span style="color:#ae81ff">Password used to access the database&lt;/span>
&lt;span style="color:#f92672">type&lt;/span>: &lt;span style="color:#ae81ff">string&lt;/span>
&lt;span style="color:#f92672">required&lt;/span>: &lt;span style="color:#66d9ef">true&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>
&lt;p>Looks good, we defined four input variables so that we can test different settings for the database connection. And also
we made the &lt;cite>db_user&lt;/cite> and &lt;cite>db_password&lt;/cite> mandatory in order for any control that depends on either of them fails if they
are not defined.&lt;/p>
&lt;p>Next let us create the tests for compliance in &lt;em>controls/compliance.rb&lt;/em>:&lt;/p>
&lt;div class="highlight">&lt;div style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">
&lt;table style="border-spacing:0;padding:0;margin:0;border:0;width:auto;overflow:auto;display:block;">&lt;tr>&lt;td style="vertical-align:top;padding:0;margin:0;border:0;">
&lt;pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">&lt;code>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 1
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 2
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 3
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 4
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 5
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 6
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 7
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 8
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 9
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">10
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">11
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">12
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">13
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">14
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">15
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">16
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">17
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">18
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">19
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">20
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">21
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td style="vertical-align:top;padding:0;margin:0;border:0;;width:100%">
&lt;pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">&lt;code class="language-ruby" data-lang="ruby">&lt;span style="color:#75715e"># controls/compliance.rb&lt;/span>
control &lt;span style="color:#e6db74">&amp;#39;mariadb-compliance&amp;#39;&lt;/span> &lt;span style="color:#66d9ef">do&lt;/span>
title &lt;span style="color:#e6db74">&amp;#39;Check for proper configuration&amp;#39;&lt;/span>
describe package(&lt;span style="color:#e6db74">&amp;#39;mariadb-server&amp;#39;&lt;/span>) &lt;span style="color:#66d9ef">do&lt;/span>
it { should be_installed }
&lt;span style="color:#66d9ef">end&lt;/span>
describe user(&lt;span style="color:#e6db74">&amp;#39;mysql&amp;#39;&lt;/span>) &lt;span style="color:#66d9ef">do&lt;/span>
it { should exist }
&lt;span style="color:#66d9ef">end&lt;/span>
describe file(&lt;span style="color:#e6db74">&amp;#39;/etc/my.cnf&amp;#39;&lt;/span>) &lt;span style="color:#66d9ef">do&lt;/span>
it { should_not be_more_permissive_than(&lt;span style="color:#e6db74">&amp;#39;644&amp;#39;&lt;/span>) }
&lt;span style="color:#66d9ef">end&lt;/span>
describe port(&lt;span style="color:#ae81ff">3306&lt;/span>) &lt;span style="color:#66d9ef">do&lt;/span>
it { should be_listening }
its(&lt;span style="color:#e6db74">&amp;#39;processes&amp;#39;&lt;/span>) { should &lt;span style="color:#66d9ef">include&lt;/span> &lt;span style="color:#e6db74">&amp;#39;mysqld&amp;#39;&lt;/span> }
&lt;span style="color:#66d9ef">end&lt;/span>
&lt;span style="color:#66d9ef">end&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>
&lt;p>As you can see we just did the same as in the introduction, plus we also check for the main config file's permissions.&lt;/p>
&lt;p>Finally, we will create the tests for integration in &lt;em>controls/integration.rb&lt;/em>:&lt;/p>
&lt;div class="highlight">&lt;div style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">
&lt;table style="border-spacing:0;padding:0;margin:0;border:0;width:auto;overflow:auto;display:block;">&lt;tr>&lt;td style="vertical-align:top;padding:0;margin:0;border:0;">
&lt;pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">&lt;code>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 1
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 2
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 3
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 4
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 5
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 6
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 7
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 8
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 9
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">10
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td style="vertical-align:top;padding:0;margin:0;border:0;;width:100%">
&lt;pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">&lt;code class="language-ruby" data-lang="ruby">&lt;span style="color:#75715e"># controls/integration.rb&lt;/span>
control &lt;span style="color:#e6db74">&amp;#39;mariadb-integration&amp;#39;&lt;/span> &lt;span style="color:#66d9ef">do&lt;/span>
title &lt;span style="color:#e6db74">&amp;#39;Checks for proper DB connection&amp;#39;&lt;/span>
sql &lt;span style="color:#f92672">=&lt;/span> mysql_session(input(&lt;span style="color:#e6db74">&amp;#39;db_user&amp;#39;&lt;/span>), input(&lt;span style="color:#e6db74">&amp;#39;db_password&amp;#39;&lt;/span>), input(&lt;span style="color:#e6db74">&amp;#39;db_host&amp;#39;&lt;/span>), input(&lt;span style="color:#e6db74">&amp;#39;db_port&amp;#39;&lt;/span>))
describe sql&lt;span style="color:#f92672">.&lt;/span>query(&lt;span style="color:#e6db74">&amp;#39;show databases;&amp;#39;&lt;/span>) &lt;span style="color:#66d9ef">do&lt;/span>
its(&lt;span style="color:#e6db74">&amp;#39;exit_status&amp;#39;&lt;/span>) { should eq(&lt;span style="color:#ae81ff">0&lt;/span>) }
&lt;span style="color:#66d9ef">end&lt;/span>
&lt;span style="color:#66d9ef">end&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>
&lt;p>Here we are testing if the connection works, by executing a common query to list the existing database, which should not
fail if we can authenticate successfully.&lt;/p>
&lt;p>Now let us test this &lt;cite>database&lt;/cite> profile:&lt;/p>
&lt;div class="highlight">&lt;pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">&lt;code class="language-shell" data-lang="shell">t0rrant@testing:~/database$ inspec exec . --log-level&lt;span style="color:#f92672">=&lt;/span>error
Profile: Basic Database &lt;span style="color:#f92672">(&lt;/span>database&lt;span style="color:#f92672">)&lt;/span>
Version: 1.0.0
Target: local://
✔ mariadb-compliance: Check &lt;span style="color:#66d9ef">for&lt;/span> proper configuration
✔ System Package mariadb-server is expected to be installed
✔ User mysql is expected to exist
✔ File /etc/my.cnf is expected not to be more permissive than &lt;span style="color:#e6db74">&amp;#34;644&amp;#34;&lt;/span>
✔ Port &lt;span style="color:#ae81ff">3306&lt;/span> is expected to be listening
✔ Port &lt;span style="color:#ae81ff">3306&lt;/span> processes is expected to include &lt;span style="color:#e6db74">&amp;#34;mysqld&amp;#34;&lt;/span>
× mariadb-integration: Checks &lt;span style="color:#66d9ef">for&lt;/span> proper DB connection
× Control Source Code Error ./controls/integration.rb:1
Input &lt;span style="color:#e6db74">&amp;#39;db_user&amp;#39;&lt;/span> is required and does not have a value.
Profile Summary: &lt;span style="color:#ae81ff">1&lt;/span> successful control, &lt;span style="color:#ae81ff">1&lt;/span> control failure, &lt;span style="color:#ae81ff">0&lt;/span> controls skipped
Test Summary: &lt;span style="color:#ae81ff">4&lt;/span> successful, &lt;span style="color:#ae81ff">1&lt;/span> failure, &lt;span style="color:#ae81ff">0&lt;/span> skipped&lt;/code>&lt;/pre>&lt;/div>
&lt;p>As we can see the compliance control seems ok, however we can't run the integration tests because we did not
define neither &lt;em>db_user&lt;/em> or &lt;em>db_password&lt;/em>. So, let us create the file &lt;em>files/input.yml&lt;/em> and define them there:&lt;/p>
&lt;div class="highlight">&lt;div style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">
&lt;table style="border-spacing:0;padding:0;margin:0;border:0;width:auto;overflow:auto;display:block;">&lt;tr>&lt;td style="vertical-align:top;padding:0;margin:0;border:0;">
&lt;pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">&lt;code>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">1
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">2
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">3
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td style="vertical-align:top;padding:0;margin:0;border:0;;width:100%">
&lt;pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">&lt;code class="language-yaml" data-lang="yaml">&lt;span style="color:#75715e"># files/input.yml&lt;/span>
&lt;span style="color:#f92672">db_user&lt;/span>: &lt;span style="color:#e6db74">&amp;#39;avaliduser&amp;#39;&lt;/span>
&lt;span style="color:#f92672">db_password&lt;/span>: &lt;span style="color:#e6db74">&amp;#39;a6v8a8l2li0dp01as0s0w6r0d&amp;#39;&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>
&lt;div class="highlight">&lt;pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">&lt;code class="language-shell" data-lang="shell">t0rrant@testing:~/database$ inspec exec . --input-file&lt;span style="color:#f92672">=&lt;/span>files/input.yml --log-level&lt;span style="color:#f92672">=&lt;/span>error
Profile: Basic Database &lt;span style="color:#f92672">(&lt;/span>database&lt;span style="color:#f92672">)&lt;/span>
Version: 1.0.0
Target: local://
✔ mariadb-compliance: Check &lt;span style="color:#66d9ef">for&lt;/span> proper configuration
✔ System Package mariadb-server is expected to be installed
✔ User mysql is expected to exist
✔ File /etc/my.cnf is expected not to be more permissive than &lt;span style="color:#e6db74">&amp;#34;644&amp;#34;&lt;/span>
✔ Port &lt;span style="color:#ae81ff">3306&lt;/span> is expected to be listening
✔ Port &lt;span style="color:#ae81ff">3306&lt;/span> processes is expected to include &lt;span style="color:#e6db74">&amp;#34;mysqld&amp;#34;&lt;/span>
✔ mariadb-integration: Checks &lt;span style="color:#66d9ef">for&lt;/span> proper DB connection
✔ Command: &lt;span style="color:#e6db74">`&lt;/span>mysql -uavaliduser -pa6v8a8l2li0dp01as0s0w6r0d -h 127.0.0.1 --port &lt;span style="color:#ae81ff">3306&lt;/span> -s -e &lt;span style="color:#e6db74">&amp;#34;show databases;&amp;#34;&lt;/span>&lt;span style="color:#e6db74">`&lt;/span> exit_status is expected to eq &lt;span style="color:#ae81ff">0&lt;/span>
Profile Summary: &lt;span style="color:#ae81ff">2&lt;/span> successful controls, &lt;span style="color:#ae81ff">0&lt;/span> control failures, &lt;span style="color:#ae81ff">0&lt;/span> controls skipped
Test Summary: &lt;span style="color:#ae81ff">5&lt;/span> successful, &lt;span style="color:#ae81ff">0&lt;/span> failures, &lt;span style="color:#ae81ff">0&lt;/span> skipped&lt;/code>&lt;/pre>&lt;/div>
&lt;p>Looks ok, although we may not want the output showing the whole command, including the user and password used to access
the database, so let us modify the integration test:&lt;/p>
&lt;div class="highlight">&lt;div style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">
&lt;table style="border-spacing:0;padding:0;margin:0;border:0;width:auto;overflow:auto;display:block;">&lt;tr>&lt;td style="vertical-align:top;padding:0;margin:0;border:0;">
&lt;pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">&lt;code>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 1
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 2
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 3
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 4
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 5
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 6
&lt;/span>&lt;span style="display:block;width:100%;background-color:#3c3d38">&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 7
&lt;/span>&lt;/span>&lt;span style="display:block;width:100%;background-color:#3c3d38">&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 8
&lt;/span>&lt;/span>&lt;span style="display:block;width:100%;background-color:#3c3d38">&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 9
&lt;/span>&lt;/span>&lt;span style="display:block;width:100%;background-color:#3c3d38">&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">10
&lt;/span>&lt;/span>&lt;span style="display:block;width:100%;background-color:#3c3d38">&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">11
&lt;/span>&lt;/span>&lt;span style="display:block;width:100%;background-color:#3c3d38">&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">12
&lt;/span>&lt;/span>&lt;span style="display:block;width:100%;background-color:#3c3d38">&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">13
&lt;/span>&lt;/span>&lt;span style="display:block;width:100%;background-color:#3c3d38">&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">14
&lt;/span>&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">15
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td style="vertical-align:top;padding:0;margin:0;border:0;;width:100%">
&lt;pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">&lt;code class="language-ruby" data-lang="ruby">&lt;span style="color:#75715e"># controls/integration.rb&lt;/span>
control &lt;span style="color:#e6db74">&amp;#39;mariadb-integration&amp;#39;&lt;/span> &lt;span style="color:#66d9ef">do&lt;/span>
title &lt;span style="color:#e6db74">&amp;#39;Checks for proper DB connection&amp;#39;&lt;/span>
sql &lt;span style="color:#f92672">=&lt;/span> mysql_session(input(&lt;span style="color:#e6db74">&amp;#39;db_user&amp;#39;&lt;/span>), input(&lt;span style="color:#e6db74">&amp;#39;db_password&amp;#39;&lt;/span>), input(&lt;span style="color:#e6db74">&amp;#39;db_host&amp;#39;&lt;/span>), input(&lt;span style="color:#e6db74">&amp;#39;db_port&amp;#39;&lt;/span>))
&lt;span style="display:block;width:100%;background-color:#3c3d38"> describe &lt;span style="color:#e6db74">&amp;#39;A simple SQL query&amp;#39;&lt;/span> &lt;span style="color:#66d9ef">do&lt;/span>
&lt;/span>&lt;span style="display:block;width:100%;background-color:#3c3d38"> subject &lt;span style="color:#66d9ef">do&lt;/span>
&lt;/span>&lt;span style="display:block;width:100%;background-color:#3c3d38"> sql&lt;span style="color:#f92672">.&lt;/span>query(&lt;span style="color:#e6db74">&amp;#39;show databases;&amp;#39;&lt;/span>)
&lt;/span>&lt;span style="display:block;width:100%;background-color:#3c3d38"> &lt;span style="color:#66d9ef">end&lt;/span>
&lt;/span>&lt;span style="display:block;width:100%;background-color:#3c3d38"> it &lt;span style="color:#e6db74">&amp;#39;should execute successfully&amp;#39;&lt;/span> &lt;span style="color:#66d9ef">do&lt;/span>
&lt;/span>&lt;span style="display:block;width:100%;background-color:#3c3d38"> expect(subject&lt;span style="color:#f92672">.&lt;/span>exit_status)&lt;span style="color:#f92672">.&lt;/span>to eq(&lt;span style="color:#ae81ff">0&lt;/span>)
&lt;/span>&lt;span style="display:block;width:100%;background-color:#3c3d38"> &lt;span style="color:#66d9ef">end&lt;/span>
&lt;/span>&lt;span style="display:block;width:100%;background-color:#3c3d38"> &lt;span style="color:#66d9ef">end&lt;/span>
&lt;/span>&lt;span style="color:#66d9ef">end&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>
&lt;p>Here we refer to RSpec syntax and use the &lt;em>subject&lt;/em> block to define our test and the &lt;em>expect&lt;/em> method for the assertion,
while using the &lt;em>it&lt;/em> block to validate the subject and customize the output through the title. Let us run the profile
again:&lt;/p>
&lt;div class="highlight">&lt;pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">&lt;code class="language-shell" data-lang="shell">t0rrant@testing:~/database$ inspec exec . --input-file&lt;span style="color:#f92672">=&lt;/span>files/example_input.yml --log-level&lt;span style="color:#f92672">=&lt;/span>error
Profile: Basic Database &lt;span style="color:#f92672">(&lt;/span>database&lt;span style="color:#f92672">)&lt;/span>
Version: 1.0.0
Target: local://
✔ mariadb-compliance: Check &lt;span style="color:#66d9ef">for&lt;/span> proper configuration
✔ System Package mariadb-server is expected to be installed
✔ User mysql is expected to exist
✔ File /etc/my.cnf is expected not to be more permissive than &lt;span style="color:#e6db74">&amp;#34;644&amp;#34;&lt;/span>
✔ Port &lt;span style="color:#ae81ff">3306&lt;/span> is expected to be listening
✔ Port &lt;span style="color:#ae81ff">3306&lt;/span> processes is expected to include &lt;span style="color:#e6db74">&amp;#34;mysqld&amp;#34;&lt;/span>
✔ mariadb-integration: Checks &lt;span style="color:#66d9ef">for&lt;/span> proper DB connection
✔ A simple SQL command should execute successfully
Profile Summary: &lt;span style="color:#ae81ff">2&lt;/span> successful controls, &lt;span style="color:#ae81ff">0&lt;/span> control failures, &lt;span style="color:#ae81ff">0&lt;/span> controls skipped
Test Summary: &lt;span style="color:#ae81ff">6&lt;/span> successful, &lt;span style="color:#ae81ff">0&lt;/span> failures, &lt;span style="color:#ae81ff">0&lt;/span> skipped&lt;/code>&lt;/pre>&lt;/div>
&lt;p>Nice, now we don't see anything sensitive and can safely show the output of the test.&lt;/p>
&lt;/div>
&lt;/div>
&lt;div class="section" id="conclusion">
&lt;h2>Conclusion&lt;/h2>
&lt;p>In this post we saw how to create InSpec tests, and that we are able to test both configuration settings, and
functionality using InSpec. We also saw how to customize a test's output so we can hide sensitive information.&lt;/p>
&lt;p>The files described in this post are available &lt;a class="reference external" href="https://implement.pt/files/integration-and-compliance-with-inspec/database.tar.gz">here&lt;/a>&lt;/p>
&lt;p>Feel free to leave comments below, any improvement to this and other articles is always welcome.&lt;/p>
&lt;p>Thanks for reading!&lt;/p>
&lt;/div>
&lt;div class="section" id="references">
&lt;h2>References&lt;/h2>
&lt;table class="docutils footnote" frame="void" id="chefinspec" rules="none">
&lt;colgroup>&lt;col class="label" />&lt;col />&lt;/colgroup>
&lt;tbody valign="top">
&lt;tr>&lt;td class="label">&lt;a class="fn-backref" href="#id1">[1]&lt;/a>&lt;/td>&lt;td>--, An Overview of Chef InSpec - Chef Web Docs &lt;a class="reference external" href="https://docs.chef.io/inspec/">[link]&lt;/a>&lt;/td>&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;table class="docutils footnote" frame="void" id="inspecprofiles" rules="none">
&lt;colgroup>&lt;col class="label" />&lt;col />&lt;/colgroup>
&lt;tbody valign="top">
&lt;tr>&lt;td class="label">[2]&lt;/td>&lt;td>&lt;em>(&lt;a class="fn-backref" href="#id2">1&lt;/a>, &lt;a class="fn-backref" href="#id3">2&lt;/a>)&lt;/em> --, About Chef InSpec Profiles - Chef Web Docs &lt;a class="reference external" href="https://docs.chef.io/inspec/profiles/">[link]&lt;/a>&lt;/td>&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;/div>
&lt;/div></content><category>System Administration</category><category>linux</category><category>devops</category><category>chef</category><category>inspec</category><category>compliance</category><category>integration</category><category>testing</category></item><item><title>Linux Networking Introduction</title><link>https://implement.pt/2020/06/linux-networking-introduction/</link><pubDate>Thu, 18 Jun 2020 23:00:00 +0100</pubDate><guid>https://implement.pt/2020/06/linux-networking-introduction/</guid><summary type="html">&lt;div class="document">
&lt;p>A beginner's crash course on Linux Networking&lt;/p>
&lt;/div></summary><content type="html">&lt;div class="document">
&lt;!-- DIAGRAM COLORS
blue 4495D1
green 97D077
purple B5739D
orange EDA868 -->
&lt;!-- {F#fig-tux}
{F#fig-comparch}
{F#fig-kernelarch}
{F#fig-gnome-terminal}
{F#fig-switching}
{F#fig-routing}
{F#fig-dns}
{F#fig-symmetricenc}
{F#fig-asymmetricenc}
{F#fig-sshprotocol}
{F#fig-diffiehellman}
{F#fig-sshauthkeypair} -->
&lt;style>
.notice,.notice:hover{color:#000!important;background-color:#ffeb3b!important}
.notice,.notice:after{content:"";display:table;clear:both}
.notice::before{content:"NOTICE: ";font-weight:bold;}
.notice{padding:0.2em 16px}.notice{margin-top:16px;margin-bottom:16px}
.todo,.todo:hover{color:#000!important;background-color:#b3bffe!important}
.todo,.todo:after{content:"";display:table;clear:both}
.todo::before{content:"TODO: ";font-weight:bold;}
.todo{padding:0.2em 16px}.todo{margin-top:16px;margin-bottom:16px}
&lt;/style>&lt;p>&lt;span class="notice">this article is intended for an audience which does not necessarily have a Computer Science or
Information Technology background and are curious about how things work. The goal is that by the end of the article, as
someone just arriving to the Linux universe, you will be able to have basic understanding of how the Linux Operating
System (OS) works, how you can connect two or more Linux systems through a network, simple best practices to keep your
system safe, and other (maybe useful) tips. Take everything with a pinch of salt as I will be simplifying some of the
topics.&lt;/span>&lt;/p>
&lt;div class="section" id="requirements">
&lt;h2>Requirements&lt;/h2>
&lt;p>To fully follow this course, you should have at least &lt;em>two&lt;/em> computers available. If you do not have a spare old computer
gaining dust in the attic you can use VirtualBox (take a look at
&lt;a class="reference external" href="https://websiteforstudents.com/enable-virtualbox-vm-to-vm-communications-on-windows-ubuntu-16-04-18-04/">this guide&lt;/a>
on how to set it up) or use a similar software with either &lt;em>NAT Networking&lt;/em> or &lt;em>Bridged&lt;/em> network configuration.&lt;/p>
&lt;p>We assume those machines have either &lt;a class="reference external" href="https://releases.ubuntu.com/20.04/">Ubuntu 20.04 (Focal Fossa)&lt;/a> or
&lt;a class="reference external" href="https://www.debian.org/releases/buster/debian-installer/">Debian 10 (Buster)&lt;/a> installed. The requirements for
either of them with a Desktop environment are:&lt;/p>
&lt;ul class="simple">
&lt;li>2 GHz single core processor&lt;/li>
&lt;li>2 GiB RAM (system memory)&lt;/li>
&lt;li>10 GB of hard drive space&lt;/li>
&lt;/ul>
&lt;p>of course the more the merrier. Remember that these requirements are the recommended &lt;strong>minimum&lt;/strong> for a usable Linux OS.&lt;/p>
&lt;p>The OS installation itself is outside of the scope of this article, however if you are to follow the defaults you should
at least be able to follow this course =)&lt;/p>
&lt;/div>
&lt;div class="section" id="introduction">
&lt;h2>Introduction&lt;/h2>
&lt;p>During the SARS-CoV-2 pandemic I was reached out by people wanting to start using Linux as an alternative everyday-use
Operating System, most of them were familiar with Ubuntu or some other Debian-based variation, or by people who now had
to interact with Linux systems. Eventually some reached a point where they had to open a command-line to follow a set of
instructions they did not really understand. Among those, there were some who were curious enough to ask &amp;quot;what does this
do?&amp;quot; or &amp;quot;why does this do what it does?&amp;quot;. Those that made those questions eventually caught themselves delving deeper
into Linux internals and how everything fits together.&lt;/p>
&lt;p>This made me think it would be worthwhile to write a small course-like article introducing new users into this complex
universe that is Linux.&lt;/p>
&lt;p>I have given this article the title Linux Networking Introduction because in today's perspective what would we really do
with a Linux system without networking? But mostly because the people that have a need to interact with a Linux system
usually do it by means of a network connection.&lt;/p>
&lt;p>That said, in this course we will go through:&lt;/p>
&lt;ol class="arabic simple">
&lt;li>&lt;a class="reference internal" href="#a-bit-of-linux-history">A Bit of Linux History&lt;/a>&lt;/li>
&lt;li>&lt;a class="reference internal" href="#an-overview-of-linux-kernel-components">An Overview of Linux Kernel Components&lt;/a>&lt;/li>
&lt;li>&lt;a class="reference internal" href="#an-overview-of-the-linux-filesystem">An Overview of the Linux Filesystem&lt;/a>&lt;/li>
&lt;li>&lt;a class="reference internal" href="#user-authentication-and-security">User Authentication and Security&lt;/a>&lt;/li>
&lt;li>&lt;a class="reference internal" href="#file-attributes-and-ownership">File Attributes and Ownership&lt;/a>&lt;/li>
&lt;li>&lt;a class="reference internal" href="#bash-your-friendly-command-line-interpreter">Bash, your friendly command-line interpreter&lt;/a>&lt;/li>
&lt;li>&lt;a class="reference internal" href="#networking-101">Networking 101&lt;/a>&lt;/li>
&lt;li>&lt;a class="reference internal" href="#secure-communications-and-ssh">Secure Communications and SSH&lt;/a>&lt;/li>
&lt;li>&lt;a class="reference internal" href="#setting-up-your-machine-for-remote-access">Setting up your machine for remote access&lt;/a>&lt;/li>
&lt;/ol>
&lt;p>This is a fairly extensive article, so I invite you to take a break between sections, take some time to sink all in and
try your own version of the examples.&lt;/p>
&lt;p>Have a nice ride :-)&lt;/p>
&lt;!-- ##################################################################################################################### -->
&lt;!-- ##################################################################################################################### -->
&lt;!-- ##################################################################################################################### -->
&lt;/div>
&lt;div class="section" id="a-bit-of-linux-history">
&lt;h2>A Bit of Linux History&lt;/h2>
&lt;p>Before talking specifically about Linux we can go a bit further back to 1969 at AT&amp;amp;T Bell Labs, where Dennis Ritchie,
Kenneth Thompson, and other colleagues began developing a small operating system for a very specific minicomputer.
That operating system was named Unix as a result of cooperation between Bell Labs and the academic community, in 1979
the &amp;quot;seventh edition&amp;quot; (V7) version of Unix was released, the base for every still existing Unix system. &lt;a class="footnote-reference" href="#tldphistory" id="id1">[1]&lt;/a>&lt;/p>
&lt;p>In 1984, Richard Stallman's &lt;a class="reference external" href="https://www.fsf.org/">Free Software Foundation (FSF)&lt;/a> began the
&lt;a class="reference external" href="https://www.gnu.org/">GNU project&lt;/a>, which had the objective of creating a freely used, read, modified and
redistributed version of the Unix operating system. The FSF created useful (and still used today) components. However,
the FSF had trouble with developing an operating system kernel to replace the Unix one (&lt;a class="reference external" href="https://www.gnu.org/software/hurd/">Hurd&lt;/a>)
and while they were developing it the Linux Kernel was released, filling the kernel gap in the GNU Project. &lt;a class="footnote-reference" href="#gnulinux" id="id2">[2]&lt;/a>&lt;/p>
&lt;p>In 1987, Andrew S. Tanenbaum developed MINIX, as a teaching tool for his textbook
&lt;a class="reference external" href="https://www.pearson.com/us/higher-education/program/Tanenbaum-Operating-Systems-Design-and-Implementation-3rd-Edition/PGM228096.html">Operating Systems Design and Implementation&lt;/a>
published in the same year. This was used by many students taking introductory courses on computer operating systems,
and one of them was Finnish student Linus Torvalds.&lt;/p>
&lt;p>In 1991, Linus Torvalds started a new project, which would later be called Linux. After a couple of months working on it
he reached out to the MINIX community for help, this marked what became the start of one of the biggest open-source
software projects:&lt;/p>
&lt;div class="highlight">&lt;pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">&lt;code class="language-fallback" data-lang="fallback">Hello everybody out there using minix -
I&amp;#39;m doing a (free) operating system (just a hobby, won&amp;#39;t be big and
professional like gnu) for 386(486) AT clones. This has been brewing
since april, and is starting to get ready. I&amp;#39;d like any feedback on
things people like/dislike in minix, as my OS resembles it somewhat
(same physical layout of the file-system (due to practical reasons)
among other things).
I&amp;#39;ve currently ported bash(1.08) and gcc(1.40), and things seem to work.
This implies that I&amp;#39;ll get something practical within a few months, and
I&amp;#39;d like to know what features most people would want. Any suggestions
are welcome, but I won&amp;#39;t promise I&amp;#39;ll implement them :-)
Linus (torv...@kruuna.helsinki.fi)
PS. Yes - it&amp;#39;s free of any minix code, and it has a multi-threaded fs.
It is NOT protable (uses 386 task switching etc), and it probably never
will support anything other than AT-harddisks, as that&amp;#39;s all I have :-(.&lt;/code>&lt;/pre>&lt;/div>
&lt;p>You can check the full conversation (plus more recent comments) in
&lt;a class="reference external" href="https://groups.google.com/forum/#!topic/comp.os.minix/dlNtH7RRrGA[1-25]">Google groups&lt;/a>&lt;/p>
&lt;p>8 years later, Linus Torvalds stated that Linux at the time had &lt;em>millions of users, thousands of developers, and a&lt;/em>
&lt;em>growing market. It is used in embedded systems; it is used to control robotic devices; it has flown on the space&lt;/em>
&lt;em>shuttle. I'd like to say that I knew this would happen, that it's all part of the plan for world domination. But&lt;/em>
&lt;em>honestly this has all taken me a bit by surprise I was much more aware of the transition from one Linux user to one&lt;/em>
&lt;em>hundred Linux users than the transition from one hundred to one million users.&lt;/em> &lt;a class="footnote-reference" href="#linuxacm" id="id3">[3]&lt;/a>&lt;/p>
&lt;div class="figure align-center" id="fig-tux">
&lt;a class="reference external image-reference" href="./img/Tux.svg">&lt;img alt="Tux - the Linux mascot" src="./img/Tux.png" />&lt;/a>
&lt;p class="caption">Tux - the Linux mascot&lt;/p>
&lt;/div>
&lt;p>23 years later, &lt;em>Linux is now the world’s largest and most pervasive open source software project in the history of&lt;/em>
&lt;em>computing. The Linux Kernel is the largest component of the Linux operating system and is charged with managing the&lt;/em>
&lt;em>hardware, running user programs, and maintaining the security and integrity of the whole system&lt;/em> &lt;a class="footnote-reference" href="#linuxfoundation" id="id4">[5]&lt;/a>
as we will see throughout this course.&lt;/p>
&lt;!-- ##################################################################################################################### -->
&lt;!-- ##################################################################################################################### -->
&lt;!-- ##################################################################################################################### -->
&lt;/div>
&lt;div class="section" id="an-overview-of-linux-kernel-components">
&lt;h2>An Overview of Linux Kernel Components&lt;/h2>
&lt;p>In &lt;a class="reference external" href="#fig-comparch">Figure 2&lt;/a> we can see how different hardware parts connect to make your computer's architecture. As a bare
minimum you should have a Central Processing Unit (CPU), some amount of Random Access Memory (RAM), some amount of
storage in the form of a Hard Disk Drive (HDD) or Solid State Drive (SSD), and some peripherals (i.e: your monitor,
keyboard and mouse). You can have more peripherals, different kinds of storage, or even more hardware connected to the
Input/Output (I/O) Bus (like network cards and graphics cards), but you cannot have less in order for an operating
system to function properly.&lt;/p>
&lt;div class="figure align-center" id="fig-comparch">
&lt;a class="reference external image-reference" href="./img/comparch.svg">&lt;object data="./img/comparch.svg" style="width: 50%;" type="image/svg+xml">Computer architecture overview&lt;/object>&lt;/a>
&lt;p class="caption">Computer architecture overview&lt;/p>
&lt;/div>
&lt;p>The operating system is what makes every one of these components work together, and some at all.&lt;/p>
&lt;p>In &lt;a class="reference external" href="#fig-kernelarch">Figure 3&lt;/a> we can see an overview of the different layers of abstraction that make the Linux Kernel
architecture.&lt;/p>
&lt;p>The bottom layer (the first), we have already mentioned earlier, represents every component physically connected to your
computer.&lt;/p>
&lt;div class="figure align-center" id="fig-kernelarch">
&lt;a class="reference external image-reference" href="./img/linux_kernel_arch.svg">&lt;object data="./img/linux_kernel_arch.svg" type="image/svg+xml">Linux Kernel architecture overview&lt;/object>&lt;/a>
&lt;p class="caption">Linux Kernel architecture overview&lt;/p>
&lt;/div>
&lt;p>The middle layer represents the &lt;em>Kernel Space&lt;/em>, this is a very special layer as it can interact directly with all the
hardware connected to your computer, it has the most privileges and can:&lt;/p>
&lt;ul class="simple">
&lt;li>manage which process is running on the CPU, through &lt;em>Process Management&lt;/em>&lt;/li>
&lt;li>manage memory allocation for individual processes, and even memory shared between processes, through
&lt;em>Memory Management&lt;/em>&lt;/li>
&lt;li>read and write storage devices through &lt;em>File Systems&lt;/em>&lt;/li>
&lt;li>draw images on your screen, send data through a network card, control a wide LED panel, or any other connected device
through &lt;em>Device Drivers&lt;/em>&lt;/li>
&lt;li>interact with other devices connected to a network through &lt;em>Networking&lt;/em>&lt;/li>
&lt;/ul>
&lt;p>The top layer, and the one you usually interact with, is &lt;em>User Space&lt;/em>. Here is where we have &lt;em>System Applications&lt;/em> that
can perform special administrative tasks, &lt;em>User Applications&lt;/em> like a browser or a music player, and &lt;em>Tools&lt;/em>, which may
go somewhere in between the previous two, available to all and may support applications, like software libraries.&lt;/p>
&lt;p>In sum, in the &lt;em>Hardware&lt;/em> layer is every device connected to you computer and every thing that makes your computer a
computer. In the &lt;em>Kernel Space&lt;/em> layer is every Kernel component that interacts directly with &lt;em>Hardware&lt;/em>. In the
&lt;em>User Space&lt;/em> layer we have applications and tools that allow us to reach the &lt;em>Hardware&lt;/em>, always going through the
&lt;em>Kernel Space&lt;/em> layer.&lt;/p>
&lt;!-- ##################################################################################################################### -->
&lt;!-- ##################################################################################################################### -->
&lt;!-- ##################################################################################################################### -->
&lt;/div>
&lt;div class="section" id="an-overview-of-the-linux-filesystem">
&lt;h2>An Overview of the Linux Filesystem&lt;/h2>
&lt;p>&lt;em>When migrating from another operating system such as Microsoft Windows to another; one thing that will profoundly&lt;/em>
&lt;em>affect the end user greatly will be the differences between the filesystems.&lt;/em> &lt;a class="footnote-reference" href="#tldpfsh" id="id5">[7]&lt;/a>&lt;/p>
&lt;p>&lt;em>A filesystem is the methods and data structures that an operating system uses to keep track of files on a disk or&lt;/em>
&lt;em>partition; that is, the way the files are organized on the disk. The word is also used to refer to a partition or disk&lt;/em>
&lt;em>that is used to store the files or the type of the filesystem. Thus, one might say I have two filesystems meaning one&lt;/em>
&lt;em>has two partitions on which one stores files, or that one is using the extended filesystem, meaning the type of the&lt;/em>
&lt;em>filesystem.&lt;/em> &lt;a class="footnote-reference" href="#tldpfsh" id="id6">[7]&lt;/a>&lt;/p>
&lt;p>&lt;em>The difference between a disk or partition and the filesystem it contains is important. A few programs (including,&lt;/em>
&lt;em>reasonably enough, programs that create filesystems) operate directly on the raw sectors of a disk or partition; if&lt;/em>
&lt;em>there is an existing file system there it will be destroyed or seriously corrupted. Most programs operate on a&lt;/em>
&lt;em>filesystem, and therefore won't work on a partition that doesn't contain one&lt;/em> &lt;a class="footnote-reference" href="#tldpfsh" id="id7">[7]&lt;/a>&lt;/p>
&lt;p>So we can look at the Linux Filesystem as a set of folders, or directories, that each have some associated function.
They can contain configuration files necessary for a proper operation of the Linux operating system, they can contain
applications (executable files), or they can contain arbitrary files such as our family photos or personal documents.&lt;/p>
&lt;p>The most top-level directory, which contains every other directory, is called the &lt;em>root&lt;/em> directory and is represented by
a forward slash (/).&lt;/p>
&lt;p>The following directories usually exist in &lt;em>/&lt;/em> :&lt;/p>
&lt;table border="1" class="docutils">
&lt;colgroup>
&lt;col width="50%" />
&lt;col width="50%" />
&lt;/colgroup>
&lt;thead valign="bottom">
&lt;tr>&lt;th class="head">Directory&lt;/th>
&lt;th class="head">Description&lt;/th>
&lt;/tr>
&lt;/thead>
&lt;tbody valign="top">
&lt;tr>&lt;td>/bin&lt;/td>
&lt;td>Essential command binaries&lt;/td>
&lt;/tr>
&lt;tr>&lt;td>/boot&lt;/td>
&lt;td>Static files of the boot loader&lt;/td>
&lt;/tr>
&lt;tr>&lt;td>/dev&lt;/td>
&lt;td>Device files&lt;/td>
&lt;/tr>
&lt;tr>&lt;td>/etc&lt;/td>
&lt;td>Host-specific system configuration&lt;/td>
&lt;/tr>
&lt;tr>&lt;td>/home&lt;/td>
&lt;td>User home directories&lt;/td>
&lt;/tr>
&lt;tr>&lt;td>/lib&lt;/td>
&lt;td>Essential shared libraries and kernel modules&lt;/td>
&lt;/tr>
&lt;tr>&lt;td>/media&lt;/td>
&lt;td>Mount point for removable media&lt;/td>
&lt;/tr>
&lt;tr>&lt;td>/mnt&lt;/td>
&lt;td>Mount point for mounting a filesystem temporarily&lt;/td>
&lt;/tr>
&lt;tr>&lt;td>/opt&lt;/td>
&lt;td>Add-on application software packages&lt;/td>
&lt;/tr>
&lt;tr>&lt;td>/proc&lt;/td>
&lt;td>Process information pseudo-file system&lt;/td>
&lt;/tr>
&lt;tr>&lt;td>/root&lt;/td>
&lt;td>Home directory for the root user&lt;/td>
&lt;/tr>
&lt;tr>&lt;td>/run&lt;/td>
&lt;td>Operating system runtime information&lt;/td>
&lt;/tr>
&lt;tr>&lt;td>/sbin&lt;/td>
&lt;td>Essential system binaries&lt;/td>
&lt;/tr>
&lt;tr>&lt;td>/srv&lt;/td>
&lt;td>Data for services provided by this system&lt;/td>
&lt;/tr>
&lt;tr>&lt;td>/tmp&lt;/td>
&lt;td>Temporary files&lt;/td>
&lt;/tr>
&lt;tr>&lt;td>/usr&lt;/td>
&lt;td>Secondary hierarchy&lt;/td>
&lt;/tr>
&lt;tr>&lt;td>/var&lt;/td>
&lt;td>Variable data&lt;/td>
&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;p>For operating systems using the &lt;em>systemd&lt;/em> init system some of these directories still exist for compatibility, however
in another form. They &amp;quot;point&amp;quot; to their homonyms under &lt;em>/usr&lt;/em> secondary hierarchy directory, these are:&lt;/p>
&lt;ul class="simple">
&lt;li>&lt;em>/bin&lt;/em>, &lt;em>/sbin/&lt;/em> which all point to &lt;em>/usr/bin&lt;/em>&lt;/li>
&lt;li>&lt;em>/lib&lt;/em> which points to &lt;em>/usr/lib&lt;/em>&lt;/li>
&lt;/ul>
&lt;p>For more information you can see systemd
&lt;a class="reference external" href="https://www.freedesktop.org/software/systemd/man/file-hierarchy.html#Compatibility%20Symlinks">documentation&lt;/a>
regarding file system hierarchy.&lt;/p>
&lt;!-- ##################################################################################################################### -->
&lt;!-- ##################################################################################################################### -->
&lt;!-- ##################################################################################################################### -->
&lt;/div>
&lt;div class="section" id="user-authentication-and-security">
&lt;h2>User Authentication and Security&lt;/h2>
&lt;p>In order for the kernel to be able to identify who is trying to access, modify or execute anything in the system it has
to have some type of authentication system. We will have two important categories, &lt;em>users&lt;/em> and &lt;em>groups&lt;/em>.&lt;/p>
&lt;p>User information is usually stored in your filesystem in the file &lt;em>/etc/passwd&lt;/em>. In this file each line represents a
user and its information separated by a colon (:).&lt;/p>
&lt;p>&lt;div class="highlight">&lt;pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">&lt;code class="language-shell" data-lang="shell">account:password:UID:GID:GECOS:directory:shell&lt;/code>&lt;/pre>&lt;/div>
Each field description can be seen in the table below:&lt;/p>
&lt;table border="1" class="docutils">
&lt;colgroup>
&lt;col width="50%" />
&lt;col width="50%" />
&lt;/colgroup>
&lt;thead valign="bottom">
&lt;tr>&lt;th class="head">Field&lt;/th>
&lt;th class="head">Description&lt;/th>
&lt;/tr>
&lt;/thead>
&lt;tbody valign="top">
&lt;tr>&lt;td>account&lt;/td>
&lt;td>The user's name. Should not contain uppercase letters&lt;/td>
&lt;/tr>
&lt;tr>&lt;td>password&lt;/td>
&lt;td>Encrypted user password or asterisks&lt;/td>
&lt;/tr>
&lt;tr>&lt;td>UID&lt;/td>
&lt;td>Numerical identification of the user&lt;/td>
&lt;/tr>
&lt;tr>&lt;td>GID&lt;/td>
&lt;td>Numerical identification of the user's primary group&lt;/td>
&lt;/tr>
&lt;tr>&lt;td>GECOS&lt;/td>
&lt;td>Optional field used for information purposes only. It usually contains the user's full name&lt;/td>
&lt;/tr>
&lt;tr>&lt;td>directory&lt;/td>
&lt;td>The user's home directory&lt;/td>
&lt;/tr>
&lt;tr>&lt;td>shell&lt;/td>
&lt;td>The command line interpreter to be used after login.&lt;/td>
&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;p>an example of a real user could be:&lt;/p>
&lt;div class="highlight">&lt;pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">&lt;code class="language-shell" data-lang="shell">t0rrant:K3xcO1Qnx8LFN:1001:1001:Manuel Torrinha,,,:/home/t0rrant:/bin/bash&lt;/code>&lt;/pre>&lt;/div>
&lt;p>Group information is usually stored in your filesystem in the file &lt;em>/etc/group&lt;/em>. In this file each line represents a
group and, like the &lt;em>/etc/passwd&lt;/em> file, its information separated by a colon (:).&lt;/p>
&lt;div class="highlight">&lt;pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">&lt;code class="language-shell" data-lang="shell">group_name:password:GID:user_list&lt;/code>&lt;/pre>&lt;/div>
&lt;p>Each field description can be seen in the table below:&lt;/p>
&lt;table border="1" class="docutils">
&lt;colgroup>
&lt;col width="50%" />
&lt;col width="50%" />
&lt;/colgroup>
&lt;thead valign="bottom">
&lt;tr>&lt;th class="head">Field&lt;/th>
&lt;th class="head">Description&lt;/th>
&lt;/tr>
&lt;/thead>
&lt;tbody valign="top">
&lt;tr>&lt;td>group_name&lt;/td>
&lt;td>The name of the group&lt;/td>
&lt;/tr>
&lt;tr>&lt;td>password&lt;/td>
&lt;td>Encrypted group password. If empty, no password is needed&lt;/td>
&lt;/tr>
&lt;tr>&lt;td>GID&lt;/td>
&lt;td>The numeric group identification&lt;/td>
&lt;/tr>
&lt;tr>&lt;td>user_list&lt;/td>
&lt;td>a list of usernames that are members of this group, separated by commas (,)&lt;/td>
&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;p>an example of a real group could be:&lt;/p>
&lt;div class="highlight">&lt;pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">&lt;code class="language-shell" data-lang="shell">ourgroup::132:t0rrant&lt;/code>&lt;/pre>&lt;/div>
&lt;p>With this information the kernel can establish who as access to what, as we will see how it works for files in the next
section, &lt;a class="reference internal" href="#file-attributes-and-ownership">File Attributes and Ownership&lt;/a>.&lt;/p>
&lt;div class="section" id="shadow-passwords">
&lt;h3>Shadow Passwords&lt;/h3>
&lt;p>You may have noticed that instead of an encrypted password in your &lt;em>/etc/passwd&lt;/em> file you have something like:&lt;/p>
&lt;div class="highlight">&lt;pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">&lt;code class="language-shell" data-lang="shell">t0rrant:x:1001:1001:Manuel Torrinha,,,:/home/t0rrant:/bin/bash&lt;/code>&lt;/pre>&lt;/div>
&lt;p>&lt;em>The /etc/passwd file, which contains information about all users, including their encrypted password, is readable by&lt;/em>
&lt;em>all users, making it possible for any user to get the encrypted password of everyone on the system. Though the&lt;/em>
&lt;em>passwords are encrypted, password−cracking programs are widely available. To combat this growing security threat,&lt;/em>
&lt;em>shadow passwords were developed&lt;/em> &lt;a class="footnote-reference" href="#tldpuserauth" id="id8">[8]&lt;/a>&lt;/p>
&lt;p>When shadow passwords are enabled the password field in the &lt;em>/etc/passwd&lt;/em> file is replaced by an &amp;quot;x&amp;quot; and the user's
actual password (encrypted) is stored in the &lt;em>/etc/shadow&lt;/em> file. This file is only readable by the &lt;em>root&lt;/em> user which
makes it a safer place to keep such sensitive information.&lt;/p>
&lt;!-- ##################################################################################################################### -->
&lt;!-- ##################################################################################################################### -->
&lt;!-- ##################################################################################################################### -->
&lt;/div>
&lt;/div>
&lt;div class="section" id="file-attributes-and-ownership">
&lt;h2>File Attributes and Ownership&lt;/h2>
&lt;p>&lt;em>On a Linux system, every file is owned by a user and a group user. There is also a third category of users, those&lt;/em>
&lt;em>that are not the user owner and don't belong to the group owning the file.&lt;/em> &lt;a class="footnote-reference" href="#tldpch3" id="id9">[11]&lt;/a>&lt;/p>
&lt;p>For each of these user categories, we can enable or disable &lt;em>read&lt;/em>, &lt;em>write&lt;/em>, or &lt;em>execute&lt;/em> permissions.&lt;/p>
&lt;p>The owner and group user for a file is represented by its identifier (&lt;em>id&lt;/em>), but more commonly you will see the &lt;em>name&lt;/em>
corresponding to that &lt;em>id&lt;/em> for readability.&lt;/p>
&lt;p>The permissions for each user category can be defined in octal (8 digit system instead of 10), which conveniently maps
to a three digit binary, meaning that we can easily represent every combination of &lt;em>rwx&lt;/em> (read, write, execute) with a
single octal digit, as we can see in the table below:&lt;/p>
&lt;table border="1" class="docutils">
&lt;colgroup>
&lt;col width="33%" />
&lt;col width="33%" />
&lt;col width="33%" />
&lt;/colgroup>
&lt;thead valign="bottom">
&lt;tr>&lt;th class="head">Octal&lt;/th>
&lt;th class="head">Binary&lt;/th>
&lt;th class="head">File Permission&lt;/th>
&lt;/tr>
&lt;/thead>
&lt;tbody valign="top">
&lt;tr>&lt;td>0&lt;/td>
&lt;td>000&lt;/td>
&lt;td>---&lt;/td>
&lt;/tr>
&lt;tr>&lt;td>1&lt;/td>
&lt;td>001&lt;/td>
&lt;td>--x&lt;/td>
&lt;/tr>
&lt;tr>&lt;td>2&lt;/td>
&lt;td>010&lt;/td>
&lt;td>-w-&lt;/td>
&lt;/tr>
&lt;tr>&lt;td>3&lt;/td>
&lt;td>011&lt;/td>
&lt;td>-wx&lt;/td>
&lt;/tr>
&lt;tr>&lt;td>4&lt;/td>
&lt;td>100&lt;/td>
&lt;td>r--&lt;/td>
&lt;/tr>
&lt;tr>&lt;td>5&lt;/td>
&lt;td>101&lt;/td>
&lt;td>r-x&lt;/td>
&lt;/tr>
&lt;tr>&lt;td>6&lt;/td>
&lt;td>110&lt;/td>
&lt;td>rw-&lt;/td>
&lt;/tr>
&lt;tr>&lt;td>7&lt;/td>
&lt;td>111&lt;/td>
&lt;td>rwx&lt;/td>
&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;p>Directories are also files in their essence, but they are distinguished by a special bit which is either &lt;em>on&lt;/em> or &lt;em>off&lt;/em>,
and represented by &lt;em>d&lt;/em> or &lt;em>-&lt;/em>, respectively.&lt;/p>
&lt;p>So if we have a representation like this&lt;/p>
&lt;div class="highlight">&lt;pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">&lt;code class="language-shell" data-lang="shell">drwxr-xr-x t0rrant t0rrant Personal
-rw-r----- t0rrant ourgoup private_file&lt;/code>&lt;/pre>&lt;/div>
&lt;p>it means that the directory &lt;em>Personal&lt;/em> can be &lt;em>read&lt;/em> and &lt;em>executed&lt;/em> by its owner &lt;em>t0rrant&lt;/em>, everyone in the group
&lt;em>t0rrant&lt;/em> and everyone else, but can only be &lt;em>written&lt;/em> by its owner &lt;em>t0rrant&lt;/em>. In octal we would represent this as
&lt;em>755&lt;/em>.&lt;/p>
&lt;p>The file &lt;em>private_file&lt;/em> can be &lt;em>read&lt;/em> by its owner &lt;em>t0rrant&lt;/em> and everyone in the group &lt;em>ourgoup&lt;/em> but can only be written
by its owner &lt;em>t0rrant&lt;/em>. In octal we would represent this as &lt;em>640&lt;/em>.&lt;/p>
&lt;p>Every directory that should have the &lt;em>read&lt;/em> permission has to also have the &lt;em>executable&lt;/em> permission, or else it will not
be accessible.&lt;/p>
&lt;p>Besides using the octal notation we can also use a character notation, which is sometimes easier to add or remove
permissions from a file. This notation is used by defining one or more of &lt;em>user&lt;/em> (u), &lt;em>group&lt;/em> (g) or &lt;em>other&lt;/em> (o)
categories, followed by the plus (+) sign if we want to add, or minus (-) if we want to remove permissions, followed
by the permissions we want to add or remove in &lt;em>rwx&lt;/em> form.&lt;/p>
&lt;p>So if we, for example, wanted to add just &lt;em>writable&lt;/em> permissions to the file &lt;em>private_file&lt;/em> for members of the group
&lt;em>ourgroup&lt;/em> we could do so by writting &lt;em>g+w&lt;/em>. Seemingly, if we wanted to add &lt;em>executable&lt;/em> permissions to the same file
to the owner and group we would write &lt;em>ug+x&lt;/em>.&lt;/p>
&lt;div class="section" id="file-types">
&lt;h3>File Types&lt;/h3>
&lt;p>In Windows systems you are probably used to distinguishing a file's type by its extension. If you have a file named
&lt;em>myfile.pdf&lt;/em> it represents a Portable Document Format (PDF) file, and Windows &amp;quot;knows&amp;quot; which application should open such
a file because it has stored somewhere the relationship between file names ending in &lt;em>.pdf&lt;/em> and the Adobe Reader
application.&lt;/p>
&lt;p>The same is not true for Linux systems. In fact, besides the file extension, the file itself has information of what
type it is in the first bytes of the file. If we could take a look at the start of the PDF file as it were a normal text
file we would see something like this:&lt;/p>
&lt;div class="highlight">&lt;pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">&lt;code class="language-shell" data-lang="shell"> %PDF-1.4&lt;/code>&lt;/pre>&lt;/div>
&lt;p>Let us take a look at another file type, Portable Network Graphics (PNG) for example&lt;/p>
&lt;div class="highlight">&lt;pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">&lt;code class="language-shell" data-lang="shell"> �PNG&lt;/code>&lt;/pre>&lt;/div>
&lt;p>That � character appears because the content cannot be converted to another readable character, which means that there
is something there, just not in a usual readable format.&lt;/p>
&lt;p>So we can see that files themselves have information of which type of file they are.&lt;/p>
&lt;!-- ##################################################################################################################### -->
&lt;!-- ##################################################################################################################### -->
&lt;!-- ##################################################################################################################### -->
&lt;/div>
&lt;/div>
&lt;div class="section" id="bash-your-friendly-command-line-interpreter">
&lt;h2>Bash, your friendly command-line interpreter&lt;/h2>
&lt;p>&lt;em>Bash is the shell, or command language interpreter, for the GNU operating system. The name is an acronym for the&lt;/em>
&lt;em>Bourne-Again SHell, a pun on Stephen Bourne, the author of the direct ancestor of the current Unix shell sh, which&lt;/em>
&lt;em>appeared in the Seventh Edition Bell Labs Research version of Unix.&lt;/em> &lt;a class="footnote-reference" href="#gnubashdocs" id="id10">[6]&lt;/a>&lt;/p>
&lt;p>In Linux graphical environments, the case with our Ubuntu 20.04, we can use &lt;em>Bash&lt;/em> by launching the &lt;em>Terminal&lt;/em>
application which is usually associated with the icon seen in &lt;a class="reference external" href="#fig-gnome-terminal">Figure 4&lt;/a> or some variation of it.&lt;/p>
&lt;div class="figure align-center" id="fig-gnome-terminal">
&lt;a class="reference external image-reference" href="./img/gnome_terminal_icon.svg">&lt;img alt="Gnome Terminal icon" src="./img/gnome_terminal_icon.png" />&lt;/a>
&lt;p class="caption">Gnome Terminal icon&lt;/p>
&lt;/div>
&lt;p>When interacting with such Terminal applications we are presented a prompt followed by a flashing cursor, which
represents where the next character we input will appear. The prompt usually has the following appearance:&lt;/p>
&lt;div class="highlight">&lt;pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">&lt;code class="language-shell" data-lang="shell">&amp;lt;current user name&amp;gt;@&amp;lt;machine name&amp;gt;:&amp;lt;current working directory&amp;gt;$&lt;/code>&lt;/pre>&lt;/div>
&lt;p>Most of you that have already interacted with a Linux system have probably copy &amp;amp; pasted some instructions to install
an application or configure something in your system.&lt;/p>
&lt;p>Now you will be able to better understand how those instructions work underneath.&lt;/p>
&lt;div class="section" id="simple-commands">
&lt;h3>Simple Commands&lt;/h3>
&lt;p>Here we will see some commands we can enter in Bash that interact with different Linux components.&lt;/p>
&lt;div class="section" id="user-system">
&lt;h4>User System&lt;/h4>
&lt;p>Identify the user running a command:&lt;/p>
&lt;div class="highlight">&lt;pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">&lt;code class="language-shell" data-lang="shell">t0rrant@testing:~$ whoami
t0rrant
t0rrant@testing:~$&lt;/code>&lt;/pre>&lt;/div>
&lt;p>See to which groups a user belongs:&lt;/p>
&lt;div class="highlight">&lt;pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">&lt;code class="language-shell" data-lang="shell">t0rrant@testing:~$ groups
t0rrant sudo
t0rrant@testing:~$&lt;/code>&lt;/pre>&lt;/div>
&lt;p>or a more broad user view:&lt;/p>
&lt;div class="highlight">&lt;pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">&lt;code class="language-shell" data-lang="shell">t0rrant@testing:~$ id
uid&lt;span style="color:#f92672">=&lt;/span>1001&lt;span style="color:#f92672">(&lt;/span>t0rrant&lt;span style="color:#f92672">)&lt;/span> gid&lt;span style="color:#f92672">=&lt;/span>1001&lt;span style="color:#f92672">(&lt;/span>t0rrant&lt;span style="color:#f92672">)&lt;/span> groups&lt;span style="color:#f92672">=&lt;/span>1001&lt;span style="color:#f92672">(&lt;/span>t0rrant&lt;span style="color:#f92672">)&lt;/span>,27&lt;span style="color:#f92672">(&lt;/span>sudo&lt;span style="color:#f92672">)&lt;/span>
t0rrant@testing:~$ id root
uid&lt;span style="color:#f92672">=&lt;/span>0&lt;span style="color:#f92672">(&lt;/span>root&lt;span style="color:#f92672">)&lt;/span> gid&lt;span style="color:#f92672">=&lt;/span>0&lt;span style="color:#f92672">(&lt;/span>root&lt;span style="color:#f92672">)&lt;/span> grupos&lt;span style="color:#f92672">=&lt;/span>0&lt;span style="color:#f92672">(&lt;/span>root&lt;span style="color:#f92672">)&lt;/span>
t0rrant@testing:~$&lt;/code>&lt;/pre>&lt;/div>
&lt;/div>
&lt;div class="section" id="file-system">
&lt;h4>File System&lt;/h4>
&lt;p>List files and directories located in our current working directory:&lt;/p>
&lt;div class="highlight">&lt;pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">&lt;code class="language-shell" data-lang="shell">t0rrant@testing:~$ ls
Desktop Documents Downloads Images Music Videos
t0rrant@testing:~$&lt;/code>&lt;/pre>&lt;/div>
&lt;p>or perhaps you have folders with other names, more folders, less folders...&lt;/p>
&lt;p>It is also common to have coloured output, in which each colour represents a particular characteristic of the file. It
may be that the file is a normal file (white), a directory (blue), an executable file (green), a link to another existing file (teal), a link
to a non-existing file (red), a file that is accessible by any user (blue with a green highlight).&lt;/p>
&lt;p>List files and directories for a specific location:&lt;/p>
&lt;div class="highlight">&lt;pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">&lt;code class="language-shell" data-lang="shell">t0rrant@testing:~$ ls Images/
dontpanic.jpg
t0rrant@testing:~$&lt;/code>&lt;/pre>&lt;/div>
&lt;p>List files, including hidden files:&lt;/p>
&lt;div class="highlight">&lt;pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">&lt;code class="language-shell" data-lang="shell">t0rrant@testing:~$ ls -a
. .bashrc Downloads .profile
.. Desktop Images Videos
.bash_history Documents Music
t0rrant@testing:~$&lt;/code>&lt;/pre>&lt;/div>
&lt;p>List files, and see more detailed properties:&lt;/p>
&lt;div class="highlight">&lt;pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">&lt;code class="language-shell" data-lang="shell">t0rrant@testing:~$ ls -l
total &lt;span style="color:#ae81ff">24&lt;/span>
drwxr-xr-x &lt;span style="color:#ae81ff">2&lt;/span> t0rrant t0rrant &lt;span style="color:#ae81ff">4096&lt;/span> May &lt;span style="color:#ae81ff">13&lt;/span> 23:58 Desktop
drwxr-xr-x &lt;span style="color:#ae81ff">2&lt;/span> t0rrant t0rrant &lt;span style="color:#ae81ff">4096&lt;/span> May &lt;span style="color:#ae81ff">13&lt;/span> 23:58 Documents
drwxr-xr-x &lt;span style="color:#ae81ff">2&lt;/span> t0rrant t0rrant &lt;span style="color:#ae81ff">4096&lt;/span> May &lt;span style="color:#ae81ff">13&lt;/span> 23:58 Downloads
drwxr-xr-x &lt;span style="color:#ae81ff">2&lt;/span> t0rrant t0rrant &lt;span style="color:#ae81ff">4096&lt;/span> May &lt;span style="color:#ae81ff">13&lt;/span> 23:58 Images
drwxr-xr-x &lt;span style="color:#ae81ff">2&lt;/span> t0rrant t0rrant &lt;span style="color:#ae81ff">4096&lt;/span> May &lt;span style="color:#ae81ff">13&lt;/span> 23:58 Music
drwxr-xr-x &lt;span style="color:#ae81ff">2&lt;/span> t0rrant t0rrant &lt;span style="color:#ae81ff">4096&lt;/span> May &lt;span style="color:#ae81ff">13&lt;/span> 23:58 Videos
t0rrant@testing:~$&lt;/code>&lt;/pre>&lt;/div>
&lt;p>Create an empty file or change its modification time, if it exists:&lt;/p>
&lt;div class="highlight">&lt;pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">&lt;code class="language-shell" data-lang="shell">t0rrant@testing:~$ touch myfile
t0rrant@testing:~$ ls -l
total &lt;span style="color:#ae81ff">24&lt;/span>
drwxr-xr-x &lt;span style="color:#ae81ff">2&lt;/span> t0rrant t0rrant &lt;span style="color:#ae81ff">4096&lt;/span> May &lt;span style="color:#ae81ff">13&lt;/span> 23:58 Desktop
drwxr-xr-x &lt;span style="color:#ae81ff">2&lt;/span> t0rrant t0rrant &lt;span style="color:#ae81ff">4096&lt;/span> May &lt;span style="color:#ae81ff">13&lt;/span> 23:58 Documents
drwxr-xr-x &lt;span style="color:#ae81ff">2&lt;/span> t0rrant t0rrant &lt;span style="color:#ae81ff">4096&lt;/span> May &lt;span style="color:#ae81ff">13&lt;/span> 23:58 Downloads
drwxr-xr-x &lt;span style="color:#ae81ff">2&lt;/span> t0rrant t0rrant &lt;span style="color:#ae81ff">4096&lt;/span> May &lt;span style="color:#ae81ff">13&lt;/span> 23:58 Images
drwxr-xr-x &lt;span style="color:#ae81ff">2&lt;/span> t0rrant t0rrant &lt;span style="color:#ae81ff">4096&lt;/span> May &lt;span style="color:#ae81ff">13&lt;/span> 23:58 Music
drwxr-xr-x &lt;span style="color:#ae81ff">2&lt;/span> t0rrant t0rrant &lt;span style="color:#ae81ff">4096&lt;/span> May &lt;span style="color:#ae81ff">13&lt;/span> 23:58 Videos
-rw-rw-r-- &lt;span style="color:#ae81ff">1&lt;/span> t0rrant t0rrant &lt;span style="color:#ae81ff">0&lt;/span> May &lt;span style="color:#ae81ff">14&lt;/span> 00:05 temp
t0rrant@testing:~$&lt;/code>&lt;/pre>&lt;/div>
&lt;p>The &lt;em>echo&lt;/em> command outputs whatever we pass to it:&lt;/p>
&lt;div class="highlight">&lt;pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">&lt;code class="language-shell" data-lang="shell">t0rrant@testing:~$ echo Hello World
Hello World&lt;/code>&lt;/pre>&lt;/div>
&lt;p>This can be useful to, for example, add content to a file:&lt;/p>
&lt;div class="highlight">&lt;pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">&lt;code class="language-shell" data-lang="shell">t0rrant@testing:~$ echo Hello World &amp;gt; temp
t0rrant@testing:~$&lt;/code>&lt;/pre>&lt;/div>
&lt;p>The &lt;em>cat&lt;/em> command outputs a file contents:&lt;/p>
&lt;div class="highlight">&lt;pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">&lt;code class="language-shell" data-lang="shell">t0rrant@testing:~$ cat temp
Hello World
t0rrant@testing:~$&lt;/code>&lt;/pre>&lt;/div>
&lt;p>The &lt;em>&amp;gt;&lt;/em> character we used earlier redirects whatever would go to the output (standard output, or &lt;em>stdout&lt;/em>) to a file,
the file name is whatever comes after the &lt;em>&amp;gt;&lt;/em>. This special character will &lt;strong>overwrite&lt;/strong> the file contents entirely!
So we can also use another special character, the &lt;em>&amp;gt;&amp;gt;&lt;/em> which &lt;strong>appends&lt;/strong> the content to the end of the file, i.e:&lt;/p>
&lt;div class="highlight">&lt;pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">&lt;code class="language-shell" data-lang="shell">t0rrant@testing:~$ cat temp
Hello World
t0rrant@testing:~$ echo Hello World! &amp;gt; temp
t0rrant@testing:~$ cat temp
Hello World!
t0rrant@testing:~$ echo Goodbye World! &amp;gt;&amp;gt; temp
t0rrant@testing:~$ cat temp
Hello World
Goodbye World
t0rrant@testing:~$&lt;/code>&lt;/pre>&lt;/div>
&lt;p>We can also see a list of previously run commands:&lt;/p>
&lt;div class="highlight">&lt;pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">&lt;code class="language-shell" data-lang="shell">t0rrant@testing:~$ history
&lt;span style="color:#ae81ff">1&lt;/span> whoami
&lt;span style="color:#ae81ff">2&lt;/span> groups
&lt;span style="color:#ae81ff">3&lt;/span> id
&lt;span style="color:#ae81ff">4&lt;/span> ls
&lt;span style="color:#ae81ff">5&lt;/span> ls -a
&lt;span style="color:#ae81ff">6&lt;/span> ls -l
&lt;span style="color:#ae81ff">7&lt;/span> touch myfile
&lt;span style="color:#ae81ff">8&lt;/span> ls -l
&lt;span style="color:#ae81ff">9&lt;/span> echo Hello World
&lt;span style="color:#ae81ff">10&lt;/span> echo Hello World &amp;gt; temp
&lt;span style="color:#ae81ff">11&lt;/span> cat temp
&lt;span style="color:#ae81ff">12&lt;/span> cat temp
&lt;span style="color:#ae81ff">13&lt;/span> echo Hello World! &amp;gt; temp
&lt;span style="color:#ae81ff">14&lt;/span> cat temp
&lt;span style="color:#ae81ff">15&lt;/span> echo Goodbye World! &amp;gt;&amp;gt; temp
&lt;span style="color:#ae81ff">16&lt;/span> cat temp
&lt;span style="color:#ae81ff">17&lt;/span> history&lt;/code>&lt;/pre>&lt;/div>
&lt;p>And run one of those commands again with the &lt;em>!&lt;/em> special command:&lt;/p>
&lt;div class="highlight">&lt;pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">&lt;code class="language-shell" data-lang="shell">t0rrant@testing:~$ !1
whoami
t0rrant
t0rrant@testing:~$&lt;/code>&lt;/pre>&lt;/div>
&lt;p>We can also run the previous command by using the &lt;em>!!&lt;/em> special command:&lt;/p>
&lt;div class="highlight">&lt;pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">&lt;code class="language-shell" data-lang="shell">t0rrant@testing:~$ !1
whoami
t0rrant
t0rrant@testing:~$ !!
whoami
t0rrant
t0rrant@testing:~$&lt;/code>&lt;/pre>&lt;/div>
&lt;/div>
&lt;div class="section" id="man-pages">
&lt;h4>Man Pages&lt;/h4>
&lt;p>A very (very!) helpful command is &lt;em>man&lt;/em>. This command allows you to read the documentation for a specific item, also
known as &lt;em>man pages&lt;/em>. It makes use of the &lt;em>less&lt;/em> command to read those files. You can use the arrow keys to move around,
use the &lt;em>h&lt;/em> key to see the help screen or the &lt;em>q&lt;/em> key to exit.&lt;/p>
&lt;div class="highlight">&lt;pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">&lt;code class="language-shell" data-lang="shell">t0rrant@testing:~$ man ls
LS&lt;span style="color:#f92672">(&lt;/span>1&lt;span style="color:#f92672">)&lt;/span> User Commands LS&lt;span style="color:#f92672">(&lt;/span>1&lt;span style="color:#f92672">)&lt;/span>
NAME
ls - list directory contents
SYNOPSIS
ls &lt;span style="color:#f92672">[&lt;/span>OPTION&lt;span style="color:#f92672">]&lt;/span>... &lt;span style="color:#f92672">[&lt;/span>FILE&lt;span style="color:#f92672">]&lt;/span>...
DESCRIPTION
List information about the FILEs &lt;span style="color:#f92672">(&lt;/span>the current directory by default&lt;span style="color:#f92672">)&lt;/span>.
Sort entries alphabetically &lt;span style="color:#66d9ef">if&lt;/span> none of -cftuvSUX nor --sort is speci‐
fied.
Mandatory arguments to long options are mandatory &lt;span style="color:#66d9ef">for&lt;/span> short options
too.
-a, --all
&lt;span style="color:#66d9ef">do&lt;/span> not ignore entries starting with .
-A, --almost-all
&lt;span style="color:#66d9ef">do&lt;/span> not list implied . and ..
--author
Manual page ls&lt;span style="color:#f92672">(&lt;/span>1&lt;span style="color:#f92672">)&lt;/span> line &lt;span style="color:#ae81ff">1&lt;/span> &lt;span style="color:#f92672">(&lt;/span>press h &lt;span style="color:#66d9ef">for&lt;/span> help or q to quit&lt;span style="color:#f92672">)&lt;/span>
t0rrant@testing:~$&lt;/code>&lt;/pre>&lt;/div>
&lt;p>There are even man pages for the &lt;em>man&lt;/em> command, try it out!&lt;/p>
&lt;/div>
&lt;/div>
&lt;div class="section" id="variables">
&lt;h3>Variables&lt;/h3>
&lt;p>A variable is a &amp;quot;word&amp;quot; you can assign a value to. Shell variables are defined in uppercase, by convention. Bash has two
types of variables:&lt;/p>
&lt;ul class="simple">
&lt;li>Global (or environment) variables&lt;/li>
&lt;li>Local variables&lt;/li>
&lt;/ul>
&lt;p>Global variables are available in all Bash sessions, they can be displayed using the &lt;cite>env&lt;/cite> command. These variables
contain information that should be persistent across all shells, such as:&lt;/p>
&lt;ul class="simple">
&lt;li>the user running the shell, &lt;cite>USER&lt;/cite>&lt;/li>
&lt;li>the name of the machine the shell runs in, &lt;cite>HOSTNAME&lt;/cite>&lt;/li>
&lt;li>the path for the current working directory, &lt;cite>PWD&lt;/cite>&lt;/li>
&lt;li>the path for the previous working directory, &lt;cite>OLDPWD&lt;/cite>&lt;/li>
&lt;li>the language used by the system, &lt;cite>LANGUAGE&lt;/cite>&lt;/li>
&lt;li>the path for the shell executable we are running, &lt;cite>SHELL&lt;/cite>&lt;/li>
&lt;/ul>
&lt;p>Local variables are words specific to an execution environment. What this means is that if you define a variable in one
Bash session, it is not available to another Bash session. They are also displayed with the &lt;cite>env&lt;/cite> command, mixed with
the environment variables.&lt;/p>
&lt;p>&lt;em>Apart from dividing variables in local and global variables, we can also divide them in categories according to the sort
of content the variable contains. In this respect, variables come in 4 types&lt;/em>: &lt;a class="footnote-reference" href="#tldpbashguide" id="id11">[9]&lt;/a>&lt;/p>
&lt;ul class="simple">
&lt;li>String variables&lt;/li>
&lt;li>Integer variables&lt;/li>
&lt;li>Constant variables&lt;/li>
&lt;li>Array variables&lt;/li>
&lt;/ul>
&lt;p>In order to simplify this subject we will focus on &lt;em>String&lt;/em> and &lt;em>Integer&lt;/em> variables only.&lt;/p>
&lt;p>To assign a value to a variable, in a shell, we do the following:&lt;/p>
&lt;div class="highlight">&lt;pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">&lt;code class="language-shell" data-lang="shell">t0rrant@testing:~$ MYVAR&lt;span style="color:#f92672">=&lt;/span>&lt;span style="color:#e6db74">&amp;#34;myvalue&amp;#34;&lt;/span>
t0rrant@testing:~$&lt;/code>&lt;/pre>&lt;/div>
&lt;p>We should not put spaces around the equal (=) sign, as it will cause the shell to error. It is also a good practice to
always surround our String variables with quotes in order to reduce our chances of making errors.&lt;/p>
&lt;p>To access a variable's value we use the dollar ($) sign in the form &lt;em>$VARNAME&lt;/em>&lt;/p>
&lt;div class="highlight">&lt;pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">&lt;code class="language-shell" data-lang="shell">t0rrant@testing:~$ echo $MYVAR
myvalue
t0rrant@testing:~$&lt;/code>&lt;/pre>&lt;/div>
&lt;p>In the case of local variables defining a variable in the form &lt;em>VARNAME=value&lt;/em> make the variable only available to the
current shell, but not to applications or processes that are child processes of that shell.&lt;/p>
&lt;div class="highlight">&lt;pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">&lt;code class="language-shell" data-lang="shell">t0rrant@testing:~$ bash -c echo $MYVAR
t0rrant@testing:~$&lt;/code>&lt;/pre>&lt;/div>
&lt;p>In order to make variables available to child processes we need to use the &lt;em>export&lt;/em> command.&lt;/p>
&lt;div class="highlight">&lt;pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">&lt;code class="language-shell" data-lang="shell">t0rrant@testing:~$ export MYVAR&lt;span style="color:#f92672">=&lt;/span>&lt;span style="color:#e6db74">&amp;#34;myvalue&amp;#34;&lt;/span>
t0rrant@testing:~$ bash -c echo $MYVAR
myvalue
t0rrant@testing:~$&lt;/code>&lt;/pre>&lt;/div>
&lt;p>Besides defining and displaying variables, we can interact with variables in interesting ways.&lt;/p>
&lt;p>We can concatenate strings:&lt;/p>
&lt;div class="highlight">&lt;pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">&lt;code class="language-shell" data-lang="shell">t0rrant@testing:~$ HELLO&lt;span style="color:#f92672">=&lt;/span>&lt;span style="color:#e6db74">&amp;#34;Olá&amp;#34;&lt;/span>
t0rrant@testing:~$ WORLD&lt;span style="color:#f92672">=&lt;/span>&lt;span style="color:#e6db74">&amp;#34;Mundo!&amp;#34;&lt;/span>
t0rrant@testing:~$ HELLOWORLD&lt;span style="color:#f92672">=&lt;/span>&lt;span style="color:#e6db74">&amp;#34;&lt;/span>$HELLO&lt;span style="color:#e6db74"> &lt;/span>$WORLD&lt;span style="color:#e6db74">&amp;#34;&lt;/span>
t0rrant@testing:~$ echo $HELLOWORLD
Olá Mundo!
t0rrant@testing:~$&lt;/code>&lt;/pre>&lt;/div>
&lt;p>We can do arithmetic operations with Integers by surrounding our operation with &lt;em>$(( ... ))&lt;/em>:&lt;/p>
&lt;div class="highlight">&lt;pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">&lt;code class="language-shell" data-lang="shell">t0rrant@testing:~$ ONE&lt;span style="color:#f92672">=&lt;/span>&lt;span style="color:#ae81ff">1&lt;/span>
t0rrant@testing:~$ RESULT&lt;span style="color:#f92672">=&lt;/span>&lt;span style="color:#66d9ef">$((&lt;/span>$ONE&lt;span style="color:#f92672">+&lt;/span>$ONE&lt;span style="color:#66d9ef">))&lt;/span>
t0rrant@testing:~$ echo $RESULT
&lt;span style="color:#ae81ff">2&lt;/span>
t0rrant@testing:~$&lt;/code>&lt;/pre>&lt;/div>
&lt;p>We can mix it all up:&lt;/p>
&lt;div class="highlight">&lt;pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">&lt;code class="language-shell" data-lang="shell">t0rrant@testing:~$ ONE&lt;span style="color:#f92672">=&lt;/span>&lt;span style="color:#ae81ff">1&lt;/span>
t0rrant@testing:~$ RESULT&lt;span style="color:#f92672">=&lt;/span>&lt;span style="color:#66d9ef">$((&lt;/span>$ONE&lt;span style="color:#f92672">+&lt;/span>$ONE&lt;span style="color:#66d9ef">))&lt;/span>
t0rrant@testing:~$ QUESTION&lt;span style="color:#f92672">=&lt;/span>&lt;span style="color:#e6db74">&amp;#34;One plus One&amp;#34;&lt;/span>
t0rrant@testing:~$ echo $QUESTION is $RESULT
One plus One is &lt;span style="color:#ae81ff">2&lt;/span>
t0rrant@testing:~$&lt;/code>&lt;/pre>&lt;/div>
&lt;p>Shell arithmetic is complex and is not the focus of this section, if you would like to know more see the
&lt;a class="reference external" href="https://www.gnu.org/software/bash/manual/html_node/Shell-Arithmetic.html#Shell-Arithmetic">official documentation&lt;/a>
on the subject.&lt;/p>
&lt;div class="section" id="special-variables">
&lt;h4>Special Variables&lt;/h4>
&lt;p>The shell treats several parameters specially. These parameters may only be referenced, assignment to them is not
allowed. &lt;a class="footnote-reference" href="#tldpbashguide" id="id12">[9]&lt;/a>&lt;/p>
&lt;table border="1" class="docutils">
&lt;colgroup>
&lt;col width="50%" />
&lt;col width="50%" />
&lt;/colgroup>
&lt;thead valign="bottom">
&lt;tr>&lt;th class="head">Variable&lt;/th>
&lt;th class="head">Value Expansion&lt;/th>
&lt;/tr>
&lt;/thead>
&lt;tbody valign="top">
&lt;tr>&lt;td>$*&lt;/td>
&lt;td>Expands to the positional parameters, starting from one. When the expansion occurs within double quotes, it
expands to a single word with the value of each parameter separated by the first character of the IFS special
variable.&lt;/td>
&lt;/tr>
&lt;tr>&lt;td>$&amp;#64;&lt;/td>
&lt;td>Expands to the positional parameters, starting from one. When the expansion occurs within double quotes, each
parameter expands to a separate word.&lt;/td>
&lt;/tr>
&lt;tr>&lt;td>$#&lt;/td>
&lt;td>Expands to the number of positional parameters in decimal.&lt;/td>
&lt;/tr>
&lt;tr>&lt;td>$?&lt;/td>
&lt;td>Expands to the exit status of the most recently executed foreground pipeline.&lt;/td>
&lt;/tr>
&lt;tr>&lt;td>$-&lt;/td>
&lt;td>A hyphen expands to the current option flags as specified upon invocation, by the set built-in command, or those
set by the shell itself (such as the -i).&lt;/td>
&lt;/tr>
&lt;tr>&lt;td>$$&lt;/td>
&lt;td>Expands to the process ID of the shell.&lt;/td>
&lt;/tr>
&lt;tr>&lt;td>$!&lt;/td>
&lt;td>Expands to the process ID of the most recently executed background (asynchronous) command.&lt;/td>
&lt;/tr>
&lt;tr>&lt;td>$0&lt;/td>
&lt;td>Expands to the name of the shell or shell script.&lt;/td>
&lt;/tr>
&lt;tr>&lt;td>$_&lt;/td>
&lt;td>The underscore variable is set at shell startup and contains the absolute file name of the shell or script being
executed as passed in the argument list. Subsequently, it expands to the last argument to the previous command, after
expansion. It is also set to the full pathname of each command executed and placed in the environment exported to
that command. When checking mail, this parameter holds the name of the mail file.&lt;/td>
&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;p>Some of these variables are pretty useless for our previous interactions with the shell, however they are &lt;strong>very&lt;/strong>
useful for shell scripting, which we will take a look into in next section.&lt;/p>
&lt;/div>
&lt;/div>
&lt;div class="section" id="file-editing">
&lt;h3>File Editing&lt;/h3>
&lt;p>One thing we have not covered yet is how to edit files. In Linux you have a lot of different options, be it a Graphical
User Interface (GUI) or Command Line Interface (CLI) applications. If you talk to one user you will ear &lt;em>Vim, of course&lt;/em>
&lt;em>, it's very powerful&lt;/em>, others will say &lt;em>Use Emacs, it's the best and most powerful!&lt;/em>,
&lt;a class="reference external" href="https://en.wikipedia.org/wiki/Editor_war">well&lt;/a> ... I say &lt;em>Use whatever tool you have the time to learn&lt;/em>, and trust
me when I say those two take some time to learn. I rather start off with something more simple and straight forward.
However, if you would really like to know about those two and other CLI text editors you should check
&lt;a class="reference external" href="https://www.openvim.com/">openvim&lt;/a> and &lt;a class="reference external" href="https://www.gnu.org/software/emacs/tour/">gnu&lt;/a> pages.&lt;/p>
&lt;p>In Ubuntu 20.04 you should have the following applications available:&lt;/p>
&lt;ul class="simple">
&lt;li>nano (CLI)&lt;/li>
&lt;li>gedit (GUI)&lt;/li>
&lt;/ul>
&lt;p>The most familiar for you to use is probably &lt;em>gedit&lt;/em> as it is a windowed application where you can use your mouse to
move around and scroll the file.&lt;/p>
&lt;p>In this course I will focus on using the CLI, so we will be using &lt;em>nano&lt;/em> in our examples.&lt;/p>
&lt;p>&lt;em>nano&lt;/em> is very simple, you execute the command with the name (or path) of the file you want to edit. If it exists, it is
opened and you can edit it. If it does not, it is created.&lt;/p>
&lt;p>Every command in nano is a combination of the &lt;span class="raw-html">&lt;kbd>CTRL&lt;/kbd>&lt;/span> key (indicated by &lt;em>^&lt;/em> in the instructions) or
the &lt;span class="raw-html">&lt;kbd>ALT&lt;/kbd>&lt;/span> key (indicated by &lt;em>M-&lt;/em> in the instructions) followed by the shortcut key.&lt;/p>
&lt;p>For example, let us create a file called &lt;em>test&lt;/em>, write something in it, save it and exit.&lt;/p>
&lt;p>&lt;div class="highlight">&lt;pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">&lt;code class="language-shell" data-lang="shell">t0rrant@testing:~$ nano test&lt;/code>&lt;/pre>&lt;/div>
our whole terminal is now another application (nano):&lt;/p>
&lt;div class="highlight">&lt;pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">&lt;code class="language-shell" data-lang="shell"> GNU nano 4.8 test
|
^G Get Help ^O Write Out ^W Where Is ^K Cut Text ^J Justify ^C Cur Pos M-U Undo
^X Exit ^R Read File ^&lt;span style="color:#ae81ff">\ &lt;/span>Replace ^U Paste Text ^T To Spell ^_ Go To Line M-E Redo&lt;/code>&lt;/pre>&lt;/div>
&lt;p>On the first screen line we have the application name and our file name. On the second line is where our file contents
start, where we see a blinking cursor is just like the one in the terminal.&lt;/p>
&lt;p>On the bottom we have some helpful commands. After we wrote something in our file we would save by pressing the
&lt;span class="raw-html">&lt;kbd>CTRL+O&lt;/kbd>&lt;/span> combination:&lt;/p>
&lt;div class="highlight">&lt;pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">&lt;code class="language-shell" data-lang="shell"> GNU nano 4.8 test Modified
Hello World!
File Name to Write: test|
^G Get Help M-D DOS Format M-A Append M-B Backup File
^C Cancel M-M Mac Format M-P Prepend ^T To Files&lt;/code>&lt;/pre>&lt;/div>
&lt;p>The cursor is now on the bottom, so we could save the file with another name if we wanted, so we press
&lt;span class="raw-html">&lt;kbd>Enter&lt;/kbd>&lt;/span> to confirm, to which the application replies how many lines it wrote:&lt;/p>
&lt;div class="highlight">&lt;pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">&lt;code class="language-shell" data-lang="shell"> GNU nano 4.8 test
Hello World!
&lt;span style="color:#f92672">[&lt;/span> Wrote &lt;span style="color:#ae81ff">1&lt;/span> line &lt;span style="color:#f92672">]&lt;/span>
^G Get Help ^O Write Out ^W Where Is ^K Cut Text ^J Justify ^C Cur Pos M-U Undo
^X Exit ^R Read File ^&lt;span style="color:#ae81ff">\ &lt;/span>Replace ^U Paste Text ^T To Spell ^_ Go To Line M-E Redo&lt;/code>&lt;/pre>&lt;/div>
&lt;p>Now we exit by pressing &lt;span class="raw-html">&lt;kbd>CTRL+X&lt;/kbd>&lt;/span>.&lt;/p>
&lt;p>If we had not saved the file before exiting, &lt;em>nano&lt;/em> would ask us if we wanted to save the file, and we would type &lt;em>Y&lt;/em>
or &lt;em>N&lt;/em> and press &lt;span class="raw-html">&lt;kbd>Enter&lt;/kbd>&lt;/span>, which would be followed by a prompt to choose the filename if we typed
&lt;em>Y&lt;/em>.&lt;/p>
&lt;/div>
&lt;div class="section" id="scripting-in-bash">
&lt;h3>Scripting in Bash&lt;/h3>
&lt;p>Like we saw in &lt;a class="reference internal" href="#file-types">File Types&lt;/a>, in Linux the beginning of a file is what tells us the type of the file and even what
application the file is supposed to be executed with. For Bash scripts we always start the file with a &lt;em>shebang&lt;/em> (#!)
followed by the path to the &lt;em>env&lt;/em> command and &lt;em>bash&lt;/em> as its argument&lt;/p>
&lt;div class="highlight">&lt;pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">&lt;code class="language-bash" data-lang="bash">&lt;span style="color:#75715e">#!/usr/bin/env bash&lt;/span>&lt;/code>&lt;/pre>&lt;/div>
&lt;p>Imagine we want to create a file named &lt;em>test.sh&lt;/em> which should display our username, relative path to itself, and the
name of the machine it is running on, its content could be:&lt;/p>
&lt;div class="highlight">&lt;div style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">
&lt;table style="border-spacing:0;padding:0;margin:0;border:0;width:auto;overflow:auto;display:block;">&lt;tr>&lt;td style="vertical-align:top;padding:0;margin:0;border:0;">
&lt;pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">&lt;code>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">1
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">2
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td style="vertical-align:top;padding:0;margin:0;border:0;;width:100%">
&lt;pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">&lt;code class="language-bash" data-lang="bash">&lt;span style="color:#75715e">#!/usr/bin/env bash
&lt;/span>&lt;span style="color:#75715e">&lt;/span>echo I am $USER, and I just ran $0 at $HOSTNAME&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>
&lt;p>After saving the file and closing &lt;em>nano&lt;/em> we need to make that file executable (remember
&lt;a class="reference internal" href="#file-attributes-and-ownership">File Attributes and Ownership&lt;/a>):&lt;/p>
&lt;div class="highlight">&lt;pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">&lt;code class="language-shell" data-lang="shell">t0rrant@testing:~$ chmod &lt;span style="color:#ae81ff">755&lt;/span> test.sh&lt;/code>&lt;/pre>&lt;/div>
&lt;p>And execute it, by indicating a path to the file:&lt;/p>
&lt;div class="highlight">&lt;pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">&lt;code class="language-shell" data-lang="shell">t0rrant@testing:~$ ./test.sh
I am t0rrant, and I just ran ./test.sh at testing&lt;/code>&lt;/pre>&lt;/div>
&lt;!-- ##################################################################################################################### -->
&lt;!-- ##################################################################################################################### -->
&lt;!-- ##################################################################################################################### -->
&lt;/div>
&lt;/div>
&lt;div class="section" id="networking-101">
&lt;h2>Networking 101&lt;/h2>
&lt;p>Nowadays we have an assortment of devices connected to the Internet and most of us have something similar to
&lt;a class="reference external" href="#fig-housenetwork">Figure 5&lt;/a> in our homes, a wireless router provided by our Internet Service Provider (ISP) which
connects our wired and wireless devices to the Internet.&lt;/p>
&lt;div class="figure align-center" id="fig-housenetwork">
&lt;a class="reference external image-reference" href="./img/house_network.svg">&lt;img alt="Common household network infrastructure" src="./img/house_network.png" />&lt;/a>
&lt;p class="caption">Common household network infrastructure&lt;/p>
&lt;/div>
&lt;p>Inside our home wireless router there is actually a combination of several components:&lt;/p>
&lt;ul class="simple">
&lt;li>a network switch&lt;/li>
&lt;li>a network modem&lt;/li>
&lt;li>a network router&lt;/li>
&lt;li>a DNS server (nameserver)&lt;/li>
&lt;/ul>
&lt;p>we will see the function for each of these components as we move along each subsection.&lt;/p>
&lt;p>There are four key concepts I would like to present in this section:&lt;/p>
&lt;ul class="simple">
&lt;li>Network Communications&lt;/li>
&lt;li>Switching&lt;/li>
&lt;li>Routing&lt;/li>
&lt;li>Domain Name System (DNS)&lt;/li>
&lt;/ul>
&lt;div class="section" id="network-communication">
&lt;h3>Network Communication&lt;/h3>
&lt;p>Network devices communicate with each other in a way that resembles a very awkward and very formal conversation.&lt;/p>
&lt;p>Comparing a network communication to a human conversation between Alice and Bob, it could go something like this:&lt;/p>
&lt;div class="text-center line-block">
&lt;div class="line">&lt;strong>*Alice initiates conversation*&lt;/strong>&lt;/div>
&lt;div class="line-block">
&lt;div class="line">Alice: Hello.&lt;/div>
&lt;div class="line">Bob: I acknowledge your message.&lt;/div>
&lt;div class="line">Alice: I acknowledge your acknowledgement.&lt;/div>
&lt;/div>
&lt;div class="line">&lt;strong>*Now they can actually have a conversation*&lt;/strong>&lt;/div>
&lt;div class="line-block">
&lt;div class="line">Alice: Today is a nice day to learn something new.&lt;/div>
&lt;div class="line">Bob: I acknowledge your message.&lt;/div>
&lt;div class="line">Alice: I read this great article about cryptography.&lt;/div>
&lt;div class="line">Bob: I acknowledge your message.&lt;/div>
&lt;div class="line">Alice: Well I have to go to the grocery store to buy&lt;/div>
&lt;div class="line">Bob: I acknowledge your message, continue.&lt;/div>
&lt;div class="line">Alice: some cheese and bread.&lt;/div>
&lt;div class="line">Bob: I acknowledge your message.&lt;/div>
&lt;/div>
&lt;div class="line">&lt;strong>*Alice terminates the conversation*&lt;/strong>&lt;/div>
&lt;div class="line-block">
&lt;div class="line">Alice: Goodbye.&lt;/div>
&lt;div class="line">Bob: I acknowledge your Goodbye.&lt;/div>
&lt;div class="line">Bob: Goodbye.&lt;/div>
&lt;div class="line">Alice: I acknowledge your Goodbye.&lt;/div>
&lt;/div>
&lt;div class="line">&lt;strong>*End of conversation*&lt;/strong>&lt;/div>
&lt;/div>
&lt;p>As you can see for every statement Alice makes, Bob acknowledges that statement, and there are two distinct protocols
for initiating and terminating a conversation.&lt;/p>
&lt;p>With network devices we need:&lt;/p>
&lt;ol class="arabic simple">
&lt;li>a way to identify a device&lt;/li>
&lt;li>a way to reach a specific device&lt;/li>
&lt;li>a way to represent a message&lt;/li>
&lt;li>a way to represent which service in the device we want to deliver the message to&lt;/li>
&lt;/ol>
&lt;p>The first is called a Media Access Control (MAC) address, and is a unique identifier assigned to every Network
Interface Controller (NIC).&lt;/p>
&lt;p>The second is called an Internet Protocol (IP) address, and is a numerical assignment for a specific device. For
simplicity we will only consider IPv4 addresses which are made up of four numbers, ranging from 0 to 254 and are
separated by a dot (.) (i.e: 192.168.0.254).&lt;/p>
&lt;p>The third is called a packet, and is what devices send to each other through the network. Each message exchanged
between devices can be very small or very large, so these messages may need to be broken down into smaller packets, this
is done by each network device.&lt;/p>
&lt;p>The fourth is called a network port, and it usually identifies a process or network service waiting for messages
(listening). This is a software port, a physical port is where physical cables connect to.&lt;/p>
&lt;p>Imagine the IP address as being an address for a certain store in a mall, but it just identifies the building,
the port identifies the number of the actual store, the actual endpoint where you can find your wanted service. You
would not be going to an electronics store asking for beer right? =P&lt;/p>
&lt;p>So, every network packet, should have a source IP address with its port and a destination IP address with its port in
order to know where to exactly send the message to and where we expect to be replied to. This is very similar to
traditional mail, just more complicated than sending a letter.&lt;/p>
&lt;p>The MAC address association with an IP address also exists, but it is treated only within the local network and that is
a whole other subject(s).&lt;/p>
&lt;/div>
&lt;div class="section" id="switching">
&lt;h3>Switching&lt;/h3>
&lt;p>To filter and deliver these packets to its correct destination we have the network switch, which connects several
network devices and &amp;quot;knows&amp;quot; where to which port to send each packet that arrives at another port.&lt;/p>
&lt;p>In &lt;a class="reference external" href="#fig-switching">Figure 6&lt;/a>, we can see an example of a switch with four connected devices. Each device will have a
network interface connected to one of the switch's network ports (or the wireless interface) and the switch will know
which IP address is associated with which MAC address. In example, if a packet arrives to the switch that has the TV's
IP address as destination, only the TV will receive that packet.&lt;/p>
&lt;div class="figure align-center" id="fig-switching">
&lt;a class="reference external image-reference" href="./img/switching.svg">&lt;img alt="Network Switch forming a network of devices" src="./img/switching.png" />&lt;/a>
&lt;p class="caption">Network Switch forming a network of devices&lt;/p>
&lt;/div>
&lt;p>So, having a switch with connected devices defines a network between those devices.&lt;/p>
&lt;/div>
&lt;div class="section" id="routing">
&lt;h3>Routing&lt;/h3>
&lt;p>In our home router we have a modem that connects to our ISP and gives us access to the Internet, but that modem will
not do much more by itself, it is just a way to make a path to the ISP.&lt;/p>
&lt;p>To have our packets being forwarded to the Internet we need the router.&lt;/p>
&lt;p>The main functions of a router are:&lt;/p>
&lt;ul class="simple">
&lt;li>connecting multiple networks&lt;/li>
&lt;li>forwarding network packets&lt;/li>
&lt;/ul>
&lt;p>In &lt;a class="reference external" href="#fig-routing">Figure 7&lt;/a> we can see an example of two networks connected through the Internet. Every network that
connects to the Internet will usually follow this setup.&lt;/p>
&lt;div class="figure align-center" id="fig-routing">
&lt;a class="reference external image-reference" href="./img/routing.svg">&lt;img alt="Two networks connected by routers through the Internet" src="./img/routing.png" />&lt;/a>
&lt;p class="caption">Two networks connected by routers through the Internet&lt;/p>
&lt;/div>
&lt;p>Our local router makes it possible to connect from our to a different network connected to another router, or a
different network connected to the same router.&lt;/p>
&lt;p>Sending a packet from our device with a local IP address to another device in a different network is made possible due
to a process called Network Address Translation (NAT). I will not delve into it here, but broadly speaking it is a way
for your router to hide your device information, exposing only itself in any forwarded packet, while at the same time
maintaining a record of the origin for each forwarded packet in order to deliver any response to their proper recipient.&lt;/p>
&lt;/div>
&lt;div class="section" id="dns">
&lt;h3>DNS&lt;/h3>
&lt;p>The Domain Name System (DNS) works somewhat like an old phone list. It is a dynamic set of servers that have records
corresponding, among other record types, names to IPv4 addresses (Alias or A records). For example, the name
&lt;em>implement.pt&lt;/em> matches the A record corresponding to the IPv4 addresses:&lt;/p>
&lt;ul class="simple">
&lt;li>104.24.106.10&lt;/li>
&lt;li>104.24.107.10&lt;/li>
&lt;li>172.67.136.196&lt;/li>
&lt;/ul>
&lt;p>which corresponds to three different entry points to access this name.&lt;/p>
&lt;p>This record keeping is recursive, meaning that when you contact a certain DNS server it will search its own records for
a match, and if it fails to find a match it will ask that information to another DNS server. This goes on until either
a record is found, returning it back to the user, or no more DNS servers are available to ask to, in which case
something like a &amp;quot;I do not know that record&amp;quot; is returned.&lt;/p>
&lt;p>Taking a look at &lt;a class="reference external" href="#fig-dns">Figure 8&lt;/a> we can see an example of what happened when the device you are using to access
this article tried to reach &lt;em>implement.pt&lt;/em>.&lt;/p>
&lt;div class="figure align-center" id="fig-dns">
&lt;a class="reference external image-reference" href="./img/dns.svg">&lt;img alt="DNS request flow example" src="./img/dns.png" />&lt;/a>
&lt;p class="caption">DNS request flow example&lt;/p>
&lt;/div>
&lt;p>First your device searched its internal records, and probably did not find anything.&lt;/p>
&lt;p>Then it asked the next DNS Server, which is usually your router.&lt;/p>
&lt;p>Your home router should also not have any records for &lt;em>implement.pt&lt;/em>, and if so it asked its default DNS Servers for it.
Usually these are your ISP's DNS servers. If they also do not have any record they will ask another, and another, until
eventually some DNS server replied with the answer &amp;quot;implement.pt. A 104.24.107.10&amp;quot; and voilá your router can now pass
that response to your device, which it did or else you would not be reading this =).&lt;/p>
&lt;p>After your device has this information it can finally connect to the server at the correct address and initiate
communication with this server at a specified port, in this case port 443.&lt;/p>
&lt;!-- ##################################################################################################################### -->
&lt;!-- ##################################################################################################################### -->
&lt;!-- ##################################################################################################################### -->
&lt;/div>
&lt;/div>
&lt;div class="section" id="secure-communications-and-ssh">
&lt;h2>Secure Communications and SSH&lt;/h2>
&lt;p>&lt;em>Encryption is the process of converting messages, information, or data into a form unreadable by anyone except the&lt;/em>
&lt;em>intended recipient. Encrypted data must be deciphered, or decrypted, before it can be read by the recipient. The root&lt;/em>
&lt;em>of the word encryption—crypt—comes from the Greek word kryptos, meaning hidden or secret.&lt;/em> &lt;a class="footnote-reference" href="#encryptionhistory" id="id13">[15]&lt;/a>&lt;/p>
&lt;p>The first known form of encryption was used around 1900 BC! by an egyptian scribe using non-standard hieroglyphs, and it
kept evolving from inscriptions in stone using signatures to authenticate its origin, to reversed-alphabet substitutions
and other shifted substitutions. &lt;a class="footnote-reference" href="#encryptionhistory" id="id14">[15]&lt;/a>&lt;/p>
&lt;p>A very famous one is the Caesar Cipher, named after the roman emperor Julius Caesar, which used a simple substitution
with the normal alphabet. For example, we could shift the alphabet left three places:&lt;/p>
&lt;div class="highlight">&lt;pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">&lt;code class="language-text" data-lang="text">Plain: ABCDEFGHIJKLMNOPQRSTUVWXYZ
Cipher: XYZABCDEFGHIJKLMNOPQRSTUVW&lt;/code>&lt;/pre>&lt;/div>
&lt;p>and if we wanted to send the message &amp;quot;ATTACK AT DAWN&amp;quot;:&lt;/p>
&lt;div class="highlight">&lt;pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">&lt;code class="language-text" data-lang="text">Plaintext: ATTACK AT DAWN
Ciphertext: XQQXZH XQ AXTK&lt;/code>&lt;/pre>&lt;/div>
&lt;p>we would send the message &amp;quot;XQQXZH XQ AXTK&amp;quot; and only who knew the cipher we were using would be able to know the correct
message.&lt;/p>
&lt;div class="section" id="symmetric-vs-asymmetric-cryptography">
&lt;h3>Symmetric vs Asymmetric Cryptography&lt;/h3>
&lt;p>Today’s crypto systems are divided into two categories: &lt;a class="footnote-reference" href="#encryptionhistory" id="id15">[15]&lt;/a>&lt;/p>
&lt;ul class="simple">
&lt;li>symmetric&lt;/li>
&lt;li>asymmetric&lt;/li>
&lt;/ul>
&lt;p>Symmetric cryptography uses the a unique key (the secret key) to encrypt and decrypt a message. We can view this as a
simple lock which only opens with a correct key, like we can see in &lt;a class="reference external" href="#fig-symmetricenc">Figure 9&lt;/a>&lt;/p>
&lt;div class="figure align-center" id="fig-symmetricenc">
&lt;a class="reference external image-reference" href="./img/symmetric_encryption.svg">&lt;img alt="Symmetric cryptography analogy" src="./img/symmetric_encryption.png" />&lt;/a>
&lt;p class="caption">Symmetric cryptography analogy&lt;/p>
&lt;/div>
&lt;p>Asymmetric (or public key) cryptography uses one key (public key) to encrypt the message and a different key (private
key) to decrypt the message. &lt;a class="footnote-reference" href="#encryptionhistory" id="id16">[15]&lt;/a> One of the advantages is that there can be unending copies of the
public key without compromising the private key.&lt;/p>
&lt;p>By far the best non-technical explanation I have seen was shared by Panayotis Vryonis in his
&lt;a class="reference external" href="https://blog.vrypan.net">blog&lt;/a> and I will share it here with you.&lt;/p>
&lt;p>Picture a box with a lock, but this lock is special (see &lt;a class="reference external" href="#fig-asymmetricenc">Figure 10&lt;/a>). It fits two separate keys (yes, two).
&lt;strong>The first one can only turn clockwise&lt;/strong> (from A to B to C), let us call this the &lt;em>private key&lt;/em>, and &lt;strong>the second&lt;/strong>
&lt;strong>one can only turn counter-clockwise&lt;/strong> (from C to B to A), let us call this the &lt;em>public key&lt;/em>. &lt;a class="footnote-reference" href="#pubkeyvrypan" id="id17">[10]&lt;/a>&lt;/p>
&lt;div class="figure align-center" id="fig-asymmetricenc">
&lt;a class="reference external image-reference" href="./img/asymmetric_encryption.svg">&lt;img alt="Asymmetric cryptography analogy" src="./img/asymmetric_encryption.png" />&lt;/a>
&lt;p class="caption">Asymmetric cryptography analogy&lt;/p>
&lt;/div>
&lt;p>With this we can do two things to an unlocked box:&lt;/p>
&lt;ul class="simple">
&lt;li>lock with the private key&lt;/li>
&lt;li>lock with the public key&lt;/li>
&lt;/ul>
&lt;p>If we lock with the public key, only the person with the private key will be able to open it, and so anyone with the
public key will be able to send secret items safely inside the box, knowing that only the owner of the private key will
be able to access them.&lt;/p>
&lt;p>If we lock the box with the private key, anyone that has the public key and can open the box can be sure that the box
was locked by whoever possesses the private key counterpart, which makes them sure that whatever is inside the box it
was placed by the one person holding the private key.&lt;/p>
&lt;p>For example, Bob has one of these specials boxes, keeps &lt;strong>one&lt;/strong> copy of the secret key for himself, and makes lots of
copies of the public key. He then spreads those copies around, his house, office, random mailboxes, subway, and gives
copies to all of his friends, including his close friend Alice.&lt;/p>
&lt;p>Alice wants to send a secret message to Bob. She has a box with this special lock made by Bob. She can put a message
inside the box and turn the lock to the &lt;strong>locked&lt;/strong> position &lt;strong>A&lt;/strong>. Then she can pass the box to anyone (that will
eventually deliver the box to Bob, of course) sure that only Bob will be able to use his secret key to move the lock to
the &lt;strong>unlocked&lt;/strong> position &lt;strong>B&lt;/strong>.&lt;/p>
&lt;p>Now imagine that Alice receives a box from someone, saying it has a message from Bob. But how can Alice be
sure it was Bob that sent it? Easily enough, if the public key that Bob gave her can open the box then the box surely
came from Bob.&lt;/p>
&lt;/div>
&lt;div class="section" id="secure-shell-ssh">
&lt;h3>Secure Shell (SSH)&lt;/h3>
&lt;p>&lt;em>The SSH protocol (also referred to as Secure Shell) is a method for secure remote login from one computer to another.&lt;/em>
&lt;em>It provides several alternative options for strong authentication, and it protects the communications security and&lt;/em>
&lt;em>integrity with strong encryption.&lt;/em> &lt;a class="footnote-reference" href="#sshcite" id="id18">[12]&lt;/a>&lt;/p>
&lt;div class="figure align-center" id="fig-sshprotocol">
&lt;a class="reference external image-reference" href="./img/How_does_the_SSH_protocol_work_.png">&lt;img alt="SSH protocol" src="./img/How_does_the_SSH_protocol_work_.png" />&lt;/a>
&lt;p class="caption">SSH protocol &lt;a class="footnote-reference" href="#sshcite" id="id19">[12]&lt;/a>&lt;/p>
&lt;/div>
&lt;p>When using SSH, we are using the same concepts we saw earlier about asymmetric cryptography. As we can see in
&lt;a class="reference external" href="#fig-sshprotocol">Figure 11&lt;/a>, the SSH Client first contacts the SSH Server through an insecure communication channel
(the network), so after the SSH Server sends the Client its public key, they use a really crafty way of having a common
secret without anyone in that insecure channel being able to know what it is. &lt;a class="footnote-reference" href="#diffiehellmanpub" id="id20">[14]&lt;/a>&lt;/p>
&lt;p>In &lt;a class="reference external" href="#fig-diffiehellman">Figure 12&lt;/a> we can see an analogy of this ingenious process, of sharing secrets through an
insecure channel, with colors instead of very large numbers (which is what roughly happens in a real-world interaction),
this process is called the Diffie-Hellman Key Exchange.&lt;/p>
&lt;div class="figure align-center" id="fig-diffiehellman">
&lt;a class="reference external image-reference" href="./img/Diffie-Hellman_Key_Exchange.svg">&lt;img alt="Sharing a common secret through a non-secure channel" src="./img/Diffie-Hellman_Key_Exchange.png" />&lt;/a>
&lt;p class="caption">Sharing a common secret through a non-secure channel &lt;a class="footnote-reference" href="#diffiehellmanwiki" id="id21">[13]&lt;/a>&lt;/p>
&lt;/div>
&lt;p>The process begins by having the two actors, Alice and Bob, agree on one color that does not need to be kept secret. In
this example, the color is yellow.&lt;/p>
&lt;p>Each of them also has a secret color that they keep to themselves, in this case red for Alice, teal for Bob.&lt;/p>
&lt;p>The crafty part of the process is that Alice and Bob each mix their own secret color together with their initially
shared color (yellow), resulting in an orange-tan color for Alice and a light-blue mix for Bob.&lt;/p>
&lt;p>They can now exchange the resulting mixed colors publicly, we assume that separating the mixed colors back to their
original individual colors takes too long and is very expensive.&lt;/p>
&lt;p>Finally, they each mix the color they received from the previous exchange with their own secret color.&lt;/p>
&lt;p>The final result is a color mixture (yellow-brown in this case) that is identical for both.&lt;/p>
&lt;p>If someone saw the exchange, they would only know the common color (yellow) and the first mixed colors (orange-tan and
light-blue), but it would be extremely difficult for them to know the resulting secret color (yellow-brown).&lt;/p>
&lt;p>Bringing the analogy back to a real-life exchange using large numbers instead of colors, this determination is
computationally expensive. It is impossible to compute in a practical amount of time. &lt;a class="footnote-reference" href="#diffiehellmanwiki" id="id22">[13]&lt;/a>&lt;/p>
&lt;p>Finally, after this &amp;quot;simple&amp;quot; key exchange the SSH Client and Server are ready to communicate securely.&lt;/p>
&lt;!-- ##################################################################################################################### -->
&lt;!-- ##################################################################################################################### -->
&lt;!-- ##################################################################################################################### -->
&lt;/div>
&lt;/div>
&lt;div class="section" id="setting-up-your-machine-for-remote-access">
&lt;h2>Setting up your machine for remote access&lt;/h2>
&lt;p>Now that we have covered BASH interaction, file manipulation, networking and secure communications, we are ready to
setup network communications between our two machines and perform interactions between them, in a secure manner.&lt;/p>
&lt;p>For this section we will assume our workstation to be called &lt;em>testing&lt;/em> and that it has the IPv4 address
&lt;em>192.168.1.10&lt;/em>, and our other machine will be called &lt;em>remote-server&lt;/em> and it has the IPv4 address &lt;em>192.168.1.11&lt;/em>.&lt;/p>
&lt;p>We also assume that a user with the same name exists on both of those machines, in this case the user is &lt;em>t0rrant&lt;/em>.&lt;/p>
&lt;table border="1" class="docutils">
&lt;colgroup>
&lt;col width="52%" />
&lt;col width="48%" />
&lt;/colgroup>
&lt;thead valign="bottom">
&lt;tr>&lt;th class="head">Hostname&lt;/th>
&lt;th class="head">IPv4 Address&lt;/th>
&lt;/tr>
&lt;/thead>
&lt;tbody valign="top">
&lt;tr>&lt;td>testing&lt;/td>
&lt;td>192.168.1.10&lt;/td>
&lt;/tr>
&lt;tr>&lt;td>remote-server&lt;/td>
&lt;td>192.168.1.11&lt;/td>
&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;p>To find out your real IPv4 addresses you can use the &lt;em>ip&lt;/em> command, as such:&lt;/p>
&lt;div class="highlight">&lt;pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">&lt;code class="language-shell" data-lang="shell">t0rrant@remote-server:~$ ip a
1: lo: &amp;lt;LOOPBACK,UP,LOWER_UP&amp;gt; mtu &lt;span style="color:#ae81ff">65536&lt;/span> qdisc noqueue state UNKNOWN group default qlen &lt;span style="color:#ae81ff">1000&lt;/span>
link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
inet 127.0.0.1/8 scope host lo
valid_lft forever preferred_lft forever
inet6 ::1/128 scope host
valid_lft forever preferred_lft forever
2: enp3s0: &amp;lt;NO-CARRIER,BROADCAST,MULTICAST,UP&amp;gt; mtu &lt;span style="color:#ae81ff">1500&lt;/span> qdisc pfifo_fast state DOWN group default qlen &lt;span style="color:#ae81ff">1000&lt;/span>
link/ether xx:xx:xx:xx:xx:xx brd ff:ff:ff:ff:ff:ff
3: enp3s1: &amp;lt;BROADCAST,MULTICAST,UP,LOWER_UP&amp;gt; mtu &lt;span style="color:#ae81ff">1500&lt;/span> qdisc noqueue state UP group default qlen &lt;span style="color:#ae81ff">1000&lt;/span>
link/ether xx:xx:xx:xx:xx:xx brd ff:ff:ff:ff:ff:ff
inet 192.168.1.11/24 brd 192.168.1.255 scope global dynamic noprefixroute enp3s1
valid_lft 67476sec preferred_lft 67476sec&lt;/code>&lt;/pre>&lt;/div>
&lt;p>From the output above you should notice three things, as you should see something similar in your machine, the interface
names are probably different and can be something like &lt;em>eth0&lt;/em>, &lt;em>wlan1&lt;/em> or &lt;em>wlo1&lt;/em>.&lt;/p>
&lt;p>The interface name, which is after each number in the list:&lt;/p>
&lt;div class="highlight">&lt;pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">&lt;code class="language-shell" data-lang="shell">1: lo
...
2: enp3s0
...
3: enp3s1
...&lt;/code>&lt;/pre>&lt;/div>
&lt;p>The interface status:&lt;/p>
&lt;div class="highlight">&lt;pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">&lt;code class="language-shell" data-lang="shell">1: lo: ... state UNKNOWN ...
...
2: enp3s0: ... state DOWN ...
...
3: enp3s1 ... state UP ...
...&lt;/code>&lt;/pre>&lt;/div>
&lt;p>Interfaces with their state &lt;strong>UP&lt;/strong> should be the ones that are connected, and thus have an IP address (inet):&lt;/p>
&lt;div class="highlight">&lt;pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">&lt;code class="language-shell" data-lang="shell">1: lo: ... state UNKNOWN ...
...
2: enp3s0: ... state DOWN ...
...
3: enp3s1 ... state UP ...
...
inet 192.168.1.11/24 ...&lt;/code>&lt;/pre>&lt;/div>
&lt;p>The &lt;em>/24&lt;/em> portion, broadly speaking, defines the size of the network your machine is connected to. What you need is the
first part, so in this case we can see that the IPv4 address for the &lt;em>enp3s1&lt;/em> interface in the &lt;em>remote-server&lt;/em> is
&lt;em>192.168.1.11&lt;/em>.&lt;/p>
&lt;div class="section" id="server-application-setup">
&lt;h3>Server Application Setup&lt;/h3>
&lt;p>First let us install the server application:&lt;/p>
&lt;div class="highlight">&lt;pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">&lt;code class="language-shell" data-lang="shell">t0rrant@remote-server:~$ sudo apt install openssh-client openssh-server&lt;/code>&lt;/pre>&lt;/div>
&lt;p>And now we can start the service&lt;/p>
&lt;div class="highlight">&lt;pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">&lt;code class="language-shell" data-lang="shell">t0rrant@remote-server:~$ sudo systemctl start ssh&lt;/code>&lt;/pre>&lt;/div>
&lt;p>see that it is running&lt;/p>
&lt;div class="highlight">&lt;pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">&lt;code class="language-shell" data-lang="shell">t0rrant@remote-server:~$ sudo systemctl status ssh
● ssh.service - OpenBSD Secure Shell server
Loaded: loaded &lt;span style="color:#f92672">(&lt;/span>/lib/systemd/system/ssh.service; enabled; vendor preset: enabled&lt;span style="color:#f92672">)&lt;/span>
Active: active &lt;span style="color:#f92672">(&lt;/span>running&lt;span style="color:#f92672">)&lt;/span> since Mon 2020-05-11 15:24:03 WEST; 5s ago
Docs: man:sshd&lt;span style="color:#f92672">(&lt;/span>8&lt;span style="color:#f92672">)&lt;/span>
man:sshd_config&lt;span style="color:#f92672">(&lt;/span>5&lt;span style="color:#f92672">)&lt;/span>
Process: &lt;span style="color:#ae81ff">9523&lt;/span> ExecStartPre&lt;span style="color:#f92672">=&lt;/span>/usr/sbin/sshd -t &lt;span style="color:#f92672">(&lt;/span>code&lt;span style="color:#f92672">=&lt;/span>exited, status&lt;span style="color:#f92672">=&lt;/span>0/SUCCESS&lt;span style="color:#f92672">)&lt;/span>
Main PID: &lt;span style="color:#ae81ff">9524&lt;/span> &lt;span style="color:#f92672">(&lt;/span>sshd&lt;span style="color:#f92672">)&lt;/span>
Tasks: &lt;span style="color:#ae81ff">1&lt;/span> &lt;span style="color:#f92672">(&lt;/span>limit: 38302&lt;span style="color:#f92672">)&lt;/span>
Memory: 1.4M
CGroup: /system.slice/ssh.service
└─9524 sshd: /usr/sbin/sshd -D &lt;span style="color:#f92672">[&lt;/span>listener&lt;span style="color:#f92672">]&lt;/span> &lt;span style="color:#ae81ff">0&lt;/span> of 10-100 startups
mai &lt;span style="color:#ae81ff">11&lt;/span> 15:24:03 testing systemd&lt;span style="color:#f92672">[&lt;/span>1&lt;span style="color:#f92672">]&lt;/span>: Starting OpenBSD Secure Shell server...
mai &lt;span style="color:#ae81ff">11&lt;/span> 15:24:03 testing sshd&lt;span style="color:#f92672">[&lt;/span>9524&lt;span style="color:#f92672">]&lt;/span>: Server listening on 0.0.0.0 port 22.
mai &lt;span style="color:#ae81ff">11&lt;/span> 15:24:03 testing sshd&lt;span style="color:#f92672">[&lt;/span>9524&lt;span style="color:#f92672">]&lt;/span>: Server listening on :: port 22.
mai &lt;span style="color:#ae81ff">11&lt;/span> 15:24:03 testing systemd&lt;span style="color:#f92672">[&lt;/span>1&lt;span style="color:#f92672">]&lt;/span>: Started OpenBSD Secure Shell server.&lt;/code>&lt;/pre>&lt;/div>
&lt;p>and from our workstation test the connection:&lt;/p>
&lt;div class="highlight">&lt;pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">&lt;code class="language-shell" data-lang="shell">t0rrant@testing:~$ ssh t0rrant@192.168.1.11
t0rrant@odin&lt;span style="color:#960050;background-color:#1e0010">&amp;#39;&lt;/span>s password:
Welcome to Ubuntu 20.04 LTS &lt;span style="color:#f92672">(&lt;/span>GNU/Linux 5.4.0-29-generic x86_64&lt;span style="color:#f92672">)&lt;/span>
* Documentation: https://help.ubuntu.com
* Management: https://landscape.canonical.com
* Support: https://ubuntu.com/advantage
System information as of Mon May &lt;span style="color:#ae81ff">11&lt;/span> 15:25:44 WEST &lt;span style="color:#ae81ff">2020&lt;/span>
System load: 0.6
Usage of /: 22.3% of 9.32GB
Memory usage: 35%
Swap usage: 0%
Processes: &lt;span style="color:#ae81ff">102&lt;/span>
Users logged in: &lt;span style="color:#ae81ff">0&lt;/span>
IPv4 address &lt;span style="color:#66d9ef">for&lt;/span> enp3s1: 192.168.1.11
&lt;span style="color:#ae81ff">0&lt;/span> updates can be installed immediately.
&lt;span style="color:#ae81ff">0&lt;/span> of these updates are security updates.
To run a command as administrator &lt;span style="color:#f92672">(&lt;/span>user &lt;span style="color:#e6db74">&amp;#34;root&amp;#34;&lt;/span>&lt;span style="color:#f92672">)&lt;/span>, use &lt;span style="color:#e6db74">&amp;#34;sudo &amp;lt;command&amp;gt;&amp;#34;&lt;/span>.
See &lt;span style="color:#e6db74">&amp;#34;man sudo_root&amp;#34;&lt;/span> &lt;span style="color:#66d9ef">for&lt;/span> details.
t0rrant@remote-server:~$&lt;/code>&lt;/pre>&lt;/div>
&lt;/div>
&lt;div class="section" id="ssh-keypair-setup">
&lt;h3>SSH Keypair Setup&lt;/h3>
&lt;p>Entering our password every time we connect to a remote server is more than tedious, it opens the possibility for
someone else to be able to &amp;quot;guess&amp;quot; our password. Of course we can find ways to remedy this, we can generate a random
password with 20+ characters, we can setup defense mechanisms to &amp;quot;lock out&amp;quot; anyone that fails to enter the correct
password some number of times, some other convoluted solution or we could gather the knowledge we gained from
&lt;a class="reference internal" href="#secure-communications-and-ssh">secure communications and SSH&lt;/a> and make use of asymmetric cryptography not only to perform secure communications, but
also to authenticate ourselves with the server.&lt;/p>
&lt;p>Remember that section, and remember that if our &amp;quot;box&amp;quot; was locked with our &lt;em>public&lt;/em> key, it can only be unlocked with
our &lt;em>private&lt;/em> key, so if the server would send a message encrypted with our &lt;em>public&lt;/em> key we could only send back the
same message &lt;strong>if&lt;/strong> we have the correct &lt;em>private&lt;/em> key. Essentially what we are doing is receiving one &amp;quot;box&amp;quot; locked
with our &lt;em>public&lt;/em> key, unlocking it with our &lt;em>private&lt;/em> key, and finally we send a new &amp;quot;box&amp;quot; to the server locked with
the server's &lt;em>public&lt;/em> key, which we have because the server sent it to us on our first connect (as seen in
&lt;a class="reference external" href="#fig-sshprotocol">Figure 11&lt;/a>).&lt;/p>
&lt;p>In &lt;a class="reference external" href="#fig-sshauthkeypair">Figure 13&lt;/a> we can see a sequence of this process.&lt;/p>
&lt;div class="figure align-center" id="fig-sshauthkeypair">
&lt;a class="reference external image-reference" href="./img/ssh_auth_keypair.svg">&lt;object data="./img/ssh_auth_keypair.svg" type="image/svg+xml">SSH authentication with messages codified with public keys&lt;/object>&lt;/a>
&lt;p class="caption">SSH authentication with messages codified with public keys&lt;/p>
&lt;/div>
&lt;p>So, let us get on to generating the keys for our special lock =)&lt;/p>
&lt;p>When we installed &lt;em>openssh-client&lt;/em> it also came with a couple of useful tools, one of them is &lt;em>ssh-keygen&lt;/em>, which
conveniently lets us generate a pair of keys:&lt;/p>
&lt;div class="highlight">&lt;pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">&lt;code class="language-shell" data-lang="shell">t0rrant@testing:~$ ssh-keygen
Generating public/private rsa key pair.
Enter file in which to save the key &lt;span style="color:#f92672">(&lt;/span>/home/t0rrant/.ssh/id_rsa&lt;span style="color:#f92672">)&lt;/span>:
Enter passphrase &lt;span style="color:#f92672">(&lt;/span>empty &lt;span style="color:#66d9ef">for&lt;/span> no passphrase&lt;span style="color:#f92672">)&lt;/span>:
Enter same passphrase again:
Your identification has been saved in /home/t0rrant/.ssh/id_rsa
Your public key has been saved in /home/t0rrant/.ssh/id_rsa.pub
The key fingerprint is:
SHA256:ILvTwLzlzZyMS7YFflmMY+WxCG7BjkN4VCRg5tae3WI t0rrant@testing
The key&lt;span style="color:#960050;background-color:#1e0010">&amp;#39;&lt;/span>s randomart image is:
+---&lt;span style="color:#f92672">[&lt;/span>RSA 3072&lt;span style="color:#f92672">]&lt;/span>----+
| +.ooo |
| + + o |
| + &lt;span style="color:#f92672">=&lt;/span> &lt;span style="color:#f92672">=&lt;/span> o |
| . * O &lt;span style="color:#f92672">=&lt;/span> * o |
| X E S &lt;span style="color:#f92672">=&lt;/span> |
| % X &lt;span style="color:#f92672">=&lt;/span> |
| + B X |
| + &lt;span style="color:#f92672">=&lt;/span> |
| o |
+----&lt;span style="color:#f92672">[&lt;/span>SHA256&lt;span style="color:#f92672">]&lt;/span>-----+&lt;/code>&lt;/pre>&lt;/div>
&lt;p>One other tool is &lt;em>ssh-copy-id&lt;/em>, which adds our public key to a server, for a specific user (the one we connect with):&lt;/p>
&lt;div class="highlight">&lt;pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">&lt;code class="language-shell" data-lang="shell">t0rrant@testing:~$ ssh-copy-id t0rrant@192.168.1.11
/usr/bin/ssh-copy-id: INFO: Source of key&lt;span style="color:#f92672">(&lt;/span>s&lt;span style="color:#f92672">)&lt;/span> to be installed: &lt;span style="color:#e6db74">&amp;#34;.ssh/id_rsa.pub&amp;#34;&lt;/span>
/usr/bin/ssh-copy-id: INFO: attempting to log in with the new key&lt;span style="color:#f92672">(&lt;/span>s&lt;span style="color:#f92672">)&lt;/span>, to filter out any that are already installed
/usr/bin/ssh-copy-id: INFO: &lt;span style="color:#ae81ff">1&lt;/span> key&lt;span style="color:#f92672">(&lt;/span>s&lt;span style="color:#f92672">)&lt;/span> remain to be installed -- &lt;span style="color:#66d9ef">if&lt;/span> you are prompted now it is to install the new keys
Number of key&lt;span style="color:#f92672">(&lt;/span>s&lt;span style="color:#f92672">)&lt;/span> added: &lt;span style="color:#ae81ff">1&lt;/span>
Now try logging into the machine, with: &lt;span style="color:#e6db74">&amp;#34;ssh &amp;#39;t0rrant@192.168.1.11&amp;#39;&amp;#34;&lt;/span>
and check to make sure that only the key&lt;span style="color:#f92672">(&lt;/span>s&lt;span style="color:#f92672">)&lt;/span> you wanted were added.
t0rrant@testing:~$&lt;/code>&lt;/pre>&lt;/div>
&lt;p>Let us try a new connection from our &lt;em>testing&lt;/em> workstation to our &lt;em>remote-server&lt;/em>:&lt;/p>
&lt;div class="highlight">&lt;pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">&lt;code class="language-shell" data-lang="shell">t0rrant@testing:~$ ssh t0rrant@192.168.1.11
Welcome to Ubuntu 20.04 LTS &lt;span style="color:#f92672">(&lt;/span>GNU/Linux 5.4.0-29-generic x86_64&lt;span style="color:#f92672">)&lt;/span>
* Documentation: https://help.ubuntu.com
* Management: https://landscape.canonical.com
* Support: https://ubuntu.com/advantage
System information as of Mon May &lt;span style="color:#ae81ff">11&lt;/span> 15:34:40 WEST &lt;span style="color:#ae81ff">2020&lt;/span>
System load: 0.6
Usage of /: 22.3% of 9.32GB
Memory usage: 35%
Swap usage: 0%
Processes: &lt;span style="color:#ae81ff">102&lt;/span>
Users logged in: &lt;span style="color:#ae81ff">0&lt;/span>
IPv4 address &lt;span style="color:#66d9ef">for&lt;/span> enp3s1: 192.168.1.11
&lt;span style="color:#ae81ff">0&lt;/span> updates can be installed immediately.
&lt;span style="color:#ae81ff">0&lt;/span> of these updates are security updates.
Last login: Mon May &lt;span style="color:#ae81ff">11&lt;/span> 15:25:44 &lt;span style="color:#ae81ff">2020&lt;/span> from 192.168.1.10
To run a command as administrator &lt;span style="color:#f92672">(&lt;/span>user &lt;span style="color:#e6db74">&amp;#34;root&amp;#34;&lt;/span>&lt;span style="color:#f92672">)&lt;/span>, use &lt;span style="color:#e6db74">&amp;#34;sudo &amp;lt;command&amp;gt;&amp;#34;&lt;/span>.
See &lt;span style="color:#e6db74">&amp;#34;man sudo_root&amp;#34;&lt;/span> &lt;span style="color:#66d9ef">for&lt;/span> details.
t0rrant@remote-server:~$&lt;/code>&lt;/pre>&lt;/div>
&lt;p>Notice that this time it did not ask us for our user's password, success :)&lt;/p>
&lt;p>Now that we have access through our keypair, we can configure the server to &lt;strong>not&lt;/strong> accept password based logins, only
accept keypair based ones.&lt;/p>
&lt;p>Open (as root) the &lt;em>/etc/ssh/sshd_config&lt;/em> file for editing&lt;/p>
&lt;div class="highlight">&lt;pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">&lt;code class="language-shell" data-lang="shell">t0rrant@remote-server:~$ sudo nano /etc/ssh/sshd_config&lt;/code>&lt;/pre>&lt;/div>
&lt;p>search for the options &lt;em>PubkeyAuthentication&lt;/em> and
&lt;em>PasswordAuthentication&lt;/em> and change them to &lt;em>yes&lt;/em> and &lt;em>no&lt;/em>, respectively, as so:&lt;/p>
&lt;div class="highlight">&lt;pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">&lt;code class="language-fallback" data-lang="fallback">PubkeyAuthentication yes
PasswordAuthentication no&lt;/code>&lt;/pre>&lt;/div>
&lt;p>make sure none of those lines start with a &lt;em>#&lt;/em>, if they do remove it.&lt;/p>
&lt;p>Save and exit by pressing &lt;span class="raw-html">&lt;kbd>CTRL+O&lt;/kb>&lt;/span> and then &lt;span class="raw-html">&lt;kbd>CTRL+X&lt;/kbd>&lt;/span>.&lt;/p>
&lt;p>Finally, restart the application&lt;/p>
&lt;div class="highlight">&lt;pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">&lt;code class="language-shell" data-lang="shell">t0rrant@remote-server:~$ sudo systemctl restart ssh&lt;/code>&lt;/pre>&lt;/div>
&lt;p>&lt;span class="notice">do not lose or share your private key, and do not forget the passphrase!&lt;/span>&lt;/p>
&lt;/div>
&lt;div class="section" id="network-interactions">
&lt;h3>Network Interactions&lt;/h3>
&lt;p>Now that we have a functional client-server connection, what can we really do with it?&lt;/p>
&lt;p>First of all, we are getting pretty tired of typing the &lt;em>remote-server&lt;/em> IPv4 address, it would be way better if we could
simply do&lt;/p>
&lt;div class="highlight">&lt;pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">&lt;code class="language-shell" data-lang="shell">ssh remote-server&lt;/code>&lt;/pre>&lt;/div>
&lt;p>and we can! Recalling the network name resolution in the previous section on &lt;a class="reference internal" href="#dns">DNS&lt;/a>, when we search for the IPv4 address
of a specific hostname, the Linux operating system does not go straight to the DNS servers asking for it. It actually
has an internal name resolver which, among other things, reads information from a special file located at &lt;em>/etc/hosts&lt;/em>.
There we can define our own records, that can be (almost) whatever we want.&lt;/p>
&lt;p>If you check its contents (for the sake of simplicity we will ignore IPv6 settings):&lt;/p>
&lt;div class="highlight">&lt;pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">&lt;code class="language-shell" data-lang="shell">t0rrant@testing:~$ cat /etc/hosts
127.0.0.1 localhost
t0rrant@testing:~$&lt;/code>&lt;/pre>&lt;/div>
&lt;p>you can see that the name &lt;em>localhost&lt;/em> is an alias for the &lt;em>127.0.0.1&lt;/em> IPv4 address, which is that special address we
discussed earlier in &lt;a class="reference internal" href="#networking-101">Networking 101&lt;/a> that represents our own machine.&lt;/p>
&lt;p>As easily as that, we can add an entry for our &lt;em>remote-server&lt;/em>, using &lt;em>nano&lt;/em>, which leaves the file as such:&lt;/p>
&lt;div class="highlight">&lt;pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">&lt;code class="language-shell" data-lang="shell">t0rrant@testing:~$ cat /etc/hosts
127.0.0.1 localhost
192.168.1.11 remote-server
t0rrant@testing:~$&lt;/code>&lt;/pre>&lt;/div>
&lt;p>Note that the hyphen (-) is the only non-alphanumeric character allowed in a hostname (for more information see the
man pages for &lt;em>hostname&lt;/em>)&lt;/p>
&lt;p>And now we can connect to &lt;em>remote-server&lt;/em> without typing its IPv4 address:&lt;/p>
&lt;div class="highlight">&lt;pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">&lt;code class="language-shell" data-lang="shell">t0rrant@testing:~$ ssh t0rrant@remote-server
Welcome to Ubuntu 20.04 LTS &lt;span style="color:#f92672">(&lt;/span>GNU/Linux 5.4.0-29-generic x86_64&lt;span style="color:#f92672">)&lt;/span>
* Documentation: https://help.ubuntu.com
* Management: https://landscape.canonical.com
* Support: https://ubuntu.com/advantage
System information as of Mon May &lt;span style="color:#ae81ff">11&lt;/span> 15:56:12 WEST &lt;span style="color:#ae81ff">2020&lt;/span>
System load: 0.6
Usage of /: 22.3% of 9.32GB
Memory usage: 35%
Swap usage: 0%
Processes: &lt;span style="color:#ae81ff">102&lt;/span>
Users logged in: &lt;span style="color:#ae81ff">0&lt;/span>
IPv4 address &lt;span style="color:#66d9ef">for&lt;/span> enp3s1: 192.168.1.11
&lt;span style="color:#ae81ff">0&lt;/span> updates can be installed immediately.
&lt;span style="color:#ae81ff">0&lt;/span> of these updates are security updates.
Last login: Mon May &lt;span style="color:#ae81ff">11&lt;/span> 15:34:40 &lt;span style="color:#ae81ff">2020&lt;/span> from 192.168.1.10
To run a command as administrator &lt;span style="color:#f92672">(&lt;/span>user &lt;span style="color:#e6db74">&amp;#34;root&amp;#34;&lt;/span>&lt;span style="color:#f92672">)&lt;/span>, use &lt;span style="color:#e6db74">&amp;#34;sudo &amp;lt;command&amp;gt;&amp;#34;&lt;/span>.
See &lt;span style="color:#e6db74">&amp;#34;man sudo_root&amp;#34;&lt;/span> &lt;span style="color:#66d9ef">for&lt;/span> details.
t0rrant@remote-server:~$&lt;/code>&lt;/pre>&lt;/div>
&lt;p>Besides connecting to our remote server and interacting with it from within a shell, we can also do other types of
operations through SSH.&lt;/p>
&lt;p>We can &lt;strong>send a file&lt;/strong>:&lt;/p>
&lt;div class="highlight">&lt;pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">&lt;code class="language-shell" data-lang="shell">t0rrant@testing:~$ scp test.sh remote-server:~/test.sh
test.sh 100% &lt;span style="color:#ae81ff">278&lt;/span> 1.2MB/s 00:00
t0rrant@testing:~$&lt;/code>&lt;/pre>&lt;/div>
&lt;p>We can &lt;strong>retrieve a file&lt;/strong>:&lt;/p>
&lt;div class="highlight">&lt;pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">&lt;code class="language-shell" data-lang="shell">t0rrant@testing:~$ scp remote-server:~/test.sh test2.sh
test.sh 100% &lt;span style="color:#ae81ff">278&lt;/span> 1.2MB/s 00:00
t0rrant@testing:~$&lt;/code>&lt;/pre>&lt;/div>
&lt;p>We can run commands remotely:&lt;/p>
&lt;div class="highlight">&lt;pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">&lt;code class="language-shell" data-lang="shell">t0rrant@testing:~$ ssh remote-server ~/test.sh
I am t0rrant, and I just ran /home/t0rrant/test.sh at remote-server
t0rrant@testing:~$&lt;/code>&lt;/pre>&lt;/div>
&lt;!-- ##################################################################################################################### -->
&lt;!-- ##################################################################################################################### -->
&lt;!-- ##################################################################################################################### -->
&lt;/div>
&lt;/div>
&lt;div class="section" id="conclusion">
&lt;h2>Conclusion&lt;/h2>
&lt;p>&lt;em>There is no place like 127.0.0.1&lt;/em>&lt;/p>
&lt;p>In this course we touched several aspects of the Linux ecosystem. Its origins, what components make the Linux Kernel,
learned how to use some of its tools, and culminated in communicating between two machines through a network.&lt;/p>
&lt;p>These were a lot of concepts to take in at once, so I hope you took your breaks and took your time exploring some of the
concepts on your own.&lt;/p>
&lt;p>I invite you to explore further, remember that a using a search engine is a good way of finding information but
sometimes the &lt;a class="reference internal" href="#man-pages">Man Pages&lt;/a> are even friendlier.&lt;/p>
&lt;p>Hopefully this was useful and you could follow and replicate the examples =)&lt;/p>
&lt;p>Feel free to leave comments below, any improvement to this and other articles is always welcome.&lt;/p>
&lt;p>Thanks for reading!&lt;/p>
&lt;!-- ##################################################################################################################### -->
&lt;!-- ##################################################################################################################### -->
&lt;!-- ##################################################################################################################### -->
&lt;/div>
&lt;div class="section" id="references">
&lt;h2>References&lt;/h2>
&lt;table class="docutils footnote" frame="void" id="tldphistory" rules="none">
&lt;colgroup>&lt;col class="label" />&lt;col />&lt;/colgroup>
&lt;tbody valign="top">
&lt;tr>&lt;td class="label">&lt;a class="fn-backref" href="#id1">[1]&lt;/a>&lt;/td>&lt;td>--, Secure Programming for Linux and Unix HOWTO - The Linux Documentation Project &lt;a class="reference external" href="https://tldp.org/HOWTO/Secure-Programs-HOWTO/history.html">[link]&lt;/a>&lt;/td>&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;table class="docutils footnote" frame="void" id="gnulinux" rules="none">
&lt;colgroup>&lt;col class="label" />&lt;col />&lt;/colgroup>
&lt;tbody valign="top">
&lt;tr>&lt;td class="label">&lt;a class="fn-backref" href="#id2">[2]&lt;/a>&lt;/td>&lt;td>Richard Stallman, Linux and GNU - The GNU Project - FSF Foundation &lt;a class="reference external" href="https://www.gnu.org/gnu/linux-and-gnu.html">[link]&lt;/a>&lt;/td>&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;table class="docutils footnote" frame="void" id="linuxacm" rules="none">
&lt;colgroup>&lt;col class="label" />&lt;col />&lt;/colgroup>
&lt;tbody valign="top">
&lt;tr>&lt;td class="label">&lt;a class="fn-backref" href="#id3">[3]&lt;/a>&lt;/td>&lt;td>Linus Torvalds, The Linux edge - Communications of the ACM, Volume 42, Number 4 (1999), Pages 38-39 &lt;a class="reference external" href="https://dl.acm.org/doi/fullHtml/10.1145/299157.299165">[link]&lt;/a>&lt;/td>&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;table class="docutils footnote" frame="void" id="linuxwikipedia" rules="none">
&lt;colgroup>&lt;col class="label" />&lt;col />&lt;/colgroup>
&lt;tbody valign="top">
&lt;tr>&lt;td class="label">[4]&lt;/td>&lt;td>--, History of Linux - Wikipedia &lt;a class="reference external" href="https://en.wikipedia.org/wiki/History_of_Linux">[link]&lt;/a>&lt;/td>&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;table class="docutils footnote" frame="void" id="linuxfoundation" rules="none">
&lt;colgroup>&lt;col class="label" />&lt;col />&lt;/colgroup>
&lt;tbody valign="top">
&lt;tr>&lt;td class="label">&lt;a class="fn-backref" href="#id4">[5]&lt;/a>&lt;/td>&lt;td>--, Linux - Linux Foundation &lt;a class="reference external" href="https://www.linuxfoundation.org/projects/linux/">[link]&lt;/a>&lt;/td>&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;table class="docutils footnote" frame="void" id="gnubashdocs" rules="none">
&lt;colgroup>&lt;col class="label" />&lt;col />&lt;/colgroup>
&lt;tbody valign="top">
&lt;tr>&lt;td class="label">&lt;a class="fn-backref" href="#id10">[6]&lt;/a>&lt;/td>&lt;td>--, Bash Reference Manual - GNU Software &lt;a class="reference external" href="https://www.gnu.org/software/bash/manual/bash.html#What-is-Bash_003f">[link]&lt;/a>&lt;/td>&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;table class="docutils footnote" frame="void" id="tldpfsh" rules="none">
&lt;colgroup>&lt;col class="label" />&lt;col />&lt;/colgroup>
&lt;tbody valign="top">
&lt;tr>&lt;td class="label">[7]&lt;/td>&lt;td>&lt;em>(&lt;a class="fn-backref" href="#id5">1&lt;/a>, &lt;a class="fn-backref" href="#id6">2&lt;/a>, &lt;a class="fn-backref" href="#id7">3&lt;/a>)&lt;/em> --, Linux Filesystem Hierarchy - The Linux Documentation Project &lt;a class="reference external" href="http://www.tldp.org/LDP/Linux-Filesystem-Hierarchy/html/Linux-Filesystem-Hierarchy.html">[link]&lt;/a>&lt;/td>&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;table class="docutils footnote" frame="void" id="tldpuserauth" rules="none">
&lt;colgroup>&lt;col class="label" />&lt;col />&lt;/colgroup>
&lt;tbody valign="top">
&lt;tr>&lt;td class="label">&lt;a class="fn-backref" href="#id8">[8]&lt;/a>&lt;/td>&lt;td>--, User Authentication HOWTO - The Linux Documentation Project &lt;a class="reference external" href="https://www.tldp.org/HOWTO/pdf/User-Authentication-HOWTO.pdf">[link]&lt;/a>&lt;/td>&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;table class="docutils footnote" frame="void" id="tldpbashguide" rules="none">
&lt;colgroup>&lt;col class="label" />&lt;col />&lt;/colgroup>
&lt;tbody valign="top">
&lt;tr>&lt;td class="label">[9]&lt;/td>&lt;td>&lt;em>(&lt;a class="fn-backref" href="#id11">1&lt;/a>, &lt;a class="fn-backref" href="#id12">2&lt;/a>)&lt;/em> --, Bash Guide for Beginners - The Linux Documentation Project &lt;a class="reference external" href="https://tldp.org/LDP/Bash-Beginners-Guide/html/sect_03_02.html">[link]&lt;/a>&lt;/td>&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;table class="docutils footnote" frame="void" id="pubkeyvrypan" rules="none">
&lt;colgroup>&lt;col class="label" />&lt;col />&lt;/colgroup>
&lt;tbody valign="top">
&lt;tr>&lt;td class="label">&lt;a class="fn-backref" href="#id17">[10]&lt;/a>&lt;/td>&lt;td>Panayotis Vryonis, public-key cryptography for non-geeks &lt;a class="reference external" href="https://blog.vrypan.net/2013/08/28/public-key-cryptography-for-non-geeks/">[link]&lt;/a>&lt;/td>&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;table class="docutils footnote" frame="void" id="tldpch3" rules="none">
&lt;colgroup>&lt;col class="label" />&lt;col />&lt;/colgroup>
&lt;tbody valign="top">
&lt;tr>&lt;td class="label">&lt;a class="fn-backref" href="#id9">[11]&lt;/a>&lt;/td>&lt;td>--, Introduction to Linux, Chapter 3, The Linux Documentation Project &lt;a class="reference external" href="https://tldp.org/LDP/intro-linux/html/sect_03_04.html">[link]&lt;/a>&lt;/td>&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;table class="docutils footnote" frame="void" id="sshcite" rules="none">
&lt;colgroup>&lt;col class="label" />&lt;col />&lt;/colgroup>
&lt;tbody valign="top">
&lt;tr>&lt;td class="label">[12]&lt;/td>&lt;td>&lt;em>(&lt;a class="fn-backref" href="#id18">1&lt;/a>, &lt;a class="fn-backref" href="#id19">2&lt;/a>)&lt;/em> Tatu Ylonen, SSH - Secure Login Connections over the Internet. Proceedings of the 6th USENIX Security Symposium, pp. 37-42, USENIX, 1996.&lt;/td>&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;table class="docutils footnote" frame="void" id="diffiehellmanwiki" rules="none">
&lt;colgroup>&lt;col class="label" />&lt;col />&lt;/colgroup>
&lt;tbody valign="top">
&lt;tr>&lt;td class="label">[13]&lt;/td>&lt;td>&lt;em>(&lt;a class="fn-backref" href="#id21">1&lt;/a>, &lt;a class="fn-backref" href="#id22">2&lt;/a>)&lt;/em> --, Diffie-Hellman key exchange - Wikipedia &lt;a class="reference external" href="https://en.wikipedia.org/wiki/Diffie%E2%80%93Hellman_key_exchange">[link]&lt;/a>&lt;/td>&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;table class="docutils footnote" frame="void" id="diffiehellmanpub" rules="none">
&lt;colgroup>&lt;col class="label" />&lt;col />&lt;/colgroup>
&lt;tbody valign="top">
&lt;tr>&lt;td class="label">&lt;a class="fn-backref" href="#id20">[14]&lt;/a>&lt;/td>&lt;td>Whitfield Diffie and Martin E. Hellman, New Directions in Cryptography. IEEE Transactions on Information Theory, vol. IT-22, no. 6, November 1976. &lt;a class="reference external" href="https://ee.stanford.edu/~hellman/publications/24.pdf">[link]&lt;/a>&lt;/td>&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;table class="docutils footnote" frame="void" id="encryptionhistory" rules="none">
&lt;colgroup>&lt;col class="label" />&lt;col />&lt;/colgroup>
&lt;tbody valign="top">
&lt;tr>&lt;td class="label">[15]&lt;/td>&lt;td>&lt;em>(&lt;a class="fn-backref" href="#id13">1&lt;/a>, &lt;a class="fn-backref" href="#id14">2&lt;/a>, &lt;a class="fn-backref" href="#id15">3&lt;/a>, &lt;a class="fn-backref" href="#id16">4&lt;/a>)&lt;/em> Melis Jacob, History of Encryption. Information Security Reading Room, SANS Institute, 2020.&lt;/td>&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;/div>
&lt;/div></content><category>GNU/Linux Fundamentals</category><category>gnu</category><category>linux</category><category>filesystem</category><category>networking</category><category>fundamentals</category><category>bash</category></item><item><title>Hacking diskimage-builder for Fun and Profit</title><link>https://implement.pt/2020/02/hacking-diskimage-builder-for-fun-and-profit/</link><pubDate>Mon, 24 Feb 2020 02:27:00 +0100</pubDate><guid>https://implement.pt/2020/02/hacking-diskimage-builder-for-fun-and-profit/</guid><summary type="html">&lt;div class="document">
&lt;p>An in-depth hands-on on how to create your custom cloud images (almost) from scratch&lt;/p>
&lt;/div></summary><content type="html">&lt;div class="document">
&lt;div class="section" id="introduction">
&lt;h2>Introduction&lt;/h2>
&lt;p>For the past year I have been responsible for redesigning and keeping a multi-region &lt;a class="reference external" href="https://www.openstack.org/">OpenStack&lt;/a> deployment up to date. I will eventually go in-depth on what that entailed, but for now
I will focus on my first challenge on that infrastructure, keeping the cloud images provided to the users up to date and
customized according to defined local policies.&lt;/p>
&lt;p>To achieve this, and after researching my choices, I chose &lt;a class="reference external" href="https://opendev.org/openstack/diskimage-builder/">diskimage-builder&lt;/a>.
It may seem overkill at first glance, but because of its modular architecture it called to my OCD nature and in the end
you can make your own modules that can depend on existing modules, so yes it seemed perfect for the job. Besides, it is
made by the Openstack team, so it is more than trustworthy.&lt;/p>
&lt;p>Before I continue let me reword myself, this is used for generating stripped down Linux bootable cloud images, you can
use them wherever you would like that supports booting the resulting filesystem. These images are cloud-ready and should
be able to retrieve any metadata you configure it to fetch from your cloud provider.&lt;/p>
&lt;p>At the end of this article you should be able to generate your weekly up to date cloud images in whatever CI solution
you master =)&lt;/p>
&lt;/div>
&lt;div class="section" id="requirements">
&lt;h2>Requirements&lt;/h2>
&lt;p>I recommend having some background prior to delving in this use case, as it can get very complex when you need to debug
more advance generation scenarios (i.e: CI). The cloud images provided by your favorite distribution should be enough to
satisfy most cloud computing needs. This process helps in reducing the cloud image footprint to your bare needs, and
allows you to customize it beyond the distribution's provided cloud images.&lt;/p>
&lt;p>That said, I advise:&lt;/p>
&lt;ul class="simple">
&lt;li>experience with chroot (I recommend reading the &lt;a class="reference external" href="https://wiki.gentoo.org/wiki/Chroot">gentoo documentation&lt;/a>)&lt;/li>
&lt;li>experience with loop devices (specifically using &lt;a class="reference external" href="https://linux.die.net/man/8/losetup">losetup&lt;/a>)&lt;/li>
&lt;li>Python experience, especially regarding &lt;a class="reference external" href="{filename}virtualenvwrapper-installation-and-usage.rst">virtual environments&lt;/a>&lt;/li>
&lt;li>know your Linux File System (&lt;a class="reference external" href="https://tldp.org/LDP/intro-linux/html/sect_03_01.html">LFS&lt;/a>)&lt;/li>
&lt;li>know your way around manipulating &lt;a class="reference external" href="https://www.tldp.org/LDP/abs/html/manipulatingvars.html">BASH variables&lt;/a>)&lt;/li>
&lt;li>have minor experience with &lt;a class="reference external" href="https://cloudinit.readthedocs.io/en/latest">cloud-init&lt;/a>&lt;/li>
&lt;li>and as always, use &lt;a class="reference external" href="https://git-scm.com/">Git&lt;/a>&lt;/li>
&lt;/ul>
&lt;/div>
&lt;div class="section" id="diskimage-builder">
&lt;h2>diskimage-builder&lt;/h2>
&lt;p>As stated in its &lt;a class="reference external" href="https://docs.openstack.org/diskimage-builder/latest/index.html">documentation&lt;/a>, &lt;cite>diskimage-builder&lt;/cite>
is a tool for automatically building customized operating-system images for use in clouds and other
environments.&lt;/p>
&lt;p>It includes support for building images based on many major distributions and can produce cloud-images in all common
formats (qcow2, vhd, raw, etc), bare metal file-system images and ram-disk images. These images are composed from the
many included elements; diskimage-builder acts as a framework to easily add your own elements for even further
customization.&lt;/p>
&lt;p>One of the main reasons I chose to use diskimage-builder was for its modular design, but I quickly discovered that it is
also used extensively by the &lt;a class="reference external" href="https://wiki.openstack.org/wiki/TripleO">TripleO project&lt;/a> and within &lt;a class="reference external" href="https://docs.openstack.org/infra/system-config">OpenStack
Infrastructure&lt;/a>, which fit perfectly in our Openstack infrastructure.&lt;/p>
&lt;/div>
&lt;div class="section" id="elements">
&lt;h2>Elements&lt;/h2>
&lt;p>&amp;quot;An element is a particular set of code that alters how the image is built, or runs within the chroot to prepare the
image.&amp;quot; &lt;a class="footnote-reference" href="#elementdesign" id="id1">[2]&lt;/a>&lt;/p>
&lt;p>&amp;quot;Images are built using a chroot and bind mounted /proc /sys and /dev. The goal of the image building process is to
produce blank slate machines that have all the necessary bits to fulfill a specific purpose in the running of an
OpenStack cloud. Images produce either a filesystem image with a label of cloudimg-rootfs, or can be customised to
produce whole disk images (but will still contain a filesystem labelled cloudimg-rootfs). Once the file system tree is
assembled a loopback device with filesystem (or partition table and file system) is created and the tree copied into it.
The file system created is an ext4 filesystem just large enough to hold the file system tree and can be resized up to
1PB in size.&amp;quot; &lt;a class="footnote-reference" href="#elementdesign" id="id2">[2]&lt;/a>&lt;/p>
&lt;p>Take this in... envision it, and now we will split the image process in its ten phases, which each element can go
through if it needs to. &lt;a class="footnote-reference" href="#elementdeveloping" id="id3">[3]&lt;/a>&lt;/p>
&lt;ol class="arabic simple">
&lt;li>&lt;a class="reference internal" href="#root-d">root.d&lt;/a>&lt;/li>
&lt;li>&lt;a class="reference internal" href="#extra-data-d">extra-data.d&lt;/a>&lt;/li>
&lt;li>&lt;a class="reference internal" href="#pre-install-d">pre-install.d&lt;/a>&lt;/li>
&lt;li>&lt;a class="reference internal" href="#install-d">install.d&lt;/a>&lt;/li>
&lt;li>&lt;a class="reference internal" href="#post-install-d">post-install.d&lt;/a>&lt;/li>
&lt;li>&lt;a class="reference internal" href="#post-root-d">post-root.d&lt;/a>&lt;/li>
&lt;li>&lt;a class="reference internal" href="#block-device-d">block-device.d&lt;/a>&lt;/li>
&lt;li>&lt;a class="reference internal" href="#pre-finalise-d">pre-finalise.d&lt;/a>&lt;/li>
&lt;li>&lt;a class="reference internal" href="#finalise-d">finalise.d&lt;/a>&lt;/li>
&lt;li>&lt;a class="reference internal" href="#cleanup-d">cleanup.d&lt;/a>&lt;/li>
&lt;/ol>
&lt;div class="section" id="root-d">
&lt;h3>root.d&lt;/h3>
&lt;p>Create or adapt the initial root filesystem content. This is where alternative distribution support is added, or
customisations such as building on an existing image.&lt;/p>
&lt;p>Only one element can use this at a time unless particular care is taken not to blindly overwrite but instead to adapt
the context extracted by other elements.&lt;/p>
&lt;blockquote>
&lt;dl class="docutils">
&lt;dt>runs:&lt;/dt>
&lt;dd>outside chroot&lt;/dd>
&lt;dt>inputs:&lt;/dt>
&lt;dd>&lt;ul class="first last simple">
&lt;li>$ARCH=i386 OR amd64 OR armhf OR arm64&lt;/li>
&lt;li>$TARGET_ROOT=/path/to/target/workarea&lt;/li>
&lt;/ul>
&lt;/dd>
&lt;/dl>
&lt;/blockquote>
&lt;/div>
&lt;div class="section" id="extra-data-d">
&lt;h3>extra-data.d&lt;/h3>
&lt;p>Pull in extra data from the host environment that hooks may need during image creation. This should copy any data (such
as SSH keys, http proxy settings and the like) somewhere under $TMP_HOOKS_PATH.&lt;/p>
&lt;blockquote>
&lt;dl class="docutils">
&lt;dt>runs:&lt;/dt>
&lt;dd>outside chroot&lt;/dd>
&lt;dt>inputs:&lt;/dt>
&lt;dd>&lt;ul class="first last simple">
&lt;li>$TMP_HOOKS_PATH&lt;/li>
&lt;/ul>
&lt;/dd>
&lt;dt>outputs:&lt;/dt>
&lt;dd>None&lt;/dd>
&lt;/dl>
&lt;/blockquote>
&lt;p>Contents placed under $TMP_HOOKS_PATH will be available at /tmp/in_target.d inside the chroot.&lt;/p>
&lt;/div>
&lt;div class="section" id="pre-install-d">
&lt;h3>pre-install.d&lt;/h3>
&lt;p>Run code in the chroot before customisation or packages are installed. A good place to add apt repositories.&lt;/p>
&lt;blockquote>
&lt;dl class="docutils">
&lt;dt>runs:&lt;/dt>
&lt;dd>in chroot&lt;/dd>
&lt;/dl>
&lt;/blockquote>
&lt;/div>
&lt;div class="section" id="install-d">
&lt;h3>install.d&lt;/h3>
&lt;p>Runs after pre-install.d in the chroot. This is a good place to install packages, chain into configuration management
tools or do other image specific operations.&lt;/p>
&lt;blockquote>
&lt;dl class="docutils">
&lt;dt>runs:&lt;/dt>
&lt;dd>in chroot&lt;/dd>
&lt;/dl>
&lt;/blockquote>
&lt;/div>
&lt;div class="section" id="post-install-d">
&lt;h3>post-install.d&lt;/h3>
&lt;p>Run code in the chroot. This is a good place to perform tasks you want to handle after the OS/application install but
before the first boot of the image. Some examples of use would be:&lt;/p>
&lt;ul class="simple">
&lt;li>Run chkconfig to disable unneeded services&lt;/li>
&lt;li>Clean the cache left by the package manager to reduce the size of the image.&lt;/li>
&lt;/ul>
&lt;blockquote>
&lt;dl class="docutils">
&lt;dt>runs:&lt;/dt>
&lt;dd>in chroot&lt;/dd>
&lt;/dl>
&lt;/blockquote>
&lt;/div>
&lt;div class="section" id="post-root-d">
&lt;h3>post-root.d&lt;/h3>
&lt;p>Run code outside the chroot. This is a good place to perform tasks that cannot run inside the chroot and must run after
installing things. The root filesystem content is rooted at $TMP_BUILD_DIR/mnt.&lt;/p>
&lt;blockquote>
&lt;dl class="docutils">
&lt;dt>runs:&lt;/dt>
&lt;dd>outside chroot&lt;/dd>
&lt;/dl>
&lt;/blockquote>
&lt;/div>
&lt;div class="section" id="block-device-d">
&lt;h3>block-device.d&lt;/h3>
&lt;p>Customise the block device that the image will be made on (for example to make partitions). Runs after the target tree
has been fully populated but before the cleanup.d phase runs.&lt;/p>
&lt;blockquote>
&lt;dl class="docutils">
&lt;dt>runs:&lt;/dt>
&lt;dd>outside chroot&lt;/dd>
&lt;dt>inputs:&lt;/dt>
&lt;dd>&lt;ul class="first last simple">
&lt;li>$IMAGE_BLOCK_DEVICE={path}&lt;/li>
&lt;li>$TARGET_ROOT={path}&lt;/li>
&lt;/ul>
&lt;/dd>
&lt;dt>outputs:&lt;/dt>
&lt;dd>&lt;ul class="first last simple">
&lt;li>$IMAGE_BLOCK_DEVICE={path}&lt;/li>
&lt;/ul>
&lt;/dd>
&lt;/dl>
&lt;/blockquote>
&lt;/div>
&lt;div class="section" id="pre-finalise-d">
&lt;h3>pre-finalise.d&lt;/h3>
&lt;p>Final tuning of the root filesystem, outside the chroot. Filesystem content has been copied into the final file system
which is rooted at $TMP_BUILD_DIR/mnt. You might do things like re-mount a cache directory that was used during the
build in this phase (with subsequent unmount in cleanup.d).&lt;/p>
&lt;blockquote>
&lt;dl class="docutils">
&lt;dt>runs:&lt;/dt>
&lt;dd>outside chroot&lt;/dd>
&lt;/dl>
&lt;/blockquote>
&lt;/div>
&lt;div class="section" id="finalise-d">
&lt;h3>finalise.d&lt;/h3>
&lt;p>Perform final tuning of the root filesystem. Runs in a chroot after the root filesystem content has been copied into the
mounted filesystem: this is an appropriate place to reset SELinux metadata, install grub bootloaders and so on.&lt;/p>
&lt;p>Because this happens inside the final image, it is important to limit operations here to only those necessary to affect
the filesystem metadata and image itself. For most operations, post-install.d is preferred.&lt;/p>
&lt;blockquote>
&lt;dl class="docutils">
&lt;dt>runs:&lt;/dt>
&lt;dd>in chroot&lt;/dd>
&lt;/dl>
&lt;/blockquote>
&lt;/div>
&lt;div class="section" id="cleanup-d">
&lt;h3>cleanup.d&lt;/h3>
&lt;p>Perform cleanup of the root filesystem content. For instance, temporary settings to use the image build environment HTTP
proxy are removed here in the dpkg element.&lt;/p>
&lt;blockquote>
&lt;dl class="docutils">
&lt;dt>runs:&lt;/dt>
&lt;dd>outside chroot&lt;/dd>
&lt;dt>inputs:&lt;/dt>
&lt;dd>&lt;ul class="first last simple">
&lt;li>$ARCH=i386 OR amd64 OR armh OR arm64&lt;/li>
&lt;li>$TARGET_ROOT=/path/to/target/workarea&lt;/li>
&lt;/ul>
&lt;/dd>
&lt;/dl>
&lt;/blockquote>
&lt;/div>
&lt;/div>
&lt;div class="section" id="cloud-init">
&lt;h2>cloud-init&lt;/h2>
&lt;p>Cloud-init is a method developed by &lt;a class="reference external" href="https://canonical.com/">Canonical&lt;/a> for cross-platform cloud instance
initialization. It is supported across major public cloud providers, provisioning systems for private cloud
infrastructure, and bare-metal installations.&lt;/p>
&lt;p>Cloud instances are initialized from a disk image and instance cloud metadata.&lt;/p>
&lt;p>Cloud-init will identify the cloud it is running on during boot, read any provided metadata from the cloud and
initialize the system accordingly. This may involve setting up the network and storage devices to configuring SSH access
key and many other aspects of a system. &lt;a class="footnote-reference" href="#cloudinit" id="id4">[4]&lt;/a>&lt;/p>
&lt;p>You can see &lt;a class="reference external" href="https://cloudinit.readthedocs.io/en/latest/topics/availability.html">here&lt;/a> the availability for
different Linux distributions as well as compatibility for different cloud execution environments.&lt;/p>
&lt;p>We should note that there are two main metadata types, EC2 and Openstack. They both have different API specifications
but you can usually find support for Openstack metadata links across different public clouds, or even both, be sure to
know your cloud provider!&lt;/p>
&lt;p>We will not go much into &lt;cite>cloud-init&lt;/cite> operation, just know that it exist and what purpose it serves. Of course, you are
free to explore the documentation, just be careful with the rabbit-hole Alice =)&lt;/p>
&lt;/div>
&lt;div class="section" id="creating-our-image">
&lt;h2>Creating our Image&lt;/h2>
&lt;p>For this article we will be building a &lt;strong>CentOS 8&lt;/strong> cloud-ready image.&lt;/p>
&lt;p>CentOS has released its version 8 cloud images last month, conveniently the diskimage-builder developers already added
the much needed changes to their centos-minimal element. Fortunately I noticed that the &lt;em>cloud-init&lt;/em> package was missing
from the generated image (I don't know if it is also missing from the upstream), so we should also add that package to
our image, or else we will not have our precious ssh-key added to access any launched instances.&lt;/p>
&lt;div class="section" id="setup">
&lt;h3>Setup&lt;/h3>
&lt;p>First we should get the tutorial files:&lt;/p>
&lt;div class="highlight">&lt;pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">&lt;code class="language-shell" data-lang="shell">t0rrant@testing:~$ wget https://implement.pt/files/hacking-diskimage-builder-for-fun-and-profit/dib-tutorial.tar.gz
t0rrant@testing:~$ tar xzf dib-tutorial.tar.gz
t0rrant@testing:~$ cd dib-tutorial&lt;/code>&lt;/pre>&lt;/div>
&lt;p>Now we should create a python virtual environment &lt;a class="reference external" href="{filename}virtualenvwrapper-installation-and-usage.rst">using virtualenv wrapper&lt;/a>
and install &lt;cite>diskimage-builder&lt;/cite> and its dependencies.&lt;/p>
&lt;div class="highlight">&lt;pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">&lt;code class="language-shell" data-lang="shell">t0rrant@testing:~/dib-tutorial$ mkvirtualenv dib-elements
&lt;span style="color:#f92672">(&lt;/span>dib-elements&lt;span style="color:#f92672">)&lt;/span> t0rrant@testing:~/dib-tutorial$ pip install diskimage-builder&lt;/code>&lt;/pre>&lt;/div>
&lt;/div>
&lt;div class="section" id="project-structure">
&lt;h3>Project Structure&lt;/h3>
&lt;p>For modularity and learning purposes we will have two elements, &lt;cite>centos-eight&lt;/cite> and &lt;cite>my_base&lt;/cite>. The &lt;cite>my_base&lt;/cite> element will
contain settings that may be common between different images, be it CentOS, Debian, Ubuntu, Gentoo, or any others.
Whereas the &lt;cite>centos-eight&lt;/cite> will take care only of things specific to CentOS 8.&lt;/p>
&lt;p>Let us start with the &lt;em>my_base&lt;/em> element.&lt;/p>
&lt;p>We should have a directory structure that looks something like this:&lt;/p>
&lt;div class="highlight">&lt;pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">&lt;code class="language-shell" data-lang="shell"> my_base/
├── element-deps
├── environment.d
│   └── 10-cloud-init-datasources
├── package-installs.yaml
├── pkg-map
├── post-install.d
│   └── 01-cloud-init-override
└── README.rst&lt;/code>&lt;/pre>&lt;/div>
&lt;p>the &lt;cite>element-deps&lt;/cite> file describes which other elements should be run alongside ours:&lt;/p>
&lt;div class="highlight">&lt;div style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">
&lt;table style="border-spacing:0;padding:0;margin:0;border:0;width:auto;overflow:auto;display:block;">&lt;tr>&lt;td style="vertical-align:top;padding:0;margin:0;border:0;">
&lt;pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">&lt;code>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">1
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">2
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">3
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">4
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">5
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">6
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">7
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">8
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">9
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td style="vertical-align:top;padding:0;margin:0;border:0;;width:100%">
&lt;pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">&lt;code class="language-fallback" data-lang="fallback">base
cloud-init-datasources
dhcp-all-interfaces
enable-serial-console
growroot
openssh-server
package-installs
pkg-map
vm&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>
&lt;p>Besides the &amp;quot;mandatory&amp;quot; &lt;cite>base&lt;/cite> and &lt;cite>vm&lt;/cite> elements, we have some important elements that we do &lt;strong>not&lt;/strong> need to configure.&lt;/p>
&lt;ul class="simple">
&lt;li>growroot - grow the root partition on first boot &lt;a class="footnote-reference" href="#growroot" id="id5">[5]&lt;/a>&lt;/li>
&lt;li>dhcp-all-interfaces - autodetect network interfaces during boot and configure them for DHCP &lt;a class="footnote-reference" href="#dhcpallinterfaces" id="id6">[6]&lt;/a>&lt;/li>
&lt;li>enable-serial-console - start getty on active serial consoles. &lt;a class="footnote-reference" href="#enableserialconsole" id="id7">[7]&lt;/a>&lt;/li>
&lt;li>openssh-server - ensures that openssh server is installed and enabled during boot &lt;a class="footnote-reference" href="#opensshserver" id="id8">[8]&lt;/a>&lt;/li>
&lt;/ul>
&lt;p>Using the &lt;cite>cloud-init-datasources&lt;/cite> element allows us to configure from where does &lt;cite>cloud-init&lt;/cite> fetch metadata to feed
to our instance, and the &lt;cite>DIB_CLOUD_INIT_DATASOURCES&lt;/cite> environment variable &lt;strong>must&lt;/strong> be set on image creation.
One way of doing this is using the &lt;cite>environment.d&lt;/cite> stage, and in our example we setup only the &lt;em>Openstack&lt;/em> data source.
Let us create the &lt;cite>10-cloud-init-datasources&lt;/cite> file inside &lt;cite>environment.d&lt;/cite>:&lt;/p>
&lt;div class="highlight">&lt;div style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">
&lt;table style="border-spacing:0;padding:0;margin:0;border:0;width:auto;overflow:auto;display:block;">&lt;tr>&lt;td style="vertical-align:top;padding:0;margin:0;border:0;">
&lt;pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">&lt;code>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">1
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td style="vertical-align:top;padding:0;margin:0;border:0;;width:100%">
&lt;pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">&lt;code class="language-bash" data-lang="bash"> export DIB_CLOUD_INIT_DATASOURCES&lt;span style="color:#f92672">=&lt;/span>&lt;span style="color:#e6db74">&amp;#34;OpenStack&amp;#34;&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>
&lt;p>Using both &lt;cite>package-installs&lt;/cite> and &lt;cite>pkg-map&lt;/cite> elements gives us a flexible way to manage package installation across
different distributions. We use &lt;cite>pkg-map&lt;/cite> to map an arbitrary name to a specific package name in a specific
distribution family, or release, and with &lt;cite>package-installs&lt;/cite> we define which of those arbitrary names we want installed
in our image. So we create the &lt;cite>package-installs.yaml&lt;/cite> file in the root of our element:&lt;/p>
&lt;div class="highlight">&lt;div style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">
&lt;table style="border-spacing:0;padding:0;margin:0;border:0;width:auto;overflow:auto;display:block;">&lt;tr>&lt;td style="vertical-align:top;padding:0;margin:0;border:0;">
&lt;pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">&lt;code>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 1
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 2
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 3
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 4
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 5
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 6
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 7
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 8
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 9
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">10
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">11
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">12
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">13
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">14
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">15
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td style="vertical-align:top;padding:0;margin:0;border:0;;width:100%">
&lt;pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">&lt;code class="language-yaml" data-lang="yaml">&lt;span style="color:#f92672">curl&lt;/span>:
&lt;span style="color:#f92672">dnsutils&lt;/span>:
&lt;span style="color:#f92672">git&lt;/span>:
&lt;span style="color:#f92672">htop&lt;/span>:
&lt;span style="color:#f92672">iptables&lt;/span>:
&lt;span style="color:#f92672">man&lt;/span>:
&lt;span style="color:#f92672">nano&lt;/span>:
&lt;span style="color:#f92672">netcat&lt;/span>:
&lt;span style="color:#f92672">ntp&lt;/span>:
&lt;span style="color:#f92672">ping&lt;/span>:
&lt;span style="color:#f92672">resolvconf&lt;/span>:
&lt;span style="color:#f92672">syslog&lt;/span>:
&lt;span style="color:#f92672">tree&lt;/span>:
&lt;span style="color:#f92672">vim&lt;/span>:
&lt;span style="color:#ae81ff">wget:&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>
&lt;p>and the corresponding package mapping in the &lt;cite>pkg-map&lt;/cite> file, in json format:&lt;/p>
&lt;div class="highlight">&lt;div style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">
&lt;table style="border-spacing:0;padding:0;margin:0;border:0;width:auto;overflow:auto;display:block;">&lt;tr>&lt;td style="vertical-align:top;padding:0;margin:0;border:0;">
&lt;pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">&lt;code>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 1
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 2
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 3
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 4
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 5
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 6
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 7
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 8
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 9
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">10
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">11
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">12
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">13
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">14
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">15
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">16
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">17
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">18
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">19
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">20
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">21
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">22
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">23
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">24
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">25
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">26
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">27
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">28
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">29
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">30
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">31
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">32
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">33
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">34
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">35
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">36
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">37
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">38
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">39
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">40
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">41
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">42
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">43
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">44
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">45
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td style="vertical-align:top;padding:0;margin:0;border:0;;width:100%">
&lt;pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">&lt;code class="language-json" data-lang="json">{
&lt;span style="color:#f92672">&amp;#34;release&amp;#34;&lt;/span>: {
&lt;span style="color:#f92672">&amp;#34;centos&amp;#34;&lt;/span>: {
&lt;span style="color:#f92672">&amp;#34;8&amp;#34;&lt;/span>: {
&lt;span style="color:#f92672">&amp;#34;ntp&amp;#34;&lt;/span>: &lt;span style="color:#e6db74">&amp;#34;chrony&amp;#34;&lt;/span>
}
}
},
&lt;span style="color:#f92672">&amp;#34;family&amp;#34;&lt;/span>: {
&lt;span style="color:#f92672">&amp;#34;redhat&amp;#34;&lt;/span>: {
&lt;span style="color:#f92672">&amp;#34;curl&amp;#34;&lt;/span>: &lt;span style="color:#e6db74">&amp;#34;curl&amp;#34;&lt;/span>,
&lt;span style="color:#f92672">&amp;#34;dnsutils&amp;#34;&lt;/span>: &lt;span style="color:#e6db74">&amp;#34;bind-utils&amp;#34;&lt;/span>,
&lt;span style="color:#f92672">&amp;#34;git&amp;#34;&lt;/span>: &lt;span style="color:#e6db74">&amp;#34;git&amp;#34;&lt;/span>,
&lt;span style="color:#f92672">&amp;#34;htop&amp;#34;&lt;/span>: &lt;span style="color:#e6db74">&amp;#34;htop&amp;#34;&lt;/span>,
&lt;span style="color:#f92672">&amp;#34;iptables&amp;#34;&lt;/span>: &lt;span style="color:#e6db74">&amp;#34;iptables&amp;#34;&lt;/span>,
&lt;span style="color:#f92672">&amp;#34;man&amp;#34;&lt;/span>: &lt;span style="color:#e6db74">&amp;#34;man-db&amp;#34;&lt;/span>,
&lt;span style="color:#f92672">&amp;#34;nano&amp;#34;&lt;/span>: &lt;span style="color:#e6db74">&amp;#34;nano&amp;#34;&lt;/span>,
&lt;span style="color:#f92672">&amp;#34;netcat&amp;#34;&lt;/span>: &lt;span style="color:#e6db74">&amp;#34;nc&amp;#34;&lt;/span>,
&lt;span style="color:#f92672">&amp;#34;ntp&amp;#34;&lt;/span>: &lt;span style="color:#e6db74">&amp;#34;ntp&amp;#34;&lt;/span>,
&lt;span style="color:#f92672">&amp;#34;ping&amp;#34;&lt;/span>: &lt;span style="color:#e6db74">&amp;#34;iputils&amp;#34;&lt;/span>,
&lt;span style="color:#f92672">&amp;#34;resolvconf&amp;#34;&lt;/span>: &lt;span style="color:#e6db74">&amp;#34;&amp;#34;&lt;/span>,
&lt;span style="color:#f92672">&amp;#34;syslog&amp;#34;&lt;/span>: &lt;span style="color:#e6db74">&amp;#34;rsyslog&amp;#34;&lt;/span>,
&lt;span style="color:#f92672">&amp;#34;tree&amp;#34;&lt;/span>: &lt;span style="color:#e6db74">&amp;#34;tree&amp;#34;&lt;/span>,
&lt;span style="color:#f92672">&amp;#34;vim&amp;#34;&lt;/span>: &lt;span style="color:#e6db74">&amp;#34;vim-enhanced&amp;#34;&lt;/span>,
&lt;span style="color:#f92672">&amp;#34;wget&amp;#34;&lt;/span>: &lt;span style="color:#e6db74">&amp;#34;wget&amp;#34;&lt;/span>
},
&lt;span style="color:#f92672">&amp;#34;debian&amp;#34;&lt;/span>:{
&lt;span style="color:#f92672">&amp;#34;curl&amp;#34;&lt;/span>: &lt;span style="color:#e6db74">&amp;#34;curl&amp;#34;&lt;/span>,
&lt;span style="color:#f92672">&amp;#34;dnsutils&amp;#34;&lt;/span>: &lt;span style="color:#e6db74">&amp;#34;dnsutils&amp;#34;&lt;/span>,
&lt;span style="color:#f92672">&amp;#34;git&amp;#34;&lt;/span>: &lt;span style="color:#e6db74">&amp;#34;git&amp;#34;&lt;/span>,
&lt;span style="color:#f92672">&amp;#34;htop&amp;#34;&lt;/span>: &lt;span style="color:#e6db74">&amp;#34;htop&amp;#34;&lt;/span>,
&lt;span style="color:#f92672">&amp;#34;iptables&amp;#34;&lt;/span>: &lt;span style="color:#e6db74">&amp;#34;iptables&amp;#34;&lt;/span>,
&lt;span style="color:#f92672">&amp;#34;man&amp;#34;&lt;/span>: &lt;span style="color:#e6db74">&amp;#34;man&amp;#34;&lt;/span>,
&lt;span style="color:#f92672">&amp;#34;nano&amp;#34;&lt;/span>: &lt;span style="color:#e6db74">&amp;#34;nano&amp;#34;&lt;/span>,
&lt;span style="color:#f92672">&amp;#34;netcat&amp;#34;&lt;/span>: &lt;span style="color:#e6db74">&amp;#34;netcat-openbsd&amp;#34;&lt;/span>,
&lt;span style="color:#f92672">&amp;#34;ntp&amp;#34;&lt;/span>: &lt;span style="color:#e6db74">&amp;#34;ntp&amp;#34;&lt;/span>,
&lt;span style="color:#f92672">&amp;#34;ping&amp;#34;&lt;/span>: &lt;span style="color:#e6db74">&amp;#34;inetutils-ping&amp;#34;&lt;/span>,
&lt;span style="color:#f92672">&amp;#34;resolvconf&amp;#34;&lt;/span>: &lt;span style="color:#e6db74">&amp;#34;resolvconf&amp;#34;&lt;/span>,
&lt;span style="color:#f92672">&amp;#34;syslog&amp;#34;&lt;/span>: &lt;span style="color:#e6db74">&amp;#34;rsyslog&amp;#34;&lt;/span>,
&lt;span style="color:#f92672">&amp;#34;tree&amp;#34;&lt;/span>: &lt;span style="color:#e6db74">&amp;#34;tree&amp;#34;&lt;/span>,
&lt;span style="color:#f92672">&amp;#34;vim&amp;#34;&lt;/span>: &lt;span style="color:#e6db74">&amp;#34;vim&amp;#34;&lt;/span>,
&lt;span style="color:#f92672">&amp;#34;wget&amp;#34;&lt;/span>: &lt;span style="color:#e6db74">&amp;#34;wget&amp;#34;&lt;/span>
}
}
}&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>
&lt;p>Which in our case will map everything under [&amp;quot;family&amp;quot;][&amp;quot;redhat&amp;quot;] except for the &lt;cite>ntp&lt;/cite> package, which we specified to be
&lt;cite>chrony&lt;/cite> for CentOS 8.&lt;/p>
&lt;p>For consistency between different elements using the &lt;cite>my_base&lt;/cite> element, we may want to override the cloud-init
configuration that comes with each distro's cloud-init version. To do that we should use the &lt;cite>post-install.d&lt;/cite> stage.
Let us create the &lt;cite>01-cloud-init-override&lt;/cite> file in the &lt;cite>post-install.d&lt;/cite> directory:&lt;/p>
&lt;div class="highlight">&lt;div style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">
&lt;table style="border-spacing:0;padding:0;margin:0;border:0;width:auto;overflow:auto;display:block;">&lt;tr>&lt;td style="vertical-align:top;padding:0;margin:0;border:0;">
&lt;pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">&lt;code>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 1
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 2
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 3
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 4
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 5
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 6
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 7
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 8
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 9
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">10
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">11
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">12
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">13
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">14
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">15
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">16
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">17
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">18
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">19
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">20
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">21
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">22
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">23
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">24
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">25
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">26
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">27
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">28
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">29
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">30
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">31
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">32
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">33
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">34
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">35
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">36
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">37
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">38
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">39
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">40
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">41
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">42
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">43
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">44
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">45
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">46
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">47
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">48
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">49
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">50
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">51
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">52
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">53
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">54
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">55
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">56
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">57
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">58
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">59
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">60
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">61
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">62
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">63
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">64
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">65
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">66
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">67
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">68
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">69
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td style="vertical-align:top;padding:0;margin:0;border:0;;width:100%">
&lt;pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">&lt;code class="language-bash" data-lang="bash">&lt;span style="color:#75715e">## DISCLAMER: this file serves as a consistency point for all cloud-init options, if you need to enable modules do it&lt;/span>
&lt;span style="color:#75715e"># here. If you need to add options, use the 50-cloud-init-config or 99-cloud-init file in individual elements&lt;/span>
tee /etc/cloud/cloud.cfg &lt;span style="color:#e6db74">&amp;lt;&amp;lt;EOF
&lt;/span>&lt;span style="color:#e6db74">cloud_init_modules:
&lt;/span>&lt;span style="color:#e6db74"> - migrator
&lt;/span>&lt;span style="color:#e6db74"> - seed_random
&lt;/span>&lt;span style="color:#e6db74"> - bootcmd
&lt;/span>&lt;span style="color:#e6db74"> - write-files
&lt;/span>&lt;span style="color:#e6db74"> - growpart
&lt;/span>&lt;span style="color:#e6db74"> - resizefs
&lt;/span>&lt;span style="color:#e6db74"> - disk_setup
&lt;/span>&lt;span style="color:#e6db74"> - mounts
&lt;/span>&lt;span style="color:#e6db74"> - update_etc_hosts
&lt;/span>&lt;span style="color:#e6db74"> - resolv_conf
&lt;/span>&lt;span style="color:#e6db74"> - ca-certs
&lt;/span>&lt;span style="color:#e6db74"> - rsyslog
&lt;/span>&lt;span style="color:#e6db74"> - mounts
&lt;/span>&lt;span style="color:#e6db74"> - apt-configure
&lt;/span>&lt;span style="color:#e6db74"> - rh_subscription
&lt;/span>&lt;span style="color:#e6db74"> - yum-add-repo
&lt;/span>&lt;span style="color:#e6db74"> - package-update-upgrade-install
&lt;/span>&lt;span style="color:#e6db74"> - users-groups
&lt;/span>&lt;span style="color:#e6db74"> - ssh
&lt;/span>&lt;span style="color:#e6db74">
&lt;/span>&lt;span style="color:#e6db74">cloud_config_modules:
&lt;/span>&lt;span style="color:#e6db74"> # Emit the cloud config ready event
&lt;/span>&lt;span style="color:#e6db74"> # this can be used by upstart jobs for &amp;#39;start on cloud-config&amp;#39;.
&lt;/span>&lt;span style="color:#e6db74"> - emit_upstart
&lt;/span>&lt;span style="color:#e6db74"> - snap
&lt;/span>&lt;span style="color:#e6db74"> - snap_config # DEPRECATED- Drop in version 18.2
&lt;/span>&lt;span style="color:#e6db74"> - ssh-import-id
&lt;/span>&lt;span style="color:#e6db74"> - locale
&lt;/span>&lt;span style="color:#e6db74"> - set-passwords
&lt;/span>&lt;span style="color:#e6db74"> - grub-dpkg
&lt;/span>&lt;span style="color:#e6db74"> - apt-pipelining
&lt;/span>&lt;span style="color:#e6db74"> - ubuntu-advantage
&lt;/span>&lt;span style="color:#e6db74"> - ntp
&lt;/span>&lt;span style="color:#e6db74"> - timezone
&lt;/span>&lt;span style="color:#e6db74"> - puppet
&lt;/span>&lt;span style="color:#e6db74"> - chef
&lt;/span>&lt;span style="color:#e6db74"> - salt-minion
&lt;/span>&lt;span style="color:#e6db74"> - mcollective
&lt;/span>&lt;span style="color:#e6db74"> - disable-ec2-metadata
&lt;/span>&lt;span style="color:#e6db74"> - runcmd
&lt;/span>&lt;span style="color:#e6db74"> - byobu
&lt;/span>&lt;span style="color:#e6db74">
&lt;/span>&lt;span style="color:#e6db74">cloud_final_modules:
&lt;/span>&lt;span style="color:#e6db74"> - fan
&lt;/span>&lt;span style="color:#e6db74"> - landscape
&lt;/span>&lt;span style="color:#e6db74"> - lxd
&lt;/span>&lt;span style="color:#e6db74"> - ubuntu-drivers
&lt;/span>&lt;span style="color:#e6db74"> - rightscale_userdata
&lt;/span>&lt;span style="color:#e6db74"> - scripts-vendor
&lt;/span>&lt;span style="color:#e6db74"> - scripts-per-once
&lt;/span>&lt;span style="color:#e6db74"> - scripts-per-boot
&lt;/span>&lt;span style="color:#e6db74"> - scripts-per-instance
&lt;/span>&lt;span style="color:#e6db74"> - scripts-user
&lt;/span>&lt;span style="color:#e6db74"> - ssh-authkey-fingerprints
&lt;/span>&lt;span style="color:#e6db74"> - keys-to-console
&lt;/span>&lt;span style="color:#e6db74"> - phone-home
&lt;/span>&lt;span style="color:#e6db74"> - final-message
&lt;/span>&lt;span style="color:#e6db74"> - power-state-change
&lt;/span>&lt;span style="color:#e6db74">
&lt;/span>&lt;span style="color:#e6db74">users:
&lt;/span>&lt;span style="color:#e6db74"> - default
&lt;/span>&lt;span style="color:#e6db74">
&lt;/span>&lt;span style="color:#e6db74">disable_root: true
&lt;/span>&lt;span style="color:#e6db74">
&lt;/span>&lt;span style="color:#e6db74">EOF&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>
&lt;p>&lt;strong>Note:&lt;/strong> the files under &lt;cite>post-install.d&lt;/cite> should have executable mode activated, or they will &lt;strong>not&lt;/strong> be executed.&lt;/p>
&lt;p>Now we can move on to our &lt;em>centos-eight&lt;/em> element, here is our directory structure:&lt;/p>
&lt;div class="highlight">&lt;pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">&lt;code class="language-shell" data-lang="shell"> centos-eight/
├── element-deps
├── package-installs.yaml
├── pkg-map
├── post-install.d
│   └── 50-cloud-init-config
└── README.rst&lt;/code>&lt;/pre>&lt;/div>
&lt;p>Taking a look at the dependencies (&lt;cite>element-deps&lt;/cite> file) for our final element, &lt;cite>centos-eight&lt;/cite>:&lt;/p>
&lt;div class="highlight">&lt;div style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">
&lt;table style="border-spacing:0;padding:0;margin:0;border:0;width:auto;overflow:auto;display:block;">&lt;tr>&lt;td style="vertical-align:top;padding:0;margin:0;border:0;">
&lt;pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">&lt;code>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">1
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">2
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">3
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">4
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">5
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td style="vertical-align:top;padding:0;margin:0;border:0;;width:100%">
&lt;pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">&lt;code class="language-fallback" data-lang="fallback">centos-minimal
my_base
epel
package-installs
pkg-map&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>
&lt;p>we can see we are including the &lt;cite>my_base&lt;/cite> element we created earlier, two new elements, and we are repeating the use for
the &lt;cite>package-installs&lt;/cite> and &lt;cite>pkg-map&lt;/cite>. We include them again as we will want to specify in this element only a special
case where we want the &lt;em>cloud-init&lt;/em> package to be installed. We do not do this in the &lt;cite>my_base&lt;/cite> element as other
distributions may already have &lt;em>cloud-init&lt;/em> installed, and they may or may not use the package manager, so we leave that
option open.&lt;/p>
&lt;p>Let us configure &lt;cite>package-install.yaml&lt;/cite> for &lt;cite>centos-eight&lt;/cite>:&lt;/p>
&lt;div class="highlight">&lt;div style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">
&lt;table style="border-spacing:0;padding:0;margin:0;border:0;width:auto;overflow:auto;display:block;">&lt;tr>&lt;td style="vertical-align:top;padding:0;margin:0;border:0;">
&lt;pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">&lt;code>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">1
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td style="vertical-align:top;padding:0;margin:0;border:0;;width:100%">
&lt;pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">&lt;code class="language-yaml" data-lang="yaml"> &lt;span style="color:#ae81ff">cloud-init:&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>
&lt;p>and also the &lt;cite>pkg-map&lt;/cite>:&lt;/p>
&lt;div class="highlight">&lt;div style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">
&lt;table style="border-spacing:0;padding:0;margin:0;border:0;width:auto;overflow:auto;display:block;">&lt;tr>&lt;td style="vertical-align:top;padding:0;margin:0;border:0;">
&lt;pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">&lt;code>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">1
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">2
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">3
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">4
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">5
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">6
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">7
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">8
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">9
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td style="vertical-align:top;padding:0;margin:0;border:0;;width:100%">
&lt;pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">&lt;code class="language-json" data-lang="json">{
&lt;span style="color:#f92672">&amp;#34;release&amp;#34;&lt;/span>: {
&lt;span style="color:#f92672">&amp;#34;centos&amp;#34;&lt;/span>: {
&lt;span style="color:#f92672">&amp;#34;8&amp;#34;&lt;/span>: {
&lt;span style="color:#f92672">&amp;#34;cloud-init&amp;#34;&lt;/span>: &lt;span style="color:#e6db74">&amp;#34;cloud-init&amp;#34;&lt;/span>
}
}
}
}&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>
&lt;p>The &lt;cite>centos-minimal&lt;/cite> element will create a minimal image based on CentOS. The use of this element will require ‘yum’ and
‘yum-utils’ to be installed if you are using Ubuntu or Debian where you generate the image. Nothing additional is needed
on Fedora or CentOS. &lt;a class="footnote-reference" href="#centosminimal" id="id9">[9]&lt;/a>&lt;/p>
&lt;p>The &lt;cite>epel&lt;/cite> element installs the Extra Packages for Enterprise Linux (EPEL) repository GPG key as well as configuration
for yum. &lt;a class="footnote-reference" href="#epel" id="id10">[10]&lt;/a>&lt;/p>
&lt;p>In this case we want to make sure some of &lt;cite>cloud-init&lt;/cite>'s options are added to our already overridden configuration, so
we will use the same stage as in the &lt;cite>my_base&lt;/cite> element, but with another priority so that it runs after, in this case
we name the file &lt;cite>50-cloud-init-config&lt;/cite> within the &lt;cite>post-instal.d&lt;/cite> directory:&lt;/p>
&lt;div class="highlight">&lt;div style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">
&lt;table style="border-spacing:0;padding:0;margin:0;border:0;width:auto;overflow:auto;display:block;">&lt;tr>&lt;td style="vertical-align:top;padding:0;margin:0;border:0;">
&lt;pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">&lt;code>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 1
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 2
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 3
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 4
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 5
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 6
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 7
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 8
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 9
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">10
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">11
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">12
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">13
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">14
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">15
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">16
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">17
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">18
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">19
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">20
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">21
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">22
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">23
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">24
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">25
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">26
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">27
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">28
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">29
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">30
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">31
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">32
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">33
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">34
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">35
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">36
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">37
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">38
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">39
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td style="vertical-align:top;padding:0;margin:0;border:0;;width:100%">
&lt;pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">&lt;code class="language-bash" data-lang="bash">yum-config-manager --save --setopt&lt;span style="color:#f92672">=&lt;/span>updates.skip_if_unavailable&lt;span style="color:#f92672">=&lt;/span>true
yum-config-manager --save --setopt&lt;span style="color:#f92672">=&lt;/span>extras.skip_if_unavailable&lt;span style="color:#f92672">=&lt;/span>true
tee --append /etc/cloud/cloud.cfg &lt;span style="color:#e6db74">&amp;lt;&amp;lt;EOF
&lt;/span>&lt;span style="color:#e6db74">mount_default_fields: [~, ~, &amp;#39;auto&amp;#39;, &amp;#39;defaults,nofail,x-systemd.requires=cloud-init.service&amp;#39;, &amp;#39;0&amp;#39;, &amp;#39;2&amp;#39;]
&lt;/span>&lt;span style="color:#e6db74">resize_rootfs_tmp: /dev
&lt;/span>&lt;span style="color:#e6db74">ssh_deletekeys: 0
&lt;/span>&lt;span style="color:#e6db74">ssh_genkeytypes: ~
&lt;/span>&lt;span style="color:#e6db74">syslog_fix_perms: ~
&lt;/span>&lt;span style="color:#e6db74">ssh_pwauth: 0
&lt;/span>&lt;span style="color:#e6db74">
&lt;/span>&lt;span style="color:#e6db74">system_info:
&lt;/span>&lt;span style="color:#e6db74"> default_user:
&lt;/span>&lt;span style="color:#e6db74"> name: centos
&lt;/span>&lt;span style="color:#e6db74"> lock_passwd: true
&lt;/span>&lt;span style="color:#e6db74"> gecos: CentOS
&lt;/span>&lt;span style="color:#e6db74"> groups: [wheel, adm, systemd-journal]
&lt;/span>&lt;span style="color:#e6db74"> sudo: [&amp;#34;ALL=(ALL) NOPASSWD:ALL&amp;#34;]
&lt;/span>&lt;span style="color:#e6db74"> shell: /bin/bash
&lt;/span>&lt;span style="color:#e6db74"> distro: rhel
&lt;/span>&lt;span style="color:#e6db74"> paths:
&lt;/span>&lt;span style="color:#e6db74"> cloud_dir: /var/lib/cloud
&lt;/span>&lt;span style="color:#e6db74"> templates_dir: /etc/cloud/templates
&lt;/span>&lt;span style="color:#e6db74"> ssh_svcname: sshd
&lt;/span>&lt;span style="color:#e6db74">
&lt;/span>&lt;span style="color:#e6db74">ntp:
&lt;/span>&lt;span style="color:#e6db74"> enabled: true
&lt;/span>&lt;span style="color:#e6db74"> ntp_client: chrony
&lt;/span>&lt;span style="color:#e6db74"> conf:
&lt;/span>&lt;span style="color:#e6db74"> service_name: chronyd
&lt;/span>&lt;span style="color:#e6db74"> servers:
&lt;/span>&lt;span style="color:#e6db74"> - 0.europe.pool.ntp.org
&lt;/span>&lt;span style="color:#e6db74"> - 1.europe.pool.ntp.org
&lt;/span>&lt;span style="color:#e6db74"> - 2.europe.pool.ntp.org
&lt;/span>&lt;span style="color:#e6db74"> - 3.europe.pool.ntp.org
&lt;/span>&lt;span style="color:#e6db74">
&lt;/span>&lt;span style="color:#e6db74">timezone: Europe/Lisbon
&lt;/span>&lt;span style="color:#e6db74">
&lt;/span>&lt;span style="color:#e6db74">EOF&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>
&lt;p>That is it for the &lt;cite>centos-eight&lt;/cite> element and we are now (almost) ready to generate our customized cloud-ready image&lt;/p>
&lt;/div>
&lt;div class="section" id="generating-the-image">
&lt;h3>Generating the image&lt;/h3>
&lt;p>Now that we have our elements setup, we can setup our environment variables:&lt;/p>
&lt;ul class="simple">
&lt;li>we define where &lt;cite>disk-image-create&lt;/cite> can find our custom elements&lt;/li>
&lt;li>we define the main distro we are creating&lt;/li>
&lt;li>we define the distro's release (this is relevant for *-minimal elements)&lt;/li>
&lt;/ul>
&lt;div class="highlight">&lt;pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">&lt;code class="language-shell" data-lang="shell">&lt;span style="color:#f92672">(&lt;/span>dib-elements&lt;span style="color:#f92672">)&lt;/span> t0rrant@testing:~/dib-tutorial$ export ELEMENTS_PATH&lt;span style="color:#f92672">=&lt;/span>elements
&lt;span style="color:#f92672">(&lt;/span>dib-elements&lt;span style="color:#f92672">)&lt;/span> t0rrant@testing:~/dib-tutorial$ export DISTRO&lt;span style="color:#f92672">=&lt;/span>centos
&lt;span style="color:#f92672">(&lt;/span>dib-elements&lt;span style="color:#f92672">)&lt;/span> t0rrant@testing:~/dib-tutorial$ export DIB_RELEASE&lt;span style="color:#f92672">=&lt;/span>&lt;span style="color:#ae81ff">8&lt;/span>&lt;/code>&lt;/pre>&lt;/div>
&lt;p>Finally we can create our image:&lt;/p>
&lt;div class="highlight">&lt;pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">&lt;code class="language-shell" data-lang="shell">&lt;span style="color:#f92672">(&lt;/span>dib-elements&lt;span style="color:#f92672">)&lt;/span> t0rrant@testing:~/dib-tutorial$ disk-image-create -x --no-tmpfs -o centos8.qcow2 block-device-mbr centos-eight&lt;/code>&lt;/pre>&lt;/div>
&lt;p>We can now upload the generated image to our cloud platform, i.e:&lt;/p>
&lt;div class="highlight">&lt;pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">&lt;code class="language-shell" data-lang="shell">&lt;span style="color:#f92672">(&lt;/span>dib-elements&lt;span style="color:#f92672">)&lt;/span> t0rrant@testing:~/dib-tutorial$ openstack image create my-centos-eight --file centos8.qcow2 --disk-format&lt;span style="color:#f92672">=&lt;/span>qcow2 --container-format&lt;span style="color:#f92672">=&lt;/span>bare&lt;/code>&lt;/pre>&lt;/div>
&lt;p>Pick up some java, wheat or malt, you've earned it! Then go play with the image you have just uploaded and see if you
can customize it even further.&lt;/p>
&lt;/div>
&lt;/div>
&lt;div class="section" id="conclusion">
&lt;h2>Conclusion&lt;/h2>
&lt;p>In this article we went through all of the components required to create a cloud-ready image (almost) from scratch.
We saw what are elements defined in diskimage builder, and what phases they go through in the image building process.
Talked briefly about cloud-init and metadata types, and finished with an in-depth tutorial of building a customized
cloud-ready CentOS 8 image.&lt;/p>
&lt;p>Hopefully this was useful and you could replicate every example =)&lt;/p>
&lt;p>Feel free to leave comments below, any improvement to this and other articles is always welcome.&lt;/p>
&lt;/div>
&lt;div class="section" id="references">
&lt;h2>References&lt;/h2>
&lt;table class="docutils footnote" frame="void" id="diskimagebuilder" rules="none">
&lt;colgroup>&lt;col class="label" />&lt;col />&lt;/colgroup>
&lt;tbody valign="top">
&lt;tr>&lt;td class="label">[1]&lt;/td>&lt;td>--, diskimage-builder - Openstack Documentation, diskimage-builder 2.33.1.dev11. &lt;a class="reference external" href="https://docs.openstack.org/diskimage-builder/latest/index.html">[link]&lt;/a>&lt;/td>&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;table class="docutils footnote" frame="void" id="elementdesign" rules="none">
&lt;colgroup>&lt;col class="label" />&lt;col />&lt;/colgroup>
&lt;tbody valign="top">
&lt;tr>&lt;td class="label">[2]&lt;/td>&lt;td>&lt;em>(&lt;a class="fn-backref" href="#id1">1&lt;/a>, &lt;a class="fn-backref" href="#id2">2&lt;/a>)&lt;/em> -- , Developer Guide (Design) - Openstack Documentation, diskimage-builder 2.33.1.dev11. &lt;a class="reference external" href="https://docs.openstack.org/diskimage-builder/latest/developer/design.html">[link]&lt;/a>&lt;/td>&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;table class="docutils footnote" frame="void" id="elementdeveloping" rules="none">
&lt;colgroup>&lt;col class="label" />&lt;col />&lt;/colgroup>
&lt;tbody valign="top">
&lt;tr>&lt;td class="label">&lt;a class="fn-backref" href="#id3">[3]&lt;/a>&lt;/td>&lt;td>-- , Developer Guide (Developing Elements) - Openstack Documentation, diskimage-builder 2.33.1.dev11. &lt;a class="reference external" href="https://docs.openstack.org/diskimage-builder/latest/developer/developing_elements.html">[link]&lt;/a>&lt;/td>&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;table class="docutils footnote" frame="void" id="cloudinit" rules="none">
&lt;colgroup>&lt;col class="label" />&lt;col />&lt;/colgroup>
&lt;tbody valign="top">
&lt;tr>&lt;td class="label">&lt;a class="fn-backref" href="#id4">[4]&lt;/a>&lt;/td>&lt;td>-- , cloud-init Documentation, cloud-init 20.1. &lt;a class="reference external" href="https://cloudinit.readthedocs.io/en/20.1/">[link]&lt;/a>&lt;/td>&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;table class="docutils footnote" frame="void" id="growroot" rules="none">
&lt;colgroup>&lt;col class="label" />&lt;col />&lt;/colgroup>
&lt;tbody valign="top">
&lt;tr>&lt;td class="label">&lt;a class="fn-backref" href="#id5">[5]&lt;/a>&lt;/td>&lt;td>--, growroot element - Openstack Documentation, diskimage-builder 2.33.1.dev11. &lt;a class="reference external" href="https://docs.openstack.org/diskimage-builder/latest/elements/growroot/README.html">[link]&lt;/a>&lt;/td>&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;table class="docutils footnote" frame="void" id="dhcpallinterfaces" rules="none">
&lt;colgroup>&lt;col class="label" />&lt;col />&lt;/colgroup>
&lt;tbody valign="top">
&lt;tr>&lt;td class="label">&lt;a class="fn-backref" href="#id6">[6]&lt;/a>&lt;/td>&lt;td>--, dhcp-all-interfaces element - Openstack Documentation, diskimage-builder 2.33.1.dev11. &lt;a class="reference external" href="https://docs.openstack.org/diskimage-builder/latest/elements/dhcp-all-interfaces/README.html">[link]&lt;/a>&lt;/td>&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;table class="docutils footnote" frame="void" id="enableserialconsole" rules="none">
&lt;colgroup>&lt;col class="label" />&lt;col />&lt;/colgroup>
&lt;tbody valign="top">
&lt;tr>&lt;td class="label">&lt;a class="fn-backref" href="#id7">[7]&lt;/a>&lt;/td>&lt;td>--, enable-serial-console element - Openstack Documentation, diskimage-builder 2.33.1.dev11. &lt;a class="reference external" href="https://docs.openstack.org/diskimage-builder/latest/elements/enable-serial-console/README.html">[link]&lt;/a>&lt;/td>&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;table class="docutils footnote" frame="void" id="opensshserver" rules="none">
&lt;colgroup>&lt;col class="label" />&lt;col />&lt;/colgroup>
&lt;tbody valign="top">
&lt;tr>&lt;td class="label">&lt;a class="fn-backref" href="#id8">[8]&lt;/a>&lt;/td>&lt;td>--, openssh-server element - Openstack Documentation, diskimage-builder 2.33.1.dev11. &lt;a class="reference external" href="https://docs.openstack.org/diskimage-builder/latest/elements/openssh-server/README.html">[link]&lt;/a>&lt;/td>&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;table class="docutils footnote" frame="void" id="centosminimal" rules="none">
&lt;colgroup>&lt;col class="label" />&lt;col />&lt;/colgroup>
&lt;tbody valign="top">
&lt;tr>&lt;td class="label">&lt;a class="fn-backref" href="#id9">[9]&lt;/a>&lt;/td>&lt;td>--, centos-minimal element - Openstack Documentation, diskimage-builder 2.33.1.dev11. &lt;a class="reference external" href="https://docs.openstack.org/diskimage-builder/latest/elements/centos-minimal/README.html">[link]&lt;/a>&lt;/td>&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;table class="docutils footnote" frame="void" id="epel" rules="none">
&lt;colgroup>&lt;col class="label" />&lt;col />&lt;/colgroup>
&lt;tbody valign="top">
&lt;tr>&lt;td class="label">&lt;a class="fn-backref" href="#id10">[10]&lt;/a>&lt;/td>&lt;td>--, epel element - Openstack Documentation, diskimage-builder 2.33.1.dev11. &lt;a class="reference external" href="https://docs.openstack.org/diskimage-builder/latest/elements/epel/README.html">[link]&lt;/a>&lt;/td>&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;/div>
&lt;/div></content><category>Cloud Computing</category><category>linux</category><category>centos</category><category>openstack</category><category>diskimage-builder</category><category>yaml</category><category>cloud-init</category><category>cloud images</category><category>filesystem</category></item><item><title>Virtualenvwrapper Installation and Usage</title><link>https://implement.pt/2019/03/virtualenvwrapper-installation-and-usage/</link><pubDate>Fri, 08 Mar 2019 14:50:00 +0100</pubDate><guid>https://implement.pt/2019/03/virtualenvwrapper-installation-and-usage/</guid><summary type="html">&lt;div class="document">
&lt;p>A simple guide to installing and using virtualenvwrapper to manage your Python Environments&lt;/p>
&lt;/div></summary><content type="html">&lt;div class="document">
&lt;div class="section" id="introduction">
&lt;h2>Introduction&lt;/h2>
&lt;p>In a &lt;a class="reference external" href="https://implement.pt/2018/09/using-python-virtual-environments-with-slurm/">previous post&lt;/a> we saw how we can create isolated
python environments so that we have different python packages, python versions, or both, for specific projects, without
messing with each other python environment.&lt;/p>
&lt;p>Here we will take a look at a very useful tool,
Doug Hellmann's &lt;a class="reference external" href="https://bitbucket.org/virtualenvwrapper/virtualenvwrapper/">virtualenvwrapper&lt;/a>, which allows us to
create, delete and overall manage our python virtual environments in a seamless way. Like its name suggests, it is
essentially a wrapper for Ian Bicking’s &lt;a class="reference external" href="https://pypi.python.org/pypi/virtualenv">virtualenv&lt;/a> tool but we will see
that it can make our python life much easier.&lt;/p>
&lt;/div>
&lt;div class="section" id="installation">
&lt;h2>Installation&lt;/h2>
&lt;p>A simple way of obtaining &lt;cite>virtualenvwrapper&lt;/cite> is to install it via &lt;cite>pip&lt;/cite>. Depending on your Linux distribution, the
&lt;cite>virtualenvwrapper.sh&lt;/cite> script you will need to source may be located in somewhat unexpected locations, even through
&lt;cite>pip&lt;/cite>, because of the default location of the python site-packages, i.e:&lt;/p>
&lt;ul class="simple">
&lt;li>&lt;strong>Ubuntu 18.04:&lt;/strong>&lt;/li>
&lt;/ul>
&lt;div class="highlight">&lt;pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">&lt;code class="language-fallback" data-lang="fallback">root@ubu-ws ~# dpkg -S virtualenvwrapper | grep virtualenvwrapper.sh
virtualenvwrapper: /usr/share/virtualenvwrapper/virtualenvwrapper.sh
root@ubu-ws ~# apt remove -y virtualenvwrapper
root@ubu-ws ~# pip install virtualenvwrapper
root@ubu-ws ~# whereis virtualenvwrapper.sh
virtualenvwrapper: /usr/local/bin/virtualenvwrapper.sh
root@ubu-ws ~#&lt;/code>&lt;/pre>&lt;/div>
&lt;ul class="simple">
&lt;li>&lt;strong>Debian 9&lt;/strong>&lt;/li>
&lt;/ul>
&lt;div class="highlight">&lt;pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">&lt;code class="language-fallback" data-lang="fallback">root@deb-ws ~# dpkg -S virtualenvwrapper | grep virtualenvwrapper.sh
virtualenvwrapper: /usr/share/virtualenvwrapper/virtualenvwrapper.sh
root@deb-ws ~# apt remove -y virtualenvwrapper
root@deb-ws ~# pip install virtualenvwrapper
root@deb-ws ~# whereis virtualenvwrapper.sh
virtualenvwrapper: /usr/local/bin/virtualenvwrapper.sh
root@deb-ws ~#&lt;/code>&lt;/pre>&lt;/div>
&lt;ul class="simple">
&lt;li>&lt;strong>CentOS 7&lt;/strong>&lt;/li>
&lt;/ul>
&lt;div class="highlight">&lt;pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">&lt;code class="language-fallback" data-lang="fallback">[root@cen-ws ~]# repoquery -l python-virtualenvwrapper | grep virtualenvwrapper.sh
/etc/profile.d/virtualenvwrapper.sh
/usr/bin/virtualenvwrapper.sh
[root@cen-ws ~]# pip install virtualenvwrapper
[root@cen-ws ~]# whereis virtualenvwrapper.sh
virtualenvwrapper: /usr/bin/virtualenvwrapper.sh
[root@cen-ws ~]#&lt;/code>&lt;/pre>&lt;/div>
&lt;p>Keep in mind that it should be installed into the same global site-packages location where &lt;cite>virtualenv&lt;/cite> is installed
&lt;a class="footnote-reference" href="#id6" id="id1">[1]&lt;/a>, and so you should install &lt;cite>virtualenvwrapper&lt;/cite> the same way you installed &lt;cite>virtualenv&lt;/cite> package, or just install
&lt;cite>virtualenvwrapper&lt;/cite> if you have not installed &lt;cite>virtualenv&lt;/cite> previously.&lt;/p>
&lt;div class="highlight">&lt;pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">&lt;code class="language-fallback" data-lang="fallback">~# pip install virtualenvwrapper
~# whereis virtualenvwrapper.sh
virtualenvwrapper: /usr/local/bin/virtualenvwrapper.sh
~#&lt;/code>&lt;/pre>&lt;/div>
&lt;div class="section" id="enable-at-shell-startup">
&lt;h3>Enable at Shell Startup&lt;/h3>
&lt;p>Note that on CentOS systems we get an extra script, &lt;em>/etc/profile.d/virtualenvwrapper.sh&lt;/em>. For those of you not familiar
with the &lt;em>/etc/profile&lt;/em> file and the &lt;em>/etc/profile.d&lt;/em> directory (take a look at the end of &lt;em>/etc/profile&lt;/em>), these
locations correspond to system-wide startup files used by the Bourne shell (aka Bash) and Bourne compatible shells
(ksh, ash, ...), and are sourced by your shell when you:&lt;/p>
&lt;ul class="simple">
&lt;li>login via TTY&lt;/li>
&lt;li>login via SSH&lt;/li>
&lt;li>launch a new shell&lt;/li>
&lt;/ul>
&lt;p>So, we should configure loading of the &lt;cite>virtualenvwrapper&lt;/cite> in either&lt;/p>
&lt;ul class="simple">
&lt;li>&lt;em>/etc/profile&lt;/em>&lt;/li>
&lt;li>&lt;em>/etc/profile.d/virtualenvwrapper.sh&lt;/em>&lt;/li>
&lt;li>&lt;em>~/.bashrc&lt;/em> (user specific)&lt;/li>
&lt;li>&lt;em>~/.profile&lt;/em> (user specific)&lt;/li>
&lt;/ul>
&lt;p>and its content should be &lt;a class="footnote-reference" href="#id6" id="id2">[1]&lt;/a>:&lt;/p>
&lt;div class="highlight">&lt;div style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">
&lt;table style="border-spacing:0;padding:0;margin:0;border:0;width:auto;overflow:auto;display:block;">&lt;tr>&lt;td style="vertical-align:top;padding:0;margin:0;border:0;">
&lt;pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">&lt;code>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">1
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">2
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">3
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td style="vertical-align:top;padding:0;margin:0;border:0;;width:100%">
&lt;pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">&lt;code class="language-bash" data-lang="bash">export WORKON_HOME&lt;span style="color:#f92672">=&lt;/span>$HOME/.virtualenvs
export PROJECT_HOME&lt;span style="color:#f92672">=&lt;/span>$HOME/Devel
source /usr/local/bin/virtualenvwrapper.sh&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>
&lt;p>where &lt;em>WORKON_HOME&lt;/em> is where your virtual environments will be stored, and &lt;em>PROJECT_HOME&lt;/em> is the location of your
development project directories (see &lt;a class="footnote-reference" href="#id8" id="id3">[3]&lt;/a>). The last line just loads all of the &lt;cite>virtualenvwrapper&lt;/cite> methods.&lt;/p>
&lt;p>After following the installation instructions above, you have to load the &lt;cite>virtualenvwrapper&lt;/cite> methods, by launching a
new shell, or if you already have a shell open you can source your startup file manually, i.e:&lt;/p>
&lt;div class="highlight">&lt;pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">&lt;code class="language-fallback" data-lang="fallback"> ~$ . .profile&lt;/code>&lt;/pre>&lt;/div>
&lt;/div>
&lt;/div>
&lt;div class="section" id="usage">
&lt;h2>Usage&lt;/h2>
&lt;p>After loading the &lt;cite>virtualenvwrapper.sh&lt;/cite>, you will have the following commands available:&lt;/p>
&lt;table border="1" class="colwidths-given docutils">
&lt;caption>Command Reference &lt;a class="footnote-reference" href="#id7" id="id4">[2]&lt;/a>&lt;/caption>
&lt;colgroup>
&lt;col width="10%" />
&lt;col width="90%" />
&lt;/colgroup>
&lt;thead valign="bottom">
&lt;tr>&lt;th class="head">Command&lt;/th>
&lt;th class="head">Description&lt;/th>
&lt;/tr>
&lt;/thead>
&lt;tbody valign="top">
&lt;tr>&lt;td>mkvirtualenv&lt;/td>
&lt;td>Create a new named (specified) environment, in the WORKON_HOME directory&lt;/td>
&lt;/tr>
&lt;tr>&lt;td>mktmpenv&lt;/td>
&lt;td>Create a new named (automatically generated) environment in the WORKON_HOME directory&lt;/td>
&lt;/tr>
&lt;tr>&lt;td>lsvirtualenv&lt;/td>
&lt;td>List all of the environments&lt;/td>
&lt;/tr>
&lt;tr>&lt;td>showvirtualenv&lt;/td>
&lt;td>Show the details for a single virtualenv&lt;/td>
&lt;/tr>
&lt;tr>&lt;td>rmvirtualenv&lt;/td>
&lt;td>Remove an environment, in the WORKON_HOME&lt;/td>
&lt;/tr>
&lt;tr>&lt;td>cpvirtualenv&lt;/td>
&lt;td>Duplicate an existing virtualenv environment. The source can be an environment managed by virtualenvwrapper or an
external environment created elsewhere.&lt;/td>
&lt;/tr>
&lt;tr>&lt;td>allvirtualenv&lt;/td>
&lt;td>Run a command in all virtualenvs under WORKON_HOME&lt;/td>
&lt;/tr>
&lt;tr>&lt;td>workon&lt;/td>
&lt;td>List or change working virtual environments&lt;/td>
&lt;/tr>
&lt;tr>&lt;td>deactivate&lt;/td>
&lt;td>Switch from a virtual environment to the system-installed version of Python&lt;/td>
&lt;/tr>
&lt;tr>&lt;td>cdvirtualenv&lt;/td>
&lt;td>Change the current working directory to $VIRTUAL_ENV&lt;/td>
&lt;/tr>
&lt;tr>&lt;td>cdsitepackages&lt;/td>
&lt;td>Change the current working directory to the site-packages for $VIRTUAL_ENV&lt;/td>
&lt;/tr>
&lt;tr>&lt;td>lssitepackages&lt;/td>
&lt;td>Shows the content of the site-packages directory of the currently-active virtualenv&lt;/td>
&lt;/tr>
&lt;tr>&lt;td>add2virtualenv&lt;/td>
&lt;td>Adds the specified directories to the Python path for the currently-active virtualenv&lt;/td>
&lt;/tr>
&lt;tr>&lt;td>toggleglobalsitepackages&lt;/td>
&lt;td>Controls whether the active virtualenv will access the packages in the global Python site-packages directory&lt;/td>
&lt;/tr>
&lt;tr>&lt;td>mkproject&lt;/td>
&lt;td>Create a new virtualenv in the WORKON_HOME and project directory in PROJECT_HOME&lt;/td>
&lt;/tr>
&lt;tr>&lt;td>setvirtualenvproject&lt;/td>
&lt;td>Bind an existing virtualenv to an existing project&lt;/td>
&lt;/tr>
&lt;tr>&lt;td>cdproject&lt;/td>
&lt;td>Change the current working directory to the one specified as the project directory for the active virtualenv&lt;/td>
&lt;/tr>
&lt;tr>&lt;td>wipeenv&lt;/td>
&lt;td>Remove all of the installed third-party packages in the current virtualenv&lt;/td>
&lt;/tr>
&lt;tr>&lt;td>virtualenvwrapper&lt;/td>
&lt;td>Print a list of commands and their descriptions as basic help output&lt;/td>
&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;p>In this post we will not go through every command, but we will go through a process similiar to the
&lt;a class="reference external" href="https://implement.pt/2018/09/using-python-virtual-environments-with-slurm/">previous post&lt;/a> to get you started. For more info about
each command please see the
&lt;a class="reference external" href="https://virtualenvwrapper.readthedocs.io/en/latest/command_ref.html">official documentation&lt;/a>&lt;/p>
&lt;div class="section" id="managing-your-virtual-environments">
&lt;h3>Managing your Virtual Environments&lt;/h3>
&lt;p>First let us see the command you will probably use the most &lt;cite>workon&lt;/cite> that, when you do not pass any arguments, it shows
a list of all the available virtual environments (located at WORKON_HOME)&lt;/p>
&lt;div class="highlight">&lt;pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">&lt;code class="language-fallback" data-lang="fallback">~$ workon
~$&lt;/code>&lt;/pre>&lt;/div>
&lt;p>Well, we have not created any yet, so let us create a new one.&lt;/p>
&lt;div class="highlight">&lt;pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">&lt;code class="language-fallback" data-lang="fallback">~$ mkvirtualenv AdS
Running virtualenv with interpreter /usr/bin/python2
New python executable in /home/t0rrant/.virtualenvs/AdS/bin/python2
Also creating executable in /home/t0rrant/.virtualenvs/AdS/bin/python
Installing setuptools, pkg_resources, pip, wheel...done.
(AdS) ~$ workon
AdS
(AdS) ~$&lt;/code>&lt;/pre>&lt;/div>
&lt;p>Looks good so let us install the &lt;cite>sympy&lt;/cite> package for this environment&lt;/p>
&lt;div class="highlight">&lt;pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">&lt;code class="language-fallback" data-lang="fallback">(AdS) ~$ pip install sympy
Collecting sympy
Downloading https://files.pythonhosted.org/packages/dd/f6/ed485ff22efdd7b371d0dbbf6d77ad61c3b3b7e0815a83c89cbb38ce35de/sympy-1.3.tar.gz (5.9MB)
100% |████████████████████████████████| 5.9MB 3.5MB/s
Collecting mpmath&amp;gt;=0.19 (from sympy)
Downloading https://files.pythonhosted.org/packages/ca/63/3384ebb3b51af9610086b23ea976e6d27d6d97bf140a76a365bd77a3eb32/mpmath-1.1.0.tar.gz (512kB)
100% |████████████████████████████████| 522kB 4.9MB/s
Building wheels for collected packages: sympy, mpmath
Building wheel for sympy (setup.py) ... done
Stored in directory: /home/t0rrant/.cache/pip/wheels/6c/59/86/478e3c0f298368c119095cc5985dedac57c0e35a85c737f823
Building wheel for mpmath (setup.py) ... done
Stored in directory: /home/t0rrant/.cache/pip/wheels/63/9d/8e/37c3f6506ed3f152733a699e92d8e0c9f5e5f01dea262f80ad
Successfully built sympy mpmath
Installing collected packages: mpmath, sympy
Successfully installed mpmath-1.1.0 sympy-1.3
(AdS) ~$&lt;/code>&lt;/pre>&lt;/div>
&lt;p>We can confirm that the sympy package is only available to this environment:&lt;/p>
&lt;div class="highlight">&lt;pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">&lt;code class="language-fallback" data-lang="fallback">(AdS) ~$ python -m sympy.abc
(AdS) ~$ deactivate
~$ python -m sympy.abc
/usr/bin/python: No module named sympy
~$ workon AdS
(AdS) ~$&lt;/code>&lt;/pre>&lt;/div>
&lt;p>Assuming that we have a project called &lt;em>AdS&lt;/em>, located in our &lt;cite>PROJECT_HOME&lt;/cite> directory, we can associate the newly
created environment to that project, via the &lt;cite>setvirtualenvproject&lt;/cite> command &lt;a class="footnote-reference" href="#id8" id="id5">[3]&lt;/a>:&lt;/p>
&lt;div class="highlight">&lt;pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">&lt;code class="language-fallback" data-lang="fallback">(AdS) ~$ cdvirtualenv
(AdS) ~/.virtualenvs/AdS$ setvirtualenvproject $PWD ~/Devel/AdS
Setting project for AdS to /home/t0rrant/Devel/AdS
(AdS) ~/.virtualenvs/AdS$ cdproject
(AdS) ~/Devel/AdS$ ls
AdS_sympy_test.py&lt;/code>&lt;/pre>&lt;/div>
&lt;p>As you can see we can now call &lt;cite>cdproject&lt;/cite> which will take us to the project associated to the currently active virtual
environment. Neat =)&lt;/p>
&lt;p>&lt;strong>Note:&lt;/strong> At this time (virtualenvwrapper v4.8.4) you can only have &lt;strong>one&lt;/strong> project associated to &lt;strong>one&lt;/strong> virtual
environment (project &lt;strong>foo&lt;/strong> should work with environment &lt;strong>A&lt;/strong>), or &lt;strong>one&lt;/strong> project associated with &lt;strong>any number&lt;/strong> of
projects (project &lt;strong>bar&lt;/strong> should work with environments &lt;strong>A&lt;/strong>, &lt;strong>B&lt;/strong>, ..., &lt;strong>Z&lt;/strong>), you &lt;strong>cannot&lt;/strong> have one virtual
environment associated with multiple projects. You can of course activate your desired virtual environment and use it
within any directory, you will just not be able to use &lt;code>cdproject&lt;/code> to change to the root directory of your
project.&lt;/p>
&lt;p>Just to check if the sympy is working in our environment:&lt;/p>
&lt;div class="highlight">&lt;div style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">
&lt;table style="border-spacing:0;padding:0;margin:0;border:0;width:auto;overflow:auto;display:block;">&lt;tr>&lt;td style="vertical-align:top;padding:0;margin:0;border:0;">
&lt;pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">&lt;code>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">1
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">2
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">3
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">4
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">5
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td style="vertical-align:top;padding:0;margin:0;border:0;;width:100%">
&lt;pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">&lt;code class="language-python" data-lang="python">&lt;span style="color:#75715e"># AdS_sympy_test.py&lt;/span>
&lt;span style="color:#75715e">#!/usr/bin/env python&lt;/span>
&lt;span style="color:#f92672">from&lt;/span> sympy &lt;span style="color:#f92672">import&lt;/span> test
test(&lt;span style="color:#e6db74">&amp;#34;_basic&amp;#34;&lt;/span>)&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>
&lt;div class="highlight">&lt;pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">&lt;code class="language-fallback" data-lang="fallback">(AdS) ~/Devel/AdS$ python AdS_sympy_test.py
============================= test process starts ==============================
executable: /home/t0rrant/.virtualenvs/AdS/bin/python (2.7.15-candidate-1) [CPython]
architecture: 64-bit
cache: yes
ground types: python
numpy: None
random seed: 21534663
hash randomization: on (PYTHONHASHSEED=417663367)
sympy/core/tests/test_basic.py[16] ................ [OK]
================== tests finished: 16 passed, in 0.06 seconds ==================
(AdS) ~/Devel/AdS$&lt;/code>&lt;/pre>&lt;/div>
&lt;p>Now we already did everything with this virtual environment so we exit it:&lt;/p>
&lt;div class="highlight">&lt;pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">&lt;code class="language-fallback" data-lang="fallback">(AdS) ~/Devel/AdS$ deactivate
~/Devel/AdS$&lt;/code>&lt;/pre>&lt;/div>
&lt;p>and we can delete the virtual environment as our project development is now finished:&lt;/p>
&lt;div class="highlight">&lt;pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">&lt;code class="language-fallback" data-lang="fallback">~/Devel/AdS$ rmvirtualenv AdS
Removing AdS...
~/Devel/AdS$ workon
~/Devel/AdS$&lt;/code>&lt;/pre>&lt;/div>
&lt;p>And we are done!&lt;/p>
&lt;/div>
&lt;/div>
&lt;div class="section" id="conclusion">
&lt;h2>Conclusion&lt;/h2>
&lt;p>In this post we saw how to install and use
&lt;a class="reference external" href="https://bitbucket.org/virtualenvwrapper/virtualenvwrapper/">virtualenvwrapper&lt;/a> to manage our Python virtual
environments.
We followed a development flow similar to a
&lt;a class="reference external" href="https://implement.pt/2018/09/using-python-virtual-environments-with-slurm/">previous post&lt;/a> about Python virtual environment, ending
with a similar environment.
The &lt;cite>virtualenvwrapper&lt;/cite> tool is very versatile, and it is possible to extend it via plugins, which was not covered here.
These extensions can allow you to, i.e: create new projects based on predefined templates, or interact with external
tools, for more information take a look at the
&lt;a class="reference external" href="https://virtualenvwrapper.readthedocs.io/en/latest/extensions.html">extensions page&lt;/a>.&lt;/p>
&lt;p>Like always, feel free to post any comments or questions in the comment box below.&lt;/p>
&lt;/div>
&lt;div class="section" id="references">
&lt;h2>References&lt;/h2>
&lt;table class="docutils footnote" frame="void" id="id6" rules="none">
&lt;colgroup>&lt;col class="label" />&lt;col />&lt;/colgroup>
&lt;tbody valign="top">
&lt;tr>&lt;td class="label">[1]&lt;/td>&lt;td>&lt;em>(&lt;a class="fn-backref" href="#id1">1&lt;/a>, &lt;a class="fn-backref" href="#id2">2&lt;/a>)&lt;/em> --, Installation - virtualenvwrapper 4.8.4.dev1 Documentation. &lt;a class="reference external" href="https://virtualenvwrapper.readthedocs.io/en/latest/install.html">[link]&lt;/a>&lt;/td>&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;table class="docutils footnote" frame="void" id="id7" rules="none">
&lt;colgroup>&lt;col class="label" />&lt;col />&lt;/colgroup>
&lt;tbody valign="top">
&lt;tr>&lt;td class="label">&lt;a class="fn-backref" href="#id4">[2]&lt;/a>&lt;/td>&lt;td>--, Command Reference - virtualenvwrapper 4.8.4.dev1 Documentation. &lt;a class="reference external" href="https://virtualenvwrapper.readthedocs.io/en/latest/command_ref.html">[link]&lt;/a>&lt;/td>&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;table class="docutils footnote" frame="void" id="id8" rules="none">
&lt;colgroup>&lt;col class="label" />&lt;col />&lt;/colgroup>
&lt;tbody valign="top">
&lt;tr>&lt;td class="label">[3]&lt;/td>&lt;td>&lt;em>(&lt;a class="fn-backref" href="#id3">1&lt;/a>, &lt;a class="fn-backref" href="#id5">2&lt;/a>)&lt;/em> --, Project Management - virtualenvwrapper 4.8.4.dev1 Documentation. &lt;a class="reference external" href="https://virtualenvwrapper.readthedocs.io/en/latest/projects.html">[link]&lt;/a>&lt;/td>&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;/div>
&lt;/div></content><category>Development and Programming</category><category>linux</category><category>slurm</category><category>python</category><category>scheduler</category><category>virtualenv</category><category>sympy</category></item><item><title>Creating Custom Resources for Your Cookbooks</title><link>https://implement.pt/2019/03/chef-custom-resources-cookbook/</link><pubDate>Thu, 07 Mar 2019 14:50:00 +0100</pubDate><guid>https://implement.pt/2019/03/chef-custom-resources-cookbook/</guid><summary type="html">&lt;div class="document">
&lt;p>An introductory guide to writing custom resources for your Chef Cookbooks&lt;/p>
&lt;/div></summary><content type="html">&lt;div class="document">
&lt;div class="section" id="introduction">
&lt;h2>Introduction&lt;/h2>
&lt;p>Like we saw in the &lt;a class="reference external" href="https://implement.pt/2019/02/hitchhikers-guide-to-chef/">previous post&lt;/a>, Chef Cookbooks can also provide custom
resources that we can use in a dependant cookbooks' recipes. These custom resources can also use other resources from
arbitrary cookbooks, or resources builtin to Chef.&lt;/p>
&lt;p>A custom resource file is a Ruby file, ending in &lt;cite>*.rb&lt;/cite>, and its syntax is, for example: &lt;a class="footnote-reference" href="#id2" id="id1">[1]&lt;/a>&lt;/p>
&lt;div class="highlight">&lt;div style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">
&lt;table style="border-spacing:0;padding:0;margin:0;border:0;width:auto;overflow:auto;display:block;">&lt;tr>&lt;td style="vertical-align:top;padding:0;margin:0;border:0;">
&lt;pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">&lt;code>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 1
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 2
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 3
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 4
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 5
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 6
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 7
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 8
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 9
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">10
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">11
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">12
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">13
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td style="vertical-align:top;padding:0;margin:0;border:0;;width:100%">
&lt;pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">&lt;code class="language-ruby" data-lang="ruby">property &lt;span style="color:#e6db74">:property_name&lt;/span>, &lt;span style="color:#66d9ef">RubyType&lt;/span>, &lt;span style="color:#e6db74">default&lt;/span>: &lt;span style="color:#e6db74">&amp;#39;value&amp;#39;&lt;/span>
load_current_value &lt;span style="color:#66d9ef">do&lt;/span>
&lt;span style="color:#75715e"># some Ruby for loading the current state of the resource&lt;/span>
&lt;span style="color:#66d9ef">end&lt;/span>
action &lt;span style="color:#e6db74">:action_name&lt;/span> &lt;span style="color:#66d9ef">do&lt;/span>
&lt;span style="color:#75715e"># a mix of built-in Chef resources and Ruby&lt;/span>
&lt;span style="color:#66d9ef">end&lt;/span>
action &lt;span style="color:#e6db74">:another_action_name&lt;/span> &lt;span style="color:#66d9ef">do&lt;/span>
&lt;span style="color:#75715e"># a mix of built-in Chef resources and Ruby&lt;/span>
&lt;span style="color:#66d9ef">end&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>
&lt;p>We will generally only have to worry about &lt;em>properties&lt;/em> and &lt;em>actions&lt;/em>. The &lt;em>load_current_value&lt;/em> block can be used in
more complex resources, to gather the current state of our resource, if it already exists, thus overriding any
properties that we may want.&lt;/p>
&lt;/div>
&lt;div class="section" id="development">
&lt;h2>Development&lt;/h2>
&lt;p>Before starting to develop our custom resource we should create a regular cookbook, with the difference that our
&lt;em>default&lt;/em> recipe may not exist, and the &lt;em>recipes&lt;/em> directory may not exist at all. This means that our resources cookbook
has to have a proper &lt;em>metadata.rb&lt;/em> file with proper versioning.&lt;/p>
&lt;p>We should create a directory named &lt;em>resources&lt;/em>, where our resource files reside. And &lt;strong>very&lt;/strong> importantly remember that
the resources made available by our cookbook are named after the cookbook's name and the resource's name. So, if our
cookbook is named &lt;cite>myapps&lt;/cite> and we have a resource file named &lt;cite>superapp&lt;/cite>, to use the &lt;cite>superapp&lt;/cite> resource we would call
the &lt;strong>myapps_superapp&lt;/strong> resource in the external cookbook.&lt;/p>
&lt;p>Let us take as an example the &lt;cite>nano&lt;/cite> text editor, an external Linux application that can be compiled manually and
installed to arbitrary directories.&lt;/p>
&lt;p>For this example we will call our resource file &lt;cite>from_tar.rb&lt;/cite> and our cookbook &lt;cite>pkg&lt;/cite>, so our directory structure should
look something like:&lt;/p>
&lt;div class="highlight">&lt;pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">&lt;code class="language-fallback" data-lang="fallback">pkg
├── Berksfile
├── metadata.rb
└── resources
   └── from_tar.rb&lt;/code>&lt;/pre>&lt;/div>
&lt;p>Our Berksfile is as simple as it gets:&lt;/p>
&lt;!-- pkg/Berksfile -->
&lt;div class="highlight">&lt;div style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">
&lt;table style="border-spacing:0;padding:0;margin:0;border:0;width:auto;overflow:auto;display:block;">&lt;tr>&lt;td style="vertical-align:top;padding:0;margin:0;border:0;">
&lt;pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">&lt;code>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">1
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">2
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">3
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td style="vertical-align:top;padding:0;margin:0;border:0;;width:100%">
&lt;pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">&lt;code class="language-ruby" data-lang="ruby">source &lt;span style="color:#e6db74">&amp;#39;https://supermarket.chef.io&amp;#39;&lt;/span>
metadata&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>
&lt;p>And so is our metadata.rb file:&lt;/p>
&lt;!-- pkg/metadata.rb -->
&lt;div class="highlight">&lt;div style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">
&lt;table style="border-spacing:0;padding:0;margin:0;border:0;width:auto;overflow:auto;display:block;">&lt;tr>&lt;td style="vertical-align:top;padding:0;margin:0;border:0;">
&lt;pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">&lt;code>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">1
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">2
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">3
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">4
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">5
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td style="vertical-align:top;padding:0;margin:0;border:0;;width:100%">
&lt;pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">&lt;code class="language-ruby" data-lang="ruby">name &lt;span style="color:#e6db74">&amp;#39;pkg&amp;#39;&lt;/span>
maintainer &lt;span style="color:#e6db74">&amp;#39;Manuel Torrinha&amp;#39;&lt;/span>
maintainer_email &lt;span style="color:#e6db74">&amp;#39;torrinha at_ implement _dot pt&amp;#39;&lt;/span>
description &lt;span style="color:#e6db74">&amp;#39;Installs tar and one resource to manage remote tar packages&amp;#39;&lt;/span>
version &lt;span style="color:#e6db74">&amp;#39;0.1.0&amp;#39;&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>
&lt;p>For our resource we plan it first:&lt;/p>
&lt;ol class="arabic simple">
&lt;li>install tar&lt;/li>
&lt;li>create extract directory&lt;/li>
&lt;li>fetch package&lt;/li>
&lt;li>extract package&lt;/li>
&lt;li>compile and install&lt;/li>
&lt;/ol>
&lt;p>Then we implement it:&lt;/p>
&lt;!-- pkg/resources/from_tar.rb -->
&lt;div class="highlight">&lt;div style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">
&lt;table style="border-spacing:0;padding:0;margin:0;border:0;width:auto;overflow:auto;display:block;">&lt;tr>&lt;td style="vertical-align:top;padding:0;margin:0;border:0;">
&lt;pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">&lt;code>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 1
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 2
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 3
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 4
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 5
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 6
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 7
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 8
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 9
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">10
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">11
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">12
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">13
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">14
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">15
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">16
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">17
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">18
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">19
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">20
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">21
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">22
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">23
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">24
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">25
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">26
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">27
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">28
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">29
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">30
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">31
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">32
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">33
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">34
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">35
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">36
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">37
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">38
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">39
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">40
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">41
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">42
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">43
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">44
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">45
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">46
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">47
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">48
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">49
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td style="vertical-align:top;padding:0;margin:0;border:0;;width:100%">
&lt;pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">&lt;code class="language-ruby" data-lang="ruby">property &lt;span style="color:#e6db74">:pkg_source&lt;/span>, String, &lt;span style="color:#e6db74">default&lt;/span>: &lt;span style="color:#e6db74">&amp;#39;https://www.nano-editor.org/dist/v3/nano-3.2.tar.gz&amp;#39;&lt;/span>
property &lt;span style="color:#e6db74">:prefix&lt;/span>, String, &lt;span style="color:#e6db74">default&lt;/span>: &lt;span style="color:#e6db74">&amp;#39;/usr/local&amp;#39;&lt;/span>
property &lt;span style="color:#e6db74">:configure_flags&lt;/span>, Array, &lt;span style="color:#e6db74">default&lt;/span>: &lt;span style="color:#f92672">[]&lt;/span>
property &lt;span style="color:#e6db74">:creates&lt;/span>, String
property &lt;span style="color:#e6db74">:tmp_dir&lt;/span>, String, &lt;span style="color:#e6db74">default&lt;/span>: &lt;span style="color:#e6db74">&amp;#39;/tmp&amp;#39;&lt;/span>
action &lt;span style="color:#e6db74">:install&lt;/span> &lt;span style="color:#66d9ef">do&lt;/span>
basename &lt;span style="color:#f92672">=&lt;/span> &lt;span style="color:#f92672">::&lt;/span>&lt;span style="color:#66d9ef">File&lt;/span>&lt;span style="color:#f92672">.&lt;/span>basename(new_resource&lt;span style="color:#f92672">.&lt;/span>pkg_source)
dirname &lt;span style="color:#f92672">=&lt;/span> basename&lt;span style="color:#f92672">.&lt;/span>chomp(&lt;span style="color:#e6db74">&amp;#39;.tar.gz&amp;#39;&lt;/span>)
src_dir &lt;span style="color:#f92672">=&lt;/span> &lt;span style="color:#e6db74">&amp;#34;&lt;/span>&lt;span style="color:#e6db74">#{&lt;/span>new_resource&lt;span style="color:#f92672">.&lt;/span>tmp_dir&lt;span style="color:#e6db74">}&lt;/span>&lt;span style="color:#e6db74">/&lt;/span>&lt;span style="color:#e6db74">#{&lt;/span>dirname&lt;span style="color:#e6db74">}&lt;/span>&lt;span style="color:#e6db74">&amp;#34;&lt;/span>
&lt;span style="color:#75715e"># Install package dependencies&lt;/span>
package &lt;span style="color:#e6db74">&amp;#39;tar&amp;#39;&lt;/span>
package &lt;span style="color:#e6db74">&amp;#39;gcc&amp;#39;&lt;/span>
package node&lt;span style="color:#f92672">[&lt;/span>&lt;span style="color:#e6db74">&amp;#39;platform_family&amp;#39;&lt;/span>&lt;span style="color:#f92672">]&lt;/span> &lt;span style="color:#f92672">==&lt;/span> &lt;span style="color:#e6db74">&amp;#39;debian&amp;#39;&lt;/span> ? &lt;span style="color:#e6db74">&amp;#39;libncursesw5-dev&amp;#39;&lt;/span>: &lt;span style="color:#e6db74">&amp;#39;ncurses-devel&amp;#39;&lt;/span>
&lt;span style="color:#75715e"># Ensure extract dir exists&lt;/span>
directory src_dir &lt;span style="color:#66d9ef">do&lt;/span>
owner &lt;span style="color:#e6db74">&amp;#39;root&amp;#39;&lt;/span>
group &lt;span style="color:#e6db74">&amp;#39;root&amp;#39;&lt;/span>
mode &lt;span style="color:#e6db74">&amp;#39;0755&amp;#39;&lt;/span>
recursive &lt;span style="color:#66d9ef">true&lt;/span>
action &lt;span style="color:#e6db74">:create&lt;/span>
&lt;span style="color:#66d9ef">end&lt;/span>
&lt;span style="color:#75715e"># Fetch nano to tmp_dir&lt;/span>
remote_file basename &lt;span style="color:#66d9ef">do&lt;/span>
source new_resource&lt;span style="color:#f92672">.&lt;/span>pkg_source
path &lt;span style="color:#e6db74">&amp;#34;&lt;/span>&lt;span style="color:#e6db74">#{&lt;/span>src_dir&lt;span style="color:#e6db74">}&lt;/span>&lt;span style="color:#e6db74">/&lt;/span>&lt;span style="color:#e6db74">#{&lt;/span>basename&lt;span style="color:#e6db74">}&lt;/span>&lt;span style="color:#e6db74">&amp;#34;&lt;/span>
mode &lt;span style="color:#e6db74">&amp;#39;0755&amp;#39;&lt;/span>
action &lt;span style="color:#e6db74">:create&lt;/span>
&lt;span style="color:#66d9ef">end&lt;/span>
&lt;span style="color:#75715e"># Unpack nano into src_dir&lt;/span>
execute &lt;span style="color:#e6db74">&amp;#39;unpack nano&amp;#39;&lt;/span> &lt;span style="color:#66d9ef">do&lt;/span>
cwd src_dir
command &lt;span style="color:#e6db74">&amp;#34;tar xfz &lt;/span>&lt;span style="color:#e6db74">#{&lt;/span>basename&lt;span style="color:#e6db74">}&lt;/span>&lt;span style="color:#e6db74">&amp;#34;&lt;/span>
creates &lt;span style="color:#e6db74">&amp;#34;&lt;/span>&lt;span style="color:#e6db74">#{&lt;/span>src_dir&lt;span style="color:#e6db74">}&lt;/span>&lt;span style="color:#e6db74">/&lt;/span>&lt;span style="color:#e6db74">#{&lt;/span>dirname&lt;span style="color:#e6db74">}&lt;/span>&lt;span style="color:#e6db74">&amp;#34;&lt;/span>
&lt;span style="color:#66d9ef">end&lt;/span>
&lt;span style="color:#75715e"># Configure and compile nano&lt;/span>
execute &lt;span style="color:#e6db74">&amp;#39;compile and install&amp;#39;&lt;/span> &lt;span style="color:#66d9ef">do&lt;/span>
cwd &lt;span style="color:#e6db74">&amp;#34;&lt;/span>&lt;span style="color:#e6db74">#{&lt;/span>src_dir&lt;span style="color:#e6db74">}&lt;/span>&lt;span style="color:#e6db74">/&lt;/span>&lt;span style="color:#e6db74">#{&lt;/span>dirname&lt;span style="color:#e6db74">}&lt;/span>&lt;span style="color:#e6db74">&amp;#34;&lt;/span>
flags &lt;span style="color:#f92672">=&lt;/span> &lt;span style="color:#f92672">[&lt;/span>new_resource&lt;span style="color:#f92672">.&lt;/span>prefix ? &lt;span style="color:#e6db74">&amp;#34;--prefix=&lt;/span>&lt;span style="color:#e6db74">#{&lt;/span>new_resource&lt;span style="color:#f92672">.&lt;/span>prefix&lt;span style="color:#e6db74">}&lt;/span>&lt;span style="color:#e6db74">&amp;#34;&lt;/span> : &lt;span style="color:#66d9ef">nil&lt;/span>, &lt;span style="color:#f92672">*&lt;/span>new_resource&lt;span style="color:#f92672">.&lt;/span>configure_flags&lt;span style="color:#f92672">].&lt;/span>compact&lt;span style="color:#f92672">.&lt;/span>join(&lt;span style="color:#e6db74">&amp;#39; &amp;#39;&lt;/span>)
command &lt;span style="color:#e6db74">&amp;#34;./configure --quiet &lt;/span>&lt;span style="color:#e6db74">#{&lt;/span>flags&lt;span style="color:#e6db74">}&lt;/span>&lt;span style="color:#e6db74"> &amp;amp;&amp;amp; make -s &amp;amp;&amp;amp; make -s install&amp;#34;&lt;/span>
creates new_resource&lt;span style="color:#f92672">.&lt;/span>creates
&lt;span style="color:#66d9ef">end&lt;/span>
&lt;span style="color:#66d9ef">end&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>
&lt;p>Note that when we want to use a property value, instead of using the variable name directly we use the namespace
&lt;cite>new_resource&lt;/cite> to access them.&lt;/p>
&lt;p>We can use this sequence not just for nano, but for any application that:&lt;/p>
&lt;ol class="arabic simple">
&lt;li>comes packaged in a '.tar.gz' format&lt;/li>
&lt;li>requires execution of &lt;cite>configure&lt;/cite>, &lt;strong>and only that&lt;/strong>, to generate a Makefile&lt;/li>
&lt;li>its Makefile supports the &lt;cite>install&lt;/cite> argument&lt;/li>
&lt;/ol>
&lt;p>This specific process is actually already implemented by the tar cookbook available at the
&lt;a class="reference external" href="https://supermarket.chef.io/cookbooks/tar">supermarket&lt;/a>.&lt;/p>
&lt;/div>
&lt;div class="section" id="testing">
&lt;h2>Testing&lt;/h2>
&lt;p>Of course that now that we have written our custom resource we want to be able to test it.&lt;/p>
&lt;p>Let us start by creating a &lt;cite>test&lt;/cite> cookbook, inside our &lt;cite>pkg&lt;/cite> resource cookbook, which will have a &lt;cite>nano_install&lt;/cite>
recipe that uses our resource.&lt;/p>
&lt;p>First create the &lt;cite>metadata.rb&lt;/cite> file:&lt;/p>
&lt;!-- .test/metadata.rb -->
&lt;div class="highlight">&lt;div style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">
&lt;table style="border-spacing:0;padding:0;margin:0;border:0;width:auto;overflow:auto;display:block;">&lt;tr>&lt;td style="vertical-align:top;padding:0;margin:0;border:0;">
&lt;pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">&lt;code>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">1
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">2
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">3
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">4
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">5
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">6
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">7
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td style="vertical-align:top;padding:0;margin:0;border:0;;width:100%">
&lt;pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">&lt;code class="language-ruby" data-lang="ruby">name &lt;span style="color:#e6db74">&amp;#39;test&amp;#39;&lt;/span>
maintainer &lt;span style="color:#e6db74">&amp;#39;Manuel Torrinha&amp;#39;&lt;/span>
maintainer_email &lt;span style="color:#e6db74">&amp;#39;torrinha at_ implement _dot pt&amp;#39;&lt;/span>
description &lt;span style="color:#e6db74">&amp;#39;Cookbook for testing the pkg cookbook&amp;#39;&lt;/span>
version &lt;span style="color:#e6db74">&amp;#39;0.1.0&amp;#39;&lt;/span>
depends &lt;span style="color:#e6db74">&amp;#39;pkg&amp;#39;&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>
&lt;p>Then, the &lt;cite>recipes/nano_install.rb&lt;/cite> file:&lt;/p>
&lt;!-- .test::nano_install -->
&lt;div class="highlight">&lt;div style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">
&lt;table style="border-spacing:0;padding:0;margin:0;border:0;width:auto;overflow:auto;display:block;">&lt;tr>&lt;td style="vertical-align:top;padding:0;margin:0;border:0;">
&lt;pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">&lt;code>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">1
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">2
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">3
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">4
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">5
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">6
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">7
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">8
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td style="vertical-align:top;padding:0;margin:0;border:0;;width:100%">
&lt;pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">&lt;code class="language-ruby" data-lang="ruby">pkg_from_tar &lt;span style="color:#e6db74">&amp;#39;nano&amp;#39;&lt;/span> &lt;span style="color:#66d9ef">do&lt;/span>
pkg_source &lt;span style="color:#e6db74">&amp;#39;https://www.nano-editor.org/dist/v3/nano-3.2.tar.gz&amp;#39;&lt;/span>
prefix &lt;span style="color:#e6db74">&amp;#39;/usr/local&amp;#39;&lt;/span>
configure_flags &lt;span style="color:#f92672">[]&lt;/span>
creates &lt;span style="color:#e6db74">&amp;#39;/usr/local/bin/nano&amp;#39;&lt;/span>
tmp_dir &lt;span style="color:#e6db74">&amp;#39;/tmp&amp;#39;&lt;/span>
action &lt;span style="color:#e6db74">:install&lt;/span>
&lt;span style="color:#66d9ef">end&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>
&lt;p>Next we update our original Berksfile to contemplate our test cookbook:&lt;/p>
&lt;!-- pkg/Berksfile -->
&lt;div class="highlight">&lt;div style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">
&lt;table style="border-spacing:0;padding:0;margin:0;border:0;width:auto;overflow:auto;display:block;">&lt;tr>&lt;td style="vertical-align:top;padding:0;margin:0;border:0;">
&lt;pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">&lt;code>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">1
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">2
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">3
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">4
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">5
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td style="vertical-align:top;padding:0;margin:0;border:0;;width:100%">
&lt;pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">&lt;code class="language-ruby" data-lang="ruby">source &lt;span style="color:#e6db74">&amp;#39;https://supermarket.chef.io&amp;#39;&lt;/span>
metadata
cookbook &lt;span style="color:#e6db74">&amp;#39;test&amp;#39;&lt;/span>, &lt;span style="color:#e6db74">path&lt;/span>: &lt;span style="color:#e6db74">&amp;#39;./test/cookbooks/test&amp;#39;&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>
&lt;p>And finally we create a &lt;cite>.kitchen.yml&lt;/cite> file for us to use with kitchen and test our resource in a local VM, in this
case using Vagrant.&lt;/p>
&lt;p>&lt;em>(Note that you will need vagrant and virtualbox installed on your workstation)&lt;/em>&lt;/p>
&lt;!-- pkg/.kitchen.yml -->
&lt;div class="highlight">&lt;div style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">
&lt;table style="border-spacing:0;padding:0;margin:0;border:0;width:auto;overflow:auto;display:block;">&lt;tr>&lt;td style="vertical-align:top;padding:0;margin:0;border:0;">
&lt;pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">&lt;code>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 1
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 2
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 3
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 4
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 5
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 6
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 7
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 8
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 9
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">10
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">11
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">12
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">13
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">14
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">15
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">16
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">17
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">18
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">19
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td style="vertical-align:top;padding:0;margin:0;border:0;;width:100%">
&lt;pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">&lt;code class="language-yaml" data-lang="yaml">&lt;span style="color:#f92672">driver&lt;/span>:
&lt;span style="color:#f92672">name&lt;/span>: &lt;span style="color:#ae81ff">vagrant&lt;/span>
&lt;span style="color:#f92672">provisioner&lt;/span>:
&lt;span style="color:#f92672">name&lt;/span>: &lt;span style="color:#ae81ff">chef_zero&lt;/span>
&lt;span style="color:#f92672">platforms&lt;/span>:
- &lt;span style="color:#f92672">name&lt;/span>: &lt;span style="color:#ae81ff">ubuntu-14.04&lt;/span>
- &lt;span style="color:#f92672">name&lt;/span>: &lt;span style="color:#ae81ff">ubuntu-16.04&lt;/span>
- &lt;span style="color:#f92672">name&lt;/span>: &lt;span style="color:#ae81ff">ubuntu-18.04&lt;/span>
- &lt;span style="color:#f92672">name&lt;/span>: &lt;span style="color:#ae81ff">debian-8&lt;/span>
- &lt;span style="color:#f92672">name&lt;/span>: &lt;span style="color:#ae81ff">debian-9&lt;/span>
- &lt;span style="color:#f92672">name&lt;/span>: &lt;span style="color:#ae81ff">centos-6&lt;/span>
- &lt;span style="color:#f92672">name&lt;/span>: &lt;span style="color:#ae81ff">centos-7&lt;/span>
&lt;span style="color:#f92672">suites&lt;/span>:
- &lt;span style="color:#f92672">name&lt;/span>: &lt;span style="color:#ae81ff">nano&lt;/span>
&lt;span style="color:#f92672">run_list&lt;/span>:
- &lt;span style="color:#ae81ff">recipe[test::nano_install]&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>
&lt;p>So now our directory structure will now look like this:&lt;/p>
&lt;div class="highlight">&lt;pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">&lt;code class="language-fallback" data-lang="fallback">pkg
├── Berksfile
├── .kitchen.yml
├── metadata.rb
├── resources
|   └── from_tar.rb
└── test
└── cookbooks
└── test
├── metadata.rb
└── recipes
└── nano_install.rb&lt;/code>&lt;/pre>&lt;/div>
&lt;p>And we can now test it with &lt;cite>kitchen&lt;/cite>:&lt;/p>
&lt;div class="highlight">&lt;pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">&lt;code class="language-shell" data-lang="shell">~/pkg$ kitchen list
Instance Driver Provisioner Verifier Transport Last Action Last Error
nano-ubuntu-1404 Vagrant ChefZero Busser Ssh &amp;lt;Not Created&amp;gt; &amp;lt;None&amp;gt;
nano-ubuntu-1604 Vagrant ChefZero Busser Ssh &amp;lt;Not Created&amp;gt; &amp;lt;None&amp;gt;
nano-ubuntu-1804 Vagrant ChefZero Busser Ssh &amp;lt;Not Created&amp;gt; &amp;lt;None&amp;gt;
nano-debian-8 Vagrant ChefZero Busser Ssh &amp;lt;Not Created&amp;gt; &amp;lt;None&amp;gt;
nano-debian-9 Vagrant ChefZero Busser Ssh &amp;lt;Not Created&amp;gt; &amp;lt;None&amp;gt;
nano-centos-6 Vagrant ChefZero Busser Ssh &amp;lt;Not Created&amp;gt; &amp;lt;None&amp;gt;
nano-centos-7 Vagrant ChefZero Busser Ssh &amp;lt;Not Created&amp;gt; &amp;lt;None&amp;gt;
~/pkg$ kitchen test debian-9
-----&amp;gt; Starting Kitchen &lt;span style="color:#f92672">(&lt;/span>v1.24.0&lt;span style="color:#f92672">)&lt;/span>
-----&amp;gt; Cleaning up any prior instances of &amp;lt;nano-debian-9&amp;gt;
-----&amp;gt; Destroying &amp;lt;nano-debian-9&amp;gt;...
Finished destroying &amp;lt;nano-debian-9&amp;gt; &lt;span style="color:#f92672">(&lt;/span>0m0.00s&lt;span style="color:#f92672">)&lt;/span>.
-----&amp;gt; Testing &amp;lt;nano-debian-9&amp;gt;
-----&amp;gt; Creating &amp;lt;nano-debian-9&amp;gt;...
&lt;span style="color:#f92672">(&lt;/span>...&lt;span style="color:#f92672">)&lt;/span>
Thank you &lt;span style="color:#66d9ef">for&lt;/span> installing Chef!
Transferring files to &amp;lt;nano-debian-9&amp;gt;
Starting Chef Client, version 14.11.21
Creating a new client identity &lt;span style="color:#66d9ef">for&lt;/span> nano-debian-9 using the validator key.
resolving cookbooks &lt;span style="color:#66d9ef">for&lt;/span> run list: &lt;span style="color:#f92672">[&lt;/span>&lt;span style="color:#e6db74">&amp;#34;test::nano_install&amp;#34;&lt;/span>&lt;span style="color:#f92672">]&lt;/span>
Synchronizing Cookbooks:
- test &lt;span style="color:#f92672">(&lt;/span>0.1.0&lt;span style="color:#f92672">)&lt;/span>
- pkg &lt;span style="color:#f92672">(&lt;/span>0.1.0&lt;span style="color:#f92672">)&lt;/span>
Installing Cookbook Gems:
Compiling Cookbooks...
Converging &lt;span style="color:#ae81ff">1&lt;/span> resources
Recipe: test::nano_install
* pkg_from_tar&lt;span style="color:#f92672">[&lt;/span>nano&lt;span style="color:#f92672">]&lt;/span> action install
* apt_package&lt;span style="color:#f92672">[&lt;/span>tar&lt;span style="color:#f92672">]&lt;/span> action install &lt;span style="color:#f92672">(&lt;/span>up to date&lt;span style="color:#f92672">)&lt;/span>
* apt_package&lt;span style="color:#f92672">[&lt;/span>gcc&lt;span style="color:#f92672">]&lt;/span> action install &lt;span style="color:#f92672">(&lt;/span>up to date&lt;span style="color:#f92672">)&lt;/span>
* apt_package&lt;span style="color:#f92672">[&lt;/span>libncursesw5-dev&lt;span style="color:#f92672">]&lt;/span> action install
- install version 6.0+20161126-1+deb9u2 of package libncursesw5-dev
* directory&lt;span style="color:#f92672">[&lt;/span>/tmp/nano-3.2&lt;span style="color:#f92672">]&lt;/span> action create
- create new directory /tmp/nano-3.2
- change mode from &lt;span style="color:#e6db74">&amp;#39;&amp;#39;&lt;/span> to &lt;span style="color:#e6db74">&amp;#39;0755&amp;#39;&lt;/span>
- change owner from &lt;span style="color:#e6db74">&amp;#39;&amp;#39;&lt;/span> to &lt;span style="color:#e6db74">&amp;#39;root&amp;#39;&lt;/span>
- change group from &lt;span style="color:#e6db74">&amp;#39;&amp;#39;&lt;/span> to &lt;span style="color:#e6db74">&amp;#39;root&amp;#39;&lt;/span>
* remote_file&lt;span style="color:#f92672">[&lt;/span>nano-3.2.tar.gz&lt;span style="color:#f92672">]&lt;/span> action create
- create new file /tmp/nano-3.2/nano-3.2.tar.gz
- update content in file /tmp/nano-3.2/nano-3.2.tar.gz from none to ca6945
&lt;span style="color:#f92672">(&lt;/span>new content is binary, diff output suppressed&lt;span style="color:#f92672">)&lt;/span>
- change mode from &lt;span style="color:#e6db74">&amp;#39;&amp;#39;&lt;/span> to &lt;span style="color:#e6db74">&amp;#39;0755&amp;#39;&lt;/span>
* execute&lt;span style="color:#f92672">[&lt;/span>unpack nano&lt;span style="color:#f92672">]&lt;/span> action run
- execute tar xfz nano-3.2.tar.gz
* execute&lt;span style="color:#f92672">[&lt;/span>compile and install&lt;span style="color:#f92672">]&lt;/span> action run
- execute ./configure --quiet --prefix&lt;span style="color:#f92672">=&lt;/span>/usr/local &lt;span style="color:#f92672">&amp;amp;&amp;amp;&lt;/span> make -s &lt;span style="color:#f92672">&amp;amp;&amp;amp;&lt;/span> make -s install
Running handlers:
Running handlers complete
Chef Client finished, 6/8 resources updated in &lt;span style="color:#ae81ff">39&lt;/span> seconds
Downloading files from &amp;lt;nano-debian-9&amp;gt;
Finished converging &amp;lt;nano-debian-9&amp;gt; &lt;span style="color:#f92672">(&lt;/span>1m3.59s&lt;span style="color:#f92672">)&lt;/span>.
-----&amp;gt; Setting up &amp;lt;nano-debian-9&amp;gt;...
Finished setting up &amp;lt;nano-debian-9&amp;gt; &lt;span style="color:#f92672">(&lt;/span>0m0.00s&lt;span style="color:#f92672">)&lt;/span>.
-----&amp;gt; Verifying &amp;lt;nano-debian-9&amp;gt;...
Preparing files &lt;span style="color:#66d9ef">for&lt;/span> transfer
Transferring files to &amp;lt;nano-debian-9&amp;gt;
Finished verifying &amp;lt;nano-debian-9&amp;gt; &lt;span style="color:#f92672">(&lt;/span>0m0.00s&lt;span style="color:#f92672">)&lt;/span>.
-----&amp;gt; Destroying &amp;lt;nano-debian-9&amp;gt;...
&lt;span style="color:#f92672">==&lt;/span>&amp;gt; default: Forcing shutdown of VM...
&lt;span style="color:#f92672">==&lt;/span>&amp;gt; default: Destroying VM and associated drives...
Vagrant instance &amp;lt;nano-debian-9&amp;gt; destroyed.
Finished destroying &amp;lt;nano-debian-9&amp;gt; &lt;span style="color:#f92672">(&lt;/span>0m4.10s&lt;span style="color:#f92672">)&lt;/span>.
Finished testing &amp;lt;nano-debian-9&amp;gt; &lt;span style="color:#f92672">(&lt;/span>2m8.26s&lt;span style="color:#f92672">)&lt;/span>.
-----&amp;gt; Kitchen is finished. &lt;span style="color:#f92672">(&lt;/span>2m9.01s&lt;span style="color:#f92672">)&lt;/span>&lt;/code>&lt;/pre>&lt;/div>
&lt;p>You can test all of the flavors by not passing any arguments after &lt;cite>kitchen test&lt;/cite>.&lt;/p>
&lt;p>You can also use &lt;cite>kitchen converge&lt;/cite> instead of test, so that the VM is not destroyed after successful convergence, and
you can see the state of the VM after applying the run list.&lt;/p>
&lt;/div>
&lt;div class="section" id="conclusion">
&lt;h2>Conclusion&lt;/h2>
&lt;p>In this post we saw how to design and implement custom resources for Chef, from scratch. We also saw how to test said
resources using kitchen and Vagrant.&lt;/p>
&lt;p>A working cookbook representing what was reviewed in this post is
&lt;a class="reference external" href="https://implement.pt/files/chef-custom-resources-cookbook/pkg-cookbook.tar.gz">available here&lt;/a>&lt;/p>
&lt;p>Feel free to post any questions, or point out any mistakes, in the comment box below.&lt;/p>
&lt;/div>
&lt;div class="section" id="references">
&lt;h2>References&lt;/h2>
&lt;table class="docutils footnote" frame="void" id="id2" rules="none">
&lt;colgroup>&lt;col class="label" />&lt;col />&lt;/colgroup>
&lt;tbody valign="top">
&lt;tr>&lt;td class="label">&lt;a class="fn-backref" href="#id1">[1]&lt;/a>&lt;/td>&lt;td>--, Custom Resources - Chef.io Documentation, 2018. &lt;a class="reference external" href="https://docs.chef.io/custom_resources.html">[link]&lt;/a>&lt;/td>&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;/div>
&lt;/div></content><category>System Administration</category><category>linux</category><category>devops</category><category>chef</category><category>configuration management</category><category>ruby</category></item><item><title>Hitchhiker's Guide to Chef</title><link>https://implement.pt/2019/02/hitchhikers-guide-to-chef/</link><pubDate>Fri, 15 Feb 2019 13:37:00 +0100</pubDate><guid>https://implement.pt/2019/02/hitchhikers-guide-to-chef/</guid><summary type="html">&lt;div class="document">
&lt;p>A beginner's guide to using Chef&lt;/p>
&lt;/div></summary><content type="html">&lt;div class="document">
&lt;!-- {F#fig-overview}
{F#fig-chef-preced} -->
&lt;div class="section" id="introduction">
&lt;h2>Introduction&lt;/h2>
&lt;p>If you have read my previous posts on Salt (&lt;a class="reference external" href="https://implement.pt/2018/10/a-comprehensive-introduction-to-salt/">part1&lt;/a> and
&lt;a class="reference external" href="https://implement.pt/2019/01/an-advanced-guide-to-salt/">part2&lt;/a>) you will now be reading about a very different approach on
configuration management. If you did not read it, you should =) at least the first part, where I write about DevOps and
Configuration Management tools in general, converging into Salt.&lt;/p>
&lt;p>One of the key differences, from the conceptual point of view, is that Chef has a third element, the Workstation, from
where the administrator performs administrative actions, according to predetermined Access Control Lists (ACLs).&lt;/p>
&lt;p>Another key difference is the way the Minions (hereby called &lt;strong>Nodes&lt;/strong>) are put into a certain state, which is
determined by the Chef client, not by the Chef server, this is somehow useful as the Node applies itself its
&lt;strong>Run List&lt;/strong> even if it has lost contact to the Chef server. Salt is also capable of this via a schedule function, but
this post is not that of comparison between tools, so I'll leave that for another time and get on to Chef's architecture
and workflow for different actions.&lt;/p>
&lt;/div>
&lt;div class="section" id="architecture">
&lt;h2>Architecture&lt;/h2>
&lt;p>Chef is complex in terms of its architecture, where it resembles an
&lt;a class="reference external" href="https://dictionary.cambridge.org/dictionary/english/umbrella-organization">umbrella organization&lt;/a>. I will try to
summarize each function, pass on to best practices in terms of interactions between them, and then go through some of
its limitations by design.&lt;/p>
&lt;div class="figure align-center" id="fig-overview">
&lt;a class="reference external image-reference" href="./img/chef_overview.svg">&lt;img alt="Chef architecture overview" src="./img/chef_overview.png" />&lt;/a>
&lt;p class="caption">Chef architecture overview&lt;/p>
&lt;/div>
&lt;p>In &lt;a class="reference external" href="#fig-overview">Figure 1&lt;/a> we can see three of the most basic interactions, and we can even consider them by the
following (oversimplified) order:&lt;/p>
&lt;ol class="arabic simple">
&lt;li>From a Chef Workstation, an admin &lt;strong>bootstraps&lt;/strong> a machine&lt;/li>
&lt;li>The machine &lt;strong>registers&lt;/strong> itself with the Chef Server, and is now a Chef Node&lt;/li>
&lt;li>The Chef Node synchronizes, with the Chef Server, any cookbook that applies to its run list, compiles its resource
collection and ultimately converges the node (applies a computed set of recipes in a deterministic order)&lt;/li>
&lt;/ol>
&lt;p>And now, the Node is in a certain predetermined state, and will make an effort to stay that way, by periodically
synchronizing its run list with the Chef Server and converging itself.&lt;/p>
&lt;p>All of this is pretty simple stuff, so let us complicate it a bit by going through each of the components' functions.&lt;/p>
&lt;/div>
&lt;div class="section" id="chef-workstation">
&lt;h2>Chef Workstation&lt;/h2>
&lt;p>The Chef Workstation can be configured by:&lt;/p>
&lt;ol class="arabic simple">
&lt;li>Installing &lt;a class="reference external" href="https://downloads.chef.io/chefdk">ChefDK&lt;/a>&lt;/li>
&lt;li>&lt;dl class="first docutils">
&lt;dt>Registering with the Chef Server:&lt;/dt>
&lt;dd>&lt;ul class="first last">
&lt;li>a user has to be created and a private_key generated, on the server side&lt;/li>
&lt;li>file &lt;cite>~/.chef/knife.rb&lt;/cite> should be present in the workstation and
&lt;a class="reference external" href="https://chef.readthedocs.io/en/latest/knife_rb.html">properly configured&lt;/a>&lt;/li>
&lt;/ul>
&lt;/dd>
&lt;/dl>
&lt;/li>
&lt;/ol>
&lt;div class="section" id="knife">
&lt;h3>Knife&lt;/h3>
&lt;p>&lt;cite>knife&lt;/cite> is a command-line tool that provides an interface between a local chef-repo and the Chef server. knife helps
users to manage &lt;a class="footnote-reference" href="#id28" id="id1">[1]&lt;/a>:&lt;/p>
&lt;ul class="simple">
&lt;li>Nodes&lt;/li>
&lt;li>Cookbooks and recipes&lt;/li>
&lt;li>Roles, Environments, and Data Bags&lt;/li>
&lt;li>Resources within various cloud environments&lt;/li>
&lt;li>The installation of the chef-client onto nodes&lt;/li>
&lt;li>Searching of indexed data on the Chef server&lt;/li>
&lt;/ul>
&lt;p>Chef.io makes available an all-in-one knife quick reference commands, you can either get the
&lt;a class="reference external" href="https://github.com/chef/quick-reference">source files&lt;/a> or get a
&lt;a class="reference external" href="./img/chef_knife_quick_reference_card_a4.pdf">ready to print PDF&lt;/a>.&lt;/p>
&lt;/div>
&lt;div class="section" id="cookbooks">
&lt;h3>Cookbooks&lt;/h3>
&lt;p>In Chef, a cookbook is the fundamental unit of configuration and policy distribution. A cookbook defines a scenario and
contains everything that is required to support that scenario &lt;a class="footnote-reference" href="#id29" id="id2">[2]&lt;/a>:&lt;/p>
&lt;ul class="simple">
&lt;li>Recipes that specify the resources to use and the order in which they are to be applied&lt;/li>
&lt;li>Attribute values&lt;/li>
&lt;li>File distributions&lt;/li>
&lt;li>Templates&lt;/li>
&lt;li>Extensions to Chef, such as custom resources and libraries&lt;/li>
&lt;/ul>
&lt;p>The reference language used to create cookbooks and defining recipes is &lt;em>Ruby&lt;/em>. There is probably as much love for it as
there is hate, in this post I will refrain about giving my personal opinion about it.&lt;/p>
&lt;p>In this section the Cookbook should be seen as the development of the scenario itself and all properties that are a part
of it.&lt;/p>
&lt;div class="section" id="recipes">
&lt;h4>Recipes&lt;/h4>
&lt;p>Taking word for word from Chef's documentation &lt;em>a recipe is the most fundamental configuration element within the&lt;/em>
&lt;em>organization&lt;/em> &lt;a class="footnote-reference" href="#id30" id="id3">[3]&lt;/a>.&lt;/p>
&lt;ul class="simple">
&lt;li>It is mostly a collection of resources&lt;/li>
&lt;li>Defines everything that is required to configure part of a system&lt;/li>
&lt;li>Must be stored in a cookbook&lt;/li>
&lt;li>May be included in another recipe&lt;/li>
&lt;li>May include other recipes&lt;/li>
&lt;li>Must be part of a run list in order to be executed&lt;/li>
&lt;li>The recipe's code is interpreted sequentially, by the order in which it is declared&lt;/li>
&lt;li>Resource execution may be delayed or expedited by notifying or be notified by another resource, thus creating an
execution sequence different from the order the resources are declared&lt;/li>
&lt;/ul>
&lt;/div>
&lt;div class="section" id="attribute-values">
&lt;h4>Attribute Values&lt;/h4>
&lt;p>Although attributes are used by the chef-client to understand certain node states, within a cookbook we can look at them
has the state we want the node to be when we execute a specific recipe.&lt;/p>
&lt;p>In a cookbook, attributes can be defined:&lt;/p>
&lt;ol class="arabic simple">
&lt;li>In attribute files, any &lt;tt class="docutils literal">&lt;span class="pre">&amp;lt;filename&amp;gt;.rb&lt;/span>&lt;/tt> placed in the &lt;tt class="docutils literal">attributes&lt;/tt> directory just under our cookbook's main
directory&lt;/li>
&lt;li>In the middle of the recipe as we need them&lt;/li>
&lt;/ol>
&lt;p>With 1. we define attributes that are static and should not depend on &lt;em>data bag items&lt;/em> values or any value that should
only be known at runtime. This is because the node attributes are used in the creation of the node object and when this
object is rebuilt the node attributes are updated based on attribute precedence, so it should not depend on values that
come from other executions, i.e: fetch a value from a data bag or the value of the current dynamically assigned ip of a
network interface created by the recipe.&lt;/p>
&lt;p>The chef-client uses six types of attributes to determine the value that is applied to a node during a chef-client run
&lt;a class="footnote-reference" href="#id31" id="id4">[4]&lt;/a>:&lt;/p>
&lt;table border="1" class="colwidths-given docutils">
&lt;caption>Attribute precedence, taken from &lt;a class="reference external" href="https://docs.chef.io/attributes.html#attribute-types">Chef documentation&lt;/a>&lt;/caption>
&lt;colgroup>
&lt;col width="40%" />
&lt;col width="60%" />
&lt;/colgroup>
&lt;thead valign="bottom">
&lt;tr>&lt;th class="head">Attribute Type&lt;/th>
&lt;th class="head">Description&lt;/th>
&lt;/tr>
&lt;/thead>
&lt;tbody valign="top">
&lt;tr>&lt;td>&lt;tt class="docutils literal">default&lt;/tt>&lt;/td>
&lt;td>&lt;!-- tag node_attribute_type_default -->
A &lt;tt class="docutils literal">default&lt;/tt> attribute is automatically reset at the start of every chef-client run and has the lowest attribute
precedence. Use &lt;tt class="docutils literal">default&lt;/tt> attributes as often as possible in cookbooks.&lt;!-- end_tag -->
&lt;/td>
&lt;/tr>
&lt;tr>&lt;td>&lt;tt class="docutils literal">force_default&lt;/tt>&lt;/td>
&lt;td>Use the &lt;tt class="docutils literal">force_default&lt;/tt> attribute to ensure that an attribute defined in a cookbook (by an attribute file or by
a recipe) takes precedence over a &lt;tt class="docutils literal">default&lt;/tt> attribute set by a role or an environment.&lt;/td>
&lt;/tr>
&lt;tr>&lt;td>&lt;tt class="docutils literal">normal&lt;/tt>&lt;/td>
&lt;td>&lt;!-- tag node_attribute_type_normal -->
A &lt;tt class="docutils literal">normal&lt;/tt> attribute is a setting that persists in the node object. A &lt;tt class="docutils literal">normal&lt;/tt> attribute has a higher
attribute precedence than a &lt;tt class="docutils literal">default&lt;/tt> attribute.&lt;!-- end_tag -->
&lt;/td>
&lt;/tr>
&lt;tr>&lt;td>&lt;tt class="docutils literal">override&lt;/tt>&lt;/td>
&lt;td>&lt;!-- tag node_attribute_type_override -->
An &lt;tt class="docutils literal">override&lt;/tt> attribute is automatically reset at the start of every chef-client run and has a higher attribute
precedence than &lt;tt class="docutils literal">default&lt;/tt>, &lt;tt class="docutils literal">force_default&lt;/tt>, and &lt;tt class="docutils literal">normal&lt;/tt> attributes. An &lt;tt class="docutils literal">override&lt;/tt> attribute is most
often specified in a recipe, but can be specified in an attribute file, for a role, and/or for an environment. A
cookbook should be authored so that it uses &lt;tt class="docutils literal">override&lt;/tt> attributes only when required.&lt;!-- end_tag -->
&lt;/td>
&lt;/tr>
&lt;tr>&lt;td>&lt;tt class="docutils literal">force_override&lt;/tt>&lt;/td>
&lt;td>Use the &lt;tt class="docutils literal">force_override&lt;/tt> attribute to ensure that an attribute defined in a cookbook (by an attribute file or
by a recipe) takes precedence over an &lt;tt class="docutils literal">override&lt;/tt> attribute set by a role or an environment.&lt;/td>
&lt;/tr>
&lt;tr>&lt;td>&lt;tt class="docutils literal">automatic&lt;/tt>&lt;/td>
&lt;td>&lt;!-- tag node_attribute_type_automatic -->
An &lt;tt class="docutils literal">automatic&lt;/tt> attribute contains data that is identified by Ohai at the beginning of every chef-client run. An
&lt;tt class="docutils literal">automatic&lt;/tt> attribute cannot be modified and always has the highest attribute precedence.&lt;!-- end_tag -->
&lt;/td>
&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;!-- end_tag -->
&lt;p>Besides attribute values behind provided from attribute files and recipes, they are also collected by
&lt;a class="reference external" href="https://docs.chef.io/ohai.html">Ohai&lt;/a> at the start of each chef-client run, i.e: &lt;tt class="docutils literal">&lt;span class="pre">node['hostname']&lt;/span>&lt;/tt> which returns
the hostname for the node, simple enough.&lt;/p>
&lt;p>When defining attributes in a attribute file, we can ommit the &lt;tt class="docutils literal">node&lt;/tt> module, as such:&lt;/p>
&lt;div class="highlight">&lt;pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">&lt;code class="language-ruby" data-lang="ruby">default&lt;span style="color:#f92672">[&lt;/span>&lt;span style="color:#e6db74">&amp;#39;myapp&amp;#39;&lt;/span>&lt;span style="color:#f92672">][&lt;/span>&lt;span style="color:#e6db74">&amp;#39;dir&amp;#39;&lt;/span>&lt;span style="color:#f92672">]&lt;/span> &lt;span style="color:#f92672">=&lt;/span> &lt;span style="color:#e6db74">&amp;#39;/srv/myapp&amp;#39;&lt;/span>&lt;/code>&lt;/pre>&lt;/div>
&lt;p>in recipes, however, we can not:&lt;/p>
&lt;div class="highlight">&lt;pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">&lt;code class="language-ruby" data-lang="ruby">node&lt;span style="color:#f92672">.&lt;/span>default&lt;span style="color:#f92672">[&lt;/span>&lt;span style="color:#e6db74">&amp;#39;myapp&amp;#39;&lt;/span>&lt;span style="color:#f92672">][&lt;/span>&lt;span style="color:#e6db74">&amp;#39;dir&amp;#39;&lt;/span>&lt;span style="color:#f92672">]&lt;/span> &lt;span style="color:#f92672">=&lt;/span> &lt;span style="color:#e6db74">&amp;#39;/srv/myapp&amp;#39;&lt;/span>&lt;/code>&lt;/pre>&lt;/div>
&lt;p>The precedence of the attributes is well &lt;a class="reference external" href="https://docs.chef.io/attributes.html#attribute-precedence">documented&lt;/a>&lt;/p>
&lt;/div>
&lt;div class="section" id="file-distributions">
&lt;h4>File Distributions&lt;/h4>
&lt;p>In a cookbook, files are managed using the following resources &lt;a class="footnote-reference" href="#id32" id="id5">[5]&lt;/a>:&lt;/p>
&lt;ul class="simple">
&lt;li>&lt;tt class="docutils literal">cookbook_file&lt;/tt> resource manages files on the node, based on source files that are located in the &lt;tt class="docutils literal">files&lt;/tt>
directory in a cookbook, using the &lt;tt class="docutils literal">source&lt;/tt> property (for more information on &lt;tt class="docutils literal">cookbook_file&lt;/tt> properties see the
&lt;a class="reference external" href="https://docs.chef.io/resource_cookbook_file.html#properties">documentation&lt;/a>):&lt;/li>
&lt;/ul>
&lt;div class="highlight">&lt;pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">&lt;code class="language-ruby" data-lang="ruby">cookbook_file &lt;span style="color:#e6db74">&amp;#39;/etc/sshd_config&amp;#39;&lt;/span> &lt;span style="color:#66d9ef">do&lt;/span>
source &lt;span style="color:#e6db74">&amp;#39;sshd_config&amp;#39;&lt;/span>
owner &lt;span style="color:#e6db74">&amp;#39;root&amp;#39;&lt;/span>
group &lt;span style="color:#e6db74">&amp;#39;root&amp;#39;&lt;/span>
mode &lt;span style="color:#e6db74">&amp;#39;0640&amp;#39;&lt;/span>
action &lt;span style="color:#e6db74">:create&lt;/span>
&lt;span style="color:#66d9ef">end&lt;/span>&lt;/code>&lt;/pre>&lt;/div>
&lt;ul class="simple">
&lt;li>&lt;tt class="docutils literal">file&lt;/tt> resource manages file content directly on the node (for more information on &lt;tt class="docutils literal">file&lt;/tt> properties see the
&lt;a class="reference external" href="https://docs.chef.io/resource_file.html#properties">documentation&lt;/a>):&lt;/li>
&lt;/ul>
&lt;div class="highlight">&lt;pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">&lt;code class="language-ruby" data-lang="ruby">file &lt;span style="color:#e6db74">&amp;#39;/etc/resolv.conf&amp;#39;&lt;/span> &lt;span style="color:#66d9ef">do&lt;/span>
content &lt;span style="color:#e6db74">&amp;#39;nameserver 1.1.1.1
&lt;/span>&lt;span style="color:#e6db74">nameserver 1.0.0.1&amp;#39;&lt;/span>
mode &lt;span style="color:#e6db74">&amp;#39;0644&amp;#39;&lt;/span>
owner &lt;span style="color:#e6db74">&amp;#39;root&amp;#39;&lt;/span>
group &lt;span style="color:#e6db74">&amp;#39;root&amp;#39;&lt;/span>
&lt;span style="color:#66d9ef">end&lt;/span>&lt;/code>&lt;/pre>&lt;/div>
&lt;ul class="simple">
&lt;li>&lt;tt class="docutils literal">remote_file&lt;/tt> resource transfers a file from a remote location to the node (for more information on &lt;tt class="docutils literal">remote_file&lt;/tt>
properties see the &lt;a class="reference external" href="https://docs.chef.io/resource_remote_file.html#properties">documentation&lt;/a>):&lt;/li>
&lt;/ul>
&lt;div class="highlight">&lt;pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">&lt;code class="language-ruby" data-lang="ruby">remote_file &lt;span style="color:#e6db74">&amp;#39;/tmp/wordpress-latest.tar.gz&amp;#39;&lt;/span> &lt;span style="color:#66d9ef">do&lt;/span>
source &lt;span style="color:#e6db74">&amp;#39;https://wordpress.org/latest.tar.gz&amp;#39;&lt;/span>
owner &lt;span style="color:#e6db74">&amp;#39;root&amp;#39;&lt;/span>
group &lt;span style="color:#e6db74">&amp;#39;root&amp;#39;&lt;/span>
mode &lt;span style="color:#e6db74">&amp;#39;0755&amp;#39;&lt;/span>
action &lt;span style="color:#e6db74">:create&lt;/span>
&lt;span style="color:#66d9ef">end&lt;/span>&lt;/code>&lt;/pre>&lt;/div>
&lt;ul class="simple">
&lt;li>&lt;tt class="docutils literal">template&lt;/tt> resource manages files that are added to nodes based on files that are located in the &lt;tt class="docutils literal">templates&lt;/tt>
directory in a cookbook (for more information on &lt;tt class="docutils literal">template&lt;/tt> properties see the
&lt;a class="reference external" href="https://docs.chef.io/resource_template.html#properties">documentation&lt;/a>):&lt;/li>
&lt;/ul>
&lt;div class="highlight">&lt;pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">&lt;code class="language-ruby" data-lang="ruby">template &lt;span style="color:#e6db74">&amp;#39;/etc/resolv.conf&amp;#39;&lt;/span> &lt;span style="color:#66d9ef">do&lt;/span>
source &lt;span style="color:#e6db74">&amp;#39;resolv.conf.erb&amp;#39;&lt;/span>
owner &lt;span style="color:#e6db74">&amp;#39;root&amp;#39;&lt;/span>
group &lt;span style="color:#e6db74">&amp;#39;root&amp;#39;&lt;/span>
mode &lt;span style="color:#e6db74">&amp;#39;0644&amp;#39;&lt;/span>
variables(&lt;span style="color:#e6db74">nameservers&lt;/span>: node&lt;span style="color:#f92672">[&lt;/span>&lt;span style="color:#e6db74">&amp;#39;nameservers&amp;#39;&lt;/span>&lt;span style="color:#f92672">]&lt;/span>) &lt;span style="color:#75715e"># optional&lt;/span>
&lt;span style="color:#66d9ef">end&lt;/span>&lt;/code>&lt;/pre>&lt;/div>
&lt;/div>
&lt;div class="section" id="templates">
&lt;h4>Templates&lt;/h4>
&lt;p>As you can see from the &lt;tt class="docutils literal">template&lt;/tt> resource in the &lt;a class="reference internal" href="#file-distributions">File Distributions&lt;/a> section, we can pass attributes as variables
to our source file, so we can have files on the node generated dynamically according to properties relating to the node
itself, cool =)&lt;/p>
&lt;p>So taking that example, we could have a template called &lt;tt class="docutils literal">resolv.conf.erb&lt;/tt> with the following content:&lt;/p>
&lt;div class="highlight">&lt;div style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">
&lt;table style="border-spacing:0;padding:0;margin:0;border:0;width:auto;overflow:auto;display:block;">&lt;tr>&lt;td style="vertical-align:top;padding:0;margin:0;border:0;">
&lt;pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">&lt;code>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">1
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">2
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">3
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">4
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">5
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td style="vertical-align:top;padding:0;margin:0;border:0;;width:100%">
&lt;pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">&lt;code class="language-ruby" data-lang="ruby">&lt;span style="color:#75715e"># DNS resolver configuration managed by Chef&lt;/span>
&lt;span style="color:#75715e"># do not edit, changes will be overridden!&lt;/span>
&lt;span style="color:#f92672">&amp;lt;&lt;/span>&lt;span style="color:#e6db74">% @nameservers.each &lt;/span> &lt;span style="color:#66d9ef">do&lt;/span> &lt;span style="color:#f92672">|&lt;/span>ns&lt;span style="color:#f92672">|&lt;/span> &lt;span style="color:#e6db74">%&amp;gt;
&lt;/span>&lt;span style="color:#e6db74">nameserver &amp;lt;%= ns %&amp;gt;&lt;/span>
&lt;span style="color:#f92672">&amp;lt;&lt;/span>&lt;span style="color:#e6db74">% end &lt;/span> &lt;span style="color:#f92672">%&amp;gt;&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>
&lt;p>and in the &lt;tt class="docutils literal">default.rb&lt;/tt> attributes file we could have:&lt;/p>
&lt;div class="highlight">&lt;pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">&lt;code class="language-ruby" data-lang="ruby">default&lt;span style="color:#f92672">[&lt;/span>&lt;span style="color:#e6db74">&amp;#39;nameservers&amp;#39;&lt;/span>&lt;span style="color:#f92672">]&lt;/span> &lt;span style="color:#f92672">=&lt;/span> &lt;span style="color:#f92672">[&lt;/span>&lt;span style="color:#e6db74">&amp;#39;1.1.1.1&amp;#39;&lt;/span>, &lt;span style="color:#e6db74">&amp;#39;1.0.0.1&amp;#39;&lt;/span>, &lt;span style="color:#e6db74">&amp;#39;8.8.8.8&amp;#39;&lt;/span>, &lt;span style="color:#e6db74">&amp;#39;8.8.4.4&amp;#39;&lt;/span>&lt;span style="color:#f92672">]&lt;/span>&lt;/code>&lt;/pre>&lt;/div>
&lt;p>when we execute the template resource defined above, in the node at the end of the run we would have the file
&lt;tt class="docutils literal">/etc/resolv.conf&lt;/tt> with the following content:&lt;/p>
&lt;div class="highlight">&lt;div style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">
&lt;table style="border-spacing:0;padding:0;margin:0;border:0;width:auto;overflow:auto;display:block;">&lt;tr>&lt;td style="vertical-align:top;padding:0;margin:0;border:0;">
&lt;pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">&lt;code>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">1
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">2
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">3
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">4
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">5
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">6
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td style="vertical-align:top;padding:0;margin:0;border:0;;width:100%">
&lt;pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">&lt;code class="language-fallback" data-lang="fallback"># DNS resolver configuration managed by Chef
# do not edit, changes will be overridden!
nameserver 1.1.1.1
nameserver 1.0.0.1
nameserver 8.8.8.8
nameserver 8.8.4.4&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>
&lt;/div>
&lt;div class="section" id="extensions-to-chef">
&lt;h4>Extensions to Chef&lt;/h4>
&lt;p>In this post I will not go in-depth on this subject, just know that these exist:&lt;/p>
&lt;table border="1" class="colwidths-given docutils">
&lt;colgroup>
&lt;col width="40%" />
&lt;col width="60%" />
&lt;/colgroup>
&lt;tbody valign="top">
&lt;tr>&lt;td>Libraries&lt;/td>
&lt;td>allow for arbitrary Ruby code to be included in a cookbook. The most common use for libraries is to write helpers
that are used throughout recipes and custom resources. A library file is a Ruby file that is located within a
cookbook’s &lt;tt class="docutils literal">/libraries&lt;/tt> directory. Because a library is built using Ruby, anything that can be done with Ruby
can be done in a library file, including advanced functionality such as extending built-in Chef classes.(for more
information see the &lt;a class="reference external" href="https://docs.chef.io/libraries.html">documentation&lt;/a>)&lt;/td>
&lt;/tr>
&lt;tr>&lt;td>Custom Resources:&lt;/td>
&lt;td>are an extension of Chef that adds your own resources; are implemented and shipped as part of a cookbook;are
reusable in the same way as resources that are built into Chef (for more information see the
&lt;a class="reference external" href="https://docs.chef.io/custom_resources.html">documentation&lt;/a>)&lt;/td>
&lt;/tr>
&lt;tr>&lt;td>Resources:&lt;/td>
&lt;td>are statements of configuration policies that: describe the desired state for a configuration item; declare the
steps needed to bring that item to the desired state; specify a resource type -- such as package, template, or
service; lists additional details (also known as resource properties), as necessary; are grouped into recipes,
which describe working configurations (for more information see the
&lt;a class="reference external" href="https://docs.chef.io/resource.html">documentation&lt;/a>)&lt;/td>
&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;/div>
&lt;div class="section" id="metadata-rb">
&lt;h4>Metadata.rb&lt;/h4>
&lt;p>Last but definitely not least, the &lt;tt class="docutils literal">metadata.rb&lt;/tt> file is one of the fundamental files of a cookbook, every cookbook
requires one and is created automatically when running &lt;tt class="docutils literal">knife cookbook create&lt;/tt> command on the Chef Workstation, but
require a very small amount of metadata information. The file is located at the top of every cookbook directory
structure, and its contents provide information that helps both the Chef Server and Client to correctly deploy cookbooks
to each node &lt;a class="footnote-reference" href="#id33" id="id6">[6]&lt;/a>.&lt;/p>
&lt;table border="1" class="colwidths-given table-rcj docutils">
&lt;caption>metadata.rb options, adapted from &lt;a class="reference external" href="https://docs.chef.io/cookbook_repo.html#metadata-rb">Chef documentation&lt;/a>&lt;/caption>
&lt;colgroup>
&lt;col width="30%" />
&lt;col width="10%" />
&lt;col width="60%" />
&lt;/colgroup>
&lt;thead valign="bottom">
&lt;tr>&lt;th class="head">Setting&lt;/th>
&lt;th class="head">Required&lt;/th>
&lt;th class="head">Description&lt;/th>
&lt;/tr>
&lt;/thead>
&lt;tbody valign="top">
&lt;tr>&lt;td>&lt;tt class="docutils literal">name&lt;/tt>&lt;/td>
&lt;td>Yes&lt;/td>
&lt;td>The name of the cookbook.&lt;/td>
&lt;/tr>
&lt;tr>&lt;td>&lt;tt class="docutils literal">version&lt;/tt>&lt;/td>
&lt;td>No&lt;/td>
&lt;td>The current version of a cookbook. Version numbers always follow a simple three-number version sequence, i.e:
&lt;code>version '2.0.0'&lt;/code>.&lt;/td>
&lt;/tr>
&lt;tr>&lt;td>&lt;tt class="docutils literal">chef_version&lt;/tt>&lt;/td>
&lt;td>No&lt;/td>
&lt;td>A range of chef-client versions that are supported by this cookbook. All
&lt;a class="reference external" href="https://docs.chef.io/config_rb_metadata.html#cookbook-version-constraints">version constraint operators&lt;/a> are
applicable to this field. i.e: &lt;code>chef_version '~&amp;gt; 14'&lt;/code>&lt;/td>
&lt;/tr>
&lt;tr>&lt;td>&lt;tt class="docutils literal">depends&lt;/tt>&lt;/td>
&lt;td>No&lt;/td>
&lt;td>This field requires that a cookbook with a matching name and version exists on the Chef server. When the match
exists, the Chef server includes the dependency as part of the set of cookbooks that are sent to the node when
the chef-client runs. It is very important that the &lt;cite>depends&lt;/cite> field contain accurate data. If a dependency
statement is inaccurate, the chef-client may not be able to complete the configuration of the system. All version
constraint operators are applicable to this field.&lt;/td>
&lt;/tr>
&lt;tr>&lt;td>&lt;tt class="docutils literal">description&lt;/tt>&lt;/td>
&lt;td>No&lt;/td>
&lt;td>A short description of a cookbook and its functionality.&lt;/td>
&lt;/tr>
&lt;tr>&lt;td>&lt;tt class="docutils literal">long_description&lt;/tt>&lt;/td>
&lt;td>No&lt;/td>
&lt;td>A longer description that ideally contains full instructions on the proper use of a cookbook, including
resources, libraries, dependencies, and so on. There are two ways to use this field: with the contents embedded
in the field itself or with the contents pulled from a file at a specified path, such as a README.rdoc located at
the top of a cookbook directory.&lt;/td>
&lt;/tr>
&lt;tr>&lt;td>&lt;tt class="docutils literal">gem&lt;/tt>&lt;/td>
&lt;td>No&lt;/td>
&lt;td>Specifies a gem dependency for installation into the chef-client through bundler. The gem installation occurs
after all cookbooks are synchronized but before loading any other cookbooks. Use this attribute one time for each
gem dependency.&lt;/td>
&lt;/tr>
&lt;tr>&lt;td>&lt;tt class="docutils literal">source_url&lt;/tt>&lt;/td>
&lt;td>No&lt;/td>
&lt;td>The URL for the location in which a cookbook’s source code is maintained. This setting is also used by Chef
Supermarket. In Chef Supermarket, this value is used to define the destination for the “View Source” link.&lt;/td>
&lt;/tr>
&lt;tr>&lt;td>&lt;tt class="docutils literal">issues_url&lt;/tt>&lt;/td>
&lt;td>No&lt;/td>
&lt;td>The URL for the location in which a cookbook’s issue tracking is maintained. This setting is also used by Chef
Supermarket. In Chef Supermarket, this value is used to define the destination for the “View Issues” link.&lt;/td>
&lt;/tr>
&lt;tr>&lt;td>&lt;tt class="docutils literal">license&lt;/tt>&lt;/td>
&lt;td>No&lt;/td>
&lt;td>The type of license under which a cookbook is distributed: Apache v2.0, GPL v2, GPL v3, MIT, or license
'Proprietary - All Rights Reserved (default). Please be aware of the licenses for files inside of a cookbook and
be sure to follow any restrictions they describe.&lt;/td>
&lt;/tr>
&lt;tr>&lt;td>&lt;tt class="docutils literal">maintainer&lt;/tt>&lt;/td>
&lt;td>No&lt;/td>
&lt;td>The name of the person responsible for maintaining a cookbook, either an individual or an organization.&lt;/td>
&lt;/tr>
&lt;tr>&lt;td>&lt;tt class="docutils literal">maintainer_email&lt;/tt>&lt;/td>
&lt;td>No&lt;/td>
&lt;td>The email address for the person responsible for maintaining a cookbook. Only one email can be listed here, so
if this needs to be forwarded to multiple people consider using an email address that is already setup for mail
forwarding.&lt;/td>
&lt;/tr>
&lt;tr>&lt;td>&lt;tt class="docutils literal">supports&lt;/tt>&lt;/td>
&lt;td>No&lt;/td>
&lt;td>Show that a cookbook has a supported platform. Use a version constraint to define dependencies for platform
versions: &amp;lt; (less than), &amp;lt;= (less than or equal to), = (equal to), &amp;gt;= (greater than or equal to), ~&amp;gt;
(approximately greater than), or &amp;gt; (greater than). To specify more than one platform, use more than one supports
field, once for each platform.&lt;/td>
&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;p>Although the only mandatory option is the cookbook's &lt;tt class="docutils literal">name&lt;/tt>, I recommend to have at least the following example for a
starting cookbook:&lt;/p>
&lt;div class="highlight">&lt;div style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">
&lt;table style="border-spacing:0;padding:0;margin:0;border:0;width:auto;overflow:auto;display:block;">&lt;tr>&lt;td style="vertical-align:top;padding:0;margin:0;border:0;">
&lt;pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">&lt;code>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 1
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 2
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 3
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 4
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 5
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 6
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 7
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 8
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 9
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">10
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">11
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">12
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">13
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">14
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">15
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td style="vertical-align:top;padding:0;margin:0;border:0;;width:100%">
&lt;pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">&lt;code class="language-ruby" data-lang="ruby">name &lt;span style="color:#e6db74">&amp;#39;&amp;lt;cookbook name&amp;gt;&amp;#39;&lt;/span>
license &lt;span style="color:#e6db74">&amp;#39;&amp;lt;license type&amp;gt;&amp;#39;&lt;/span>
maintainer &lt;span style="color:#e6db74">&amp;#39;&amp;lt;first and last name&amp;gt;&amp;#39;&lt;/span>
maintainer_email &lt;span style="color:#e6db74">&amp;#39;&amp;lt;your e-mail&amp;gt;&amp;#39;&lt;/span>
issues_url &lt;span style="color:#e6db74">&amp;#39;&amp;lt;github url&amp;gt;/issues&amp;#39;&lt;/span>
source_url &lt;span style="color:#e6db74">&amp;#39;&amp;lt;github_url&amp;gt;&amp;#39;&lt;/span>
description &lt;span style="color:#e6db74">&amp;#39;small description&amp;#39;&lt;/span>
long_description &lt;span style="color:#66d9ef">IO&lt;/span>&lt;span style="color:#f92672">.&lt;/span>read(&lt;span style="color:#66d9ef">File&lt;/span>&lt;span style="color:#f92672">.&lt;/span>join(&lt;span style="color:#66d9ef">File&lt;/span>&lt;span style="color:#f92672">.&lt;/span>dirname(__FILE__), &lt;span style="color:#e6db74">&amp;#39;README.md&amp;#39;&lt;/span>))
version &lt;span style="color:#e6db74">&amp;#39;0.0.1&amp;#39;&lt;/span>
chef_version &lt;span style="color:#e6db74">&amp;#39;~&amp;gt; 14.0&amp;#39;&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>
&lt;p>This metadata template is almost supermarket ready, missing just the cookbook's dependencies and Operating System (OS)
support, so I think it's a great start.&lt;/p>
&lt;/div>
&lt;/div>
&lt;div class="section" id="linting">
&lt;h3>Linting&lt;/h3>
&lt;p>Linters are very important in a development workflow, a linter verifies a program or program segments against standard
libraries. It checks the code for common portability errors. It tests the programming against some tried and true
guidelines. linting your code is a necessary (though not sufficient) step in writing clean, portable, effective
programs. But lint is not perfect. It will not magically salvage bad code. It will not find all your bugs. &lt;a class="footnote-reference" href="#id34" id="id7">[7]&lt;/a> I
certainly do not have anything to add to this sentence besides the tools we should use for our cookbooks:&lt;/p>
&lt;ul class="simple">
&lt;li>&lt;a class="reference external" href="https://docs.chef.io/cookstyle.html">cookstyle&lt;/a>&lt;/li>
&lt;li>&lt;span class="strike">foodcritic&lt;/span> (&lt;a class="reference external" href="https://blog.chef.io/goodbye-foodcritic/">deprecated since 25/09/2020&lt;/a>)&lt;/li>
&lt;/ul>
&lt;/div>
&lt;div class="section" id="tests">
&lt;h3>Tests&lt;/h3>
&lt;p>According to Chef's documentation, we can use ChefSpec to simulate the convergence of resources on a node:&lt;/p>
&lt;ul class="simple">
&lt;li>Is an extension of RSpec, a behavior-driven development (BDD) framework for Ruby&lt;/li>
&lt;li>Is the fastest way to test resources and recipes&lt;/li>
&lt;/ul>
&lt;p>ChefSpec is a framework that tests resources and recipes as part of a simulated chef-client run. ChefSpec tests execute
very quickly. When used as part of the cookbook authoring workflow, ChefSpec tests are often the first indicator of
problems that may exist within a cookbook. &lt;a class="footnote-reference" href="#id35" id="id8">[8]&lt;/a>&lt;/p>
&lt;p>It does not make sense to make tests to ensure that, i.e: a package was really installed or the contents of a template
file are up to date as the recipes that apply those things already do them. It is however sane to write tests reflecting
a set of operations that should be applied to the node according to the current stable state of the recipes, this
assures that if some regression, i.e: a wrong file is changed or a package dependency is no longer available, the
recipe(s) will fail some test and you will notice that something is wrong before converging a node.&lt;/p>
&lt;/div>
&lt;div class="section" id="compliance">
&lt;h3>Compliance&lt;/h3>
&lt;p>InSpec is a free and open-source framework for testing and auditing your applications and infrastructure. InSpec works
by comparing the actual state of your system with the desired state that you express in easy-to-read and easy-to-write
InSpec code. InSpec detects violations and displays findings in the form of a report, but puts you in control of
remediation. &lt;a class="footnote-reference" href="#id36" id="id9">[9]&lt;/a>&lt;/p>
&lt;p>It can be integrated with Chef, but that is out of the scope of this post. Maybe
&lt;a class="reference external" href="https://implement.pt/2020/10/integration-and-compliance-with-inspec/">another day&lt;/a>.&lt;/p>
&lt;/div>
&lt;div class="section" id="kitchen">
&lt;h3>Kitchen&lt;/h3>
&lt;p>&lt;a class="reference external" href="https://kitchen.ci/">Kitchen&lt;/a> is a tool that allows us to test infrastructure code on one or more platforms in
isolation.&lt;/p>
&lt;p>&amp;quot;A &lt;a class="reference external" href="https://kitchen.ci/docs/drivers">driver&lt;/a> plugin architecture is used to run code on various cloud providers and
virtualization technologies such as Vagrant, Amazon EC2, and Docker.&lt;/p>
&lt;p>For Chef workflows, cookbook dependency resolution via &lt;a class="reference external" href="https://docs.chef.io/berkshelf.html">Berkshelf&lt;/a> or
&lt;a class="reference external" href="https://docs.chef.io/config_rb_policyfile.html">Policyfiles&lt;/a> is supported or include a &lt;tt class="docutils literal">cookbooks/&lt;/tt> directory and
Kitchen will know what to do.&lt;/p>
&lt;p>Kitchen is used by all &lt;a class="reference external" href="https://github.com/chef-cookbooks/">Chef-managed community cookbooks&lt;/a> and is the integration
testing tool of choice for cookbooks.&amp;quot;&lt;/p>
&lt;p>We specify every requirement for kitchen in &lt;cite>.kitchen.yml&lt;/cite> file usually located in the root directory of our coookbook,
but really kitchen only cares about having that file present in whatever directory you are calling it.&lt;/p>
&lt;p>An example file for a &lt;tt class="docutils literal">.kitchen.yml&lt;/tt> configuration file running on Openstack would be:&lt;/p>
&lt;div class="highlight">&lt;div style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">
&lt;table style="border-spacing:0;padding:0;margin:0;border:0;width:auto;overflow:auto;display:block;">&lt;tr>&lt;td style="vertical-align:top;padding:0;margin:0;border:0;">
&lt;pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">&lt;code>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 1
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 2
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 3
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 4
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 5
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 6
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 7
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 8
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 9
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">10
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">11
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">12
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">13
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">14
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">15
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">16
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">17
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">18
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">19
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">20
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">21
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">22
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">23
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">24
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">25
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">26
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">27
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">28
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">29
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">30
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">31
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">32
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">33
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">34
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">35
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">36
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">37
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">38
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">39
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">40
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">41
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">42
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">43
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">44
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">45
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">46
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">47
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">48
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">49
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">50
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">51
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">52
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">53
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">54
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">55
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">56
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">57
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">58
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">59
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td style="vertical-align:top;padding:0;margin:0;border:0;;width:100%">
&lt;pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">&lt;code class="language-yaml" data-lang="yaml">&lt;span style="color:#f92672">driver&lt;/span>:
&lt;span style="color:#f92672">name&lt;/span>: &lt;span style="color:#ae81ff">openstack&lt;/span>
&lt;span style="color:#f92672">openstack_auth_url&lt;/span>: &lt;span style="color:#ae81ff">&amp;lt;%= ENV[&amp;#39;OS_AUTH_URL&amp;#39;] %&amp;gt;&lt;/span>
&lt;span style="color:#f92672">openstack_domain_name&lt;/span>: &lt;span style="color:#ae81ff">&amp;lt;%= ENV[&amp;#39;OS_USER_DOMAIN_NAME&amp;#39;] %&amp;gt;&lt;/span>
&lt;span style="color:#f92672">openstack_region&lt;/span>: &lt;span style="color:#ae81ff">&amp;lt;%= ENV[&amp;#39;OS_REGION_NAME&amp;#39;] %&amp;gt;&lt;/span>
&lt;span style="color:#f92672">openstack_project_name&lt;/span>: &lt;span style="color:#ae81ff">&amp;lt;%= ENV[&amp;#39;OS_PROJECT_NAME&amp;#39;] %&amp;gt;&lt;/span>
&lt;span style="color:#f92672">openstack_project_id&lt;/span>: &lt;span style="color:#ae81ff">&amp;lt;%= ENV[&amp;#39;OS_PROJECT_ID&amp;#39;] %&amp;gt;&lt;/span>
&lt;span style="color:#f92672">openstack_username&lt;/span>: &lt;span style="color:#ae81ff">&amp;lt;%= ENV[&amp;#39;OS_USERNAME&amp;#39;] %&amp;gt;&lt;/span>
&lt;span style="color:#f92672">openstack_api_key&lt;/span>: &lt;span style="color:#ae81ff">&amp;lt;%= ENV[&amp;#39;OS_API_KEY&amp;#39;] %&amp;gt;&lt;/span>
&lt;span style="color:#f92672">flavor_ref&lt;/span>: &lt;span style="color:#ae81ff">&amp;lt;%= ENV[&amp;#39;OS_FLAVOR&amp;#39;] %&amp;gt;&lt;/span>
&lt;span style="color:#f92672">key_name&lt;/span>: &lt;span style="color:#ae81ff">&amp;lt;%= ENV[&amp;#39;OS_KEYPAIR_NAME&amp;#39;] %&amp;gt;&lt;/span>
&lt;span style="color:#f92672">provisioner&lt;/span>:
&lt;span style="color:#f92672">name&lt;/span>: &lt;span style="color:#ae81ff">chef_zero&lt;/span>
&lt;span style="color:#f92672">data_bags_path&lt;/span>: &lt;span style="color:#e6db74">&amp;#34;data_bags&amp;#34;&lt;/span>
&lt;span style="color:#f92672">encrypted_data_bag_secret_key_path&lt;/span>: &lt;span style="color:#e6db74">&amp;#34;secrets/encrypted_data_bag_secret&amp;#34;&lt;/span>
&lt;span style="color:#f92672">transport&lt;/span>:
&lt;span style="color:#f92672">ssh_key&lt;/span>: &lt;span style="color:#ae81ff">.&amp;lt;%= ENV[&amp;#39;OS_KEYPAIR_NAME&amp;#39;] %&amp;gt;.pem&lt;/span>
&lt;span style="color:#f92672">verifier&lt;/span>:
&lt;span style="color:#f92672">name&lt;/span>: &lt;span style="color:#ae81ff">inspec&lt;/span>
&lt;span style="color:#f92672">platforms&lt;/span>:
- &lt;span style="color:#f92672">name&lt;/span>: &lt;span style="color:#ae81ff">ubuntu-18&lt;/span>
&lt;span style="color:#f92672">transport&lt;/span>:
&lt;span style="color:#f92672">username&lt;/span>: &lt;span style="color:#ae81ff">ubuntu&lt;/span>
&lt;span style="color:#f92672">driver&lt;/span>:
&lt;span style="color:#f92672">image_ref&lt;/span>: &lt;span style="color:#ae81ff">Ubuntu-18.04-Latest&lt;/span>
&lt;span style="color:#f92672">server_name_prefix&lt;/span>: &lt;span style="color:#ae81ff">ubuntu-bionic&lt;/span>
- &lt;span style="color:#f92672">name&lt;/span>: &lt;span style="color:#ae81ff">debian-9&lt;/span>
&lt;span style="color:#f92672">transport&lt;/span>:
&lt;span style="color:#f92672">username&lt;/span>: &lt;span style="color:#ae81ff">debian&lt;/span>
&lt;span style="color:#f92672">driver&lt;/span>:
&lt;span style="color:#f92672">image_ref&lt;/span>: &lt;span style="color:#ae81ff">Debian-9-Latest&lt;/span>
&lt;span style="color:#f92672">server_name_prefix&lt;/span>: &lt;span style="color:#ae81ff">debian-stretch&lt;/span>
&lt;span style="color:#f92672">suites&lt;/span>:
- &lt;span style="color:#f92672">name&lt;/span>: &lt;span style="color:#ae81ff">defaults&lt;/span>
&lt;span style="color:#f92672">run_list&lt;/span>:
- &lt;span style="color:#ae81ff">recipe[mycookbook::predefault]&lt;/span>
- &lt;span style="color:#ae81ff">recipe[mycookbook::default]&lt;/span>
- &lt;span style="color:#ae81ff">recipe[mycookbook::postdefault]&lt;/span>
&lt;span style="color:#f92672">verifier&lt;/span>:
&lt;span style="color:#f92672">inspec_tests&lt;/span>:
- &lt;span style="color:#ae81ff">test/integration/predefault&lt;/span>
- &lt;span style="color:#ae81ff">test/integration/default&lt;/span>
- &lt;span style="color:#ae81ff">test/integration/postdefault&lt;/span>
&lt;span style="color:#f92672">attributes&lt;/span>:
- &lt;span style="color:#f92672">name&lt;/span>: &lt;span style="color:#ae81ff">verytastyrun&lt;/span>
&lt;span style="color:#f92672">run_list&lt;/span>:
- &lt;span style="color:#ae81ff">recipe[mycookbook::saltandpepper]&lt;/span>
&lt;span style="color:#f92672">verifier&lt;/span>:
&lt;span style="color:#f92672">inspec_tests&lt;/span>:
- &lt;span style="color:#ae81ff">test/integration/salt&lt;/span>
- &lt;span style="color:#ae81ff">test/integration/pepper&lt;/span>
- &lt;span style="color:#ae81ff">test/integration/spicy&lt;/span>
&lt;span style="color:#ae81ff">attributes:&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>
&lt;p>In this configuration we have a 2 suites:&lt;/p>
&lt;ul class="simple">
&lt;li>defaults&lt;/li>
&lt;li>verytastyrun&lt;/li>
&lt;/ul>
&lt;p>We use the Openstack driver, and so we have to configure our environment:&lt;/p>
&lt;table border="1" class="colwidths-given table-rcj docutils">
&lt;colgroup>
&lt;col width="10%" />
&lt;col width="20%" />
&lt;col width="70%" />
&lt;/colgroup>
&lt;thead valign="bottom">
&lt;tr>&lt;th class="head">Property Name&lt;/th>
&lt;th class="head">Environment Variable&lt;/th>
&lt;th class="head">Description&lt;/th>
&lt;/tr>
&lt;/thead>
&lt;tbody valign="top">
&lt;tr>&lt;td>openstack_auth_url&lt;/td>
&lt;td>OS_AUTH_URL&lt;/td>
&lt;td>URL for the Openstack stackcontroller&lt;/td>
&lt;/tr>
&lt;tr>&lt;td>openstack_domain_name&lt;/td>
&lt;td>OS_USER_DOMAIN_NAME&lt;/td>
&lt;td>name of the Openstack domain we will be authentication with&lt;/td>
&lt;/tr>
&lt;tr>&lt;td>openstack_region&lt;/td>
&lt;td>OS_REGION_NAME&lt;/td>
&lt;td>Openstack region where we want to launch our instances&lt;/td>
&lt;/tr>
&lt;tr>&lt;td>openstack_project_name&lt;/td>
&lt;td>OS_PROJECT_NAME&lt;/td>
&lt;td>The project where we want to launch the instances (note: this is not enough for auth)&lt;/td>
&lt;/tr>
&lt;tr>&lt;td>openstack_project_id&lt;/td>
&lt;td>OS_PROJECT_ID&lt;/td>
&lt;td>The project ID&lt;/td>
&lt;/tr>
&lt;tr>&lt;td>openstack_username&lt;/td>
&lt;td>OS_USERNAME&lt;/td>
&lt;td>Openstack username to authenticate with&lt;/td>
&lt;/tr>
&lt;tr>&lt;td>openstack_api_key&lt;/td>
&lt;td>OS_API_KEY&lt;/td>
&lt;td>API Key corresponding to the previous username&lt;/td>
&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;p>We also configure the environment variable &lt;tt class="docutils literal">OS_KEYPAIR_NAME&lt;/tt> which should correspond to the name of the keypair
created in Openstack with our &lt;tt class="docutils literal">OS_USERNAME&lt;/tt> account, and the private key should be located in &lt;tt class="docutils literal">.OS_KEYPAIR_NAME.pem&lt;/tt>
in the root directory of our cookbook, as defined by the &lt;tt class="docutils literal">&lt;span class="pre">transport['ssh_key']&lt;/span>&lt;/tt> yaml key.&lt;/p>
&lt;p>Next we specify which provisioner to use, in our case we are testing a chef cookbook so we chose &lt;tt class="docutils literal">chef_zero&lt;/tt>. Here we
can specify &lt;a class="reference external" href="https://docs.chef.io/config_yml_kitchen.html#provisioner-settings">configuration options&lt;/a> to be passed
to chef, in this example we define options for &lt;a class="reference internal" href="#data-bags">data bags&lt;/a>.&lt;/p>
&lt;p>We then define which platforms should be converged, which should be available in Openstack. We can define a prefix for
the instance name with the &lt;tt class="docutils literal">server_name_prefix&lt;/tt> property.&lt;/p>
&lt;p>Finally, in the &lt;tt class="docutils literal">suites&lt;/tt> section we define which convergence run lists should be made available to kitchen. In this
example we have two:&lt;/p>
&lt;ul class="simple">
&lt;li>defaults&lt;/li>
&lt;li>verytastyrun&lt;/li>
&lt;/ul>
&lt;p>In the &lt;tt class="docutils literal">run_list&lt;/tt> property we should specify from which cookbook we want to get each recipe.&lt;/p>
&lt;p>A usual command sequence to converge a suite and destroy the converged instances would be:&lt;/p>
&lt;ol class="arabic simple">
&lt;li>&lt;code>localhost:~$ kitchen converge &amp;lt;platform name&amp;gt;&lt;/code>&lt;/li>
&lt;li>&lt;code>localhost:~$ kitchen destroy &amp;lt;platform name&amp;gt;&lt;/code>&lt;/li>
&lt;/ol>
&lt;p>If, however, we would like to just create one of the machines, i.e: debian, login into it, do some manual testing and &lt;em>then&lt;/em> converge the machine we would:&lt;/p>
&lt;ol class="arabic simple">
&lt;li>&lt;code>localhost:~$ kitchen create debian&lt;/code>&lt;/li>
&lt;li>&lt;code>localhost:~$ kitchen login debian&lt;/code>&lt;/li>
&lt;li>&lt;code>debian-stretch-asdfgh:~$ logout&lt;/code>&lt;/li>
&lt;li>&lt;code>localhost:~$ kitchen converge debian&lt;/code>&lt;/li>
&lt;li>&lt;code>localhost:~$ kitchen destroy debian&lt;/code>&lt;/li>
&lt;/ol>
&lt;/div>
&lt;/div>
&lt;div class="section" id="chef-server">
&lt;h2>Chef Server&lt;/h2>
&lt;p>We have now arrived at the core component of our infrastructure, the Chef Server. In this section I will present, to
some extend, the capabilities of the Chef Server and you to interact with it.&lt;/p>
&lt;p>Interaction with Chef's API may be done via GET/POST HTTP methods, via the &lt;tt class="docutils literal">knife&lt;/tt> tool via plugins, built-in or
custom made, or from within the Chef Server itself via the &lt;tt class="docutils literal">&lt;span class="pre">chef-server-ctl&lt;/span>&lt;/tt>. In this section I will focus on
operations with the &lt;tt class="docutils literal">&lt;span class="pre">chef-server-ctl&lt;/span>&lt;/tt> tool, however you can assume that every command is similar in syntax with the
&lt;tt class="docutils literal">knife&lt;/tt> tool.&lt;/p>
&lt;p>You can interact with virtually any component of the Chef Server via its API, so I will not post here all of the
operations, instead you can check them out in the &lt;a class="reference external" href="https://docs.chef.io/api_chef_server.html">documentation&lt;/a>.&lt;/p>
&lt;p>The first thoughts about any of the following components should be &amp;quot;What can I manage and how can I manage it?&amp;quot;, so let
us get on with the individual components of the Chef Server, what we can manage of each component and how do we do it.&lt;/p>
&lt;div class="section" id="users">
&lt;h3>Users&lt;/h3>
&lt;p>Like any architecture which has an authentication component, we have users. What can we do with it?&lt;/p>
&lt;table border="1" class="colwidths-given docutils">
&lt;colgroup>
&lt;col width="30%" />
&lt;col width="70%" />
&lt;/colgroup>
&lt;thead valign="bottom">
&lt;tr>&lt;th class="head">Description&lt;/th>
&lt;th class="head">Command&lt;/th>
&lt;/tr>
&lt;/thead>
&lt;tbody valign="top">
&lt;tr>&lt;td>Create a user&lt;/td>
&lt;td>&lt;tt class="docutils literal">$ &lt;span class="pre">chef-server-ctl&lt;/span> &lt;span class="pre">user-create&lt;/span> USER_NAME FIRST_NAME [MIDDLE_NAME] LAST_NAME EMAIL 'PASSWORD' &lt;span class="pre">[-f&lt;/span> USER_NAME.pem]&lt;/tt>&lt;/td>
&lt;/tr>
&lt;tr>&lt;td>Delete a user&lt;/td>
&lt;td>&lt;tt class="docutils literal">$ &lt;span class="pre">chef-server-ctl&lt;/span> &lt;span class="pre">user-delete&lt;/span> USER_NAME&lt;/tt>&lt;/td>
&lt;/tr>
&lt;tr>&lt;td>Edit a user's details&lt;/td>
&lt;td>&lt;tt class="docutils literal">$ &lt;span class="pre">chef-server-ctl&lt;/span> &lt;span class="pre">user-edit&lt;/span> USER_NAME&lt;/tt>&lt;/td>
&lt;/tr>
&lt;tr>&lt;td>View a list of users&lt;/td>
&lt;td>&lt;tt class="docutils literal">$ &lt;span class="pre">chef-server-ctl&lt;/span> &lt;span class="pre">user-list&lt;/span> &lt;span class="pre">[--with-uri]&lt;/span>&lt;/tt>&lt;/td>
&lt;/tr>
&lt;tr>&lt;td>Show a user's details&lt;/td>
&lt;td>&lt;tt class="docutils literal">$ &lt;span class="pre">chef-server-ctl&lt;/span> &lt;span class="pre">user-show&lt;/span> USER_NAME &lt;span class="pre">[--with-orgs]&lt;/span>&lt;/tt>&lt;/td>
&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;/div>
&lt;div class="section" id="organizations">
&lt;h3>Organizations&lt;/h3>
&lt;p>An organization is the top-level entity for role-based access control in the Chef server. Each organization contains the
default groups (admins, clients, and users, plus billing_admins for the hosted Chef server), at least one user and at
least one node (on which the chef-client is installed). The Chef server supports multiple organizations. The Chef server
includes a single default organization that is defined during setup. Additional organizations can be created after the
initial setup and configuration of the Chef server. &lt;a class="footnote-reference" href="#id37" id="id10">[10]&lt;/a>&lt;/p>
&lt;table border="1" class="colwidths-given docutils">
&lt;colgroup>
&lt;col width="30%" />
&lt;col width="70%" />
&lt;/colgroup>
&lt;thead valign="bottom">
&lt;tr>&lt;th class="head">Description&lt;/th>
&lt;th class="head">Command&lt;/th>
&lt;/tr>
&lt;/thead>
&lt;tbody valign="top">
&lt;tr>&lt;td>Create an organization&lt;/td>
&lt;td>&lt;tt class="docutils literal">$ &lt;span class="pre">chef-server-ctl&lt;/span> &lt;span class="pre">org-create&lt;/span> ORG_NAME &amp;quot;ORG_FULL_NAME&amp;quot; &lt;span class="pre">[--association_user&lt;/span> USER_NAME] &lt;span class="pre">[-f&lt;/span> FILE_NAME]&lt;/tt>&lt;/td>
&lt;/tr>
&lt;tr>&lt;td>Delete an organization&lt;/td>
&lt;td>&lt;tt class="docutils literal">$ &lt;span class="pre">chef-server-ctl&lt;/span> &lt;span class="pre">org-delete&lt;/span> ORG_NAME&lt;/tt>&lt;/td>
&lt;/tr>
&lt;tr>&lt;td>View a list of organizations&lt;/td>
&lt;td>&lt;tt class="docutils literal">$ &lt;span class="pre">chef-server-ctl&lt;/span> &lt;span class="pre">org-list&lt;/span> &lt;span class="pre">[--all-orgs]&lt;/span> &lt;span class="pre">[--with-uri]&lt;/span>&lt;/tt>&lt;/td>
&lt;/tr>
&lt;tr>&lt;td>Show an organization's details&lt;/td>
&lt;td>&lt;tt class="docutils literal">$ &lt;span class="pre">chef-server-ctl&lt;/span> &lt;span class="pre">org-show&lt;/span> ORG_NAME&lt;/tt>&lt;/td>
&lt;/tr>
&lt;tr>&lt;td>Add a user to an organization&lt;/td>
&lt;td>&lt;tt class="docutils literal">$ &lt;span class="pre">chef-server-ctl&lt;/span> &lt;span class="pre">org-user-add&lt;/span> ORG_NAME USER_NAME &lt;span class="pre">--admin&lt;/span>&lt;/tt>&lt;/td>
&lt;/tr>
&lt;tr>&lt;td>Remove a user from an organization&lt;/td>
&lt;td>&lt;tt class="docutils literal">$ &lt;span class="pre">chef-server-ctl&lt;/span> &lt;span class="pre">org-user-remove&lt;/span> ORG_NAME USER_NAME&lt;/tt>&lt;/td>
&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;/div>
&lt;div class="section" id="groups">
&lt;h3>Groups&lt;/h3>
&lt;p>A group is used to define access to object types and objects in the Chef server and also to assign permissions that
determine what types of tasks are available to members of that group who are authorized to perform them. Groups are
configured per-organization. &lt;a class="footnote-reference" href="#id37" id="id11">[10]&lt;/a>&lt;/p>
&lt;p>Individual users who are members of a group will inherit the permissions assigned to the group. The Chef server includes
the following default groups: &lt;tt class="docutils literal">admins&lt;/tt>, &lt;tt class="docutils literal">clients&lt;/tt>, and &lt;tt class="docutils literal">users&lt;/tt>. For users of the hosted Chef server, an additional
default group is provided: &lt;tt class="docutils literal">billing_admins&lt;/tt>. &lt;a class="footnote-reference" href="#id37" id="id12">[10]&lt;/a>&lt;/p>
&lt;p>There is another group named &lt;tt class="docutils literal">&lt;span class="pre">server-admins&lt;/span>&lt;/tt>, it is a global group that grants its members permission to create, read,
update, and delete user accounts, with the exception of superuser accounts. The server-admins group is useful for users
who are responsible for day-to-day administration of the Chef server, especially user management via the knife user
subcommand. Before members can be added to the server-admins group, they must already have a user account on the Chef
server. &lt;a class="footnote-reference" href="#id37" id="id13">[10]&lt;/a>&lt;/p>
&lt;table border="1" class="colwidths-given docutils">
&lt;colgroup>
&lt;col width="30%" />
&lt;col width="70%" />
&lt;/colgroup>
&lt;thead valign="bottom">
&lt;tr>&lt;th class="head">Description&lt;/th>
&lt;th class="head">Command&lt;/th>
&lt;/tr>
&lt;/thead>
&lt;tbody valign="top">
&lt;tr>&lt;td>Add a user to the &lt;tt class="docutils literal">&lt;span class="pre">server-admins&lt;/span>&lt;/tt> group&lt;/td>
&lt;td>&lt;tt class="docutils literal">$ &lt;span class="pre">chef-server-ctl&lt;/span> &lt;span class="pre">grant-server-admin-permissions&lt;/span> USERNAME&lt;/tt>&lt;/td>
&lt;/tr>
&lt;tr>&lt;td>Remove a user from the &lt;tt class="docutils literal">&lt;span class="pre">server-admins&lt;/span>&lt;/tt> group&lt;/td>
&lt;td>&lt;tt class="docutils literal">$ &lt;span class="pre">chef-server-ctl&lt;/span> &lt;span class="pre">remove-server-admin-permissions&lt;/span>&lt;/tt>&lt;/td>
&lt;/tr>
&lt;tr>&lt;td>List users that belong to the &lt;tt class="docutils literal">&lt;span class="pre">server-admins&lt;/span>&lt;/tt> group&lt;/td>
&lt;td>&lt;tt class="docutils literal">$ &lt;span class="pre">chef-server-ctl&lt;/span> &lt;span class="pre">list-server-admins&lt;/span>&lt;/tt>&lt;/td>
&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;p>Group administration is done via chef-web, chef-automate or the
&lt;a class="reference external" href="https://manage.chef.io/">hosted chef management interface&lt;/a>.&lt;/p>
&lt;/div>
&lt;div class="section" id="id14">
&lt;h3>Cookbooks&lt;/h3>
&lt;p>We have already covered &lt;a class="reference external" href="#cookbooks">what a Cookbook&lt;/a> are. In this section the Cookbook should be seen &lt;em>not&lt;/em> as the
development of the scenario itself but as one or more versions of a already developed scenario which each version should
be frozen. Meaning that if any changes should occur to a certain version of the cookbook, the cookbook is no longer on
that version. Although not mandatory, this practice makes it harder to break something that depends on a cookbook's
version.&lt;/p>
&lt;p>To manage cookbooks present on the Chef Server we can perform the following operations, using the
&lt;a class="reference external" href="https://docs.chef.io/knife_cookbook.html">knife&lt;/a> and &lt;a class="reference external" href="https://docs.chef.io/ctl_chef.html">chef&lt;/a> commands:&lt;/p>
&lt;table border="1" class="colwidths-given docutils">
&lt;colgroup>
&lt;col width="30%" />
&lt;col width="70%" />
&lt;/colgroup>
&lt;thead valign="bottom">
&lt;tr>&lt;th class="head">Description&lt;/th>
&lt;th class="head">Command&lt;/th>
&lt;/tr>
&lt;/thead>
&lt;tbody valign="top">
&lt;tr>&lt;td>Create a new cookbook&lt;/td>
&lt;td>&lt;tt class="docutils literal">$ chef generate cookbook COOKBOOK_PATH/COOKBOOK_NAME [&amp;lt;options&amp;gt;]&lt;/tt>&lt;/td>
&lt;/tr>
&lt;tr>&lt;td>Delete one or more cookbooks&lt;/td>
&lt;td>&lt;tt class="docutils literal">$ knife cookbook delete COOKBOOK_NAME [COOKBOOK_VERSION] [&amp;lt;options&amp;gt;]&lt;/tt>&lt;/td>
&lt;/tr>
&lt;tr>&lt;td>Delete one or more cookbook, using a regular expression&lt;/td>
&lt;td>&lt;tt class="docutils literal">$ knife cookbook bulk delete REGEX &lt;span class="pre">-p&lt;/span>&lt;/tt>&lt;/td>
&lt;/tr>
&lt;tr>&lt;td>Download a cookbook from the Chef Server to the current working directory&lt;/td>
&lt;td>&lt;tt class="docutils literal">$ knife cookbook download COOKBOOK_NAME [COOKBOOK_VERSION] [&amp;lt;options&amp;gt;]&lt;/tt>&lt;/td>
&lt;/tr>
&lt;tr>&lt;td>View a list of cookbooks currently available on the Chef Server&lt;/td>
&lt;td>&lt;tt class="docutils literal">$ knife cookbook list &lt;span class="pre">[--all]&lt;/span> &lt;span class="pre">[--with-uri]&lt;/span>&lt;/tt>&lt;/td>
&lt;/tr>
&lt;tr>&lt;td>Generate the metadata for one or more cookbooks.&lt;/td>
&lt;td>&lt;tt class="docutils literal">$ knife cookbook metadata [&amp;lt;options&amp;gt;]&lt;/tt>&lt;/td>
&lt;/tr>
&lt;tr>&lt;td>Load the metadata for a cookbook from a file.&lt;/td>
&lt;td>&lt;tt class="docutils literal">$ knife cookbook metadata from file FILE&lt;/tt>&lt;/td>
&lt;/tr>
&lt;tr>&lt;td>View information about a cookbook, parts of a cookbook, or a file associated with it&lt;/td>
&lt;td>&lt;tt class="docutils literal">$ knife cookbook show COOKBOOK_NAME [COOKBOOK_VERSION] &lt;span class="pre">[PART...]&lt;/span> [FILE_NAME] (options)&lt;/tt>&lt;/td>
&lt;/tr>
&lt;tr>&lt;td>Test a cookbook for syntax errors&lt;/td>
&lt;td>&lt;tt class="docutils literal">$ knife cookbook test COOKBOOK_NAME [&amp;lt;options&amp;gt;]&lt;/tt>&lt;/td>
&lt;/tr>
&lt;tr>&lt;td>Upload one or more cookbooks from a local repository to the Chef server&lt;/td>
&lt;td>&lt;tt class="docutils literal">$ knife cookbook upload &lt;span class="pre">[COOKBOOK_NAME...]&lt;/span> [&amp;lt;options&amp;gt;]&lt;/tt>&lt;/td>
&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;/div>
&lt;div class="section" id="nodes">
&lt;h3>Nodes&lt;/h3>
&lt;p>To manage nodes that exist on a Chef Server we either use the &lt;tt class="docutils literal">knife&lt;/tt>
&lt;a class="reference external" href="https://docs.chef.io/knife_node.html">command&lt;/a> or the
&lt;a class="reference external" href="https://docs.chef.io/server_manage_nodes.html">web management interface&lt;/a>.&lt;/p>
&lt;/div>
&lt;div class="section" id="run-lists">
&lt;h3>Run-lists&lt;/h3>
&lt;p>A run-list defines all of the information necessary for Chef to configure a node into the desired state. A run-list is
&lt;a class="footnote-reference" href="#id38" id="id15">[11]&lt;/a>:&lt;/p>
&lt;ul class="simple">
&lt;li>An ordered list of roles and/or recipes that are run in the exact order defined in the run-list; if a recipe appears
more than once in the run-list, the chef-client will not run it twice&lt;/li>
&lt;li>Always specific to the node on which it runs; nodes may have a run-list that is identical to the run-list used by
other nodes&lt;/li>
&lt;li>Stored as part of the node object on the Chef server&lt;/li>
&lt;li>Maintained using knife and then uploaded from the workstation to the Chef server, or maintained using Chef Automate&lt;/li>
&lt;/ul>
&lt;/div>
&lt;div class="section" id="policies">
&lt;h3>Policies&lt;/h3>
&lt;p>Policy maps business and operational requirements, process, and workflow to settings and objects stored on the Chef
server &lt;a class="footnote-reference" href="#id39" id="id16">[12]&lt;/a>:&lt;/p>
&lt;ul class="simple">
&lt;li>Roles define server types, such as “web server” or “database server”&lt;/li>
&lt;li>Environments define process, such as “dev”, “staging”, or “production”&lt;/li>
&lt;li>Certain types of data - passwords, user account data, and other sensitive items - can be placed in data bags, which
are located in a secure sub-area on the Chef server that can only be accessed by nodes that authenticate to the Chef
server with the correct SSL certificates&lt;/li>
&lt;li>The cookbooks (and cookbook versions) in which organization-specific configuration policies are maintained&lt;/li>
&lt;/ul>
&lt;/div>
&lt;div class="section" id="environments">
&lt;h3>Environments&lt;/h3>
&lt;p>An environment is a way to map an organization’s real-life workflow to what can be configured and managed when using
Chef server. Every organization begins with a single environment called the &lt;tt class="docutils literal">_default&lt;/tt> environment, which cannot be
modified (or deleted). Additional environments can be created to reflect each organization’s patterns and workflow. For
example, creating &lt;tt class="docutils literal">production&lt;/tt>, &lt;tt class="docutils literal">staging&lt;/tt>, &lt;tt class="docutils literal">testing&lt;/tt>, and &lt;tt class="docutils literal">development&lt;/tt> environments. Generally, an environment
is also associated with one (or more) cookbook versions. &lt;a class="footnote-reference" href="#id40" id="id17">[13]&lt;/a>&lt;/p>
&lt;/div>
&lt;div class="section" id="roles">
&lt;h3>Roles&lt;/h3>
&lt;p>A role is a way to define certain patterns and processes that exist across nodes in an organization as belonging to a
single job function. Each role consists of zero (or more) attributes and a run-list. Each node can have zero (or more)
roles assigned to it. When a role is run against a node, the configuration details of that node are compared against the
attributes of the role, and then the contents of that role’s run-list are applied to the node’s configuration details.
When a chef-client runs, it merges its own attributes and run-lists with those contained within each assigned
role. &lt;a class="footnote-reference" href="#id41" id="id18">[14]&lt;/a>&lt;/p>
&lt;/div>
&lt;div class="section" id="attributes">
&lt;h3>Attributes&lt;/h3>
&lt;p>&lt;a class="reference external" href="#attribute-values">Previously&lt;/a> we saw what are attributes and how to define them in a cookbook. However, attributes
can also be defined in:&lt;/p>
&lt;ul class="simple">
&lt;li>The state of the Node itself&lt;/li>
&lt;li>&lt;a class="reference internal" href="#roles">Roles&lt;/a>&lt;/li>
&lt;li>&lt;a class="reference internal" href="#environments">Environments&lt;/a>&lt;/li>
&lt;/ul>
&lt;p>We also saw which types of precedence exist, but taking this new information that Roles, Environments, and Node state
play a part on this we can view the attribute precedence as a table, as shown in {F#fig-chef-preced}:&lt;/p>
&lt;div class="figure align-center" id="fig-chef-preced">
&lt;a class="reference external image-reference" href="./img/overview_chef_attributes_table.png">&lt;img alt="Figure 2" src="./img/overview_chef_attributes_table.png" />&lt;/a>
&lt;p class="caption">Chef attributes overview&lt;/p>
&lt;/div>
&lt;p>For more information on attributes take a look at &lt;a class="footnote-reference" href="#id31" id="id19">[4]&lt;/a>.&lt;/p>
&lt;/div>
&lt;div class="section" id="data-bags">
&lt;h3>Data Bags&lt;/h3>
&lt;p>Data bags store global variables as JSON data. Data bags are indexed for searching and can be loaded by a cookbook or
accessed during a search. &lt;a class="footnote-reference" href="#id42" id="id20">[15]&lt;/a>&lt;/p>
&lt;p>We will usually use the &lt;tt class="docutils literal">knife&lt;/tt> tool to manage data bags, although creating data bags manually can be useful to test
cookbooks with &lt;a class="reference internal" href="#kitchen">Kitchen&lt;/a>.&lt;/p>
&lt;!-- knife_data_bags -->
&lt;table border="1" class="colwidths-given docutils">
&lt;caption>Manage data bags using &lt;tt class="docutils literal">knife&lt;/tt>&lt;/caption>
&lt;colgroup>
&lt;col width="30%" />
&lt;col width="70%" />
&lt;/colgroup>
&lt;thead valign="bottom">
&lt;tr>&lt;th class="head">Description&lt;/th>
&lt;th class="head">Command&lt;/th>
&lt;/tr>
&lt;/thead>
&lt;tbody valign="top">
&lt;tr>&lt;td>Create an empty data bag (or an item)&lt;/td>
&lt;td>&lt;tt class="docutils literal">$ knife data bag create DATA_BAG_NAME (DATA_BAG_ITEM)&lt;/tt>&lt;/td>
&lt;/tr>
&lt;tr>&lt;td>Create a data bag item a file&lt;/td>
&lt;td>&lt;tt class="docutils literal">$ knife data bag from file BAG_NAME ITEM_NAME.json&lt;/tt>&lt;/td>
&lt;/tr>
&lt;tr>&lt;td>Edit a data bag item&lt;/td>
&lt;td>&lt;tt class="docutils literal">$ knife data bag edit admins charlie&lt;/tt>&lt;/td>
&lt;/tr>
&lt;tr>&lt;td>List existing data bags&lt;/td>
&lt;td>&lt;tt class="docutils literal">$ knife data bag list&lt;/tt>&lt;/td>
&lt;/tr>
&lt;tr>&lt;td>List items from a data bag&lt;/td>
&lt;td>&lt;tt class="docutils literal">$ knife data bag show DATA_BAG_NAME&lt;/tt>&lt;/td>
&lt;/tr>
&lt;tr>&lt;td>Show contents of a data bag item&lt;/td>
&lt;td>&lt;tt class="docutils literal">$ knife data bag show DATA_BAG_NAME ITEM_NAME&lt;/tt>&lt;/td>
&lt;/tr>
&lt;tr>&lt;td>Search &lt;a class="reference external" href="https://docs.chef.io/data_bags.html#with-search">query&lt;/a> for data bag items&lt;/td>
&lt;td>&lt;tt class="docutils literal">$ knife search DATA_BAG_NAME QUERY_STRING&lt;/tt>&lt;/td>
&lt;/tr>
&lt;tr>&lt;td>Delete a data bag (or an item)&lt;/td>
&lt;td>&lt;tt class="docutils literal">$ knife data bag delete DATA_BAG_NAME (DATA_BAG_ITEM)&lt;/tt>&lt;/td>
&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;p>In order to use manually created data bags in our recipes we can create a directory named &lt;tt class="docutils literal">data_bags&lt;/tt> that follows a
simple directory/file structure, i.e:&lt;/p>
&lt;div class="highlight">&lt;pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">&lt;code class="language-fallback" data-lang="fallback">data_bags/
├── db_passwords
│   ├── mysqlroot.json
│   └── slurm.json
├── secrets
│   └── munge.json  
├── service_passwords
│   └── myservice.json
└── user_passwords
├── alice.json
└── bob.json&lt;/code>&lt;/pre>&lt;/div>
&lt;p>This way every subdirectory within the &lt;tt class="docutils literal">data_bags&lt;/tt> directory corresponds to a data bag, and every &lt;tt class="docutils literal">.json&lt;/tt> file
corresponds to an item to each of those data bags.&lt;/p>
&lt;p>Each data bag item should also follow a specific structure: &lt;a class="footnote-reference" href="#id42" id="id21">[15]&lt;/a>&lt;/p>
&lt;div class="highlight">&lt;div style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">
&lt;table style="border-spacing:0;padding:0;margin:0;border:0;width:auto;overflow:auto;display:block;">&lt;tr>&lt;td style="vertical-align:top;padding:0;margin:0;border:0;">
&lt;pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">&lt;code>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">1
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">2
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">3
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">4
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">5
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">6
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td style="vertical-align:top;padding:0;margin:0;border:0;;width:100%">
&lt;pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">&lt;code class="language-json" data-lang="json">{
&lt;span style="color:#960050;background-color:#1e0010">/*&lt;/span> &lt;span style="color:#960050;background-color:#1e0010">This&lt;/span> &lt;span style="color:#960050;background-color:#1e0010">is&lt;/span> &lt;span style="color:#960050;background-color:#1e0010">a&lt;/span> &lt;span style="color:#960050;background-color:#1e0010">supported&lt;/span> &lt;span style="color:#960050;background-color:#1e0010">comment&lt;/span> &lt;span style="color:#960050;background-color:#1e0010">style&lt;/span> &lt;span style="color:#960050;background-color:#1e0010">*/&lt;/span>
&lt;span style="color:#960050;background-color:#1e0010">//&lt;/span> &lt;span style="color:#960050;background-color:#1e0010">This&lt;/span> &lt;span style="color:#960050;background-color:#1e0010">style&lt;/span> &lt;span style="color:#960050;background-color:#1e0010">is&lt;/span> &lt;span style="color:#960050;background-color:#1e0010">also&lt;/span> &lt;span style="color:#960050;background-color:#1e0010">supported&lt;/span>
&lt;span style="color:#f92672">&amp;#34;id&amp;#34;&lt;/span>: &lt;span style="color:#e6db74">&amp;#34;ITEM_NAME&amp;#34;&lt;/span>,
&lt;span style="color:#f92672">&amp;#34;key&amp;#34;&lt;/span>: &lt;span style="color:#e6db74">&amp;#34;value&amp;#34;&lt;/span>
}&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>
&lt;div class="section" id="encrypted-data-bags">
&lt;h4>Encrypted Data Bags&lt;/h4>
&lt;p>A data bag item may be encrypted using shared secret encryption. This allows each data bag item to store confidential
information (such as a database password) or to be managed in a source control system (without plain-text data appearing
in revision history). Each data bag item may be encrypted individually; if a data bag contains multiple encrypted data
bag items, these data bag items are not required to share the same encryption keys. &lt;a class="footnote-reference" href="#id42" id="id22">[15]&lt;/a>&lt;/p>
&lt;p>the &lt;tt class="docutils literal">knife&lt;/tt> command can encrypt and decrypt data bag items, using the &lt;tt class="docutils literal">create&lt;/tt>, &lt;tt class="docutils literal">edit&lt;/tt>, &lt;tt class="docutils literal">from file&lt;/tt> or &lt;tt class="docutils literal">show&lt;/tt>
&lt;a class="reference external" href="#data-bags">arguments&lt;/a> and the options:&lt;/p>
&lt;table border="1" class="colwidths-given docutils">
&lt;colgroup>
&lt;col width="30%" />
&lt;col width="70%" />
&lt;/colgroup>
&lt;thead valign="bottom">
&lt;tr>&lt;th class="head">Option&lt;/th>
&lt;th class="head">Description&lt;/th>
&lt;/tr>
&lt;/thead>
&lt;tbody valign="top">
&lt;tr>&lt;td>&lt;tt class="docutils literal">&lt;span class="pre">--secret&lt;/span> SECRET&lt;/tt>&lt;/td>
&lt;td>The encryption key that is used for values contained within a data bag item. If &lt;tt class="docutils literal">SECRET&lt;/tt> is not specified, the
chef-client looks for a secret at the path specified by the &lt;tt class="docutils literal">encrypted_data_bag_secret&lt;/tt> setting in the
client.rb file&lt;/td>
&lt;/tr>
&lt;tr>&lt;td>&lt;tt class="docutils literal">&lt;span class="pre">--secret-file&lt;/span> FILE&lt;/tt>&lt;/td>
&lt;td>the path to the file that contains the encryption key&lt;/td>
&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;p>To create the secret key, one can use OpenSSL to generate a random number to be used as a secret key:&lt;/p>
&lt;div class="highlight">&lt;pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">&lt;code class="language-fallback" data-lang="fallback">$ openssl rand -base64 512 | tr -d &amp;#39;\r\n&amp;#39; &amp;gt; encrypted_data_bag_secret&lt;/code>&lt;/pre>&lt;/div>
&lt;/div>
&lt;/div>
&lt;/div>
&lt;div class="section" id="chef-node">
&lt;h2>Chef Node&lt;/h2>
&lt;p>A node is any machine - physical, virtual, cloud, network device, etc. - that is under management by Chef.&lt;/p>
&lt;p>The types of nodes that can be managed by Chef include, but are not limited to, the following: &lt;a class="footnote-reference" href="#id43" id="id23">[16]&lt;/a>&lt;/p>
&lt;table border="1" class="colwidths-given docutils">
&lt;colgroup>
&lt;col width="20%" />
&lt;col width="80%" />
&lt;/colgroup>
&lt;tbody valign="top">
&lt;tr>&lt;td>Node Type&lt;/td>
&lt;td>Description&lt;/td>
&lt;/tr>
&lt;tr>&lt;td>Server&lt;/td>
&lt;td>A physical node is any active device attached to a network that can run a chef-client and allows that
chef-client communicates with a Chef server.&lt;/td>
&lt;/tr>
&lt;tr>&lt;td>Cloud&lt;/td>
&lt;td>A cloud-based node is hosted in an external cloud-based service, such as Amazon Web Services (AWS), OpenStack,
Rackspace, Google Compute Engine, or Microsoft Azure. Plugins are available for knife that provide support for
external cloud-based services.&lt;/td>
&lt;/tr>
&lt;tr>&lt;td>Virtual Machine&lt;/td>
&lt;td>A virtual node is a machine that runs only as a software implementation, but otherwise behaves much like a
physical machine.&lt;/td>
&lt;/tr>
&lt;tr>&lt;td>Network Device&lt;/td>
&lt;td>A network node is any networking device that is being managed by a chef-client, such as networking devices by
Juniper Networks, Arista, Cisco, and F5.&lt;/td>
&lt;/tr>
&lt;tr>&lt;td>Container&lt;/td>
&lt;td>Containers are an approach to virtualization that allows a single operating system to host many working
configurations, where each working configuration - a container - is assigned a single responsibility that is
isolated from all other responsibilities.&lt;/td>
&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;p>The key components, which gather other components, of nodes are the &lt;a class="reference internal" href="#chef-client">Chef Client&lt;/a> and &lt;a class="reference internal" href="#ohai">Ohai&lt;/a>.&lt;/p>
&lt;div class="section" id="chef-client">
&lt;h3>Chef Client&lt;/h3>
&lt;p>A chef-client is an agent that runs locally on every node. When a chef-client is run, it will perform all the steps that
are required to bring the node into an expected state, including: &lt;a class="footnote-reference" href="#id43" id="id24">[16]&lt;/a>&lt;/p>
&lt;ul class="simple">
&lt;li>Registering and authenticating the node with the Chef server&lt;/li>
&lt;li>Building the node object&lt;/li>
&lt;li>Synchronizing cookbooks&lt;/li>
&lt;li>Compiling the resource collection by loading each of the required cookbooks, including recipes, attributes, and all
other dependencies&lt;/li>
&lt;li>Taking the appropriate and required actions to configure the node&lt;/li>
&lt;li>Looking for exceptions and notifications, handling each as required&lt;/li>
&lt;/ul>
&lt;/div>
&lt;div class="section" id="ohai">
&lt;h3>Ohai&lt;/h3>
&lt;p>Ohai is a tool that is used to collect system configuration data and is run at the beginning of every Chef run to
determine the system's state.&lt;/p>
&lt;p>The types of attributes Ohai collects include but are not limited to:&lt;/p>
&lt;ul class="simple">
&lt;li>Operating System&lt;/li>
&lt;li>Network&lt;/li>
&lt;li>Memory&lt;/li>
&lt;li>Disk&lt;/li>
&lt;li>CPU&lt;/li>
&lt;li>Kernel&lt;/li>
&lt;li>Host names&lt;/li>
&lt;li>Fully qualified domain names&lt;/li>
&lt;li>Virtualization&lt;/li>
&lt;li>Cloud provider metadata&lt;/li>
&lt;/ul>
&lt;p>Attributes that are collected by Ohai are automatic level attributes, in that these attributes are used by the
chef-client to ensure that these attributes remain unchanged after the chef-client is done configuring the node. &lt;a class="footnote-reference" href="#id43" id="id25">[16]&lt;/a>&lt;/p>
&lt;/div>
&lt;/div>
&lt;div class="section" id="chef-supermarket">
&lt;h2>Chef Supermarket&lt;/h2>
&lt;p>Chef Supermarket is the site for community cookbooks. It provides an easily searchable cookbook repository and a
friendly web UI. Cookbooks that are part of the Chef Supermarket are accessible by any Chef user.&lt;/p>
&lt;p>There are two ways to use Chef Supermarket:&lt;/p>
&lt;ul class="simple">
&lt;li>The public Chef Supermarket is hosted by Chef and is located at &lt;a class="reference external" href="https://supermarket.chef.io/">Chef Supermarket&lt;/a>&lt;/li>
&lt;li>A private Chef Supermarket may be installed on-premise behind the firewall on the internal network. Cookbook retrieval
from a private Chef Supermarket is often faster than from the public Chef Supermarket because of closer proximity and
fewer cookbooks to resolve. A private Chef Supermarket can also help formalize internal cookbook release management
processes (e.g. “a cookbook is not released until it’s published on the private Chef Supermarket”). &lt;a class="footnote-reference" href="#id44" id="id26">[17]&lt;/a>&lt;/li>
&lt;/ul>
&lt;div class="section" id="berkshelf">
&lt;h3>Berkshelf&lt;/h3>
&lt;p>Berkshelf can include multiple Chef Supermarket instances for dependency resolution. Cookbook dependency resolution is
performed from the top down. The first source defined in the Berksfile will be searched for the cookbook before the
second source.&lt;/p>
&lt;p>The Berksfile first looks for the cookbook on the private Chef Supermarket and, if not discovered there, then looks on
the public Chef Supermarket. &lt;a class="footnote-reference" href="#id44" id="id27">[17]&lt;/a>&lt;/p>
&lt;div class="highlight">&lt;div style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">
&lt;table style="border-spacing:0;padding:0;margin:0;border:0;width:auto;overflow:auto;display:block;">&lt;tr>&lt;td style="vertical-align:top;padding:0;margin:0;border:0;">
&lt;pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">&lt;code>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">1
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">2
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td style="vertical-align:top;padding:0;margin:0;border:0;;width:100%">
&lt;pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">&lt;code class="language-ruby" data-lang="ruby">source &lt;span style="color:#e6db74">&amp;#39;https://your_private_supermarket_url&amp;#39;&lt;/span>
source &lt;span style="color:#e6db74">&amp;#39;https://supermarket.chef.io&amp;#39;&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>
&lt;/div>
&lt;/div>
&lt;div class="section" id="conclusion">
&lt;h2>Conclusion&lt;/h2>
&lt;p>In this post we have seen the architecture of the Chef configuration management tool, went through each component, and
saw how each component can interact with on another. This was not intended as a first touch tutorial on Chef, but more
as a summary and helpful reference for Chef's components and interactions.&lt;/p>
&lt;p>Check Chef.io &lt;a class="reference external" href="https://learn.chef.io/">learn site&lt;/a> for well-structured and paced tutorials.&lt;/p>
&lt;/div>
&lt;div class="section" id="references">
&lt;h2>References&lt;/h2>
&lt;table class="docutils footnote" frame="void" id="id28" rules="none">
&lt;colgroup>&lt;col class="label" />&lt;col />&lt;/colgroup>
&lt;tbody valign="top">
&lt;tr>&lt;td class="label">&lt;a class="fn-backref" href="#id1">[1]&lt;/a>&lt;/td>&lt;td>--, About Knife - Chef.io Documentation, 2018. &lt;a class="reference external" href="https://docs.chef.io/knife.html">[link]&lt;/a>&lt;/td>&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;table class="docutils footnote" frame="void" id="id29" rules="none">
&lt;colgroup>&lt;col class="label" />&lt;col />&lt;/colgroup>
&lt;tbody valign="top">
&lt;tr>&lt;td class="label">&lt;a class="fn-backref" href="#id2">[2]&lt;/a>&lt;/td>&lt;td>--, About Cookbooks - Chef.io Documentation, 2018. &lt;a class="reference external" href="https://docs.chef.io/cookbooks.html">[link]&lt;/a>&lt;/td>&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;table class="docutils footnote" frame="void" id="id30" rules="none">
&lt;colgroup>&lt;col class="label" />&lt;col />&lt;/colgroup>
&lt;tbody valign="top">
&lt;tr>&lt;td class="label">&lt;a class="fn-backref" href="#id3">[3]&lt;/a>&lt;/td>&lt;td>--, About Recipes - Chef.io Documentation, 2018. &lt;a class="reference external" href="https://docs.chef.io/recipes.html">[link]&lt;/a>&lt;/td>&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;table class="docutils footnote" frame="void" id="id31" rules="none">
&lt;colgroup>&lt;col class="label" />&lt;col />&lt;/colgroup>
&lt;tbody valign="top">
&lt;tr>&lt;td class="label">[4]&lt;/td>&lt;td>&lt;em>(&lt;a class="fn-backref" href="#id4">1&lt;/a>, &lt;a class="fn-backref" href="#id19">2&lt;/a>)&lt;/em> --, About Attributes - Chef.io Documentation, 2018. &lt;a class="reference external" href="https://docs.chef.io/attributes.html">[link]&lt;/a>&lt;/td>&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;table class="docutils footnote" frame="void" id="id32" rules="none">
&lt;colgroup>&lt;col class="label" />&lt;col />&lt;/colgroup>
&lt;tbody valign="top">
&lt;tr>&lt;td class="label">&lt;a class="fn-backref" href="#id5">[5]&lt;/a>&lt;/td>&lt;td>--, About Files - Chef.io Documentation, 2018. &lt;a class="reference external" href="https://docs.chef.io/files.html">[link]&lt;/a>&lt;/td>&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;table class="docutils footnote" frame="void" id="id33" rules="none">
&lt;colgroup>&lt;col class="label" />&lt;col />&lt;/colgroup>
&lt;tbody valign="top">
&lt;tr>&lt;td class="label">&lt;a class="fn-backref" href="#id6">[6]&lt;/a>&lt;/td>&lt;td>--, Cookbook Directories and Metadata - Chef.io Documentation, 2018. &lt;a class="reference external" href="https://docs.chef.io/cookbook_repo.html">[link]&lt;/a>&lt;/td>&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;table class="docutils footnote" frame="void" id="id34" rules="none">
&lt;colgroup>&lt;col class="label" />&lt;col />&lt;/colgroup>
&lt;tbody valign="top">
&lt;tr>&lt;td class="label">&lt;a class="fn-backref" href="#id7">[7]&lt;/a>&lt;/td>&lt;td>Ian F. Darwin, Checking C programs with lint, O’Reilly &amp;amp; Associates, 1990, ISBN: 9780937175309,0937175307&lt;/td>&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;table class="docutils footnote" frame="void" id="id35" rules="none">
&lt;colgroup>&lt;col class="label" />&lt;col />&lt;/colgroup>
&lt;tbody valign="top">
&lt;tr>&lt;td class="label">&lt;a class="fn-backref" href="#id8">[8]&lt;/a>&lt;/td>&lt;td>--, ChefSpec Documentation,`[link] &amp;lt;&lt;a class="reference external" href="https://docs.chef.io/chefspec.html">https://docs.chef.io/chefspec.html&lt;/a>&amp;gt;`__&lt;/td>&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;table class="docutils footnote" frame="void" id="id36" rules="none">
&lt;colgroup>&lt;col class="label" />&lt;col />&lt;/colgroup>
&lt;tbody valign="top">
&lt;tr>&lt;td class="label">&lt;a class="fn-backref" href="#id9">[9]&lt;/a>&lt;/td>&lt;td>--, InSpec Documentation, &lt;a class="reference external" href="https://www.inspec.io/docs/">[link]&lt;/a>&lt;/td>&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;table class="docutils footnote" frame="void" id="id37" rules="none">
&lt;colgroup>&lt;col class="label" />&lt;col />&lt;/colgroup>
&lt;tbody valign="top">
&lt;tr>&lt;td class="label">[10]&lt;/td>&lt;td>&lt;em>(&lt;a class="fn-backref" href="#id10">1&lt;/a>, &lt;a class="fn-backref" href="#id11">2&lt;/a>, &lt;a class="fn-backref" href="#id12">3&lt;/a>, &lt;a class="fn-backref" href="#id13">4&lt;/a>)&lt;/em> --, Organizations and Groups - Chef.io Documentation, 2018 &lt;a class="reference external" href="https://docs.chef.io/server_orgs.html">[link]&lt;/a>&lt;/td>&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;table class="docutils footnote" frame="void" id="id38" rules="none">
&lt;colgroup>&lt;col class="label" />&lt;col />&lt;/colgroup>
&lt;tbody valign="top">
&lt;tr>&lt;td class="label">&lt;a class="fn-backref" href="#id15">[11]&lt;/a>&lt;/td>&lt;td>--, About Run-lists - Chef.io Documentation, 2018 &lt;a class="reference external" href="https://docs.chef.io/run_lists.html">[link]&lt;/a>&lt;/td>&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;table class="docutils footnote" frame="void" id="id39" rules="none">
&lt;colgroup>&lt;col class="label" />&lt;col />&lt;/colgroup>
&lt;tbody valign="top">
&lt;tr>&lt;td class="label">&lt;a class="fn-backref" href="#id16">[12]&lt;/a>&lt;/td>&lt;td>--, About Policy - Chef.io Documentation, 2018 &lt;a class="reference external" href="https://docs.chef.io/policy.html">[link]&lt;/a>&lt;/td>&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;table class="docutils footnote" frame="void" id="id40" rules="none">
&lt;colgroup>&lt;col class="label" />&lt;col />&lt;/colgroup>
&lt;tbody valign="top">
&lt;tr>&lt;td class="label">&lt;a class="fn-backref" href="#id17">[13]&lt;/a>&lt;/td>&lt;td>--, About Environments - Chef.io Documentation, 2018 &lt;a class="reference external" href="https://docs.chef.io/environments.html">[link]&lt;/a>&lt;/td>&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;table class="docutils footnote" frame="void" id="id41" rules="none">
&lt;colgroup>&lt;col class="label" />&lt;col />&lt;/colgroup>
&lt;tbody valign="top">
&lt;tr>&lt;td class="label">&lt;a class="fn-backref" href="#id18">[14]&lt;/a>&lt;/td>&lt;td>--, About Roles - Chef.io Documentation, 2018 &lt;a class="reference external" href="https://docs.chef.io/roles.html">[link]&lt;/a>&lt;/td>&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;table class="docutils footnote" frame="void" id="id42" rules="none">
&lt;colgroup>&lt;col class="label" />&lt;col />&lt;/colgroup>
&lt;tbody valign="top">
&lt;tr>&lt;td class="label">[15]&lt;/td>&lt;td>&lt;em>(&lt;a class="fn-backref" href="#id20">1&lt;/a>, &lt;a class="fn-backref" href="#id21">2&lt;/a>, &lt;a class="fn-backref" href="#id22">3&lt;/a>)&lt;/em> --, About Data Bags - Chef.io Documentation, 2018 &lt;a class="reference external" href="https://docs.chef.io/data_bags.html">[link]&lt;/a>&lt;/td>&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;table class="docutils footnote" frame="void" id="id43" rules="none">
&lt;colgroup>&lt;col class="label" />&lt;col />&lt;/colgroup>
&lt;tbody valign="top">
&lt;tr>&lt;td class="label">[16]&lt;/td>&lt;td>&lt;em>(&lt;a class="fn-backref" href="#id23">1&lt;/a>, &lt;a class="fn-backref" href="#id24">2&lt;/a>, &lt;a class="fn-backref" href="#id25">3&lt;/a>)&lt;/em> --, About Nodes - Chef.io Documentation, 2018 &lt;a class="reference external" href="https://docs.chef.io/nodes.html">[link]&lt;/a>&lt;/td>&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;table class="docutils footnote" frame="void" id="id44" rules="none">
&lt;colgroup>&lt;col class="label" />&lt;col />&lt;/colgroup>
&lt;tbody valign="top">
&lt;tr>&lt;td class="label">[17]&lt;/td>&lt;td>&lt;em>(&lt;a class="fn-backref" href="#id26">1&lt;/a>, &lt;a class="fn-backref" href="#id27">2&lt;/a>)&lt;/em> --, Chef Supermarket - Chef.io Documentation, 2018 &lt;a class="reference external" href="https://docs.chef.io/supermarket.html">[link]&lt;/a>&lt;/td>&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;/div>
&lt;/div></content><category>System Administration</category><category>linux</category><category>devops</category><category>chef</category><category>configuration management</category><category>ruby</category></item><item><title>An Advanced Guide to Salt</title><link>https://implement.pt/2019/01/an-advanced-guide-to-salt/</link><pubDate>Wed, 30 Jan 2019 13:37:00 +0100</pubDate><guid>https://implement.pt/2019/01/an-advanced-guide-to-salt/</guid><summary type="html">&lt;div class="document">
&lt;p>A more advanced usage guide of salt by Saltstack - custom execution and state modules&lt;/p>
&lt;/div></summary><content type="html">&lt;div class="document">
&lt;div class="section" id="introduction">
&lt;h2>Introduction&lt;/h2>
&lt;p>In this post we will be talking about more advanced scenarios using Salt, specifically how to write your own execution
and state modules.&lt;/p>
&lt;p>Like we saw &lt;a class="reference external" href="https://implement.pt/2018/10/a-comprehensive-introduction-to-salt/">before&lt;/a>, we configure all of the salt subsystem in
files within &lt;em>/srv/salt/&lt;/em>.&lt;/p>
&lt;p>The location for our custom execution and state modules is in the same place we place our SLS files, however, within two
specific directories: _modules and _states, for execution and state modules, respectively.&lt;/p>
&lt;/div>
&lt;div class="section" id="execution-module">
&lt;h2>Execution Module&lt;/h2>
&lt;p>Let us take the example of creating a custom execution module that can manage users in a system, in a way that we can:&lt;/p>
&lt;ul class="simple">
&lt;li>see which home directories have no users associated with&lt;/li>
&lt;li>disable a user from login&lt;/li>
&lt;li>enable a user to login&lt;/li>
&lt;li>force a user to change its password&lt;/li>
&lt;/ul>
&lt;p>Let us call the module &lt;cite>myuser&lt;/cite>, so it does not clash with any existing module name. For info on all of salt's existing
modules check &lt;a class="reference external" href="https://docs.saltstack.com/en/latest/ref/modules/all/index.html">this list&lt;/a>.&lt;/p>
&lt;p>First we create the module file in the proper location:&lt;/p>
&lt;div class="highlight">&lt;pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">&lt;code class="language-fallback" data-lang="fallback">root@master:~# tree /srv/salt
/srv/salt
└── states
└── base
   │  ...
├── _modules
│   └── myuser.py
...&lt;/code>&lt;/pre>&lt;/div>
&lt;p>Now we start writing our module. The first thing we should do, after imports, is tell the salt system the proper module
name to use:&lt;/p>
&lt;div class="highlight">&lt;div style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">
&lt;table style="border-spacing:0;padding:0;margin:0;border:0;width:auto;overflow:auto;display:block;">&lt;tr>&lt;td style="vertical-align:top;padding:0;margin:0;border:0;">
&lt;pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">&lt;code>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">1
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">2
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">3
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">4
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">5
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">6
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td style="vertical-align:top;padding:0;margin:0;border:0;;width:100%">
&lt;pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">&lt;code class="language-python" data-lang="python">&lt;span style="color:#e6db74">&amp;#39;&amp;#39;&amp;#39;
&lt;/span>&lt;span style="color:#e6db74">set the module name
&lt;/span>&lt;span style="color:#e6db74">&amp;#39;&amp;#39;&amp;#39;&lt;/span>
__virtualname__ &lt;span style="color:#f92672">=&lt;/span> &lt;span style="color:#e6db74">&amp;#39;myuser&amp;#39;&lt;/span>
&lt;span style="color:#66d9ef">def&lt;/span> &lt;span style="color:#a6e22e">__virtual__&lt;/span>():
&lt;span style="color:#66d9ef">return&lt;/span> __virtualname__&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>
&lt;p>Now we can implement our first function, let us call it &lt;cite>get_orphan_home_dirs&lt;/cite>:&lt;/p>
&lt;div class="highlight">&lt;div style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">
&lt;table style="border-spacing:0;padding:0;margin:0;border:0;width:auto;overflow:auto;display:block;">&lt;tr>&lt;td style="vertical-align:top;padding:0;margin:0;border:0;">
&lt;pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">&lt;code>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 1
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 2
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 3
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 4
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 5
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 6
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 7
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 8
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 9
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">10
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">11
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">12
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">13
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">14
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">15
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">16
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">17
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">18
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">19
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">20
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td style="vertical-align:top;padding:0;margin:0;border:0;;width:100%">
&lt;pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">&lt;code class="language-python" data-lang="python">&lt;span style="color:#e6db74">&amp;#39;&amp;#39;&amp;#39;
&lt;/span>&lt;span style="color:#e6db74">returns a directory list of directories, which the users that they belong to no longer exist
&lt;/span>&lt;span style="color:#e6db74">
&lt;/span>&lt;span style="color:#e6db74">CLI Example::
&lt;/span>&lt;span style="color:#e6db74">
&lt;/span>&lt;span style="color:#e6db74"> salt &amp;#39;*&amp;#39; myuser.get_orphan_home_dirs
&lt;/span>&lt;span style="color:#e6db74">&amp;#39;&amp;#39;&amp;#39;&lt;/span>
&lt;span style="color:#66d9ef">def&lt;/span> &lt;span style="color:#a6e22e">get_orphan_home_dirs&lt;/span>():
ret &lt;span style="color:#f92672">=&lt;/span> dict()
root&lt;span style="color:#f92672">=&lt;/span>&lt;span style="color:#e6db74">&amp;#39;/home&amp;#39;&lt;/span>
dirs &lt;span style="color:#f92672">=&lt;/span> os&lt;span style="color:#f92672">.&lt;/span>listdir(root)
&lt;span style="color:#66d9ef">for&lt;/span> dir_ &lt;span style="color:#f92672">in&lt;/span> [d &lt;span style="color:#66d9ef">for&lt;/span> d &lt;span style="color:#f92672">in&lt;/span> dirs &lt;span style="color:#66d9ef">if&lt;/span> os&lt;span style="color:#f92672">.&lt;/span>path&lt;span style="color:#f92672">.&lt;/span>isdir(os&lt;span style="color:#f92672">.&lt;/span>path&lt;span style="color:#f92672">.&lt;/span>join(root, d))]:
found &lt;span style="color:#f92672">=&lt;/span> False
&lt;span style="color:#66d9ef">for&lt;/span> p &lt;span style="color:#f92672">in&lt;/span> [u &lt;span style="color:#66d9ef">for&lt;/span> u &lt;span style="color:#f92672">in&lt;/span> pwd&lt;span style="color:#f92672">.&lt;/span>getpwall() &lt;span style="color:#66d9ef">if&lt;/span> (u&lt;span style="color:#f92672">.&lt;/span>pw_uid &lt;span style="color:#f92672">&amp;gt;=&lt;/span> &lt;span style="color:#ae81ff">1000&lt;/span>)]: &lt;span style="color:#75715e"># here we skip system users&lt;/span>
&lt;span style="color:#66d9ef">if&lt;/span> p&lt;span style="color:#f92672">.&lt;/span>pw_dir &lt;span style="color:#f92672">==&lt;/span> os&lt;span style="color:#f92672">.&lt;/span>path&lt;span style="color:#f92672">.&lt;/span>join(root, dir_):
found &lt;span style="color:#f92672">=&lt;/span> True
&lt;span style="color:#66d9ef">break&lt;/span>
&lt;span style="color:#66d9ef">if&lt;/span> &lt;span style="color:#f92672">not&lt;/span> found: &lt;span style="color:#75715e"># we got a culprit&lt;/span>
ret[root&lt;span style="color:#f92672">+&lt;/span>dir_] &lt;span style="color:#f92672">=&lt;/span> &lt;span style="color:#e6db74">&amp;#39;User {} Not Found!&amp;#39;&lt;/span>&lt;span style="color:#f92672">.&lt;/span>format(dir_)
&lt;span style="color:#66d9ef">return&lt;/span> ret&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>
&lt;p>Before we can test this execution module we need to update the cache on the minion:&lt;/p>
&lt;div class="highlight">&lt;pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">&lt;code class="language-fallback" data-lang="fallback">root@master:~$ salt &amp;#39;minion-1&amp;#39; saltutil.sync_modules
minion-1:
----------
- modules.myuser
root@master:~$&lt;/code>&lt;/pre>&lt;/div>
&lt;p>Now we can test it on the salt-master:&lt;/p>
&lt;div class="highlight">&lt;pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">&lt;code class="language-fallback" data-lang="fallback">root@master:~$ salt &amp;#39;minion-1&amp;#39; myuser.get_orphan_home_dirs
minion-1:
----------
/home/does-not-exist:
User does-not-exist Not Found!
root@master:~$&lt;/code>&lt;/pre>&lt;/div>
&lt;p>And everything looks good, so we can move on to the next functions, disable/enable user accounts:&lt;/p>
&lt;div class="highlight">&lt;div style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">
&lt;table style="border-spacing:0;padding:0;margin:0;border:0;width:auto;overflow:auto;display:block;">&lt;tr>&lt;td style="vertical-align:top;padding:0;margin:0;border:0;">
&lt;pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">&lt;code>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 1
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 2
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 3
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 4
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 5
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 6
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 7
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 8
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 9
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">10
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">11
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">12
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">13
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">14
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">15
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">16
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">17
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">18
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">19
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">20
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">21
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">22
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">23
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">24
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">25
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">26
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">27
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">28
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">29
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">30
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">31
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">32
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">33
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">34
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">35
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">36
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">37
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">38
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">39
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">40
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">41
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">42
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">43
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">44
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">45
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">46
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">47
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">48
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">49
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">50
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">51
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">52
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td style="vertical-align:top;padding:0;margin:0;border:0;;width:100%">
&lt;pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">&lt;code class="language-python" data-lang="python">&lt;span style="color:#e6db74">&amp;#39;&amp;#39;&amp;#39;
&lt;/span>&lt;span style="color:#e6db74">disables a user from being able to login to the minion
&lt;/span>&lt;span style="color:#e6db74">
&lt;/span>&lt;span style="color:#e6db74">CLI Example::
&lt;/span>&lt;span style="color:#e6db74">
&lt;/span>&lt;span style="color:#e6db74"> salt &amp;#39;*&amp;#39; myuser.disable_login username
&lt;/span>&lt;span style="color:#e6db74">&amp;#39;&amp;#39;&amp;#39;&lt;/span>
&lt;span style="color:#66d9ef">def&lt;/span> &lt;span style="color:#a6e22e">disable_login&lt;/span>(name):
ret &lt;span style="color:#f92672">=&lt;/span> dict()
minion_id &lt;span style="color:#f92672">=&lt;/span> __salt__[&lt;span style="color:#e6db74">&amp;#39;grains.get&amp;#39;&lt;/span>](&lt;span style="color:#e6db74">&amp;#39;id&amp;#39;&lt;/span>)
&lt;span style="color:#66d9ef">try&lt;/span>:
pwd&lt;span style="color:#f92672">.&lt;/span>getpwnam(name)
__salt__[&lt;span style="color:#e6db74">&amp;#39;shadow.set_expire&amp;#39;&lt;/span>](name, &lt;span style="color:#ae81ff">0&lt;/span>)
ret[&lt;span style="color:#e6db74">&amp;#39;result&amp;#39;&lt;/span>] &lt;span style="color:#f92672">=&lt;/span> True
ret[&lt;span style="color:#e6db74">&amp;#39;comment&amp;#39;&lt;/span>] &lt;span style="color:#f92672">=&lt;/span> None
ret[&lt;span style="color:#e6db74">&amp;#39;changes&amp;#39;&lt;/span>] &lt;span style="color:#f92672">=&lt;/span> {&lt;span style="color:#e6db74">&amp;#39;disable_login&amp;#39;&lt;/span>: {&lt;span style="color:#e6db74">&amp;#39;old&amp;#39;&lt;/span>: &lt;span style="color:#e6db74">&amp;#39;&amp;#39;&lt;/span>,
&lt;span style="color:#e6db74">&amp;#39;new&amp;#39;&lt;/span>: name &lt;span style="color:#f92672">+&lt;/span> &lt;span style="color:#e6db74">&amp;#39;@&amp;#39;&lt;/span> &lt;span style="color:#f92672">+&lt;/span> minion_id}}
&lt;span style="color:#66d9ef">except&lt;/span> &lt;span style="color:#a6e22e">KeyError&lt;/span>:
ret[&lt;span style="color:#e6db74">&amp;#39;result&amp;#39;&lt;/span>] &lt;span style="color:#f92672">=&lt;/span> True
ret[&lt;span style="color:#e6db74">&amp;#39;changes&amp;#39;&lt;/span>] &lt;span style="color:#f92672">=&lt;/span> dict()
ret[&lt;span style="color:#e6db74">&amp;#39;comment&amp;#39;&lt;/span>] &lt;span style="color:#f92672">=&lt;/span> &lt;span style="color:#e6db74">&amp;#39;User not Present &amp;#39;&lt;/span> &lt;span style="color:#f92672">+&lt;/span> name &lt;span style="color:#f92672">+&lt;/span> &lt;span style="color:#e6db74">&amp;#39; @ &amp;#39;&lt;/span> &lt;span style="color:#f92672">+&lt;/span> minion_id
&lt;span style="color:#66d9ef">return&lt;/span> ret
&lt;span style="color:#e6db74">&amp;#39;&amp;#39;&amp;#39;
&lt;/span>&lt;span style="color:#e6db74">enables a user to login to the minion
&lt;/span>&lt;span style="color:#e6db74">
&lt;/span>&lt;span style="color:#e6db74">CLI Example::
&lt;/span>&lt;span style="color:#e6db74">
&lt;/span>&lt;span style="color:#e6db74"> salt &amp;#39;*&amp;#39; myuser.enable_login username
&lt;/span>&lt;span style="color:#e6db74">&amp;#39;&amp;#39;&amp;#39;&lt;/span>
&lt;span style="color:#66d9ef">def&lt;/span> &lt;span style="color:#a6e22e">enable_login&lt;/span>(name):
ret &lt;span style="color:#f92672">=&lt;/span> dict()
minion_id &lt;span style="color:#f92672">=&lt;/span> __salt__[&lt;span style="color:#e6db74">&amp;#39;grains.get&amp;#39;&lt;/span>](&lt;span style="color:#e6db74">&amp;#39;id&amp;#39;&lt;/span>)
&lt;span style="color:#66d9ef">try&lt;/span>:
pwd&lt;span style="color:#f92672">.&lt;/span>getpwnam(name)
__salt__[&lt;span style="color:#e6db74">&amp;#39;shadow.set_expire&amp;#39;&lt;/span>](name, &lt;span style="color:#f92672">-&lt;/span>&lt;span style="color:#ae81ff">1&lt;/span>)
ret[&lt;span style="color:#e6db74">&amp;#39;result&amp;#39;&lt;/span>] &lt;span style="color:#f92672">=&lt;/span> True
ret[&lt;span style="color:#e6db74">&amp;#39;comment&amp;#39;&lt;/span>] &lt;span style="color:#f92672">=&lt;/span> None
ret[&lt;span style="color:#e6db74">&amp;#39;changes&amp;#39;&lt;/span>] &lt;span style="color:#f92672">=&lt;/span> {&lt;span style="color:#e6db74">&amp;#39;enable_login&amp;#39;&lt;/span>: {&lt;span style="color:#e6db74">&amp;#39;old&amp;#39;&lt;/span>: &lt;span style="color:#e6db74">&amp;#39;&amp;#39;&lt;/span>,
&lt;span style="color:#e6db74">&amp;#39;new&amp;#39;&lt;/span>: name &lt;span style="color:#f92672">+&lt;/span> &lt;span style="color:#e6db74">&amp;#39;@&amp;#39;&lt;/span> &lt;span style="color:#f92672">+&lt;/span> minion_id}}
&lt;span style="color:#66d9ef">except&lt;/span> &lt;span style="color:#a6e22e">KeyError&lt;/span>:
ret[&lt;span style="color:#e6db74">&amp;#39;result&amp;#39;&lt;/span>] &lt;span style="color:#f92672">=&lt;/span> True
ret[&lt;span style="color:#e6db74">&amp;#39;changes&amp;#39;&lt;/span>] &lt;span style="color:#f92672">=&lt;/span> dict()
ret[&lt;span style="color:#e6db74">&amp;#39;comment&amp;#39;&lt;/span>] &lt;span style="color:#f92672">=&lt;/span> &lt;span style="color:#e6db74">&amp;#39;User not Present &amp;#39;&lt;/span> &lt;span style="color:#f92672">+&lt;/span> name &lt;span style="color:#f92672">+&lt;/span> &lt;span style="color:#e6db74">&amp;#39; @ &amp;#39;&lt;/span> &lt;span style="color:#f92672">+&lt;/span> minion_id
&lt;span style="color:#66d9ef">return&lt;/span> ret&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>
&lt;p>All of the Salt execution modules are available to each other and modules can call functions available in other
execution modules. &lt;a class="footnote-reference" href="#id4" id="id1">[1]&lt;/a>&lt;/p>
&lt;p>The variable &lt;cite>__salt__&lt;/cite> is packed into the modules after they are loaded into the Salt minion. &lt;a class="footnote-reference" href="#id4" id="id2">[1]&lt;/a>&lt;/p>
&lt;p>The &lt;cite>__salt__&lt;/cite> variable is a Python dictionary containing all of the Salt functions. Dictionary keys are strings
representing the names of the modules and the values are the functions themselves. &lt;a class="footnote-reference" href="#id4" id="id3">[1]&lt;/a>&lt;/p>
&lt;p>Here we make use of the &lt;cite>shadow&lt;/cite> module, you can see details about it
&lt;a class="reference external" href="https://docs.saltstack.com/en/latest/ref/modules/all/salt.modules.shadow.html#module-salt.modules.shadow">here&lt;/a>&lt;/p>
&lt;p>Let us test these with the user user &lt;cite>temp&lt;/cite>:&lt;/p>
&lt;div class="highlight">&lt;pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">&lt;code class="language-fallback" data-lang="fallback">root@master:~# salt &amp;#39;minion-1&amp;#39; myuser.disable_login temp
minion-1:
----------
changes:
----------
disable_login:
----------
new:
temp@minion-1
old:
comment:
None
result:
True&lt;/code>&lt;/pre>&lt;/div>
&lt;p>Check that it is disabled:&lt;/p>
&lt;div class="highlight">&lt;pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">&lt;code class="language-fallback" data-lang="fallback">temp@local:~$ ssh minion-1
Your account has expired; please contact your system administrator
Connection to minion-1 closed by remote host.
Connection to minion-1 closed.&lt;/code>&lt;/pre>&lt;/div>
&lt;p>Now we enable it back.&lt;/p>
&lt;div class="highlight">&lt;pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">&lt;code class="language-fallback" data-lang="fallback">root@master:~# salt &amp;#39;minion-1&amp;#39; myuser.enable_login temp
minion-1:
----------
changes:
----------
enable_login:
----------
new:
temp@minion-1
old:
comment:
None
result:
True&lt;/code>&lt;/pre>&lt;/div>
&lt;p>And check that it works.&lt;/p>
&lt;div class="highlight">&lt;pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">&lt;code class="language-fallback" data-lang="fallback">temp@local:~$ ssh minion-1
Welcome to Ubuntu 16.04.5 LTS (GNU/Linux 4.4.0-131-generic x86_64)
temp@minion-1:~$&lt;/code>&lt;/pre>&lt;/div>
&lt;p>Nice, now we just have one task left, make a function that forces a user to change its password:&lt;/p>
&lt;div class="highlight">&lt;div style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">
&lt;table style="border-spacing:0;padding:0;margin:0;border:0;width:auto;overflow:auto;display:block;">&lt;tr>&lt;td style="vertical-align:top;padding:0;margin:0;border:0;">
&lt;pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">&lt;code>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 1
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 2
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 3
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 4
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 5
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 6
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 7
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 8
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 9
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">10
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">11
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">12
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">13
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">14
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">15
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">16
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">17
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">18
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">19
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">20
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">21
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">22
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">23
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">24
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">25
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">26
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">27
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td style="vertical-align:top;padding:0;margin:0;border:0;;width:100%">
&lt;pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">&lt;code class="language-python" data-lang="python">&lt;span style="color:#e6db74">&amp;#39;&amp;#39;&amp;#39;
&lt;/span>&lt;span style="color:#e6db74">Expire the password validity for a user, forcing the user to change the password at next successful login
&lt;/span>&lt;span style="color:#e6db74">
&lt;/span>&lt;span style="color:#e6db74">CLI Example::
&lt;/span>&lt;span style="color:#e6db74">
&lt;/span>&lt;span style="color:#e6db74"> salt &amp;#39;*&amp;#39; myuser.expire_passwd username
&lt;/span>&lt;span style="color:#e6db74">&amp;#39;&amp;#39;&amp;#39;&lt;/span>
&lt;span style="color:#66d9ef">def&lt;/span> &lt;span style="color:#a6e22e">expire_passwd&lt;/span>(name):
ret &lt;span style="color:#f92672">=&lt;/span> dict()
minion_id &lt;span style="color:#f92672">=&lt;/span> __salt__[&lt;span style="color:#e6db74">&amp;#39;grains.get&amp;#39;&lt;/span>](&lt;span style="color:#e6db74">&amp;#39;id&amp;#39;&lt;/span>)
&lt;span style="color:#66d9ef">try&lt;/span>:
old &lt;span style="color:#f92672">=&lt;/span> spwd&lt;span style="color:#f92672">.&lt;/span>getspnam(name)
__salt__[&lt;span style="color:#e6db74">&amp;#39;shadow.set_date&amp;#39;&lt;/span>](name, &lt;span style="color:#ae81ff">0&lt;/span>)
ret[&lt;span style="color:#e6db74">&amp;#39;result&amp;#39;&lt;/span>] &lt;span style="color:#f92672">=&lt;/span> True
ret[&lt;span style="color:#e6db74">&amp;#39;changes&amp;#39;&lt;/span>] &lt;span style="color:#f92672">=&lt;/span> {&lt;span style="color:#e6db74">&amp;#39;expire_passwd&amp;#39;&lt;/span>: {&lt;span style="color:#e6db74">&amp;#39;old&amp;#39;&lt;/span>: old&lt;span style="color:#f92672">.&lt;/span>sp_lstchg,
&lt;span style="color:#e6db74">&amp;#39;new&amp;#39;&lt;/span>: spwd&lt;span style="color:#f92672">.&lt;/span>getspnam(name)&lt;span style="color:#f92672">.&lt;/span>sp_lstchg}}
ret[&lt;span style="color:#e6db74">&amp;#39;comment&amp;#39;&lt;/span>] &lt;span style="color:#f92672">=&lt;/span> None
&lt;span style="color:#66d9ef">except&lt;/span> &lt;span style="color:#a6e22e">KeyError&lt;/span>:
ret[&lt;span style="color:#e6db74">&amp;#39;result&amp;#39;&lt;/span>] &lt;span style="color:#f92672">=&lt;/span> True
ret[&lt;span style="color:#e6db74">&amp;#39;changes&amp;#39;&lt;/span>] &lt;span style="color:#f92672">=&lt;/span> dict()
ret[&lt;span style="color:#e6db74">&amp;#39;comment&amp;#39;&lt;/span>] &lt;span style="color:#f92672">=&lt;/span> &lt;span style="color:#e6db74">&amp;#39;User not Present &amp;#39;&lt;/span> &lt;span style="color:#f92672">+&lt;/span> name &lt;span style="color:#f92672">+&lt;/span> &lt;span style="color:#e6db74">&amp;#39; @ &amp;#39;&lt;/span> &lt;span style="color:#f92672">+&lt;/span> minion_id
&lt;span style="color:#66d9ef">return&lt;/span> ret&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>
&lt;p>Let us test it, then.&lt;/p>
&lt;div class="highlight">&lt;pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">&lt;code class="language-fallback" data-lang="fallback">root@master:~# salt &amp;#39;minion-1&amp;#39; myuser.expire_passwd temp
minion-1:
----------
changes:
----------
expire_passwd:
----------
new:
0
old:
-1
comment:
None
result:
True&lt;/code>&lt;/pre>&lt;/div>
&lt;p>If we did everything right, this will trigger a password change on the user's next successful login. And the user is
forced to change to a different password.&lt;/p>
&lt;div class="highlight">&lt;pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">&lt;code class="language-fallback" data-lang="fallback">temp@local:~$ ssh minion-1
You are required to change your password immediately (root enforced)
WARNING: Your password has expired.
You must change your password now and login again!
Changing password for temp.
(current) UNIX password:
Enter new UNIX password:
Retype new UNIX password:
Password unchanged
Enter new UNIX password:
Retype new UNIX password:
passwd: password updated successfully
temp@minion-1:~$ logout
Connection to minion-1 closed.&lt;/code>&lt;/pre>&lt;/div>
&lt;p>Looks good =)&lt;/p>
&lt;/div>
&lt;div class="section" id="state-module">
&lt;h2>State Module&lt;/h2>
&lt;p>Like we saw in the &lt;a class="reference external" href="https://implement.pt/2018/10/a-comprehensive-introduction-to-salt/#configuration-management">previous post&lt;/a>, to
apply a state we use the &lt;em>state&lt;/em> execution module, and its function &lt;em>apply&lt;/em>. One key difference between using execution
modules and state modules is the possibility of using the keyword &lt;strong>test&lt;/strong> that, when &lt;em>True&lt;/em>, will not effectively apply
the state but rather it will show what it would do and/or which changes it would apply. We will see how to define this
behaviour.&lt;/p>
&lt;p>Just like for the execution module we should first create the state file in the proper location.&lt;/p>
&lt;div class="highlight">&lt;pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">&lt;code class="language-fallback" data-lang="fallback">root@master:~# tree /srv/salt
/srv/salt
└── states
└── base
   │  ...
├── _states
│ └── myuser.py
...&lt;/code>&lt;/pre>&lt;/div>
&lt;p>And we can start writing our state module. Like the execution module let us call it &lt;cite>myuser&lt;/cite>. As the
&lt;cite>get_orphan_home_dirs&lt;/cite> function is informational only, we will be implementing the other three &lt;cite>disable_login&lt;/cite>,
&lt;cite>enable_login&lt;/cite> and &lt;cite>expire_passwd&lt;/cite>.&lt;/p>
&lt;div class="highlight">&lt;div style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">
&lt;table style="border-spacing:0;padding:0;margin:0;border:0;width:auto;overflow:auto;display:block;">&lt;tr>&lt;td style="vertical-align:top;padding:0;margin:0;border:0;">
&lt;pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">&lt;code>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 1
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 2
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 3
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 4
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 5
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 6
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 7
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 8
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 9
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">10
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">11
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">12
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">13
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">14
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">15
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">16
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">17
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">18
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">19
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">20
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">21
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">22
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">23
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">24
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">25
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">26
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">27
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">28
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">29
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">30
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">31
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">32
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">33
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">34
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">35
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">36
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">37
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">38
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">39
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">40
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">41
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">42
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">43
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">44
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">45
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">46
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">47
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">48
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">49
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">50
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">51
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td style="vertical-align:top;padding:0;margin:0;border:0;;width:100%">
&lt;pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">&lt;code class="language-python" data-lang="python">&lt;span style="color:#66d9ef">def&lt;/span> &lt;span style="color:#a6e22e">disable_login&lt;/span>(name):
ret &lt;span style="color:#f92672">=&lt;/span> dict()
&lt;span style="color:#66d9ef">if&lt;/span> __opts__[&lt;span style="color:#e6db74">&amp;#39;test&amp;#39;&lt;/span>]:
ret[&lt;span style="color:#e6db74">&amp;#39;name&amp;#39;&lt;/span>] &lt;span style="color:#f92672">=&lt;/span> &lt;span style="color:#e6db74">&amp;#39;login&amp;#39;&lt;/span>
ret[&lt;span style="color:#e6db74">&amp;#39;changes&amp;#39;&lt;/span>] &lt;span style="color:#f92672">=&lt;/span> dict()
ret[&lt;span style="color:#e6db74">&amp;#39;result&amp;#39;&lt;/span>] &lt;span style="color:#f92672">=&lt;/span> None
ret[&lt;span style="color:#e6db74">&amp;#39;comment&amp;#39;&lt;/span>] &lt;span style="color:#f92672">=&lt;/span> &lt;span style="color:#e6db74">&amp;#39;Would disable login for user {0}&amp;#39;&lt;/span>&lt;span style="color:#f92672">.&lt;/span>format(name)
&lt;span style="color:#66d9ef">return&lt;/span> ret
ret &lt;span style="color:#f92672">=&lt;/span> __salt__[&lt;span style="color:#e6db74">&amp;#39;myuser.disable_login&amp;#39;&lt;/span>](name)
ret[&lt;span style="color:#e6db74">&amp;#39;name&amp;#39;&lt;/span>] &lt;span style="color:#f92672">=&lt;/span> &lt;span style="color:#e6db74">&amp;#39;login&amp;#39;&lt;/span>
&lt;span style="color:#66d9ef">return&lt;/span> ret
&lt;span style="color:#66d9ef">def&lt;/span> &lt;span style="color:#a6e22e">enable_login&lt;/span>(name):
ret &lt;span style="color:#f92672">=&lt;/span> dict()
&lt;span style="color:#66d9ef">if&lt;/span> __opts__[&lt;span style="color:#e6db74">&amp;#39;test&amp;#39;&lt;/span>]:
ret[&lt;span style="color:#e6db74">&amp;#39;name&amp;#39;&lt;/span>] &lt;span style="color:#f92672">=&lt;/span> &lt;span style="color:#e6db74">&amp;#39;login&amp;#39;&lt;/span>
ret[&lt;span style="color:#e6db74">&amp;#39;changes&amp;#39;&lt;/span>] &lt;span style="color:#f92672">=&lt;/span> dict()
ret[&lt;span style="color:#e6db74">&amp;#39;result&amp;#39;&lt;/span>] &lt;span style="color:#f92672">=&lt;/span> None
ret[&lt;span style="color:#e6db74">&amp;#39;comment&amp;#39;&lt;/span>] &lt;span style="color:#f92672">=&lt;/span> &lt;span style="color:#e6db74">&amp;#39;Would enable login for user {0}&amp;#39;&lt;/span>&lt;span style="color:#f92672">.&lt;/span>format(name)
&lt;span style="color:#66d9ef">return&lt;/span> ret
ret &lt;span style="color:#f92672">=&lt;/span> __salt__[&lt;span style="color:#e6db74">&amp;#39;myuser.enable_login&amp;#39;&lt;/span>](name)
ret[&lt;span style="color:#e6db74">&amp;#39;name&amp;#39;&lt;/span>] &lt;span style="color:#f92672">=&lt;/span> &lt;span style="color:#e6db74">&amp;#39;login&amp;#39;&lt;/span>
&lt;span style="color:#66d9ef">return&lt;/span> ret
&lt;span style="color:#66d9ef">def&lt;/span> &lt;span style="color:#a6e22e">expire_passwd&lt;/span>(name):
ret &lt;span style="color:#f92672">=&lt;/span> dict()
&lt;span style="color:#66d9ef">if&lt;/span> __opts__[&lt;span style="color:#e6db74">&amp;#39;test&amp;#39;&lt;/span>]:
ret[&lt;span style="color:#e6db74">&amp;#39;name&amp;#39;&lt;/span>] &lt;span style="color:#f92672">=&lt;/span> &lt;span style="color:#e6db74">&amp;#39;login&amp;#39;&lt;/span>
ret[&lt;span style="color:#e6db74">&amp;#39;changes&amp;#39;&lt;/span>] &lt;span style="color:#f92672">=&lt;/span> dict()
ret[&lt;span style="color:#e6db74">&amp;#39;result&amp;#39;&lt;/span>] &lt;span style="color:#f92672">=&lt;/span> None
ret[&lt;span style="color:#e6db74">&amp;#39;comment&amp;#39;&lt;/span>] &lt;span style="color:#f92672">=&lt;/span> &lt;span style="color:#e6db74">&amp;#39;Would enable login for user {0}&amp;#39;&lt;/span>&lt;span style="color:#f92672">.&lt;/span>format(name)
&lt;span style="color:#66d9ef">return&lt;/span> ret
ret &lt;span style="color:#f92672">=&lt;/span> __salt__[&lt;span style="color:#e6db74">&amp;#39;myuser.expire_passwd&amp;#39;&lt;/span>](name)
ret[&lt;span style="color:#e6db74">&amp;#39;name&amp;#39;&lt;/span>] &lt;span style="color:#f92672">=&lt;/span> &lt;span style="color:#e6db74">&amp;#39;login&amp;#39;&lt;/span>
&lt;span style="color:#66d9ef">return&lt;/span> ret&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>
&lt;p>And now we can use these in SLS files, i.e:&lt;/p>
&lt;div class="highlight">&lt;div style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">
&lt;table style="border-spacing:0;padding:0;margin:0;border:0;width:auto;overflow:auto;display:block;">&lt;tr>&lt;td style="vertical-align:top;padding:0;margin:0;border:0;">
&lt;pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">&lt;code>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 1
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 2
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 3
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 4
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 5
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 6
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 7
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 8
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 9
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">10
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">11
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">12
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">13
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">14
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">15
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">16
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">17
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">18
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">19
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">20
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">21
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">22
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">23
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">24
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">25
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">26
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">27
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">28
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">29
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">30
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">31
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">32
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">33
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">34
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">35
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td style="vertical-align:top;padding:0;margin:0;border:0;;width:100%">
&lt;pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">&lt;code class="language-jinja" data-lang="jinja">&lt;span style="color:#75715e">{%&lt;/span>- &lt;span style="color:#66d9ef">set&lt;/span> shell &lt;span style="color:#f92672">=&lt;/span> &lt;span style="color:#e6db74">&amp;#39;/bin/bash&amp;#39;&lt;/span> -&lt;span style="color:#75715e">%}&lt;/span>
&lt;span style="color:#75715e">{%&lt;/span>- &lt;span style="color:#66d9ef">set&lt;/span> home_root &lt;span style="color:#f92672">=&lt;/span> &lt;span style="color:#e6db74">&amp;#39;/home&amp;#39;&lt;/span> -&lt;span style="color:#75715e">%}&lt;/span>
&lt;span style="color:#75715e">{%&lt;/span> &lt;span style="color:#66d9ef">for&lt;/span> username &lt;span style="color:#66d9ef">in&lt;/span> &lt;span style="color:#f92672">[&lt;/span>&lt;span style="color:#e6db74">&amp;#39;alice&amp;#39;&lt;/span>&lt;span style="color:#f92672">,&lt;/span> &lt;span style="color:#e6db74">&amp;#39;bob&amp;#39;&lt;/span>&lt;span style="color:#f92672">]&lt;/span> &lt;span style="color:#75715e">%}&lt;/span>
add user &lt;span style="color:#75715e">{{&lt;/span> username &lt;span style="color:#75715e">}}&lt;/span> and ensure the login is enabled:
user.present:
- name: &lt;span style="color:#75715e">{{&lt;/span> username &lt;span style="color:#75715e">}}&lt;/span>
- shell: &lt;span style="color:#75715e">{{&lt;/span> shell &lt;span style="color:#75715e">}}&lt;/span>
- home: &lt;span style="color:#75715e">{{&lt;/span> home_root &lt;span style="color:#75715e">}}&lt;/span>/&lt;span style="color:#75715e">{{&lt;/span> username &lt;span style="color:#75715e">}}&lt;/span>
- createhome: True
- password: &amp;lt;some hash previously created with mkpasswd -m sha-256&amp;gt;
myuser.enable_login:
- name: &lt;span style="color:#75715e">{{&lt;/span> username &lt;span style="color:#75715e">}}&lt;/span>
- require:
- user: add user &lt;span style="color:#75715e">{{&lt;/span> username &lt;span style="color:#75715e">}}&lt;/span> and ensure the login is enabled
force user &lt;span style="color:#75715e">{{&lt;/span> username &lt;span style="color:#75715e">}}&lt;/span> to change its password:
myuser.expire_passwd:
- name: &lt;span style="color:#75715e">{{&lt;/span> username &lt;span style="color:#75715e">}}&lt;/span>
- require:
- myuser: add user &lt;span style="color:#75715e">{{&lt;/span> username &lt;span style="color:#75715e">}}&lt;/span> and ensure the login is enabled
&lt;span style="color:#75715e">{%&lt;/span> &lt;span style="color:#66d9ef">endfor&lt;/span> &lt;span style="color:#75715e">%}&lt;/span>
&lt;span style="color:#75715e">{%&lt;/span> &lt;span style="color:#66d9ef">for&lt;/span> username &lt;span style="color:#66d9ef">in&lt;/span> &lt;span style="color:#f92672">[&lt;/span>&lt;span style="color:#e6db74">&amp;#39;charlie&amp;#39;&lt;/span>&lt;span style="color:#f92672">,&lt;/span> &lt;span style="color:#e6db74">&amp;#39;lucy&amp;#39;&lt;/span>&lt;span style="color:#f92672">]&lt;/span> &lt;span style="color:#75715e">%}&lt;/span>
add user &lt;span style="color:#75715e">{{&lt;/span> username &lt;span style="color:#75715e">}}&lt;/span> and disable its login:
user.present:
- name: &lt;span style="color:#75715e">{{&lt;/span> username &lt;span style="color:#75715e">}}&lt;/span>
- shell: &lt;span style="color:#75715e">{{&lt;/span> shell &lt;span style="color:#75715e">}}&lt;/span>
- home: &lt;span style="color:#75715e">{{&lt;/span> home_root &lt;span style="color:#75715e">}}&lt;/span>/&lt;span style="color:#75715e">{{&lt;/span> username &lt;span style="color:#75715e">}}&lt;/span>
- createhome: True
- password: &amp;lt;some hash previously created with mkpasswd -m sha-256&amp;gt;
myuser.disable_login:
- name: &lt;span style="color:#75715e">{{&lt;/span> username &lt;span style="color:#75715e">}}&lt;/span>
- require:
- user: add user &lt;span style="color:#75715e">{{&lt;/span> username &lt;span style="color:#75715e">}}&lt;/span> and disable its login
&lt;span style="color:#75715e">{%&lt;/span> &lt;span style="color:#66d9ef">endfor&lt;/span> &lt;span style="color:#75715e">%}&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>
&lt;p>Before testing our newly created state module we have to update the minion's cache.&lt;/p>
&lt;div class="highlight">&lt;pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">&lt;code class="language-fallback" data-lang="fallback">root@master:~$ salt minion-1 saltutil.sync_states
minion-1:
----------
- states.myuser
root@master:~$&lt;/code>&lt;/pre>&lt;/div>
&lt;p>Now let us test it.&lt;/p>
&lt;div class="highlight">&lt;pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">&lt;code class="language-fallback" data-lang="fallback">root@master:~$ salt minion-1 state.apply users test=True
minion-1:
----------
ID: add user alice and ensure the login is enabled
Function: user.present
Name: alice
Result: None
Comment: User alice set to be added
Started: 14:23:49.860660
Duration: 0.967 ms
Changes:
----------
ID: add user bob and ensure the login is enabled
Function: user.present
Name: bob
Result: None
Comment: User bob set to be added
Started: 14:23:49.860660
Duration: 0.967 ms
Changes:
----------
ID: add user alice and ensure the login is enabled
Function: myuser.enable_login
Name: alice
Result: None
Comment: Would enable login for user alice
Started: 14:23:48.895331
Duration: 0.473 ms
Changes:
----------
ID: add user bob and ensure the login is enabled
Function: myuser.enable_login
Name: bob
Result: None
Comment: Would enable login for user bob
Started: 14:23:48.895431
Duration: 0.473 ms
Changes:
----------
ID: force user alice to change its password
Function: myuser.expire_passwd
Name: alice
Result: None
Comment: Would force password change for user alice
Started: 14:23:48.895531
Duration: 0.473 ms
Changes:
----------
ID: add user charlie and ensure the login is enabled
Function: user.present
Name: charlie
Result: None
Comment: User charlie set to be added
Started: 14:23:49.860660
Duration: 0.967 ms
Changes:
----------
ID: add user lucy and ensure the login is enabled
Function: user.present
Name: lucy
Result: None
Comment: User lucy set to be added
Started: 14:23:49.860660
Duration: 0.967 ms
Changes:
----------
ID: force user bob to change its password
Function: myuser.expire_passwd
Name: bob
Result: None
Comment: Would force password change for user bob
Started: 14:23:48.896661
Duration: 0.473 ms
Changes:
----------
ID: add user charlie and disable its login
Function: myuser.disable_login
Name: charlie
Result: None
Comment: Would force password change for user charlie
Started: 14:23:49.896761
Duration: 0.473 ms
Changes:
----------
ID: add user lucy and disable its login
Function: myuser.disable_login
Name: lucy
Result: None
Comment: Would force password change for user lucy
Started: 14:23:49.896771
Duration: 0.473 ms
Changes:
----------
Summary for minion-1
--------------
Succeeded: 10 (unchanged=10)
Failed: 0
--------------
Total states run: 10
Total run time: 9.325 ms&lt;/code>&lt;/pre>&lt;/div>
&lt;p>The test looks good so now we can apply it.&lt;/p>
&lt;div class="highlight">&lt;pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">&lt;code class="language-fallback" data-lang="fallback">root@master:~$ salt minion-1 state.apply users
minion-1:
----------
.....
.....
Summary for minion-1
--------------
Succeeded: 10 (changed=10)
Failed: 0
--------------
Total states run: 10
Total run time: 9.325 ms
root@master:~$&lt;/code>&lt;/pre>&lt;/div>
&lt;p>Looks good =)&lt;/p>
&lt;p>Now we have 4 new users added to minion-1, alice and bob set with a forced password change, and charlie and lucy which
exist but cannot login.&lt;/p>
&lt;/div>
&lt;div class="section" id="conclusion">
&lt;h2>Conclusion&lt;/h2>
&lt;p>In this post we saw how to write both an execution module and a state module for the Saltstack Salt configuration
management tool. We also saw how to test these modules and use them in a SLS file.&lt;/p>
&lt;p>The files described in this post are available here:&lt;/p>
&lt;ul class="simple">
&lt;li>&lt;a class="reference external" href="https://implement.pt/files/an-advanced-guide-to-salt/_modules/myuser.py">execution module&lt;/a>&lt;/li>
&lt;li>&lt;a class="reference external" href="https://implement.pt/files/an-advanced-guide-to-salt/_states/myuser.py">state module&lt;/a>&lt;/li>
&lt;li>&lt;a class="reference external" href="https://implement.pt/files/an-advanced-guide-to-salt/users.sls">SLS file&lt;/a>&lt;/li>
&lt;/ul>
&lt;p>Feel free to leave comments below, any improvement to this and other articles is always welcome.&lt;/p>
&lt;p>Thanks for reading!&lt;/p>
&lt;/div>
&lt;div class="section" id="references">
&lt;h2>References&lt;/h2>
&lt;table class="docutils footnote" frame="void" id="id4" rules="none">
&lt;colgroup>&lt;col class="label" />&lt;col />&lt;/colgroup>
&lt;tbody valign="top">
&lt;tr>&lt;td class="label">[1]&lt;/td>&lt;td>&lt;em>(&lt;a class="fn-backref" href="#id1">1&lt;/a>, &lt;a class="fn-backref" href="#id2">2&lt;/a>, &lt;a class="fn-backref" href="#id3">3&lt;/a>)&lt;/em> &lt;a class="reference external" href="https://docs.saltstack.com/en/latest/ref/modules/index.html">https://docs.saltstack.com/en/latest/ref/modules/index.html&lt;/a>&lt;/td>&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;/div>
&lt;/div></content><category>System Administration</category><category>linux</category><category>python</category><category>devops</category><category>saltstack</category><category>configuration management</category></item><item><title>A Comprehensive Introduction to Salt</title><link>https://implement.pt/2018/10/a-comprehensive-introduction-to-salt/</link><pubDate>Mon, 01 Oct 2018 09:30:00 +0100</pubDate><guid>https://implement.pt/2018/10/a-comprehensive-introduction-to-salt/</guid><summary type="html">&lt;div class="document">
&lt;p>A comprehensive introduction to using salt, more than a configuration management tool, from Saltstack&lt;/p>
&lt;/div></summary><content type="html">&lt;div class="document">
&lt;!-- {F#fig-delaet}
{F#fig-saltstack}
{F#fig-monolith}
{F#fig-netarch1}
{F#fig-netarch2}
{F#fig-reactor} -->
&lt;div class="section" id="introduction">
&lt;h2>Introduction&lt;/h2>
&lt;p>Recent advances in IT infrastructures, namely for &lt;cite>Cloud-based&lt;/cite> Services, where scalability, elasticity and availability
are key factors, have also seen advances in the way Management and Administration of these critical infrastructures are
achieved, be it on log analysis and systems monitoring or on configuration management and automation. This is a rising
subject, with exponential development evolution as new tools with different approaches have emerged in the last few
years. &lt;a class="footnote-reference" href="#id27" id="id1">[2]&lt;/a>&lt;/p>
&lt;div class="figure align-center" id="fig-delaet">
&lt;a class="reference external image-reference" href="./img/delaet_framework.png">&lt;img alt="Figure 1" src="./img/delaet_framework.png" />&lt;/a>
&lt;p class="caption">A conceptual architecture of system configuration tools&lt;/p>
&lt;/div>
&lt;p>Delaet &lt;em>et al.&lt;/em> &lt;a class="footnote-reference" href="#id28" id="id2">[3]&lt;/a> provide a framework for evaluating different System Configuration tools. They also define a
conceptual architecture for these tools, illustrated in &lt;a class="reference external" href="#fig-delaet">Figure 1&lt;/a>.
In essence, this type of tool is composed by a &lt;em>master&lt;/em> element (typically residing in a deployment node) containing a
set of specialized modules that &lt;em>translate&lt;/em> through some form of manifest files, the specific configuration for each
component of the remote managed nodes of the infrastructure. Each remote managed node, through some form of local
&lt;em>agent&lt;/em> conveys to the &lt;em>master&lt;/em> detailed information about the node and executes configuration actions determined by
the &lt;em>master&lt;/em>. The &lt;em>master&lt;/em> compiles in a repository a catalog (inventory) with information on the nodes and on how they
should be configured.&lt;/p>
&lt;p>In their paper, John Benson &lt;em>et al.&lt;/em> &lt;a class="footnote-reference" href="#id26" id="id3">[1]&lt;/a>, address the challenge of building an effective multi-cloud application
deployment controller as a customer add-on outside of a cloud utility service using automation tools such as Ansible
(&lt;a class="reference external" href="ansible.com">ansible.com&lt;/a>), SaltStack (&lt;a class="reference external" href="saltstack.com">saltstack.com&lt;/a>) and Chef (&lt;a class="reference external" href="www.chef.io">www.chef.io&lt;/a>) and compare them to each other as well
as with the case where there is no automation framework just plain shell code.&lt;/p>
&lt;p>The authors compare those three tools in terms of performance in executing three sets of tasks (installing a chemistry
software package, installing an analytics software package, installing both software packages), the amount of code
needed to execute those tasks and different features they have or do not have, such as a GUI and the need for an agent.&lt;/p>
&lt;p>Ebert, C. &lt;em>et al.&lt;/em> &lt;a class="footnote-reference" href="#id29" id="id4">[4]&lt;/a> address and discuss this recent organization culture coined &lt;strong>DevOps&lt;/strong>, namely the toolset used
at different phases, from Build, to Deployment, to Operations. The authors conclude that &amp;quot;DevOps is impacting the entire
software and IT industry. Building on lean and agile practices, DevOps means end-to-end automation in software
development and delivery.&amp;quot;&lt;a class="footnote-reference" href="#id29" id="id5">[4]&lt;/a>, with which I agree on. The authors also state that &amp;quot;Hardly anybody will be able to
approach it with a cookbook-style approach, but most developers will benefit from better connecting development and
operations.&amp;quot;&lt;a class="footnote-reference" href="#id29" id="id6">[4]&lt;/a> with which I do not totally agree with, as I will try to demonstrate, by showing that a reactive
infrastructure can be created to act autonomously on certain events, without leaving the cookbook-like approach, but
through a set of triggered events that can be used to automate software development and delivery. There are already
tools, such as &lt;strong>SaltStack&lt;/strong> that makes possible for certain events to intelligently trigger actions, allowing therefore
IT operators to create &lt;em>autonomic&lt;/em> systems and data centers instead of just static runbooks. The authors also state that
a &amp;quot;mutual understanding from requirements onward to maintenance, service, and product evolution will improve cycle time
by 10 to 30 percent and reduce costs up 20 percent.&amp;quot;&lt;a class="footnote-reference" href="#id29" id="id7">[4]&lt;/a> pointing out that the major drivers are fewer requirements
changes, focused testing, quality assurance, and a much faster delivery cycle with feature-driven teams. They also point
out the large dynamics of this &lt;strong>DevOps&lt;/strong> culture as &amp;quot;each company needs its own approach to achieve DevOps, from
architecture to tools to culture.&amp;quot;&lt;a class="footnote-reference" href="#id29" id="id8">[4]&lt;/a>&lt;/p>
&lt;div class="figure align-center" id="fig-saltstack">
&lt;a class="reference external image-reference" href="./img/salt_framework.png">&lt;img alt="Figure 2" src="./img/salt_framework.png" />&lt;/a>
&lt;p class="caption">Saltstack architecture&lt;/p>
&lt;/div>
&lt;p>&lt;strong>Saltstack&lt;/strong> took a different approach for its architecture, illustrated in
&lt;a class="figure-ref" href="#fig-saltstack">&lt;/a>
, when compared to the
architecture defined by Delaet &lt;em>et al.&lt;/em> &lt;a class="footnote-reference" href="#id28" id="id9">[3]&lt;/a>. Comparing both, there are some similarities
but it can also be seen that there is a bidirectional channel of communication between the managed devices and the Salt
Master which corresponds to an Event Bus that connects to different parts of the tool. The Runner is used for executing
modules specific to the Salt Master but can also provide information about the managed devices. The Reactor is
responsible for receiving events and corresponding them to the appropriate state or even to fire another event as a
consequence of the former. As stated in their documentation, &amp;quot;Salt Engines are long-running, external system processes
that leverage Salt&amp;quot;(&lt;a class="reference external" href="https://docs.saltstack.com">https://docs.saltstack.com&lt;/a>), allowing to integrate external tools into the Salt system, for
example to send to the salt Event Bus messages of a channel on the &lt;strong>Slack&lt;/strong> (&lt;a class="reference external" href="slack.com">slack.com&lt;/a>) communication tool (see
&lt;a class="reference external" href="https://github.com/saltstack/salt/blob/develop/salt/engines/slack.py">https://github.com/saltstack/salt/blob/develop/salt/engines/slack.py&lt;/a>).&lt;/p>
&lt;/div>
&lt;div class="section" id="architecture">
&lt;h2>Architecture&lt;/h2>
&lt;p>For this work we will be using Saltstack's orchestration tool Salt. Its requirements for a device to be managed by it
can be as little as having a REpresentational State Transfer (REST) API through which one can interact with. Its overall
architecture can be seen in
&lt;a class="figure-ref" href="#fig-saltstack">&lt;/a>
. In this section we will see the architectural, network and hardware requirements,
as well as Salt's software requirements and how to use Salt as a full infrastructure management tool.&lt;/p>
&lt;div class="figure align-center" id="fig-monolith">
&lt;a class="reference external image-reference" href="./img/salt_monolith.png">&lt;img alt="Figure 3" src="./img/salt_monolith.png" />&lt;/a>
&lt;p class="caption">Monolithic example&lt;/p>
&lt;/div>
&lt;p>The minimum configuration for Salt is one machine, which takes the role of either Master and Minion, or just a Minion, with no
master see
&lt;a class="figure-ref" href="#fig-monolith">&lt;/a>
. Although this does not achieve much, it still allows to test operations, be it
configuration management, reactions to certain events, monitoring of processes or to just do some tests with Salt.&lt;/p>
&lt;dl class="docutils">
&lt;dt>Consider the two scenarios represented in &lt;a class="reference external" href="#fig-netarch1">Figure 4&lt;/a> and &lt;a class="reference external" href="#fig-netarch2">Figure 5&lt;/a>:&lt;/dt>
&lt;dd>&lt;ol class="first last arabic simple">
&lt;li>an Operator within the Control layer&lt;/li>
&lt;li>an Operator outside the Control layer, with Control and Infrastructure layers behind a Network Address Translation
(NAT)&lt;/li>
&lt;/ol>
&lt;/dd>
&lt;/dl>
&lt;p>In the first scenario, the Operator is either within the Main Controller itself or in the network neighbourhood, meaning
that it has network access within the private network of which the Main Controller is part of.&lt;/p>
&lt;p>In the second scenario, the Operator and the Main Controller are in distinct networks, meaning that the Operator with
have to have external access to the infrastructure, either using the Main Controller has a bastion server, or by
assigning the minions with a public Internet Protocol (IP) address. We will see that as public IP addresses are not that
abundant, using a bastion becomes a necessity. The Salt administration tool, by Saltstack, has several key components
which make it a complete framework for managing and administering an IT infrastructure. In each of the following
subsections we will see what they are used for and how to make use of them.&lt;/p>
&lt;p>&amp;quot;Running pre-defined or arbitrary commands on remote hosts, also known as remote execution, is the core function of
Salt&amp;quot; &lt;a class="footnote-reference" href="#id30" id="id10">[5]&lt;/a>.
Remote execution in Salt is achieved through &lt;em>execution modules&lt;/em> and &lt;em>returners&lt;/em>. Salt also contains a configuration
management framework, which complements the remote execution functions by allowing more complex and interdependent
operations. The framework functions are executed on the minion's side, allowing for scalable, simultaneous configuration
of a great number of minion targets.&lt;/p>
&lt;p>&amp;quot;The Salt Event System is used to fire off events enabling third party applications or external processes to react to
behavior within Salt&amp;quot; &lt;a class="footnote-reference" href="#id31" id="id11">[6]&lt;/a>. These events can be launched from within the Salt infrastructure or from applications
residing outside of it. To monitor non-Salt processes one can use the &lt;em>beacon system&lt;/em> which &amp;quot;allows the minion to hook
into a variety of system processes and continually monitor these processes. When monitored activity occurs in a system
process, an event is sent on the Salt event bus that can be used to trigger a reactor&amp;quot; &lt;a class="footnote-reference" href="#id32" id="id12">[7]&lt;/a>.&lt;/p>
&lt;p>The pinnacle of the two previous components is using that information to trigger actions in response to those kinds of
events. In Salt we have the &lt;em>reactor system&lt;/em> &amp;quot;a simple interface to watching Salt's event bus for event tags that match
a given pattern and then running one or more commands in response.&amp;quot; &lt;a class="footnote-reference" href="#id33" id="id13">[8]&lt;/a>.&lt;/p>
&lt;div class="figure align-center" id="fig-netarch1">
&lt;a class="reference external image-reference" href="./img/salt_netarch1.png">&lt;img alt="Figure 4" src="./img/salt_netarch1.png" />&lt;/a>
&lt;p class="caption">Operator within the Control layer&lt;/p>
&lt;/div>
&lt;div class="figure align-center" id="fig-netarch2">
&lt;a class="reference external image-reference" href="./img/salt_netarch2.png">&lt;img alt="Figure 5" src="./img/salt_netarch2.png" />&lt;/a>
&lt;p class="caption">Operator outside the Control layer&lt;/p>
&lt;/div>
&lt;p>Most of the information may be static, but it can belong to each individual minion. This information can be either
gathered from the minion itself or attributed to it by the Salt system. The first &amp;quot;is called the grains interface,
because it presents salt with grains of information. Grains are collected for the operating system, domain name, IP
address, kernel, OS type, memory, and many other system properties.&amp;quot; &lt;a class="footnote-reference" href="#id34" id="id14">[9]&lt;/a>. The latter is called the &lt;em>pillar&lt;/em>, which
&amp;quot;is an interface for Salt designed to offer global values that can be distributed to minions&amp;quot; &lt;a class="footnote-reference" href="#id35" id="id15">[10]&lt;/a>, in a private way
relative to every other minion, if need be.&lt;/p>
&lt;/div>
&lt;div class="section" id="directory-structures">
&lt;h2>Directory Structures&lt;/h2>
&lt;p>There are two essential locations for salt related files (excluding service files):&lt;/p>
&lt;ul class="simple">
&lt;li>/etc/salt&lt;/li>
&lt;li>/srv/salt&lt;/li>
&lt;/ul>
&lt;p>In &lt;em>/etc/salt&lt;/em> we have the configuration files for salt master and minion, as well as the keys for known minions (if
it is a salt master machine) and the key for the salt master (if it is a salt minion machine).&lt;/p>
&lt;div class="highlight">&lt;pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">&lt;code class="language-shell" data-lang="shell">root@master:~# tree /etc/salt
/etc/salt
├── master
├── minion
├── minion_id
└── pki
   ├── master
   │   ├── master.pem
   │   ├── master.pub
   │   ├── minions
   │   │   ├── minion1
   │   │   ├── minion2
   │   │   └── minion3
   │   ├── minions_autosign
   │   ├── minions_denied
   │   ├── minions_pre
   │   └── minions_rejected
   │
   │
   │
   └── minion
   ├── minion_master.pub
   ├── minion.pem
   └── minion.pub&lt;/code>&lt;/pre>&lt;/div>
&lt;p>&lt;strong>/etc/salt/master&lt;/strong>: master config file&lt;/p>
&lt;p>&lt;strong>/etc/salt/minion&lt;/strong>: minion config file&lt;/p>
&lt;p>&lt;strong>/etc/salt/minion_id&lt;/strong>: minion identifier file&lt;/p>
&lt;p>&lt;strong>/etc/salt/pki/master/master.pem&lt;/strong>: master private key&lt;/p>
&lt;p>&lt;strong>/etc/salt/pki/master/master.pub&lt;/strong>: master public key&lt;/p>
&lt;p>&lt;strong>/etc/salt/pki/minion/minion_master.pub&lt;/strong>: master public key&lt;/p>
&lt;p>&lt;strong>/etc/salt/pki/minion/minion.pub&lt;/strong>: minion public key&lt;/p>
&lt;p>&lt;strong>/etc/salt/pki/minion/minion.pem&lt;/strong>: minion private key&lt;/p>
&lt;p>In &lt;em>/srv/salt&lt;/em> we store the state, pillar and reactor files, we will read more about this further ahead.&lt;/p>
&lt;div class="section" id="configuration-files">
&lt;h3>Configuration Files&lt;/h3>
&lt;p>We will now see basic, and some optional, settings for both salt master and salt minion.&lt;/p>
&lt;div class="section" id="etc-salt-master">
&lt;h4>/etc/salt/master&lt;/h4>
&lt;div class="highlight">&lt;div style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">
&lt;table style="border-spacing:0;padding:0;margin:0;border:0;width:auto;overflow:auto;display:block;">&lt;tr>&lt;td style="vertical-align:top;padding:0;margin:0;border:0;">
&lt;pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">&lt;code>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 1
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 2
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 3
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 4
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 5
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 6
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 7
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 8
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 9
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">10
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">11
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">12
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">13
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">14
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">15
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">16
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">17
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">18
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">19
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">20
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">21
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">22
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">23
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">24
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">25
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">26
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">27
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">28
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">29
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">30
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">31
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">32
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">33
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">34
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">35
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">36
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">37
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">38
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">39
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">40
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">41
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">42
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">43
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">44
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td style="vertical-align:top;padding:0;margin:0;border:0;;width:100%">
&lt;pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">&lt;code class="language-yaml" data-lang="yaml">&lt;span style="color:#75715e"># The address of the interface to bind to, here we bind to all&lt;/span>
&lt;span style="color:#f92672">interface&lt;/span>: &lt;span style="color:#ae81ff">0.0.0.0&lt;/span>
&lt;span style="color:#75715e"># The port used by the publisher, default 4505&lt;/span>
&lt;span style="color:#f92672">publish_port&lt;/span>: &lt;span style="color:#ae81ff">4505&lt;/span>
&lt;span style="color:#75715e"># The user to run salt as&lt;/span>
&lt;span style="color:#f92672">user&lt;/span>: &lt;span style="color:#ae81ff">root&lt;/span>
&lt;span style="color:#75715e"># Number of hours to keep old job information&lt;/span>
&lt;span style="color:#f92672">keep_jobs&lt;/span>: &lt;span style="color:#ae81ff">168&lt;/span>
&lt;span style="color:#75715e"># Timeout for the salt command and api, default is 5 seconds&lt;/span>
&lt;span style="color:#f92672">timeout&lt;/span>: &lt;span style="color:#ae81ff">15&lt;/span>
&lt;span style="color:#75715e"># SLS files location&lt;/span>
&lt;span style="color:#f92672">file_roots&lt;/span>:
&lt;span style="color:#f92672">base&lt;/span>:
- &lt;span style="color:#ae81ff">/srv/salt/states/base/&lt;/span>
&lt;span style="color:#75715e"># Top file to use&lt;/span>
&lt;span style="color:#f92672">state_top&lt;/span>: &lt;span style="color:#ae81ff">top.sls&lt;/span>
&lt;span style="color:#75715e"># Pillar location&lt;/span>
&lt;span style="color:#f92672">pillar_roots&lt;/span>:
&lt;span style="color:#f92672">base&lt;/span>:
- &lt;span style="color:#ae81ff">/srv/salt/pillar/base&lt;/span>
&lt;span style="color:#75715e"># Reactor Configurations:&lt;/span>
&lt;span style="color:#f92672">reactor&lt;/span>:
- &lt;span style="color:#f92672">&amp;#39;salt/auth&amp;#39;&lt;/span>:
- &lt;span style="color:#ae81ff">/srv/salt/reactor/auth-pending.sls&lt;/span>
&lt;span style="color:#75715e"># if we just want to see what actually changes, set this to False&lt;/span>
&lt;span style="color:#f92672">state_verbose&lt;/span>: &lt;span style="color:#66d9ef">False&lt;/span>
&lt;span style="color:#75715e"># log file location, we leave this to the system&amp;#39;s logger&lt;/span>
&lt;span style="color:#f92672">log_file&lt;/span>: &lt;span style="color:#ae81ff">file:///dev/log&lt;/span>
&lt;span style="color:#75715e"># The level of messages to send to the log file.&lt;/span>
&lt;span style="color:#75715e"># One of &amp;#39;info&amp;#39;, &amp;#39;quiet&amp;#39;, &amp;#39;critical&amp;#39;, &amp;#39;error&amp;#39;, &amp;#39;debug&amp;#39;, &amp;#39;warning&amp;#39;.&lt;/span>
&lt;span style="color:#f92672">log_level&lt;/span>: &lt;span style="color:#ae81ff">info&lt;/span>
&lt;span style="color:#f92672">log_fmt_logfile: &amp;#39;%(asctime)-15s salt-master[%(process)d] %(name)s&lt;/span>: &lt;span style="color:#ae81ff">%(message)s&amp;#39;&lt;/span>
&lt;span style="color:#f92672">log_datefmt_logfile&lt;/span>: &lt;span style="color:#e6db74">&amp;#39;%b %d %H:%M:%S&amp;#39;&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>
&lt;p>From reading this configuration file we can see that we will be listening on all interfaces using the default publish
port, running as user root, keep job information for a week, increasing the default timeout for salt commands, and
logging to the system's logger with a custom-defined format:&lt;/p>
&lt;div class="highlight">&lt;pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">&lt;code class="language-fallback" data-lang="fallback">[TIMESTAMP] salt-master[PID] SALT_FUNCTION: MESSAGE&lt;/code>&lt;/pre>&lt;/div>
&lt;p>Besides this we also set up the base directories for &lt;em>state files&lt;/em>, &lt;em>pillar data&lt;/em> and &lt;em>reactor states&lt;/em>. We will read
about this further in this post, in &lt;a class="reference internal" href="#configuration-management">Configuration Management&lt;/a>, &lt;a class="reference internal" href="#storing-and-accessing-data">Storing and Accessing Data&lt;/a> and &lt;a class="reference internal" href="#event-system">Event System&lt;/a>,
respectively.&lt;/p>
&lt;/div>
&lt;div class="section" id="etc-salt-minion">
&lt;h4>/etc/salt/minion&lt;/h4>
&lt;div class="highlight">&lt;div style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">
&lt;table style="border-spacing:0;padding:0;margin:0;border:0;width:auto;overflow:auto;display:block;">&lt;tr>&lt;td style="vertical-align:top;padding:0;margin:0;border:0;">
&lt;pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">&lt;code>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 1
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 2
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 3
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 4
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 5
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 6
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 7
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 8
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 9
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">10
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">11
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">12
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">13
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">14
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td style="vertical-align:top;padding:0;margin:0;border:0;;width:100%">
&lt;pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">&lt;code class="language-yaml" data-lang="yaml">&lt;span style="color:#75715e"># salt master address&lt;/span>
&lt;span style="color:#f92672">master&lt;/span>: &lt;span style="color:#ae81ff">salt-master.local&lt;/span>
&lt;span style="color:#75715e"># this overrides the salt auto naming (from hostname -f)&lt;/span>
&lt;span style="color:#f92672">minion_id&lt;/span>: &lt;span style="color:#ae81ff">minion1&lt;/span>
&lt;span style="color:#75715e"># log file location, we leave this to the system&amp;#39;s logger&lt;/span>
&lt;span style="color:#f92672">log_file&lt;/span>: &lt;span style="color:#ae81ff">file:///dev/log&lt;/span>
&lt;span style="color:#75715e"># The level of messages to send to the log file.&lt;/span>
&lt;span style="color:#75715e"># One of &amp;#39;info&amp;#39;, &amp;#39;quiet&amp;#39;, &amp;#39;critical&amp;#39;, &amp;#39;error&amp;#39;, &amp;#39;debug&amp;#39;, &amp;#39;warning&amp;#39;.&lt;/span>
&lt;span style="color:#f92672">log_level&lt;/span>: &lt;span style="color:#ae81ff">info&lt;/span>
&lt;span style="color:#f92672">log_fmt_logfile: &amp;#39;%(asctime)-15s salt-minion[%(process)d] %(name)s&lt;/span>: &lt;span style="color:#ae81ff">%(message)s&amp;#39;&lt;/span>
&lt;span style="color:#f92672">log_datefmt_logfile&lt;/span>: &lt;span style="color:#e6db74">&amp;#39;%b %d %H:%M:%S&amp;#39;&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>
&lt;p>As we can see, the most basic configuration file for a salt minion is its salt master network location, it can be
configured via a fqdn, a hostname defined in &lt;em>/etc/hosts/&lt;/em> file or via IP address.&lt;/p>
&lt;p>We can also see the &lt;em>minion_id&lt;/em> setting which forces a minion identifier, instead of letting the salt-minion choose one
based on the machine's hostname.&lt;/p>
&lt;/div>
&lt;/div>
&lt;div class="section" id="subsystem-files">
&lt;h3>Subsystem Files&lt;/h3>
&lt;p>Like we saw before, we configure all of the salt subsystem files within &lt;em>/srv/salt/&lt;/em>, here we have a tree view of its
subdirectories (not ordered on purpose):&lt;/p>
&lt;div class="highlight">&lt;pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">&lt;code class="language-shell" data-lang="shell">root@master:~# tree /srv/salt
/srv/salt
└── states
│ └── base
│    ├── ntp
│    │   ├── etc
│    │   │   └── ntp.conf.tmpl
│    │   └── init.sls
│ └── top.sls
├── pillar
│   └── base
│   ├── ntp
│   │   └── init.sls
│   └── top.sls
└── reactor
   └── auth-pending.sls&lt;/code>&lt;/pre>&lt;/div>
&lt;/div>
&lt;/div>
&lt;div class="section" id="the-top-file">
&lt;h2>The Top File&lt;/h2>
&lt;p>An infrastructure can be seen as being an applicational stack by itself, by having groups of different machines set up
in small clusters, each cluster performing a sequence of tasks. The Top file is were the attribution of configuration
role(s) and Minion(s) is made. This kind of file is used both for State and Pillar systems.
Top files can be structurally seen has having three levels:&lt;/p>
&lt;table class="docutils field-list" frame="void" rules="none">
&lt;col class="field-name" />
&lt;col class="field-body" />
&lt;tbody valign="top">
&lt;tr class="field">&lt;th class="field-name">Environment:&lt;/th>&lt;td class="field-body">(&lt;span class="formula">&lt;i>ϵ&lt;/i>&lt;/span>) A directory structure containing a set of SaLt State (SLS) files.&lt;/td>
&lt;/tr>
&lt;tr class="field">&lt;th class="field-name">Target:&lt;/th>&lt;td class="field-body">(&lt;span class="formula">&lt;i>θ&lt;/i>&lt;/span>) A predicate used to target Minions.&lt;/td>
&lt;/tr>
&lt;tr class="field">&lt;th class="field-name">State files:&lt;/th>&lt;td class="field-body">(&lt;span class="formula">&lt;i>σ&lt;/i>&lt;/span>) A set of SLS files to apply to a target. Each file describes one or more states to be
executed on matched Minions.&lt;/td>
&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;p>These three levels relate in such a way that (&lt;span class="formula">&lt;i>θ&lt;/i> ∈ &lt;i>ϵ&lt;/i>&lt;/span>) , and (&lt;span class="formula">&lt;i>σ&lt;/i> ∈ &lt;i>θ&lt;/i>&lt;/span>). Reordering
these two relations we have that:&lt;/p>
&lt;ul class="simple">
&lt;li>Environments contain targets&lt;/li>
&lt;li>Targets contain states&lt;/li>
&lt;/ul>
&lt;p>Putting these concepts together, we can describe a scenario (top file) in which the state defined in the SLS file
&lt;em>ntp/init.sls&lt;/em> is enforced to all minions.&lt;/p>
&lt;p>&lt;em>top.sls:&lt;/em>&lt;/p>
&lt;div class="highlight">&lt;div style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">
&lt;table style="border-spacing:0;padding:0;margin:0;border:0;width:auto;overflow:auto;display:block;">&lt;tr>&lt;td style="vertical-align:top;padding:0;margin:0;border:0;">
&lt;pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">&lt;code>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">1
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">2
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">3
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td style="vertical-align:top;padding:0;margin:0;border:0;;width:100%">
&lt;pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">&lt;code class="language-yaml" data-lang="yaml">&lt;span style="color:#f92672">base&lt;/span>:
&lt;span style="color:#f92672">&amp;#39;*&amp;#39;&lt;/span>:
- &lt;span style="color:#ae81ff">ntp&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>
&lt;/div>
&lt;div class="section" id="remote-execution">
&lt;h2>Remote Execution&lt;/h2>
&lt;p>Before delving into the more intricate orchestration-like functionalities of salt, let us use the most (probably) basic
operation of salt, which you probably will use &lt;strong>a lot&lt;/strong>.&lt;/p>
&lt;p>The function is &lt;em>cmd.run&lt;/em>, made available from the &lt;em>cmd&lt;/em> execution module (see &lt;a class="footnote-reference" href="#id36" id="id16">[11]&lt;/a> for a list of all available
execution modules). And we can use it like this, from a salt master environment:&lt;/p>
&lt;div class="highlight">&lt;pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">&lt;code class="language-shell" data-lang="shell">root@master:~# salt &lt;span style="color:#ae81ff">\*&lt;/span> cmd.run &lt;span style="color:#e6db74">&amp;#39;hostname &amp;amp;&amp;amp; pwd &amp;amp;&amp;amp; ls -la&amp;#39;&lt;/span>&lt;/code>&lt;/pre>&lt;/div>
&lt;p>This will run the command, or sequence of commands, in every targeted minion and return its result. You can also specify
the shell you want to use with the &lt;em>shell&lt;/em> argument.&lt;/p>
&lt;div class="highlight">&lt;pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">&lt;code class="language-shell" data-lang="shell">root@master:~# salt &lt;span style="color:#ae81ff">\*&lt;/span> cmd.run &lt;span style="color:#e6db74">&amp;#39;hostname &amp;amp;&amp;amp; ls -la&amp;#39;&lt;/span> shell&lt;span style="color:#f92672">=&lt;/span>/bin/bash
minion3:
minion3.local
/root
total &lt;span style="color:#ae81ff">40&lt;/span>
drwx------ &lt;span style="color:#ae81ff">7&lt;/span> root root &lt;span style="color:#ae81ff">4096&lt;/span> Sep &lt;span style="color:#ae81ff">27&lt;/span> 16:35 .
drwxr-xr-x &lt;span style="color:#ae81ff">23&lt;/span> root root &lt;span style="color:#ae81ff">4096&lt;/span> Oct &lt;span style="color:#ae81ff">2&lt;/span> 07:00 ..
-rw------- &lt;span style="color:#ae81ff">1&lt;/span> root root &lt;span style="color:#ae81ff">2336&lt;/span> Sep &lt;span style="color:#ae81ff">29&lt;/span> 14:11 .bash_history
-rw-r--r-- &lt;span style="color:#ae81ff">1&lt;/span> root root &lt;span style="color:#ae81ff">3201&lt;/span> Sep &lt;span style="color:#ae81ff">3&lt;/span> 14:19 .bashrc
drwx------ &lt;span style="color:#ae81ff">3&lt;/span> root root &lt;span style="color:#ae81ff">4096&lt;/span> Sep &lt;span style="color:#ae81ff">19&lt;/span> 15:21 .cache
drwx------ &lt;span style="color:#ae81ff">3&lt;/span> root root &lt;span style="color:#ae81ff">4096&lt;/span> Sep &lt;span style="color:#ae81ff">27&lt;/span> 16:35 .config
drwxr-xr-x &lt;span style="color:#ae81ff">2&lt;/span> root root &lt;span style="color:#ae81ff">4096&lt;/span> Aug &lt;span style="color:#ae81ff">17&lt;/span> 17:00 .nano
-rw-r--r-- &lt;span style="color:#ae81ff">1&lt;/span> root root &lt;span style="color:#ae81ff">148&lt;/span> Aug &lt;span style="color:#ae81ff">17&lt;/span> &lt;span style="color:#ae81ff">2015&lt;/span> .profile
drwx------ &lt;span style="color:#ae81ff">2&lt;/span> root root &lt;span style="color:#ae81ff">4096&lt;/span> Sep &lt;span style="color:#ae81ff">19&lt;/span> 15:21 .ssh
drwxr-xr-x &lt;span style="color:#ae81ff">3&lt;/span> root root &lt;span style="color:#ae81ff">4096&lt;/span> Sep &lt;span style="color:#ae81ff">6&lt;/span> 16:43 .virtualenvs
minion1:
master.local
/root
total &lt;span style="color:#ae81ff">40&lt;/span>
drwx------ &lt;span style="color:#ae81ff">7&lt;/span> root root &lt;span style="color:#ae81ff">4096&lt;/span> Sep &lt;span style="color:#ae81ff">27&lt;/span> 16:35 .
drwxr-xr-x &lt;span style="color:#ae81ff">23&lt;/span> root root &lt;span style="color:#ae81ff">4096&lt;/span> Oct &lt;span style="color:#ae81ff">2&lt;/span> 07:00 ..
-rw------- &lt;span style="color:#ae81ff">1&lt;/span> root root &lt;span style="color:#ae81ff">2336&lt;/span> Sep &lt;span style="color:#ae81ff">29&lt;/span> 14:11 .bash_history
-rw-r--r-- &lt;span style="color:#ae81ff">1&lt;/span> root root &lt;span style="color:#ae81ff">3201&lt;/span> Sep &lt;span style="color:#ae81ff">3&lt;/span> 14:19 .bashrc
drwx------ &lt;span style="color:#ae81ff">3&lt;/span> root root &lt;span style="color:#ae81ff">4096&lt;/span> Sep &lt;span style="color:#ae81ff">19&lt;/span> 15:21 .cache
drwx------ &lt;span style="color:#ae81ff">3&lt;/span> root root &lt;span style="color:#ae81ff">4096&lt;/span> Sep &lt;span style="color:#ae81ff">27&lt;/span> 16:35 .config
drwxr-xr-x &lt;span style="color:#ae81ff">2&lt;/span> root root &lt;span style="color:#ae81ff">4096&lt;/span> Aug &lt;span style="color:#ae81ff">17&lt;/span> 17:00 .nano
-rw-r--r-- &lt;span style="color:#ae81ff">1&lt;/span> root root &lt;span style="color:#ae81ff">148&lt;/span> Aug &lt;span style="color:#ae81ff">17&lt;/span> &lt;span style="color:#ae81ff">2015&lt;/span> .profile
drwx------ &lt;span style="color:#ae81ff">2&lt;/span> root root &lt;span style="color:#ae81ff">4096&lt;/span> Sep &lt;span style="color:#ae81ff">19&lt;/span> 15:21 .ssh
drwxr-xr-x &lt;span style="color:#ae81ff">3&lt;/span> root root &lt;span style="color:#ae81ff">4096&lt;/span> Sep &lt;span style="color:#ae81ff">6&lt;/span> 16:43 .virtualenvs
minion2:
minion2.local
/root
total &lt;span style="color:#ae81ff">40&lt;/span>
drwx------ &lt;span style="color:#ae81ff">7&lt;/span> root root &lt;span style="color:#ae81ff">4096&lt;/span> Sep &lt;span style="color:#ae81ff">27&lt;/span> 16:35 .
drwxr-xr-x &lt;span style="color:#ae81ff">23&lt;/span> root root &lt;span style="color:#ae81ff">4096&lt;/span> Oct &lt;span style="color:#ae81ff">2&lt;/span> 07:00 ..
-rw------- &lt;span style="color:#ae81ff">1&lt;/span> root root &lt;span style="color:#ae81ff">2336&lt;/span> Sep &lt;span style="color:#ae81ff">29&lt;/span> 14:11 .bash_history
-rw-r--r-- &lt;span style="color:#ae81ff">1&lt;/span> root root &lt;span style="color:#ae81ff">3201&lt;/span> Sep &lt;span style="color:#ae81ff">3&lt;/span> 14:19 .bashrc
drwx------ &lt;span style="color:#ae81ff">3&lt;/span> root root &lt;span style="color:#ae81ff">4096&lt;/span> Sep &lt;span style="color:#ae81ff">19&lt;/span> 15:21 .cache
drwx------ &lt;span style="color:#ae81ff">3&lt;/span> root root &lt;span style="color:#ae81ff">4096&lt;/span> Sep &lt;span style="color:#ae81ff">27&lt;/span> 16:35 .config
drwxr-xr-x &lt;span style="color:#ae81ff">2&lt;/span> root root &lt;span style="color:#ae81ff">4096&lt;/span> Aug &lt;span style="color:#ae81ff">17&lt;/span> 17:00 .nano
-rw-r--r-- &lt;span style="color:#ae81ff">1&lt;/span> root root &lt;span style="color:#ae81ff">148&lt;/span> Aug &lt;span style="color:#ae81ff">17&lt;/span> &lt;span style="color:#ae81ff">2015&lt;/span> .profile
drwx------ &lt;span style="color:#ae81ff">2&lt;/span> root root &lt;span style="color:#ae81ff">4096&lt;/span> Sep &lt;span style="color:#ae81ff">19&lt;/span> 15:21 .ssh
drwxr-xr-x &lt;span style="color:#ae81ff">3&lt;/span> root root &lt;span style="color:#ae81ff">4096&lt;/span> Sep &lt;span style="color:#ae81ff">6&lt;/span> 16:43 .virtualenvs
root@master:~#&lt;/code>&lt;/pre>&lt;/div>
&lt;p>Another well used execution module is &lt;em>pkg&lt;/em>, it will try to identify the package system present in the minion's system
and use it to perform the requested operations.&lt;/p>
&lt;div class="highlight">&lt;pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">&lt;code class="language-shell" data-lang="shell">root@master:~# salt minion&lt;span style="color:#ae81ff">\*&lt;/span> pkg.latest_version postfix
minion1:
3.1.0-3ubuntu0.3
minion2:
3.1.0-3ubuntu0.3
minion3:
3.1.0-3ubuntu0.3
root@master:~# salt minion&lt;span style="color:#ae81ff">\*&lt;/span> pkg.install postfix
minion1:
----------
postfix:
----------
new:
3.1.0-3ubuntu0.3
old:
&lt;span style="color:#f92672">(&lt;/span>... suppressed output ...&lt;span style="color:#f92672">)&lt;/span>
minion3:
----------
postfix:
----------
new:
3.1.0-3ubuntu0.3
old:
&lt;span style="color:#f92672">(&lt;/span>... suppressed output ...&lt;span style="color:#f92672">)&lt;/span>
minion2:
----------
postfix:
----------
new:
3.1.0-3ubuntu0.3
old:
&lt;span style="color:#f92672">(&lt;/span>... suppressed output ...&lt;span style="color:#f92672">)&lt;/span>&lt;/code>&lt;/pre>&lt;/div>
&lt;/div>
&lt;div class="section" id="configuration-management">
&lt;h2>Configuration Management&lt;/h2>
&lt;p>Remote execution is very useful, however we may want more complex scenarios to occur, then we can make use of SLS files,
states, and state modules &lt;a class="footnote-reference" href="#id37" id="id17">[12]&lt;/a>.&lt;/p>
&lt;p>From the tree view seen in &lt;a class="reference internal" href="#subsystem-files">Subsystem Files&lt;/a> and the configuration of the &lt;em>file_roots&lt;/em> option, all of our available
states are to be placed in &lt;strong>/srv/salt/states/base&lt;/strong>.&lt;/p>
&lt;p>SLS files are mainly written in YAML Ain't Markup Language (YAML) &lt;a class="footnote-reference" href="#id38" id="id18">[13]&lt;/a> and like most salt related files, can be
templated using Jinja &lt;a class="footnote-reference" href="#id39" id="id19">[14]&lt;/a>, by default.&lt;/p>
&lt;p>&lt;em>ntp/init.sls:&lt;/em>&lt;/p>
&lt;div class="highlight">&lt;div style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">
&lt;table style="border-spacing:0;padding:0;margin:0;border:0;width:auto;overflow:auto;display:block;">&lt;tr>&lt;td style="vertical-align:top;padding:0;margin:0;border:0;">
&lt;pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">&lt;code>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 1
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 2
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 3
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 4
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 5
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 6
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 7
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 8
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 9
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">10
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">11
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">12
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">13
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">14
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">15
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">16
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">17
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">18
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td style="vertical-align:top;padding:0;margin:0;border:0;;width:100%">
&lt;pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">&lt;code class="language-yaml" data-lang="yaml">&lt;span style="color:#f92672">ntp&lt;/span>:
&lt;span style="color:#ae81ff">pkg.installed&lt;/span>
&lt;span style="color:#f92672">service.running&lt;/span>:
- &lt;span style="color:#f92672">enable&lt;/span>: &lt;span style="color:#66d9ef">True&lt;/span>
- &lt;span style="color:#f92672">watch&lt;/span>:
- &lt;span style="color:#f92672">file&lt;/span>: &lt;span style="color:#ae81ff">/etc/ntp.conf&lt;/span>
&lt;span style="color:#f92672">/etc/ntp.conf&lt;/span>:
&lt;span style="color:#f92672">file.managed&lt;/span>:
- &lt;span style="color:#f92672">source&lt;/span>: &lt;span style="color:#ae81ff">salt://ntp/etc/ntp.conf.tmpl&lt;/span>
- &lt;span style="color:#f92672">mode&lt;/span>: &lt;span style="color:#ae81ff">644&lt;/span>
- &lt;span style="color:#f92672">user&lt;/span>: &lt;span style="color:#ae81ff">root&lt;/span>
- &lt;span style="color:#f92672">require&lt;/span>:
- &lt;span style="color:#f92672">pkg&lt;/span>: &lt;span style="color:#ae81ff">ntp&lt;/span>
&lt;span style="color:#f92672">ntp-shutdown&lt;/span>:
&lt;span style="color:#f92672">service.dead&lt;/span>:
- &lt;span style="color:#f92672">name&lt;/span>: &lt;span style="color:#ae81ff">ntp&lt;/span>
- &lt;span style="color:#f92672">onchanges&lt;/span>:
- &lt;span style="color:#f92672">file&lt;/span>: &lt;span style="color:#ae81ff">/etc/ntp.conf&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>
&lt;p>&lt;em>ntp/etc/ntp.conf.tmpl:&lt;/em>&lt;/p>
&lt;div class="highlight">&lt;div style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">
&lt;table style="border-spacing:0;padding:0;margin:0;border:0;width:auto;overflow:auto;display:block;">&lt;tr>&lt;td style="vertical-align:top;padding:0;margin:0;border:0;">
&lt;pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">&lt;code>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 1
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 2
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 3
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 4
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 5
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 6
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 7
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 8
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 9
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">10
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">11
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td style="vertical-align:top;padding:0;margin:0;border:0;;width:100%">
&lt;pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">&lt;code class="language-jinja" data-lang="jinja"># This file managed by Salt, do not edit
server ntp1.example.com
server ntp1.example.com
# Only allow read-only access from localhost
restrict default noquery nopeer
restrict 127.0.0.1
restrict ::1
# Location of drift file
driftfile /var/lib/ntp/ntp.drift&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>
&lt;p>This salt state file, when applied, will:&lt;/p>
&lt;ol class="arabic simple">
&lt;li>install the ntp package, using the system's package manager&lt;/li>
&lt;li>create/change the file &lt;em>/etc/ntp/ntp.conf&lt;/em>, on the minion, according to the contents of the
&lt;em>/srv/salt/states/base/ntp/etc/ntp.conf.tmpl&lt;/em>, on master&lt;/li>
&lt;li>make sure the &lt;em>ntp&lt;/em> service is stopped if there are changes to the previously managed file.&lt;/li>
&lt;li>make sure the &lt;em>ntp&lt;/em> service is running and enabled, restarting it if there were changes to the managed file (it
should not restart because of the the &lt;em>ntp-shutdown&lt;/em> state.&lt;/li>
&lt;/ol>
&lt;p>To apply a state we use the &lt;em>state&lt;/em> execution module, and its function &lt;em>apply&lt;/em>. One key difference between using
execution modules and state modules is the possibility of using the keyword &lt;strong>test&lt;/strong> that, when &lt;em>True&lt;/em>, will not
effectively apply the state but rather it will show what it would do and, in our case, which changes it would apply.&lt;/p>
&lt;div class="highlight">&lt;pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">&lt;code class="language-fallback" data-lang="fallback">root@master:~# salt --state-verbose=True minion2 state.apply ntp test=True
minion2:
----------
ID: ntp-pkg
Function: pkg.installed
Name: ntp
Result: True
Comment: All specified packages are already installed
Started: 15:26:10.712651
Duration: 928.968 ms
Changes:
----------
ID: ntp-pkg
Function: file.managed
Name: /etc/ntp.conf
Result: None
Comment: The file /etc/ntp.conf is set to be changed
Started: 15:26:11.664815
Duration: 63.205 ms
Changes:
----------
diff:
---
+++
@@ -1,53 +1,20 @@
-# /etc/ntp.conf, configuration for ntpd; see ntp.conf(5) for help
+# This file managed by Salt, do not edit
+#
+#
+# With the default settings below, ntpd will only synchronize your clock.
-driftfile /var/lib/ntp/ntp.drift
-
-
-# Enable this if you want statistics to be logged.
-#statsdir /var/log/ntpstats/
-
-statistics loopstats peerstats clockstats
-filegen loopstats file loopstats type day enable
-filegen peerstats file peerstats type day enable
-filegen clockstats file clockstats type day enable
-
-# Specify one or more NTP servers.
# Use servers from the NTP Pool Project. Approved by Ubuntu Technical Board
# on 2011-02-08 (LP: #104525). See http://www.pool.ntp.org/join.html for
# more information.
-server ntp1.somewhere.in.time
-server ntp2.somewhere.in.time
+server ntp1.example.com
+server ntp2.example.com
-# Use Ubuntu&amp;#39;s ntp server as a fallback.
-server ntp.ubuntu.com
-# Access control configuration; see /usr/share/doc/ntp-doc/html/accopt.html for
-# details. The web page &amp;lt;http://support.ntp.org/bin/view/Support/AccessRestrictions&amp;gt;
-# might also be helpful.
-#
-# Note that &amp;#34;restrict&amp;#34; applies to both servers and clients, so a configuration
-# that might be intended to block requests from certain clients could also end
-# up blocking replies from your own upstream servers.
-
-# By default, exchange time with everybody, but don&amp;#39;t allow configuration.
-restrict -4 default kod notrap nomodify nopeer noquery
-restrict -6 default kod notrap nomodify nopeer noquery
-
-# Local users may interrogate the ntp server more closely.
+# Only allow read-only access from localhost
+restrict default noquery nopeer
restrict 127.0.0.1
restrict ::1
-# Clients from this (example!) subnet have unlimited access, but only if
-# cryptographically authenticated.
-#restrict 192.168.123.0 mask 255.255.255.0 notrust
-
-
-# If you want to provide time to your local subnet, change the next line.
-# (Again, the address is an example only.)
-#broadcast 192.168.123.255
-
-# If you want to listen to time broadcasts on your local subnet, de-comment the
-# next lines. Please do this only if you trust everybody on the network!
-#disable auth
-#broadcastclient
+# Location of drift file
+driftfile /var/lib/ntp/ntp.drift
----------
ID: ntp-pkg
Function: service.running
Name: ntp
Result: None
Comment: Service ntp is set to start
Started: 15:26:11.729479
Duration: 134.939 ms
Changes:
----------
ID: ntpdate
Function: pkg.installed
Result: True
Comment: All specified packages are already installed
Started: 15:26:11.864881
Duration: 25.952 ms
Changes:
Summary for minion2
------------
Succeeded: 4 (unchanged=2, changed=1)
Failed: 0
------------
Total states run: 4
Total run time: 1.153 s&lt;/code>&lt;/pre>&lt;/div>
&lt;p>Now for the actual application of the state&lt;/p>
&lt;div class="highlight">&lt;pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">&lt;code class="language-fallback" data-lang="fallback">----------
ID: ntp-pkg
Function: pkg.installed
Name: ntp
Result: True
Comment: All specified packages are already installed
Started: 15:30:11.488468
Duration: 902.224 ms
Changes:
----------
ID: ntp-pkg
Function: file.managed
Name: /etc/ntp.conf
Result: True
Comment: File /etc/ntp.conf updated
Started: 15:30:12.394500
Duration: 94.999 ms
Changes:
----------
diff:
---
+++
@@ -1,53 +1,20 @@
-# /etc/ntp.conf, configuration for ntpd; see ntp.conf(5) for help
+# This file managed by Salt, do not edit
+#
+#
+# With the default settings below, ntpd will only synchronize your clock.
-driftfile /var/lib/ntp/ntp.drift
-
-
-# Enable this if you want statistics to be logged.
-#statsdir /var/log/ntpstats/
-
-statistics loopstats peerstats clockstats
-filegen loopstats file loopstats type day enable
-filegen peerstats file peerstats type day enable
-filegen clockstats file clockstats type day enable
-
-# Specify one or more NTP servers.
# Use servers from the NTP Pool Project. Approved by Ubuntu Technical Board
# on 2011-02-08 (LP: #104525). See http://www.pool.ntp.org/join.html for
# more information.
-server ntp1.somewhere.in.time
-server ntp2.somewhere.in.time
+server ntp1.example.com
+server ntp2.example.com
-# Use Ubuntu&amp;#39;s ntp server as a fallback.
-server ntp.ubuntu.com
-# Access control configuration; see /usr/share/doc/ntp-doc/html/accopt.html for
-# details. The web page &amp;lt;http://support.ntp.org/bin/view/Support/AccessRestrictions&amp;gt;
-# might also be helpful.
-#
-# Note that &amp;#34;restrict&amp;#34; applies to both servers and clients, so a configuration
-# that might be intended to block requests from certain clients could also end
-# up blocking replies from your own upstream servers.
-
-# By default, exchange time with everybody, but don&amp;#39;t allow configuration.
-restrict -4 default kod notrap nomodify nopeer noquery
-restrict -6 default kod notrap nomodify nopeer noquery
-
-# Local users may interrogate the ntp server more closely.
+# Only allow read-only access from localhost
+restrict default noquery nopeer
restrict 127.0.0.1
restrict ::1
-# Clients from this (example!) subnet have unlimited access, but only if
-# cryptographically authenticated.
-#restrict 192.168.123.0 mask 255.255.255.0 notrust
-
-
-# If you want to provide time to your local subnet, change the next line.
-# (Again, the address is an example only.)
-#broadcast 192.168.123.255
-
-# If you want to listen to time broadcasts on your local subnet, de-comment the
-# next lines. Please do this only if you trust everybody on the network!
-#disable auth
-#broadcastclient
+# Location of drift file
+driftfile /var/lib/ntp/ntp.drift
----------
ID: ntp-pkg
Function: service.running
Name: ntp
Result: True
Started: 15:30:12.587597
Duration: 49.742 ms
Changes:
----------
ID: ntpdate
Function: pkg.installed
Result: True
Comment: All specified packages are already installed
Started: 15:30:12.637827
Duration: 29.567 ms
Changes:
Summary for minion2
------------
Succeeded: 4 (changed=1)
Failed: 0
------------
Total states run: 4
Total run time: 1.077 s&lt;/code>&lt;/pre>&lt;/div>
&lt;p>Now if we apply the &lt;em>ntp&lt;/em> state again we will see no changes, and no service stop/start.&lt;/p>
&lt;p>SLS files can include other SLS files, use externally generated data and depend on states declared on included SLS
files.&lt;/p>
&lt;p>Each state can &lt;em>require&lt;/em> other state completion, be &lt;em>required in&lt;/em> other states, &lt;em>watch&lt;/em> a certain state, execute
&lt;em>unless&lt;/em> a given command returns True, execute &lt;em>only if&lt;/em> a certain command returns True and more. &lt;a class="footnote-reference" href="#id27" id="id20">[2]&lt;/a> &lt;a class="footnote-reference" href="#id40" id="id21">[15]&lt;/a>&lt;/p>
&lt;/div>
&lt;div class="section" id="storing-and-accessing-data">
&lt;h2>Storing and Accessing Data&lt;/h2>
&lt;p>When planning and implementing this type of orchestration, it is useful to have information that can be shared between
states, and which is known only to the intended recipients (i.e: specific minions).&lt;/p>
&lt;p>In salt we have the &lt;em>Pillar&lt;/em>, Its configuration is similar to the one used by States, as we saw in &lt;a class="reference internal" href="#subsystem-files">Subsystem Files&lt;/a>
its files are located in &lt;strong>/srv/salt/pillar/base&lt;/strong>.&lt;/p>
&lt;p>The way the information is defined and assigned is similar to states, but instead of defining State Module executions we
define information in the form of YAML, as shown&lt;/p>
&lt;p>&lt;em>/srv/salt/pillar/base/ntp/init.sls:&lt;/em>&lt;/p>
&lt;div class="highlight">&lt;div style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">
&lt;table style="border-spacing:0;padding:0;margin:0;border:0;width:auto;overflow:auto;display:block;">&lt;tr>&lt;td style="vertical-align:top;padding:0;margin:0;border:0;">
&lt;pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">&lt;code>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">1
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">2
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">3
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">4
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">5
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">6
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td style="vertical-align:top;padding:0;margin:0;border:0;;width:100%">
&lt;pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">&lt;code class="language-yaml" data-lang="yaml">&lt;span style="color:#f92672">ntp&lt;/span>:
&lt;span style="color:#f92672">servers&lt;/span>:
- &lt;span style="color:#ae81ff">ntp1.somewhere.in.time&lt;/span>
- &lt;span style="color:#ae81ff">ntp2.somewhere.in.time&lt;/span>
&lt;span style="color:#f92672">ntp_conf&lt;/span>: &lt;span style="color:#ae81ff">/etc/ntp.conf&lt;/span>
&lt;span style="color:#f92672">drift_file&lt;/span>: &lt;span style="color:#ae81ff">/var/lib/ntp/ntp.drift&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>
&lt;p>and we define the attributions of data in the corresponding pillar top file, where all minions are assigned information
regarding Network Time Protocol (NTP) server domain names and their NTP service configuration file.&lt;/p>
&lt;p>&lt;em>/srv/salt/pillar/base/top.sls:&lt;/em>&lt;/p>
&lt;div class="highlight">&lt;div style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">
&lt;table style="border-spacing:0;padding:0;margin:0;border:0;width:auto;overflow:auto;display:block;">&lt;tr>&lt;td style="vertical-align:top;padding:0;margin:0;border:0;">
&lt;pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">&lt;code>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">1
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">2
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">3
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td style="vertical-align:top;padding:0;margin:0;border:0;;width:100%">
&lt;pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">&lt;code class="language-yaml" data-lang="yaml">&lt;span style="color:#f92672">base&lt;/span>:
&lt;span style="color:#f92672">&amp;#39;*&amp;#39;&lt;/span>:
- &lt;span style="color:#ae81ff">ntp&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>
&lt;p>This information can be used within states or even template files, using the &lt;em>pillar&lt;/em> keyword.&lt;/p>
&lt;p>Taking the previous SLS file and its managed template, we can now dynamically fill NTP related info.&lt;/p>
&lt;p>&lt;em>ntp/init.sls:&lt;/em>&lt;/p>
&lt;div class="highlight">&lt;div style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">
&lt;table style="border-spacing:0;padding:0;margin:0;border:0;width:auto;overflow:auto;display:block;">&lt;tr>&lt;td style="vertical-align:top;padding:0;margin:0;border:0;">
&lt;pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">&lt;code>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 1
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 2
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 3
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 4
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 5
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 6
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 7
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 8
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 9
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">10
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">11
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">12
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">13
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">14
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">15
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">16
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">17
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">18
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td style="vertical-align:top;padding:0;margin:0;border:0;;width:100%">
&lt;pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">&lt;code class="language-yaml" data-lang="yaml">&lt;span style="color:#f92672">ntp&lt;/span>:
&lt;span style="color:#ae81ff">pkg.installed&lt;/span>
&lt;span style="color:#f92672">service.running&lt;/span>:
- &lt;span style="color:#f92672">enable&lt;/span>: &lt;span style="color:#66d9ef">True&lt;/span>
- &lt;span style="color:#f92672">watch&lt;/span>:
- &lt;span style="color:#f92672">file&lt;/span>: {{ &lt;span style="color:#ae81ff">pillar[&amp;#39;ntp&amp;#39;][&amp;#39;ntp_conf&amp;#39;] }}&lt;/span>
{{ &lt;span style="color:#f92672">pillar[&amp;#39;ntp&amp;#39;][&amp;#39;ntp_conf&amp;#39;] }}&lt;/span>:
&lt;span style="color:#f92672">file.managed&lt;/span>:
- &lt;span style="color:#f92672">source&lt;/span>: &lt;span style="color:#ae81ff">salt://ntp/etc/ntp.conf.tmpl&lt;/span>
- &lt;span style="color:#f92672">mode&lt;/span>: &lt;span style="color:#ae81ff">644&lt;/span>
- &lt;span style="color:#f92672">user&lt;/span>: &lt;span style="color:#ae81ff">root&lt;/span>
- &lt;span style="color:#f92672">require&lt;/span>:
- &lt;span style="color:#f92672">pkg&lt;/span>: &lt;span style="color:#ae81ff">ntp&lt;/span>
&lt;span style="color:#f92672">ntp-shutdown&lt;/span>:
&lt;span style="color:#f92672">service.dead&lt;/span>:
- &lt;span style="color:#f92672">name&lt;/span>: &lt;span style="color:#ae81ff">ntp&lt;/span>
- &lt;span style="color:#f92672">onchanges&lt;/span>:
- &lt;span style="color:#f92672">file&lt;/span>: {{ &lt;span style="color:#ae81ff">pillar[&amp;#39;ntp&amp;#39;][&amp;#39;ntp_conf&amp;#39;] }}&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>
&lt;p>&lt;em>ntp/etc/ntp.conf.tmpl:&lt;/em>&lt;/p>
&lt;div class="highlight">&lt;div style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">
&lt;table style="border-spacing:0;padding:0;margin:0;border:0;width:auto;overflow:auto;display:block;">&lt;tr>&lt;td style="vertical-align:top;padding:0;margin:0;border:0;">
&lt;pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">&lt;code>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 1
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 2
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 3
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 4
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 5
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 6
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 7
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 8
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 9
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">10
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">11
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">12
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td style="vertical-align:top;padding:0;margin:0;border:0;;width:100%">
&lt;pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">&lt;code class="language-jinja" data-lang="jinja"># This file managed by Salt, do not edit
&lt;span style="color:#75715e">{%&lt;/span> &lt;span style="color:#66d9ef">for&lt;/span> server &lt;span style="color:#66d9ef">in&lt;/span> pillar&lt;span style="color:#f92672">[&lt;/span>&lt;span style="color:#e6db74">&amp;#39;ntp&amp;#39;&lt;/span>&lt;span style="color:#f92672">][&lt;/span>&lt;span style="color:#e6db74">&amp;#39;servers&amp;#39;&lt;/span>&lt;span style="color:#f92672">]&lt;/span> &lt;span style="color:#75715e">%}&lt;/span>
server &lt;span style="color:#75715e">{{&lt;/span> server &lt;span style="color:#75715e">}}&lt;/span>
&lt;span style="color:#75715e">{%&lt;/span> &lt;span style="color:#66d9ef">endfor&lt;/span> &lt;span style="color:#75715e">%}&lt;/span>
# Only allow read-only access from localhost
restrict default noquery nopeer
restrict 127.0.0.1
restrict ::1
# Location of drift file
driftfile &lt;span style="color:#75715e">{{&lt;/span> pillar&lt;span style="color:#f92672">[&lt;/span>&lt;span style="color:#e6db74">&amp;#39;ntp&amp;#39;&lt;/span>&lt;span style="color:#f92672">][&lt;/span>&lt;span style="color:#e6db74">&amp;#39;drift_file&amp;#39;&lt;/span>&lt;span style="color:#f92672">]&lt;/span> &lt;span style="color:#75715e">}}&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>
&lt;p>This example could be further enhanced, let us try and make this able to configure ntp servers on a windows machine. For
that we need to use another data source in the salt system, the &lt;em>grains&lt;/em>, which is generated by the minion itself.&lt;/p>
&lt;div class="highlight">&lt;pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">&lt;code class="language-fallback" data-lang="fallback">root@master:~# salt win\* grains.get os
windows1:
Windows
root@master:~#&lt;/code>&lt;/pre>&lt;/div>
&lt;p>Interesting, so now we can have selective behaviour in our state:&lt;/p>
&lt;p>&lt;em>ntp/etc/ntp.conf.tmpl:&lt;/em>&lt;/p>
&lt;div class="highlight">&lt;div style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">
&lt;table style="border-spacing:0;padding:0;margin:0;border:0;width:auto;overflow:auto;display:block;">&lt;tr>&lt;td style="vertical-align:top;padding:0;margin:0;border:0;">
&lt;pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">&lt;code>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 1
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 2
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 3
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 4
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 5
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 6
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 7
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 8
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 9
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">10
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">11
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">12
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">13
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">14
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">15
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">16
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">17
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">18
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">19
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">20
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">21
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">22
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">23
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td style="vertical-align:top;padding:0;margin:0;border:0;;width:100%">
&lt;pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">&lt;code class="language-jinja" data-lang="jinja">ntp:
&lt;span style="color:#75715e">{%&lt;/span> &lt;span style="color:#66d9ef">if&lt;/span> grains&lt;span style="color:#f92672">[&lt;/span>&lt;span style="color:#e6db74">&amp;#39;os&amp;#39;&lt;/span>&lt;span style="color:#f92672">]&lt;/span> &lt;span style="color:#f92672">==&lt;/span> &lt;span style="color:#e6db74">&amp;#39;Windows&amp;#39;&lt;/span> &lt;span style="color:#75715e">%}&lt;/span>
ntp.managed:
- servers: &lt;span style="color:#75715e">{{&lt;/span> pillar&lt;span style="color:#f92672">[&lt;/span>&lt;span style="color:#e6db74">&amp;#39;ntp&amp;#39;&lt;/span>&lt;span style="color:#f92672">][&lt;/span>&lt;span style="color:#e6db74">&amp;#39;servers&amp;#39;&lt;/span>&lt;span style="color:#f92672">]&lt;/span> &lt;span style="color:#75715e">}}&lt;/span>
&lt;span style="color:#75715e">{%&lt;/span> &lt;span style="color:#66d9ef">else&lt;/span> &lt;span style="color:#75715e">%}&lt;/span>
pkg.installed
service.running:
- enable: True
- watch:
- file: &lt;span style="color:#75715e">{{&lt;/span> pillar&lt;span style="color:#f92672">[&lt;/span>&lt;span style="color:#e6db74">&amp;#39;ntp&amp;#39;&lt;/span>&lt;span style="color:#f92672">][&lt;/span>&lt;span style="color:#e6db74">&amp;#39;ntp_conf&amp;#39;&lt;/span>&lt;span style="color:#f92672">]&lt;/span> &lt;span style="color:#75715e">}}&lt;/span>
&lt;span style="color:#75715e">{{&lt;/span> pillar&lt;span style="color:#f92672">[&lt;/span>&lt;span style="color:#e6db74">&amp;#39;ntp&amp;#39;&lt;/span>&lt;span style="color:#f92672">][&lt;/span>&lt;span style="color:#e6db74">&amp;#39;ntp_conf&amp;#39;&lt;/span>&lt;span style="color:#f92672">]&lt;/span> &lt;span style="color:#75715e">}}&lt;/span>:
file.managed:
- source: salt://ntp/etc/ntp.conf.tmpl
- mode: 644
- user: root
- require:
- pkg: ntp
ntp-shutdown:
service.dead:
- name: ntp
- onchanges:
- file: &lt;span style="color:#75715e">{{&lt;/span> pillar&lt;span style="color:#f92672">[&lt;/span>&lt;span style="color:#e6db74">&amp;#39;ntp&amp;#39;&lt;/span>&lt;span style="color:#f92672">][&lt;/span>&lt;span style="color:#e6db74">&amp;#39;ntp_conf&amp;#39;&lt;/span>&lt;span style="color:#f92672">]&lt;/span> &lt;span style="color:#75715e">}}&lt;/span>
&lt;span style="color:#75715e">{%&lt;/span> &lt;span style="color:#66d9ef">endif&lt;/span> &lt;span style="color:#75715e">%}&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>
&lt;p>And now if we apply the state to the windows machine:&lt;/p>
&lt;div class="highlight">&lt;pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">&lt;code class="language-fallback" data-lang="fallback">root@master:~# salt win\* state.apply ntp test&lt;/code>&lt;/pre>&lt;/div>
&lt;/div>
&lt;div class="section" id="event-system">
&lt;h2>Event System&lt;/h2>
&lt;p>In &lt;a class="reference internal" href="#introduction">Introduction&lt;/a> we saw in &lt;a class="reference external" href="#fig-saltstack">Figure 2&lt;/a> a general view of the Salt System, which has a Reactor
element, this element is part of a more broad system, the Event System (represented in &lt;a class="reference external" href="#fig-reactor">Figure 6&lt;/a> which
is &amp;quot;used to fire off events enabling third party applications or external processes to react to behavior within Salt&amp;quot;
&lt;a class="footnote-reference" href="#id31" id="id22">[6]&lt;/a>.&lt;/p>
&lt;p>The Event System has two main components:&lt;/p>
&lt;table class="docutils field-list" frame="void" rules="none">
&lt;col class="field-name" />
&lt;col class="field-body" />
&lt;tbody valign="top">
&lt;tr class="field">&lt;th class="field-name">Event Socket:&lt;/th>&lt;td class="field-body">from where events are published&lt;/td>
&lt;/tr>
&lt;tr class="field">&lt;th class="field-name">Event Library:&lt;/th>&lt;td class="field-body">used for listening to events and forward them to the salt system&lt;/td>
&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;div class="figure align-center" id="fig-reactor">
&lt;a class="reference external image-reference" href="./img/salt_reactor.png">&lt;img alt="Figure 6" src="./img/salt_reactor.png" />&lt;/a>
&lt;p class="caption">Saltstack Event System Flow Example&lt;/p>
&lt;/div>
&lt;p>&amp;quot;The event system is a local ZeroMQ PUB interface which fires salt events. This event bus is an open system used for
sending information notifying Salt and other systems about operations&amp;quot; &lt;a class="footnote-reference" href="#id33" id="id23">[8]&lt;/a>.
This associates SLS files to event tags on the master, which the SLS files in turn represent reactions.&lt;/p>
&lt;p>This means that the reactor system has two steps to work properly. First, the reactor key needs to be configured in the
master configuration file, this associates event tags SLS reaction files. Second, these reaction files use a YAML data
structure similar to the state system to define which reactions are to be executed.&lt;/p>
&lt;p>&lt;em>/etc/salt/master:&lt;/em>&lt;/p>
&lt;div class="highlight">&lt;div style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">
&lt;table style="border-spacing:0;padding:0;margin:0;border:0;width:auto;overflow:auto;display:block;">&lt;tr>&lt;td style="vertical-align:top;padding:0;margin:0;border:0;">
&lt;pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">&lt;code>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">1
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">2
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">3
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">4
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">5
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">6
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">7
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td style="vertical-align:top;padding:0;margin:0;border:0;;width:100%">
&lt;pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">&lt;code class="language-yaml" data-lang="yaml">&lt;span style="color:#ae81ff">(... suppressed ...)&lt;/span>
&lt;span style="color:#f92672">reactor&lt;/span>:
- &lt;span style="color:#f92672">&amp;#39;salt/auth&amp;#39;&lt;/span>:
- &lt;span style="color:#ae81ff">/srv/salt/reactor/auth-pending.sls&lt;/span>
&lt;span style="color:#ae81ff">(... suppressed ...)&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>
&lt;p>this entry means that for every &lt;em>'salt/auth'&lt;/em> event, the reactor state &lt;em>/srv/salt/reactor/auth-pending.sls&lt;/em> should be
executed. Let us see what that reactor state actually does. &lt;a class="footnote-reference" href="#id33" id="id24">[8]&lt;/a>&lt;/p>
&lt;p>&lt;em>auth-pending.sls:&lt;/em>&lt;/p>
&lt;div class="highlight">&lt;div style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">
&lt;table style="border-spacing:0;padding:0;margin:0;border:0;width:auto;overflow:auto;display:block;">&lt;tr>&lt;td style="vertical-align:top;padding:0;margin:0;border:0;">
&lt;pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">&lt;code>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 1
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 2
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 3
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 4
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 5
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 6
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 7
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 8
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 9
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">10
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">11
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">12
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">13
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">14
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">15
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">16
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">17
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">18
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">19
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td style="vertical-align:top;padding:0;margin:0;border:0;;width:100%">
&lt;pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">&lt;code class="language-jinja" data-lang="jinja">&lt;span style="color:#75715e">{# minion failed to authenticate -- remove accepted key #}&lt;/span>
&lt;span style="color:#75715e">{%&lt;/span> &lt;span style="color:#66d9ef">if&lt;/span> &lt;span style="color:#66d9ef">not&lt;/span> data&lt;span style="color:#f92672">[&lt;/span>&lt;span style="color:#e6db74">&amp;#39;result&amp;#39;&lt;/span>&lt;span style="color:#f92672">]&lt;/span> &lt;span style="color:#66d9ef">and&lt;/span> data&lt;span style="color:#f92672">[&lt;/span>&lt;span style="color:#e6db74">&amp;#39;id&amp;#39;&lt;/span>&lt;span style="color:#f92672">]&lt;/span>.endswith&lt;span style="color:#f92672">(&lt;/span>&lt;span style="color:#e6db74">&amp;#39;mydomain.com&amp;#39;&lt;/span>&lt;span style="color:#f92672">)&lt;/span> &lt;span style="color:#75715e">%}&lt;/span>
minion_remove:
wheel.key.delete:
- match: &lt;span style="color:#75715e">{{&lt;/span> data&lt;span style="color:#f92672">[&lt;/span>&lt;span style="color:#e6db74">&amp;#39;id&amp;#39;&lt;/span>&lt;span style="color:#f92672">]&lt;/span> &lt;span style="color:#75715e">}}&lt;/span>
minion_rejoin:
cmd.cmd.run:
- tgt: master.local
- arg:
- ssh -o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no &amp;#34;&lt;span style="color:#75715e">{{&lt;/span> data&lt;span style="color:#f92672">[&lt;/span>&lt;span style="color:#e6db74">&amp;#39;id&amp;#39;&lt;/span>&lt;span style="color:#f92672">]&lt;/span> &lt;span style="color:#75715e">}}&lt;/span>&amp;#34; &amp;#39;sleep 10 &amp;amp;&amp;amp; systemctl restart salt-minion&amp;#39;
&lt;span style="color:#75715e">{%&lt;/span> &lt;span style="color:#66d9ef">endif&lt;/span> &lt;span style="color:#75715e">%}&lt;/span>
&lt;span style="color:#75715e">{# minion is sending new key -- accept this key #}&lt;/span>
&lt;span style="color:#75715e">{%&lt;/span> &lt;span style="color:#66d9ef">if&lt;/span> &lt;span style="color:#e6db74">&amp;#39;act&amp;#39;&lt;/span> &lt;span style="color:#66d9ef">in&lt;/span> data &lt;span style="color:#66d9ef">and&lt;/span> data&lt;span style="color:#f92672">[&lt;/span>&lt;span style="color:#e6db74">&amp;#39;act&amp;#39;&lt;/span>&lt;span style="color:#f92672">]&lt;/span> &lt;span style="color:#f92672">==&lt;/span> &lt;span style="color:#e6db74">&amp;#39;pend&amp;#39;&lt;/span> &lt;span style="color:#66d9ef">and&lt;/span> data&lt;span style="color:#f92672">[&lt;/span>&lt;span style="color:#e6db74">&amp;#39;id&amp;#39;&lt;/span>&lt;span style="color:#f92672">]&lt;/span>.endswith&lt;span style="color:#f92672">(&lt;/span>&lt;span style="color:#e6db74">&amp;#39;mydomain.com&amp;#39;&lt;/span>&lt;span style="color:#f92672">)&lt;/span> &lt;span style="color:#75715e">%}&lt;/span>
minion_add:
wheel.key.accept:
- match: &lt;span style="color:#75715e">{{&lt;/span> data&lt;span style="color:#f92672">[&lt;/span>&lt;span style="color:#e6db74">&amp;#39;id&amp;#39;&lt;/span>&lt;span style="color:#f92672">]&lt;/span> &lt;span style="color:#75715e">}}&lt;/span>
&lt;span style="color:#75715e">{%&lt;/span> &lt;span style="color:#66d9ef">endif&lt;/span> &lt;span style="color:#75715e">%}&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>
&lt;p>&amp;quot;In this SLS file, we say that if the key was rejected we will delete the key on the master and then also tell the
master to ssh in to the minion and tell it to restart the minion, since a minion process will die if the key is
rejected.
We also say that if the key is pending and the id starts with ink we will accept the key. A minion that is waiting on a
pending key will retry authentication every ten seconds by default.&amp;quot; &lt;a class="footnote-reference" href="#id33" id="id25">[8]&lt;/a>&lt;/p>
&lt;/div>
&lt;div class="section" id="conclusion">
&lt;h2>Conclusion&lt;/h2>
&lt;p>In this post we saw from where some of the system configuration tools methods come from, and the similarities between
then and now. We briefly explored the content management and orchestration tool Salt, saw how to execute commands
remotely via the &lt;em>cmd.run&lt;/em> execution module function, how to orchestrate more complex scenarios with the &lt;em>state.apply&lt;/em>
state module function, how to generate, share and fetch static and dynamic information via the &lt;em>grains&lt;/em> and the
&lt;em>pillar&lt;/em> subsystems, and we also saw how to catch certain events and react to them via the &lt;a class="reference internal" href="#event-system">Event System&lt;/a>.
Hope this was useful, and that now you have some grasp of what is salt and how it can be powerful to plan and manage an
IT infrastructure.
Feel free to use the comment box below to ask any questions.&lt;/p>
&lt;/div>
&lt;div class="section" id="references">
&lt;h2>References&lt;/h2>
&lt;table class="docutils footnote" frame="void" id="id26" rules="none">
&lt;colgroup>&lt;col class="label" />&lt;col />&lt;/colgroup>
&lt;tbody valign="top">
&lt;tr>&lt;td class="label">&lt;a class="fn-backref" href="#id3">[1]&lt;/a>&lt;/td>&lt;td>J. O. Benson, J. J. Prevost, and P. Rad,
&lt;em>Survey of automated software deployment for computational and engineering research,&lt;/em>
&lt;em>10th Annual International Systems Conference&lt;/em>,
SysCon 2016 - Proceedings, 2016.&lt;/td>&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;table class="docutils footnote" frame="void" id="id27" rules="none">
&lt;colgroup>&lt;col class="label" />&lt;col />&lt;/colgroup>
&lt;tbody valign="top">
&lt;tr>&lt;td class="label">[2]&lt;/td>&lt;td>&lt;em>(&lt;a class="fn-backref" href="#id1">1&lt;/a>, &lt;a class="fn-backref" href="#id20">2&lt;/a>)&lt;/em> &lt;ol class="last upperalpha simple" start="13">
&lt;li>Torrinha, &lt;em>Adaptive Management and Administration of IT Infrastructures&lt;/em>, MSc thesis, Instituto Superior Técnico, Lisbon, Portugal, 2017.&lt;/li>
&lt;/ol>
&lt;/td>&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;table class="docutils footnote" frame="void" id="id28" rules="none">
&lt;colgroup>&lt;col class="label" />&lt;col />&lt;/colgroup>
&lt;tbody valign="top">
&lt;tr>&lt;td class="label">[3]&lt;/td>&lt;td>&lt;em>(&lt;a class="fn-backref" href="#id2">1&lt;/a>, &lt;a class="fn-backref" href="#id9">2&lt;/a>)&lt;/em> &lt;ol class="last upperalpha simple" start="20">
&lt;li>Delaet, W. Joosen, and B. Vanbrabant, &lt;em>A survey of system configuration tools&lt;/em>, International Conference on Large Installation System Adminstration, pp. 1 - 8, 2010.&lt;/li>
&lt;/ol>
&lt;/td>&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;table class="docutils footnote" frame="void" id="id29" rules="none">
&lt;colgroup>&lt;col class="label" />&lt;col />&lt;/colgroup>
&lt;tbody valign="top">
&lt;tr>&lt;td class="label">[4]&lt;/td>&lt;td>&lt;em>(&lt;a class="fn-backref" href="#id4">1&lt;/a>, &lt;a class="fn-backref" href="#id5">2&lt;/a>, &lt;a class="fn-backref" href="#id6">3&lt;/a>, &lt;a class="fn-backref" href="#id7">4&lt;/a>, &lt;a class="fn-backref" href="#id8">5&lt;/a>)&lt;/em> &lt;ol class="last upperalpha simple" start="3">
&lt;li>Ebert, G. Gallardo, J. Hernantes, and N. Serrano, &lt;em>DevOps&lt;/em>, vol. 33, no. 3, pp. 94 - 100, 2016.&lt;/li>
&lt;/ol>
&lt;/td>&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;table class="docutils footnote" frame="void" id="id30" rules="none">
&lt;colgroup>&lt;col class="label" />&lt;col />&lt;/colgroup>
&lt;tbody valign="top">
&lt;tr>&lt;td class="label">&lt;a class="fn-backref" href="#id10">[5]&lt;/a>&lt;/td>&lt;td>--, &lt;em>Remote Execution - Saltstack Documentation - v2018.3.2&lt;/em>, 2018. &lt;a class="reference external" href="https://docs.saltstack.com/en/latest/topics/execution/index.html">[link]&lt;/a>&lt;/td>&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;table class="docutils footnote" frame="void" id="id31" rules="none">
&lt;colgroup>&lt;col class="label" />&lt;col />&lt;/colgroup>
&lt;tbody valign="top">
&lt;tr>&lt;td class="label">[6]&lt;/td>&lt;td>&lt;em>(&lt;a class="fn-backref" href="#id11">1&lt;/a>, &lt;a class="fn-backref" href="#id22">2&lt;/a>)&lt;/em> --, &lt;em>Event System - Saltstack Documentation - v2018.3.2&lt;/em>, 2018. &lt;a class="reference external" href="https://docs.saltstack.com/en/latest/topics/event/events.html">[link]&lt;/a>&lt;/td>&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;table class="docutils footnote" frame="void" id="id32" rules="none">
&lt;colgroup>&lt;col class="label" />&lt;col />&lt;/colgroup>
&lt;tbody valign="top">
&lt;tr>&lt;td class="label">&lt;a class="fn-backref" href="#id12">[7]&lt;/a>&lt;/td>&lt;td>--, &lt;em>Beacons - Saltstack Documentation - v2018.3.2&lt;/em>, 2018. &lt;a class="reference external" href="https://docs.saltstack.com/en/latest/topics/beacons/index.html">[link]&lt;/a>&lt;/td>&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;table class="docutils footnote" frame="void" id="id33" rules="none">
&lt;colgroup>&lt;col class="label" />&lt;col />&lt;/colgroup>
&lt;tbody valign="top">
&lt;tr>&lt;td class="label">[8]&lt;/td>&lt;td>&lt;em>(&lt;a class="fn-backref" href="#id13">1&lt;/a>, &lt;a class="fn-backref" href="#id23">2&lt;/a>, &lt;a class="fn-backref" href="#id24">3&lt;/a>, &lt;a class="fn-backref" href="#id25">4&lt;/a>)&lt;/em> --, &lt;em>Reactor System - Saltstack Documentation - v2018.3.2&lt;/em>, 2018. &lt;a class="reference external" href="https://docs.saltstack.com/en/latest/topics/reactor/index.html">[link]&lt;/a>&lt;/td>&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;table class="docutils footnote" frame="void" id="id34" rules="none">
&lt;colgroup>&lt;col class="label" />&lt;col />&lt;/colgroup>
&lt;tbody valign="top">
&lt;tr>&lt;td class="label">&lt;a class="fn-backref" href="#id14">[9]&lt;/a>&lt;/td>&lt;td>--, &lt;em>Grains - Saltstack Documentation - v2018.3.2&lt;/em>, 2018. &lt;a class="reference external" href="https://docs.saltstack.com/en/latest/topics/grains/index.html">[link]&lt;/a>&lt;/td>&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;table class="docutils footnote" frame="void" id="id35" rules="none">
&lt;colgroup>&lt;col class="label" />&lt;col />&lt;/colgroup>
&lt;tbody valign="top">
&lt;tr>&lt;td class="label">&lt;a class="fn-backref" href="#id15">[10]&lt;/a>&lt;/td>&lt;td>--, &lt;em>Pillar - Saltstack Documentation - v2018.3.2&lt;/em>, 2018. &lt;a class="reference external" href="https://docs.saltstack.com/en/latest/topics/pillar/index.html">[link]&lt;/a>&lt;/td>&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;table class="docutils footnote" frame="void" id="id36" rules="none">
&lt;colgroup>&lt;col class="label" />&lt;col />&lt;/colgroup>
&lt;tbody valign="top">
&lt;tr>&lt;td class="label">&lt;a class="fn-backref" href="#id16">[11]&lt;/a>&lt;/td>&lt;td>--, &lt;em>List of execution modules - Saltstack Documentation - v2018.3.2&lt;/em>, 2018 &lt;a class="reference external" href="https://docs.saltstack.com/en/latest/ref/modules/all/">[link]&lt;/a>&lt;/td>&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;table class="docutils footnote" frame="void" id="id37" rules="none">
&lt;colgroup>&lt;col class="label" />&lt;col />&lt;/colgroup>
&lt;tbody valign="top">
&lt;tr>&lt;td class="label">&lt;a class="fn-backref" href="#id17">[12]&lt;/a>&lt;/td>&lt;td>--, &lt;em>List of state modules - Saltstack Documentation - v2018.3.2&lt;/em>, 2018 &lt;a class="reference external" href="https://docs.saltstack.com/en/latest/ref/states/all/">[link]&lt;/a>&lt;/td>&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;table class="docutils footnote" frame="void" id="id38" rules="none">
&lt;colgroup>&lt;col class="label" />&lt;col />&lt;/colgroup>
&lt;tbody valign="top">
&lt;tr>&lt;td class="label">&lt;a class="fn-backref" href="#id18">[13]&lt;/a>&lt;/td>&lt;td>--, YAML website &lt;a class="reference external" href="http://yaml.org/">[link]&lt;/a>&lt;/td>&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;table class="docutils footnote" frame="void" id="id39" rules="none">
&lt;colgroup>&lt;col class="label" />&lt;col />&lt;/colgroup>
&lt;tbody valign="top">
&lt;tr>&lt;td class="label">&lt;a class="fn-backref" href="#id19">[14]&lt;/a>&lt;/td>&lt;td>--, Jinja2 website &lt;a class="reference external" href="https://palletsprojects.com/p/jinja/">[link]&lt;/a>&lt;/td>&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;table class="docutils footnote" frame="void" id="id40" rules="none">
&lt;colgroup>&lt;col class="label" />&lt;col />&lt;/colgroup>
&lt;tbody valign="top">
&lt;tr>&lt;td class="label">&lt;a class="fn-backref" href="#id21">[15]&lt;/a>&lt;/td>&lt;td>--, &lt;em>Requisites and Other Global State Arguments - Saltstack Documentation - v2018.3.2&lt;/em>, 2018 &lt;a class="reference external" href="https://docs.saltstack.com/en/latest/ref/states/requisites.html">[link]&lt;/a>&lt;/td>&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;/div>
&lt;/div></content><category>System Administration</category><category>linux</category><category>python</category><category>devops</category><category>saltstack</category><category>configuration management</category></item><item><title>Slurm in Ubuntu Clusters - Part 2</title><link>https://implement.pt/2018/09/slurm-in-ubuntu-clusters-pt2/</link><pubDate>Mon, 24 Sep 2018 09:30:18 +0100</pubDate><guid>https://implement.pt/2018/09/slurm-in-ubuntu-clusters-pt2/</guid><summary type="html">&lt;div class="document">
&lt;p>A guide to using and administering Slurm in a cluster&lt;/p>
&lt;/div></summary><content type="html">&lt;div class="document">
&lt;div class="section" id="overview">
&lt;h2>Overview&lt;/h2>
&lt;div class="figure align-center">
&lt;a class="reference external image-reference" href="./img/slurm_overview.png">&lt;img alt="slurm architecture diagram" src="./img/slurm_overview.png" />&lt;/a>
&lt;p class="caption">Overview of the slurm infrastructure&lt;/p>
&lt;/div>
&lt;p>In this article we will not go through the installation and configuration of the shared filesystem described above.
We assume that the &lt;strong>HOME&lt;/strong> directory is shared between the controller and the compute nodes, in the future I'll write
about how to achieve this using NFS (for simplicity) and &lt;a class="reference external" href="https://www.beegfs.io">BeeGFS&lt;/a>, for now let us follow with
Slurm usage.&lt;/p>
&lt;/div>
&lt;div class="section" id="summary-of-commands">
&lt;h2>Summary of commands&lt;/h2>
&lt;ul class="simple">
&lt;li>&lt;strong>scontrol&lt;/strong> - used to view and modify Slurm configuration and state.&lt;/li>
&lt;li>&lt;strong>sacct&lt;/strong> - displays accounting data for all jobs and job steps in the Slurm job accounting log or Slurm database.&lt;/li>
&lt;li>&lt;strong>sinfo&lt;/strong> - show information about the compute nodes status.&lt;/li>
&lt;li>&lt;strong>squeue&lt;/strong> - show information about the scheduler's job queue.&lt;/li>
&lt;li>&lt;strong>smap&lt;/strong> - show information about slurm jobs, partitions, and set configurations parameters.&lt;/li>
&lt;li>&lt;strong>sview&lt;/strong> - graphical user interface to view and modify the slurm state.&lt;/li>
&lt;li>&lt;strong>salloc&lt;/strong> - obtain a Slurm job allocation execute a command, and then release the allocation when the command is finished.&lt;/li>
&lt;li>&lt;strong>srun&lt;/strong> - run parallel jobs.&lt;/li>
&lt;li>&lt;strong>scancel&lt;/strong> - cancel a submitted job.&lt;/li>
&lt;li>&lt;strong>sbatch&lt;/strong> - submit a batch script to Slurm.&lt;/li>
&lt;/ul>
&lt;/div>
&lt;div class="section" id="changing-the-state-of-a-node">
&lt;h2>Changing the state of a Node&lt;/h2>
&lt;p>With the &lt;strong>scontrol&lt;/strong> command we can control, among other things, the state of each compute node.
If, for example, we need to put an idle node down for maintenance we would do the following:&lt;/p>
&lt;div class="highlight">&lt;pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">&lt;code class="language-shell" data-lang="shell">root@controller:~# scontrol update NodeName&lt;span style="color:#f92672">=&lt;/span>compute-1 State&lt;span style="color:#f92672">=&lt;/span>Down Reason&lt;span style="color:#f92672">=&lt;/span>&lt;span style="color:#e6db74">&amp;#39;Maintenance&amp;#39;&lt;/span>
root@controller:~# sinfo
PARTITION AVAIL TIMELIMIT NODES STATE NODELIST
base* up 3-00:00:00 &lt;span style="color:#ae81ff">1&lt;/span> down* compute-1
base* up 3-00:00:00 &lt;span style="color:#ae81ff">3&lt;/span> idle compute-&lt;span style="color:#f92672">[&lt;/span>2-4&lt;span style="color:#f92672">]&lt;/span>
root@controller:~# scontrol update NodeName&lt;span style="color:#f92672">=&lt;/span>compute-1 State&lt;span style="color:#f92672">=&lt;/span>Idle
root@controller:~# sinfo
PARTITION AVAIL TIMELIMIT NODES STATE NODELIST
base* up 3-00:00:00 &lt;span style="color:#ae81ff">4&lt;/span> idle compute-&lt;span style="color:#f92672">[&lt;/span>1-4&lt;span style="color:#f92672">]&lt;/span>
root@controller:~#&lt;/code>&lt;/pre>&lt;/div>
&lt;p>If, on the other hand, the node we want to put down is executing jobs (its state is &lt;em>allocated&lt;/em> or &lt;em>mix&lt;/em>), we need to
put it in the &lt;strong>draining&lt;/strong> state. Which means that the node will no longer accept new jobs and when all the running jobs
complete, the node's state will be &lt;strong>drained&lt;/strong>, and we can then put it in the &lt;strong>down&lt;/strong> state.&lt;/p>
&lt;div class="highlight">&lt;pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">&lt;code class="language-shell" data-lang="shell">root@controller:~# scontrol update NodeName&lt;span style="color:#f92672">=&lt;/span>compute-1 State&lt;span style="color:#f92672">=&lt;/span>Drain Reason&lt;span style="color:#f92672">=&lt;/span>&lt;span style="color:#e6db74">&amp;#39;Maintenance&amp;#39;&lt;/span>
root@controller:~# sinfo
PARTITION AVAIL TIMELIMIT NODES STATE NODELIST
base* up 3-00:00:00 &lt;span style="color:#ae81ff">1&lt;/span> drng compute-1
base* up 3-00:00:00 &lt;span style="color:#ae81ff">3&lt;/span> idle compute-&lt;span style="color:#f92672">[&lt;/span>2-4&lt;span style="color:#f92672">]&lt;/span>
root@controller:~# sinfo
PARTITION AVAIL TIMELIMIT NODES STATE NODELIST
base* up 3-00:00:00 &lt;span style="color:#ae81ff">1&lt;/span> drain compute-1
base* up 3-00:00:00 &lt;span style="color:#ae81ff">3&lt;/span> idle compute-&lt;span style="color:#f92672">[&lt;/span>2-4&lt;span style="color:#f92672">]&lt;/span>
root@controller:~# scontrol update NodeName&lt;span style="color:#f92672">=&lt;/span>compute-1 State&lt;span style="color:#f92672">=&lt;/span>Idle
root@controller:~# sinfo
PARTITION AVAIL TIMELIMIT NODES STATE NODELIST
base* up 3-00:00:00 &lt;span style="color:#ae81ff">4&lt;/span> idle compute-&lt;span style="color:#f92672">[&lt;/span>1-4&lt;span style="color:#f92672">]&lt;/span>
root@controller:~#&lt;/code>&lt;/pre>&lt;/div>
&lt;/div>
&lt;div class="section" id="executing-jobs-interactively">
&lt;h2>Executing jobs interactively&lt;/h2>
&lt;p>To execute jobs interactively, there are usually two steps:&lt;/p>
&lt;ol class="arabic simple">
&lt;li>Allocate resources&lt;/li>
&lt;li>Run parallel job&lt;/li>
&lt;/ol>
&lt;p>As an example, we want to run a BASH shell in a compute cluster.
We will ask for just 100MB of RAM and 1 cpu.&lt;/p>
&lt;div class="highlight">&lt;pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">&lt;code class="language-shell" data-lang="shell">username@controller:~$ salloc --cpus-per-task&lt;span style="color:#f92672">=&lt;/span>&lt;span style="color:#ae81ff">1&lt;/span> --mem&lt;span style="color:#f92672">=&lt;/span>100MB
salloc: Pending job allocation &lt;span style="color:#ae81ff">27564&lt;/span>
salloc: job &lt;span style="color:#ae81ff">27564&lt;/span> queued and waiting &lt;span style="color:#66d9ef">for&lt;/span> resources
salloc: job &lt;span style="color:#ae81ff">27564&lt;/span> has been allocated resources
salloc: Granted job allocation &lt;span style="color:#ae81ff">27564&lt;/span>
username@controller:~$ srun --pty /bin/bash
username@node4:~$ hostname
node4
username@node4:~$ exit
username@node4:~$ exit
salloc: Relinquishing job allocation &lt;span style="color:#ae81ff">27564&lt;/span>
username@controller:~$&lt;/code>&lt;/pre>&lt;/div>
&lt;/div>
&lt;div class="section" id="batch-file">
&lt;h2>Batch file&lt;/h2>
&lt;p>If instead of an interactive job we want a job running on its own for a long amount of time, we can create a batch
script and submit it to the slurm scheduler queue.&lt;/p>
&lt;p>Ley us take the example code for a simple &lt;cite>Hello World!&lt;/cite> program.&lt;/p>
&lt;p>&lt;em>helloworld.c&lt;/em>&lt;/p>
&lt;div class="highlight">&lt;div style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">
&lt;table style="border-spacing:0;padding:0;margin:0;border:0;width:auto;overflow:auto;display:block;">&lt;tr>&lt;td style="vertical-align:top;padding:0;margin:0;border:0;">
&lt;pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">&lt;code>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">1
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">2
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">3
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">4
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td style="vertical-align:top;padding:0;margin:0;border:0;;width:100%">
&lt;pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">&lt;code class="language-c" data-lang="c">&lt;span style="color:#75715e">#include&lt;/span> &lt;span style="color:#75715e">&amp;lt;stdio.h&amp;gt;&lt;/span>&lt;span style="color:#75715e">
&lt;/span>&lt;span style="color:#75715e">&lt;/span>&lt;span style="color:#66d9ef">int&lt;/span> &lt;span style="color:#a6e22e">main&lt;/span>(&lt;span style="color:#66d9ef">int&lt;/span> argc, &lt;span style="color:#66d9ef">char&lt;/span>&lt;span style="color:#f92672">**&lt;/span> argv){
printf(&lt;span style="color:#960050;background-color:#1e0010">&amp;#39;&lt;/span>Hello World&lt;span style="color:#f92672">!&lt;/span>&lt;span style="color:#960050;background-color:#1e0010">\&lt;/span>n&lt;span style="color:#960050;background-color:#1e0010">&amp;#39;&lt;/span>);
}&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>
&lt;p>Let us compile it.&lt;/p>
&lt;div class="highlight">&lt;pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">&lt;code class="language-shell" data-lang="shell">username@controller:~$ gcc -o helloworld helloworld.c&lt;/code>&lt;/pre>&lt;/div>
&lt;p>And now we can create a batch file to submit the job.&lt;/p>
&lt;p>&lt;em>simple_hello.batch&lt;/em>&lt;/p>
&lt;div class="highlight">&lt;div style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">
&lt;table style="border-spacing:0;padding:0;margin:0;border:0;width:auto;overflow:auto;display:block;">&lt;tr>&lt;td style="vertical-align:top;padding:0;margin:0;border:0;">
&lt;pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">&lt;code>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">1
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">2
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">3
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">4
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">5
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">6
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td style="vertical-align:top;padding:0;margin:0;border:0;;width:100%">
&lt;pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">&lt;code class="language-bash" data-lang="bash">&lt;span style="color:#75715e">#!/bin/bash
&lt;/span>&lt;span style="color:#75715e">&lt;/span>
WORKDIR&lt;span style="color:#f92672">=&lt;/span>/home/username/helloworld
cd $WORKDIR
./helloword&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>
&lt;p>Let us submit the job with the help of the &lt;em>sbatch&lt;/em> command and see its output.&lt;/p>
&lt;div class="highlight">&lt;pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">&lt;code class="language-shell" data-lang="shell">username@controller:~/helloworld$ sbatch simple_hello.batch
Submitted job &lt;span style="color:#ae81ff">12345&lt;/span>
username@controller:~/helloworld$ ls
simple_hello.batch simple_hello-12345.out simple_hello-12345.err
username@controller:~/helloworld$ cat simple_hello-12345.err
username@controller:~/helloworld$ cat simple_hello-12345.out
Hello World!
username@controller:~/helloworld$&lt;/code>&lt;/pre>&lt;/div>
&lt;p>It works!&lt;/p>
&lt;p>Now let us try a more complex example, making a C program which will run on multiple nodes, via MPI, and outputs the
name of the node it is running on and the rank attributed to it &lt;a class="citation-reference" href="#mpituto" id="id1">[MPITUTO]&lt;/a>.&lt;/p>
&lt;p>&lt;em>helloworld_mpi.c&lt;/em>&lt;/p>
&lt;div class="highlight">&lt;div style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">
&lt;table style="border-spacing:0;padding:0;margin:0;border:0;width:auto;overflow:auto;display:block;">&lt;tr>&lt;td style="vertical-align:top;padding:0;margin:0;border:0;">
&lt;pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">&lt;code>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 1
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 2
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 3
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 4
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 5
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 6
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 7
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 8
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 9
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">10
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">11
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">12
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">13
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">14
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">15
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">16
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">17
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">18
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">19
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">20
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">21
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">22
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">23
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">24
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">25
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">26
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">27
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">28
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">29
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">30
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">31
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">32
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">33
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td style="vertical-align:top;padding:0;margin:0;border:0;;width:100%">
&lt;pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">&lt;code class="language-c" data-lang="c">&lt;span style="color:#75715e">#include&lt;/span> &lt;span style="color:#75715e">&amp;lt;stdio.h&amp;gt;&lt;/span>&lt;span style="color:#75715e">
&lt;/span>&lt;span style="color:#75715e">#include&lt;/span> &lt;span style="color:#75715e">&amp;lt;unistd.h&amp;gt;&lt;/span>&lt;span style="color:#75715e">
&lt;/span>&lt;span style="color:#75715e">#include&lt;/span> &lt;span style="color:#75715e">&amp;lt;mpi.h&amp;gt;&lt;/span>&lt;span style="color:#75715e">
&lt;/span>&lt;span style="color:#75715e">&lt;/span>
&lt;span style="color:#66d9ef">int&lt;/span> &lt;span style="color:#a6e22e">main&lt;/span>(&lt;span style="color:#66d9ef">int&lt;/span> argc, &lt;span style="color:#66d9ef">char&lt;/span>&lt;span style="color:#f92672">**&lt;/span> argv){
&lt;span style="color:#75715e">// Init the MPI environment
&lt;/span>&lt;span style="color:#75715e">&lt;/span> MPI_Init(NULL, NULL);
&lt;span style="color:#75715e">// Get the number of processes
&lt;/span>&lt;span style="color:#75715e">&lt;/span> &lt;span style="color:#66d9ef">int&lt;/span> world_size;
MPI_Comm_size(MPI_COMM_WORLD, &lt;span style="color:#f92672">&amp;amp;&lt;/span>world_size);
&lt;span style="color:#75715e">// Get the rank of the process
&lt;/span>&lt;span style="color:#75715e">&lt;/span> &lt;span style="color:#66d9ef">int&lt;/span> world_rank;
MPI_Comm_rank(MPI_COMM_WORLD, &lt;span style="color:#f92672">&amp;amp;&lt;/span>world_rank);
&lt;span style="color:#75715e">// Get the name of the processor
&lt;/span>&lt;span style="color:#75715e">&lt;/span> &lt;span style="color:#66d9ef">char&lt;/span> processor_name[MPI_MAX_PROCESSOR_NAME];
&lt;span style="color:#66d9ef">int&lt;/span> name_len;
MPI_Get_processor_name(processor_name, &lt;span style="color:#f92672">&amp;amp;&lt;/span>name_len);
&lt;span style="color:#75715e">// Print a hello world message
&lt;/span>&lt;span style="color:#75715e">&lt;/span> printf(&lt;span style="color:#e6db74">&amp;#34;Hello world from processor %s, rank %d out of %d processors&lt;/span>&lt;span style="color:#ae81ff">\n&lt;/span>&lt;span style="color:#e6db74">&amp;#34;&lt;/span>,
processor_name,
world_rank,
world_size);
&lt;span style="color:#75715e">// sleep for 10 seconds, just enough to see the job executing
&lt;/span>&lt;span style="color:#75715e">&lt;/span> sleep(&lt;span style="color:#ae81ff">10&lt;/span>);
&lt;span style="color:#75715e">// Finalize the MPI environment
&lt;/span>&lt;span style="color:#75715e">&lt;/span> MPI_Finalize();
}&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>
&lt;p>Compile the code with MPI capabilities.&lt;/p>
&lt;div class="highlight">&lt;pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">&lt;code class="language-shell" data-lang="shell">username@controller:~/helloworld$ mpicc -o helloworld_mpi helloworld_mpi.c
username@controller:~/helloworld$ ls
helloworld_mpi helloworld_mpi.c
username@controller:~/helloworld$&lt;/code>&lt;/pre>&lt;/div>
&lt;p>&lt;em>helloworld_mpi.batch&lt;/em>&lt;/p>
&lt;div class="highlight">&lt;div style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">
&lt;table style="border-spacing:0;padding:0;margin:0;border:0;width:auto;overflow:auto;display:block;">&lt;tr>&lt;td style="vertical-align:top;padding:0;margin:0;border:0;">
&lt;pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">&lt;code>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 1
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 2
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 3
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 4
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 5
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 6
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 7
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 8
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 9
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">10
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">11
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">12
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">13
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">14
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td style="vertical-align:top;padding:0;margin:0;border:0;;width:100%">
&lt;pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">&lt;code class="language-bash" data-lang="bash">&lt;span style="color:#75715e">#!/bin/bash
&lt;/span>&lt;span style="color:#75715e">&lt;/span>&lt;span style="color:#75715e">#SBATCH --job-name=hello-world-mpi&lt;/span>
&lt;span style="color:#75715e">#SBATCH --output=/home/username/helloworld/helloworld_mpi-%j.out&lt;/span>
&lt;span style="color:#75715e">#SBATCH --error=/home/username/helloworld/helloworld_mpi-%j.err&lt;/span>
&lt;span style="color:#75715e">#SBATCH --nodes=2&lt;/span>
&lt;span style="color:#75715e">#SBATCH --ntasks=8&lt;/span>
&lt;span style="color:#75715e">#SBATCH --time=72:00:00&lt;/span>
&lt;span style="color:#75715e">#SBATCH --mem=1G&lt;/span>
PATH&lt;span style="color:#f92672">=&lt;/span>/path/to/mpidir/bin:$PATH
WORKDIR&lt;span style="color:#f92672">=&lt;/span>/home/username/helloworld
cd $WORKDIR
mpiexec -np &lt;span style="color:#ae81ff">8&lt;/span> ./helloworld_mpi&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>
&lt;p>Now we can submit the batch script as a job to queue.&lt;/p>
&lt;div class="highlight">&lt;pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">&lt;code class="language-shell" data-lang="shell">username@controller:~/helloworld$ sbatch helloworld_mpi.sbatch
Submitted batch job &lt;span style="color:#ae81ff">27560&lt;/span>
username@controller:~/helloworld$ squeue
JOBID PARTITION NAME USER ST TIME NODES NODELIST&lt;span style="color:#f92672">(&lt;/span>REASON&lt;span style="color:#f92672">)&lt;/span>
&lt;span style="color:#ae81ff">27560&lt;/span> base hello-w_ username PD 0:00 &lt;span style="color:#ae81ff">2&lt;/span> &lt;span style="color:#f92672">(&lt;/span>Resources&lt;span style="color:#f92672">)&lt;/span>
username@controller:~/helloworld$ squeue
JOBID PARTITION NAME USER ST TIME NODES NODELIST&lt;span style="color:#f92672">(&lt;/span>REASON&lt;span style="color:#f92672">)&lt;/span>
&lt;span style="color:#ae81ff">27560&lt;/span> base hello-w_ username R 0:05 &lt;span style="color:#ae81ff">2&lt;/span> node6,node7&lt;/code>&lt;/pre>&lt;/div>
&lt;p>On this sequence we can see that the job was submitted to the queue, and it was waiting for resources
to be available and run the job. Then, the job started running and was allocated to node6 and node7.&lt;/p>
&lt;p>As the job is finished, let us check the job's output:&lt;/p>
&lt;div class="highlight">&lt;pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">&lt;code class="language-shell" data-lang="shell">username@controller:~/helloworld$ cat helloworld_mpi-27560.err
username@controller:~/helloworld$ cat helloworld_mpi-27560.out
Hello world from processor node7, rank &lt;span style="color:#ae81ff">7&lt;/span> out of &lt;span style="color:#ae81ff">8&lt;/span> processors
Hello world from processor node6, rank &lt;span style="color:#ae81ff">5&lt;/span> out of &lt;span style="color:#ae81ff">8&lt;/span> processors
Hello world from processor node6, rank &lt;span style="color:#ae81ff">4&lt;/span> out of &lt;span style="color:#ae81ff">8&lt;/span> processors
Hello world from processor node6, rank &lt;span style="color:#ae81ff">0&lt;/span> out of &lt;span style="color:#ae81ff">8&lt;/span> processors
Hello world from processor node6, rank &lt;span style="color:#ae81ff">2&lt;/span> out of &lt;span style="color:#ae81ff">8&lt;/span> processors
Hello world from processor node6, rank &lt;span style="color:#ae81ff">3&lt;/span> out of &lt;span style="color:#ae81ff">8&lt;/span> processors
Hello world from processor node6, rank &lt;span style="color:#ae81ff">6&lt;/span> out of &lt;span style="color:#ae81ff">8&lt;/span> processors
Hello world from processor node6, rank &lt;span style="color:#ae81ff">1&lt;/span> out of &lt;span style="color:#ae81ff">8&lt;/span> processors
username@controller:~/helloworld$&lt;/code>&lt;/pre>&lt;/div>
&lt;p>As we can see, the job was allocated to 7 of node6's CPUs and 1 of node7's CPUs.&lt;/p>
&lt;p>As we can also notice, the order of execution of each process is not deterministic, it has to do with the time
the process reaches the processor, and also which other processes may be running and yielding in that same processor.&lt;/p>
&lt;p>MPI programming is not really in the scope of this article, however if you are interested you can check
&lt;a class="reference external" href="http://mpitutorial.com/">these tutorials&lt;/a>.&lt;/p>
&lt;/div>
&lt;div class="section" id="getting-job-and-node-information">
&lt;h2>Getting job and node information&lt;/h2>
&lt;p>In &lt;a class="reference external" href="https://implement.pt/2018/09/slurm-in-ubuntu-clusters-pt1/">part 1 of Slurm in Ubuntu Clusters&lt;/a> we saw how to see the general node
state with the &lt;strong>sinfo&lt;/strong> command.&lt;/p>
&lt;div class="highlight">&lt;pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">&lt;code class="language-shell" data-lang="shell">username@controller:~$ sinfo
PARTITION AVAIL TIMELIMIT NODES STATE NODELIST
base* up 3-00:00:00 &lt;span style="color:#ae81ff">1&lt;/span> idle compute-&lt;span style="color:#f92672">[&lt;/span>1-4&lt;span style="color:#f92672">]&lt;/span>
long up infinite &lt;span style="color:#ae81ff">1&lt;/span> idle compute-&lt;span style="color:#f92672">[&lt;/span>1-4&lt;span style="color:#f92672">]&lt;/span>&lt;/code>&lt;/pre>&lt;/div>
&lt;p>In the previous section we also saw how to monitor the job submission queue with the &lt;strong>squeue&lt;/strong> command.&lt;/p>
&lt;p>There are three more commands that are also very useful to analyse the state of the cluster, and also the job history
for a certain user or a set of the users.&lt;/p>
&lt;p>The &lt;strong>smap&lt;/strong> command is similar to the &lt;strong>squeue&lt;/strong>, in a way that it also shows the slurm scheduling queue.
However it also shows information about Slurm jobs, partitions, and set configurations parameters.&lt;/p>
&lt;div class="highlight">&lt;pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">&lt;code class="language-shell" data-lang="shell">username@controller:~$ smap -c
JOBID PARTITION USER NAME ST TIME NODES NODELIST
&lt;span style="color:#ae81ff">27560&lt;/span> base username hellow R 00:00:05 &lt;span style="color:#ae81ff">1&lt;/span> node6,node7&lt;/code>&lt;/pre>&lt;/div>
&lt;p>The &lt;strong>sacct&lt;/strong> command displays accounting data for all jobs and job steps in the Slurm job accounting log or Slurm database.
With it you can see information about past submitted jobs.&lt;/p>
&lt;div class="highlight">&lt;pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">&lt;code class="language-shell" data-lang="shell">username@controller:~$ sacct
JobID JobName Partition Account AllocCPUS State ExitCode
------------ ---------- ---------- ---------- ---------- ---------- --------
27560.batch batch &lt;span style="color:#ae81ff">7&lt;/span> COMPLETED 0:0
27560.0 hydra_pmi+ &lt;span style="color:#ae81ff">2&lt;/span> COMPLETED 0:0&lt;/code>&lt;/pre>&lt;/div>
&lt;p>The &lt;strong>sview&lt;/strong> command shows a graphical user interface to view and modify the Slurm state. Of course that you can only
modify slurm objects to which you have access, i.e: you can cancel your submitted jobs but not one of another user.&lt;/p>
&lt;p>To use this command you require X11 forwarding activated on your SSH session, if you are connecting from a windows
machine you can use &lt;a class="reference external" href="https://sourceforge.net/projects/xming/">XMing&lt;/a>, a X Window System Server for Windows.&lt;/p>
&lt;p>If you are using &lt;a class="reference external" href="https://www.bitvise.com/">Bitvise SSH Client&lt;/a>, you can activate X11 forwarding on the client side
by going to the &lt;tt class="docutils literal">Terminal&lt;/tt> tab and enable the &lt;tt class="docutils literal">X11 Forwarding&lt;/tt> option.&lt;/p>
&lt;/div>
&lt;div class="section" id="conclusion">
&lt;h2>Conclusion&lt;/h2>
&lt;p>We have now seen how to set up a slurm cluster and how to manage its jobs and nodes.&lt;/p>
&lt;p>There are a lot of details that I have skipped for simplicity, but the information present in this article should get
you up and running on the way to master slurm clusters.&lt;/p>
&lt;/div>
&lt;div class="section" id="references">
&lt;h2>References&lt;/h2>
&lt;table class="docutils citation" frame="void" id="mpituto" rules="none">
&lt;colgroup>&lt;col class="label" />&lt;col />&lt;/colgroup>
&lt;tbody valign="top">
&lt;tr>&lt;td class="label">&lt;a class="fn-backref" href="#id1">[MPITUTO]&lt;/a>&lt;/td>&lt;td>MPI Tutorials &lt;a class="reference external" href="https://mpitutorial.com/tutorials/mpi-hello-world/">[link]&lt;/a>&lt;/td>&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;/div>
&lt;/div></content><category>High Performance Computing</category><category>linux</category><category>slurm</category><category>python</category><category>scheduler</category><category>hpc</category><category>cluster</category><category>MPI</category></item><item><title>Slurm in Ubuntu Clusters - Part 1</title><link>https://implement.pt/2018/09/slurm-in-ubuntu-clusters-pt1/</link><pubDate>Mon, 17 Sep 2018 09:30:00 +0100</pubDate><guid>https://implement.pt/2018/09/slurm-in-ubuntu-clusters-pt1/</guid><summary type="html">&lt;div class="document">
&lt;p>A guide to setting up and configuring Slurm in Ubuntu 16.04&lt;/p>
&lt;/div></summary><content type="html">&lt;div class="document">
&lt;p>In the High Performance Computing (HPC) world it is usual to have a job scheduler. Its function is to receive job
allocation requests, and job to the run on the system. In the image below we can see an example of an architecture
that makes use of these systems.&lt;/p>
&lt;div class="figure align-center">
&lt;a class="reference external image-reference" href="./img/hpc_job_sched.png">&lt;img alt="scheduler diagram" src="./img/hpc_job_sched.png" />&lt;/a>
&lt;p class="caption">Overview of job scheduling in HPC&lt;/p>
&lt;/div>
&lt;p>In this post we will be looking into how to set up and configure &lt;a class="reference external" href="https://slurm.schedmd.com/">[Slurm Workload Manager]&lt;/a>, formerly known as Simple Linux Utility for Resource
Management &lt;a class="citation-reference" href="#cce2003" id="id1">[CCE2003]&lt;/a>&lt;/p>
&lt;div class="section" id="controller">
&lt;h2>Controller&lt;/h2>
&lt;p>The controller machine is where one should submit its jobs. It runs the slurmctld service, which controls the compute
nodes. And in our case it will also run the slurmdbd service, which controls the accounting and qos settings for the
queues.&lt;/p>
&lt;div class="figure align-center">
&lt;a class="reference external image-reference" href="./img/slurm_controller.png">&lt;img alt="slurm controller components" src="./img/slurm_controller.png" />&lt;/a>
&lt;p class="caption">Slurm controller components&lt;/p>
&lt;/div>
&lt;dl class="docutils">
&lt;dt>O.S pkgs:&lt;/dt>
&lt;dd>&lt;ul class="first last simple">
&lt;li>mailutils&lt;/li>
&lt;li>slurm-llnl&lt;/li>
&lt;li>sview&lt;/li>
&lt;li>mariadb-client&lt;/li>
&lt;li>mariadb-server&lt;/li>
&lt;li>libmariadb-client-lgpl-dev&lt;/li>
&lt;li>python-dev&lt;/li>
&lt;li>python-mysqldb&lt;/li>
&lt;li>slurm-llnl-slurmdbd&lt;/li>
&lt;/ul>
&lt;/dd>
&lt;/dl>
&lt;div class="highlight">&lt;pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">&lt;code class="language-shell" data-lang="shell">root@controller# apt-get install -y mailutils slurm-llnl sview mariadb-client mariadb-server libmariadb-client-lgpl-dev python-dev python-mysqldb slurm-llnl-slurmdbd&lt;/code>&lt;/pre>&lt;/div>
&lt;div class="section" id="database">
&lt;h3>Database&lt;/h3>
&lt;p>After installing MariaDB we should confine the connections to the DB to &lt;em>localhost&lt;/em> as we will be using this only for
the slurmdbd installed in the local machine. In the file &lt;em>/etc/mysql/mariadb.conf.d/50-server.cnf&lt;/em> we should have the
following setting:&lt;/p>
&lt;div class="highlight">&lt;div style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">
&lt;table style="border-spacing:0;padding:0;margin:0;border:0;width:auto;overflow:auto;display:block;">&lt;tr>&lt;td style="vertical-align:top;padding:0;margin:0;border:0;">
&lt;pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">&lt;code>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">1
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td style="vertical-align:top;padding:0;margin:0;border:0;;width:100%">
&lt;pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">&lt;code class="language-fallback" data-lang="fallback"> bind-address = localhost&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>
&lt;p>If you will be accepting connections from other machines, i.e if you want to have the database running on a separate
machine than the one having the slurmdbd, then you should change this setting accordingly.&lt;/p>
&lt;p>If you have the need to receive connections from multiple distinct subnets, you have to set this to &lt;strong>0.0.0.0&lt;/strong> and
create firewall policies blocking all incoming traffic to port &lt;strong>3306&lt;/strong> except for those subnets.&lt;/p>
&lt;p>Now, let us create the necessary databases and grant the slurm user rights to manage them, as user &lt;em>root&lt;/em>:&lt;/p>
&lt;div class="highlight">&lt;pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">&lt;code class="language-mysql" data-lang="mysql">root&lt;span style="color:#f92672">@&lt;/span>controller&lt;span style="color:#75715e"># mysql -u root
&lt;/span>&lt;span style="color:#75715e">&lt;/span>Welcome &lt;span style="color:#66d9ef">to&lt;/span> the MariaDB monitor. Commands end &lt;span style="color:#66d9ef">with&lt;/span> ; &lt;span style="color:#66d9ef">or&lt;/span> &lt;span style="color:#960050;background-color:#1e0010">\&lt;/span>g.
Your MariaDB connection id &lt;span style="color:#66d9ef">is&lt;/span> &lt;span style="color:#ae81ff">8&lt;/span>
Server version: &lt;span style="color:#ae81ff">10&lt;/span>.&lt;span style="color:#ae81ff">0&lt;/span>.&lt;span style="color:#ae81ff">36&lt;/span>&lt;span style="color:#f92672">-&lt;/span>MariaDB&lt;span style="color:#f92672">-&lt;/span>&lt;span style="color:#ae81ff">0&lt;/span>ubuntu0.&lt;span style="color:#ae81ff">16&lt;/span>.&lt;span style="color:#ae81ff">04&lt;/span>.&lt;span style="color:#ae81ff">1&lt;/span> Ubuntu &lt;span style="color:#ae81ff">16&lt;/span>.&lt;span style="color:#ae81ff">04&lt;/span>
&lt;span style="color:#a6e22e">Copyright&lt;/span> (c) &lt;span style="color:#ae81ff">2000&lt;/span>, &lt;span style="color:#ae81ff">2018&lt;/span>, Oracle, MariaDB Corporation Ab &lt;span style="color:#66d9ef">and&lt;/span> others.
Type &lt;span style="color:#e6db74">&amp;#39;help;&amp;#39;&lt;/span> &lt;span style="color:#66d9ef">or&lt;/span> &lt;span style="color:#e6db74">&amp;#39;\h&amp;#39;&lt;/span> &lt;span style="color:#66d9ef">for&lt;/span> help. Type &lt;span style="color:#e6db74">&amp;#39;\c&amp;#39;&lt;/span> &lt;span style="color:#66d9ef">to&lt;/span> clear the current input statement.
MariaDB [(none)]&lt;span style="color:#f92672">&amp;gt;&lt;/span> &lt;span style="color:#66d9ef">grant&lt;/span> &lt;span style="color:#66d9ef">usage&lt;/span> &lt;span style="color:#66d9ef">on&lt;/span> &lt;span style="color:#f92672">*&lt;/span>.&lt;span style="color:#f92672">*&lt;/span> &lt;span style="color:#66d9ef">to&lt;/span> &lt;span style="color:#e6db74">&amp;#39;slurm&amp;#39;&lt;/span>&lt;span style="color:#f92672">@&lt;/span>&lt;span style="color:#e6db74">&amp;#39;%&amp;#39;&lt;/span> &lt;span style="color:#66d9ef">identified&lt;/span> &lt;span style="color:#66d9ef">by&lt;/span> &lt;span style="color:#e6db74">&amp;#39;safepassword&amp;#39;&lt;/span>
Query OK, &lt;span style="color:#ae81ff">1&lt;/span> row &lt;span style="color:#a6e22e">affected&lt;/span> (&lt;span style="color:#ae81ff">0&lt;/span>.&lt;span style="color:#ae81ff">00&lt;/span> sec)
MariaDB [(none)]&lt;span style="color:#f92672">&amp;gt;&lt;/span> &lt;span style="color:#66d9ef">create&lt;/span> &lt;span style="color:#66d9ef">database&lt;/span> slurm_acct_db
Query OK, &lt;span style="color:#ae81ff">1&lt;/span> row &lt;span style="color:#a6e22e">affected&lt;/span> (&lt;span style="color:#ae81ff">0&lt;/span>.&lt;span style="color:#ae81ff">00&lt;/span> sec)
MariaDB [(none)]&lt;span style="color:#f92672">&amp;gt;&lt;/span> &lt;span style="color:#66d9ef">create&lt;/span> &lt;span style="color:#66d9ef">database&lt;/span> slurm_job_db
Query OK, &lt;span style="color:#ae81ff">1&lt;/span> row &lt;span style="color:#a6e22e">affected&lt;/span> (&lt;span style="color:#ae81ff">0&lt;/span>.&lt;span style="color:#ae81ff">00&lt;/span> sec)
MariaDB [(none)]&lt;span style="color:#f92672">&amp;gt;&lt;/span> &lt;span style="color:#66d9ef">grant&lt;/span> &lt;span style="color:#66d9ef">all&lt;/span> &lt;span style="color:#66d9ef">privileges&lt;/span> &lt;span style="color:#66d9ef">on&lt;/span> slurm_acct_db.&lt;span style="color:#f92672">*&lt;/span> &lt;span style="color:#66d9ef">to&lt;/span> &lt;span style="color:#e6db74">&amp;#39;slurm&amp;#39;&lt;/span>&lt;span style="color:#f92672">@&lt;/span>&lt;span style="color:#e6db74">&amp;#39;localhost&amp;#39;&lt;/span>;
Query OK, &lt;span style="color:#ae81ff">1&lt;/span> row &lt;span style="color:#a6e22e">affected&lt;/span> (&lt;span style="color:#ae81ff">0&lt;/span>.&lt;span style="color:#ae81ff">00&lt;/span> sec)
MariaDB [(none)]&lt;span style="color:#f92672">&amp;gt;&lt;/span> &lt;span style="color:#66d9ef">grant&lt;/span> &lt;span style="color:#66d9ef">all&lt;/span> &lt;span style="color:#66d9ef">privileges&lt;/span> &lt;span style="color:#66d9ef">on&lt;/span> slurm_acct_db.&lt;span style="color:#f92672">*&lt;/span> &lt;span style="color:#66d9ef">to&lt;/span> &lt;span style="color:#e6db74">&amp;#39;slurm&amp;#39;&lt;/span>&lt;span style="color:#f92672">@&lt;/span>&lt;span style="color:#e6db74">&amp;#39;localhost&amp;#39;&lt;/span>;
Query OK, &lt;span style="color:#ae81ff">1&lt;/span> row &lt;span style="color:#a6e22e">affected&lt;/span> (&lt;span style="color:#ae81ff">0&lt;/span>.&lt;span style="color:#ae81ff">00&lt;/span> sec)
MariaDB [(none)]&lt;span style="color:#f92672">&amp;gt;&lt;/span> &lt;span style="color:#66d9ef">exit&lt;/span>
Bye
root&lt;span style="color:#f92672">@&lt;/span>controller&lt;span style="color:#75715e">#&lt;/span>&lt;/code>&lt;/pre>&lt;/div>
&lt;/div>
&lt;div class="section" id="node-authentication">
&lt;h3>Node Authentication&lt;/h3>
&lt;p>The subsystem responsible for authentication between the slurmd and slurmctl is &lt;strong>munge&lt;/strong>. After installing the
&lt;strong>slurm-llnl&lt;/strong> package you should now have the &lt;strong>munge&lt;/strong> package also installed.&lt;/p>
&lt;p>First, let us configure the default options for the munge service:&lt;/p>
&lt;p>&lt;em>/etc/default/munge:&lt;/em>&lt;/p>
&lt;div class="highlight">&lt;div style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">
&lt;table style="border-spacing:0;padding:0;margin:0;border:0;width:auto;overflow:auto;display:block;">&lt;tr>&lt;td style="vertical-align:top;padding:0;margin:0;border:0;">
&lt;pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">&lt;code>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">1
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td style="vertical-align:top;padding:0;margin:0;border:0;;width:100%">
&lt;pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">&lt;code class="language-fallback" data-lang="fallback">OPTIONS=&amp;#34;--syslog --key-file /etc/munge/munge.key&amp;#34;&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>
&lt;p>Next, we create a key, &lt;strong>common to all comunicating nodes&lt;/strong>, in &lt;em>/etc/munge/munge.key&lt;/em>.&lt;/p>
&lt;p>You can create a secret key using a variety of methods &lt;a class="citation-reference" href="#mun2018" id="id2">[MUN2018]&lt;/a>:&lt;/p>
&lt;ol class="arabic simple">
&lt;li>Wait around for some random data (recommended for the paranoid):&lt;/li>
&lt;/ol>
&lt;div class="highlight">&lt;pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">&lt;code class="language-shell" data-lang="shell">root@controller# dd &lt;span style="color:#66d9ef">if&lt;/span>&lt;span style="color:#f92672">=&lt;/span>/dev/random bs&lt;span style="color:#f92672">=&lt;/span>&lt;span style="color:#ae81ff">1&lt;/span> count&lt;span style="color:#f92672">=&lt;/span>&lt;span style="color:#ae81ff">1024&lt;/span> &amp;gt;/etc/munge/munge.key&lt;/code>&lt;/pre>&lt;/div>
&lt;ol class="arabic simple" start="2">
&lt;li>Grab some pseudorandom data (recommended for the impatient):&lt;/li>
&lt;/ol>
&lt;div class="highlight">&lt;pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">&lt;code class="language-shell" data-lang="shell">root@controller# dd &lt;span style="color:#66d9ef">if&lt;/span>&lt;span style="color:#f92672">=&lt;/span>/dev/urandom bs&lt;span style="color:#f92672">=&lt;/span>&lt;span style="color:#ae81ff">1&lt;/span> count&lt;span style="color:#f92672">=&lt;/span>&lt;span style="color:#ae81ff">1024&lt;/span> &amp;gt;/etc/munge/munge.key&lt;/code>&lt;/pre>&lt;/div>
&lt;ol class="arabic simple" start="3">
&lt;li>Enter the hash of a password (not recommended):&lt;/li>
&lt;/ol>
&lt;div class="highlight">&lt;pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">&lt;code class="language-shell" data-lang="shell">root@controller# echo -n &lt;span style="color:#e6db74">&amp;#34;foo&amp;#34;&lt;/span> | sha512sum | cut -d&lt;span style="color:#e6db74">&amp;#39; &amp;#39;&lt;/span> -f1 &amp;gt;/etc/munge/munge.key&lt;/code>&lt;/pre>&lt;/div>
&lt;ol class="arabic simple" start="4">
&lt;li>Enter a password directly (really not recommended):&lt;/li>
&lt;/ol>
&lt;div class="highlight">&lt;pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">&lt;code class="language-shell" data-lang="shell">root@controller# echo &lt;span style="color:#e6db74">&amp;#34;foo&amp;#34;&lt;/span> &amp;gt;/etc/munge/munge.key&lt;/code>&lt;/pre>&lt;/div>
&lt;p>Now the service can be started:&lt;/p>
&lt;div class="highlight">&lt;pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">&lt;code class="language-shell" data-lang="shell">root@controller# systemctl start munge&lt;/code>&lt;/pre>&lt;/div>
&lt;p>&lt;em>As a side note, if you are not able to manage binary files due to some kind of data conversion, i.e: using a
configuration manager that may give you a hard time copying raw binary data, you can convert the file contents to a
base64 representation of the data.&lt;/em>&lt;/p>
&lt;div class="highlight">&lt;pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">&lt;code class="language-shell" data-lang="shell">root@controller# dd &lt;span style="color:#66d9ef">if&lt;/span>&lt;span style="color:#f92672">=&lt;/span>/dev/random bs&lt;span style="color:#f92672">=&lt;/span>&lt;span style="color:#ae81ff">1&lt;/span> count&lt;span style="color:#f92672">=&lt;/span>&lt;span style="color:#ae81ff">1024&lt;/span> | base64 &amp;gt; /etc/munge/munge.key&lt;/code>&lt;/pre>&lt;/div>
&lt;/div>
&lt;div class="section" id="accounting-storage">
&lt;h3>Accounting Storage&lt;/h3>
&lt;p>The subsystem responsible for the job completion storage, maintaining information about all of the submitted jobs to
the slurm system.&lt;/p>
&lt;p>&lt;em>This is optional to install to have a functioning slurm system, but in order to have a more solid understanding of&lt;/em>
&lt;em>what happened with each past job it is necessary to have some record of those job.&lt;/em>&lt;/p>
&lt;p>After we have the &lt;strong>slurm-llnl-slurmdbd&lt;/strong> package installed we configure it, by editing the
&lt;em>/etc/slurm-llnl/slurmdb.conf&lt;/em> file:&lt;/p>
&lt;div class="highlight">&lt;div style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">
&lt;table style="border-spacing:0;padding:0;margin:0;border:0;width:auto;overflow:auto;display:block;">&lt;tr>&lt;td style="vertical-align:top;padding:0;margin:0;border:0;">
&lt;pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">&lt;code>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 1
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 2
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 3
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 4
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 5
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 6
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 7
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 8
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 9
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">10
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">11
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td style="vertical-align:top;padding:0;margin:0;border:0;;width:100%">
&lt;pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">&lt;code class="language-fallback" data-lang="fallback">AuthType=auth/munge
AuthInfo=/var/run/munge/munge.socket.2
StorageHost=localhost
StoragePort=3306
StorageUser=slurm
StoragePass=safepassword
StorageType=accounting_storage/mysql
StorageLoc=slurm_acct_db
LogFile=/var/log/slurm-llnl/slurmdbd.log
PidFile=/var/run/slurm-llnl/slurmdbd.pid
SlurmUser=slurm&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>
&lt;p>Now we can start the slurmdbd service:&lt;/p>
&lt;div class="highlight">&lt;pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">&lt;code class="language-shell" data-lang="shell">root@controller# systemctl start slurmdbd&lt;/code>&lt;/pre>&lt;/div>
&lt;/div>
&lt;div class="section" id="central-controller">
&lt;h3>Central Controller&lt;/h3>
&lt;p>The central controller system is provided by the &lt;strong>slurmctld&lt;/strong> service. This is the system that monitors the resources
status, manages job queues and allocates resources to compute nodes.&lt;/p>
&lt;p>The main configuration file is &lt;strong>/etc/slurm-llnl/slurm.conf&lt;/strong> this file has to be present in the &lt;em>controller&lt;/em> and all
of the &lt;em>compute nodes&lt;/em> and it also has to be consistent between all of them.&lt;/p>
&lt;div class="highlight">&lt;div style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">
&lt;table style="border-spacing:0;padding:0;margin:0;border:0;width:auto;overflow:auto;display:block;">&lt;tr>&lt;td style="vertical-align:top;padding:0;margin:0;border:0;">
&lt;pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">&lt;code>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 1
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 2
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 3
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 4
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 5
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 6
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 7
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 8
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 9
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">10
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">11
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">12
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">13
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">14
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">15
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">16
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">17
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">18
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">19
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">20
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">21
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">22
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">23
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">24
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">25
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">26
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">27
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">28
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">29
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">30
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">31
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">32
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">33
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">34
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">35
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">36
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">37
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">38
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">39
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">40
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">41
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">42
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">43
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">44
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">45
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">46
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">47
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">48
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">49
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">50
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">51
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">52
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">53
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">54
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">55
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">56
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">57
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">58
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">59
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">60
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">61
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">62
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">63
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">64
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td style="vertical-align:top;padding:0;margin:0;border:0;;width:100%">
&lt;pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">&lt;code class="language-fallback" data-lang="fallback"># General
ControlMachine=entry-node
AuthType=auth/munge
CacheGroups=0
CryptoType=crypto/munge
JobCheckpointDir=/var/lib/slurm-llnl/checkpoint
KillOnBadExit=01
MpiDefault=pmi2
MailProg=/usr/bin/mail
PrivateData=usage,users,accounts
ProctrackType=proctrack/cgroup
PrologFlags=Alloc,Contain
PropagateResourceLimits=NONE
RebootProgram=/sbin/reboot
ReturnToService=1
SlurmctldPidFile=/var/run/slurm-llnl/slurmctld.pid
SlurmctldPort=6817
SlurmdPidFile=/var/run/slurm-llnl/slurmd.pid
SlurmdPort=6818
SlurmdSpoolDir=/var/lib/slurm-llnl/slurmd
SlurmUser=slurm
StateSaveLocation=/var/lib/slurm-llnl/slurmctld
SwitchType=switch/none
TaskPlugin=task/cgroup
# Timers
InactiveLimit=0
KillWait=30
MinJobAge=300
SlurmctldTimeout=120
SlurmdTimeout=300
Waittime=0
# Scheduler
FastSchedule=1
SchedulerType=sched/backfill
SchedulerPort=7321
SelectType=select/cons_res
SelectTypeParameters=CR_CPU_Memory
# Preemptions
PreemptType=preempt/partition_prio
PreemptMode=REQUEUE
# Accounting
AccountingStorageType=accounting_storage/slurmdbd
AccountingStoreJobComment=YES
ClusterName=mycluster
JobAcctGatherFrequency=30
JobAcctGatherType=jobacct_gather/linux
SlurmctldDebug=3
SlurmctldLogFile=/var/log/slurm-llnl/slurmctld.log
SlurmdDebug=3
SlurmdLogFile=/var/log/slurm-llnl/slurmd.log
SlurmSchedLogFile= /var/log/slurm-llnl/slurmschd.log
SlurmSchedLogLevel=3
NodeName=compute-1 Procs=48 Sockets=4 CoresPerSocket=12 ThreadsPerCore=1 RealMemory=128000 Weight=4
NodeName=compute-2 Procs=48 Sockets=4 CoresPerSocket=12 ThreadsPerCore=1 RealMemory=254000 Weight=3
NodeName=compute-3 Procs=96 Sockets=2 CoresPerSocket=24 ThreadsPerCore=2 RealMemory=256000 Weight=3
NodeName=compute-4 Procs=96 Sockets=2 CoresPerSocket=24 ThreadsPerCore=2 RealMemory=256000 Weight=3
PartitionName=base Nodes=compute-1,compute-2,compute-3,compute-4 Default=Yes MaxTime=72:00:00 Priority=1 State=UP
PartitionName=long Nodes=compute-1,compute-2,compute-3,compute-4 Default=No MaxTime=UNLIMITED Priority=1 State=UP AllowGroups=long&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>
&lt;p>In this config file we configured 4 compute nodes and 2 partitions.&lt;/p>
&lt;p>In the node section we need to give a profile for each machine, which we can get with a combination of the output of &lt;strong>/proc/cpuinfo&lt;/strong> and &lt;strong>/proc/meminfo&lt;/strong>, and of course, knowledge about the underlying architecture.&lt;/p>
&lt;p>The &lt;em>RealMemory&lt;/em> parameter refers to the available memory you want to have available for resources, you do not need to match it with 100% of the available physical memory (and you should not, keep a few MBs reserved for the operating system).&lt;/p>
&lt;p>Now we can start the slurmctld service:&lt;/p>
&lt;div class="highlight">&lt;pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">&lt;code class="language-shell" data-lang="shell">root@controller# systemctl start slurmctld&lt;/code>&lt;/pre>&lt;/div>
&lt;/div>
&lt;/div>
&lt;div class="section" id="compute-nodes">
&lt;h2>Compute Nodes&lt;/h2>
&lt;p>A compute node is a machine which will receive jobs to execute, sent from the Controller, it runs the slurmd service.&lt;/p>
&lt;div class="figure align-center">
&lt;a class="reference external image-reference" href="./img/slurm_compute.png">&lt;img alt="slurm compute node components" src="./img/slurm_compute.png" />&lt;/a>
&lt;p class="caption">Slurm compute node components&lt;/p>
&lt;/div>
&lt;dl class="docutils">
&lt;dt>O.S. pkgs:&lt;/dt>
&lt;dd>&lt;ul class="first last simple">
&lt;li>slurm-llnl&lt;/li>
&lt;li>slurm-wlm-basic-plugins&lt;/li>
&lt;/ul>
&lt;/dd>
&lt;/dl>
&lt;div class="section" id="authentication">
&lt;h3>Authentication&lt;/h3>
&lt;p>As mentioned before in &lt;a class="reference internal" href="#node-authentication">Node Authentication&lt;/a>, we need to setup the default options for the munge service, create a key,
and start the service. On the compute nodes we can just copy the key file from the controller:&lt;/p>
&lt;div class="highlight">&lt;pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">&lt;code class="language-shell" data-lang="shell">root@controller# &lt;span style="color:#66d9ef">for&lt;/span> i in &lt;span style="color:#e6db74">`&lt;/span>seq &lt;span style="color:#ae81ff">1&lt;/span> 4&lt;span style="color:#e6db74">`&lt;/span>; &lt;span style="color:#66d9ef">do&lt;/span> scp /etc/munge/munge.key compute-&lt;span style="color:#e6db74">${&lt;/span>i&lt;span style="color:#e6db74">}&lt;/span>:/etc/munge/munge.key; &lt;span style="color:#66d9ef">done&lt;/span>&lt;/code>&lt;/pre>&lt;/div>
&lt;p>and then start the service:&lt;/p>
&lt;div class="highlight">&lt;pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">&lt;code class="language-shell" data-lang="shell">root@compute-1# systemctl start munge&lt;/code>&lt;/pre>&lt;/div>
&lt;/div>
&lt;div class="section" id="compute-node-daemon">
&lt;h3>Compute Node Daemon&lt;/h3>
&lt;p>The subsystem responsible for launching and managing &lt;em>slurmstepd&lt;/em> is &lt;em>slurmd&lt;/em>. Slurmstepd is launched for a batch job
and each job step, launches user application tasks, manages accounting, application I/O, profiling, signals,
etc. &lt;a class="citation-reference" href="#bmisc17" id="id3">[BMISC17]&lt;/a>&lt;/p>
&lt;p>As mentioned before in &lt;a class="reference internal" href="#central-controller">Central Controller&lt;/a> the file &lt;em>/etc/slurm-llnl/slurm.conf&lt;/em> has to be identical to the one
created in the controller. Similar to the munge key, you can copy the file from the controller to the compute nodes.&lt;/p>
&lt;p>Control Groups provide a mechanism for aggregating/partitioning sets of tasks, and all their future children, into
hierarchical groups with specialized behaviour. &lt;a class="citation-reference" href="#krncgrp" id="id4">[KRNCGRP]&lt;/a>&lt;/p>
&lt;p>In the &lt;em>/etc/slurm-llnl/slurm.conf&lt;/em> file you may have already noticed the following parameters:&lt;/p>
&lt;div class="highlight">&lt;pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">&lt;code class="language-fallback" data-lang="fallback">TaskPlugin=task/cgroup
ProctrackType=proctrack/cgroup&lt;/code>&lt;/pre>&lt;/div>
&lt;p>The proctrack/cgroup plugin is an alternative to other proctrack plugins such as proctrack/linux for process tracking
and suspend/resume capability. proctrack/cgroup uses the freezer subsystem which is more reliable for tracking and
control than proctrack/linux. &lt;a class="citation-reference" href="#slucgrp" id="id5">[SLUCGRP]&lt;/a>&lt;/p>
&lt;p>The task/cgroup plugin is an alternative to other task plugins such as the task/affinity plugin for task management, and
provides the following features &lt;a class="citation-reference" href="#slucgrp" id="id6">[SLUCGRP]&lt;/a>&lt;/p>
&lt;ul class="simple">
&lt;li>The ability to confine jobs and steps to their allocated cpuset.&lt;/li>
&lt;li>The ability to bind tasks to sockets, cores and threads within their step's allocated cpuset on a node.&lt;/li>
&lt;li>Supports block and cyclic distribution of allocated cpus to tasks for binding.&lt;/li>
&lt;li>The ability to confine jobs and steps to specific memory resources.&lt;/li>
&lt;li>The ability to confine jobs to their allocated set of generic resources (gres devices).&lt;/li>
&lt;/ul>
&lt;p>The &lt;em>task/cgroup&lt;/em> plugin uses the cpuset, memory and devices subsystems.&lt;/p>
&lt;p>There are many specific options for the &lt;em>task/cgroup&lt;/em> plugin in cgroup.conf. The general options also apply. See the
&lt;a class="reference external" href="https://slurm.schedmd.com/cgroup.conf.html">cgroup.conf man page&lt;/a> for details.&lt;/p>
&lt;p>&lt;em>/etc/slurm-llnl/cgroup.conf&lt;/em>&lt;/p>
&lt;div class="highlight">&lt;div style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">
&lt;table style="border-spacing:0;padding:0;margin:0;border:0;width:auto;overflow:auto;display:block;">&lt;tr>&lt;td style="vertical-align:top;padding:0;margin:0;border:0;">
&lt;pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">&lt;code>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">1
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">2
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td style="vertical-align:top;padding:0;margin:0;border:0;;width:100%">
&lt;pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">&lt;code class="language-fallback" data-lang="fallback">CgroupAutomount=yes
ConstrainCores=yes&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>
&lt;dl class="docutils">
&lt;dt>CgroupAutomount&lt;/dt>
&lt;dd>In our case we want the cgroup plugins to first try to mount the required subsystems, instead of failing if these
subsystems are not mounted.&lt;/dd>
&lt;dt>ConstrainCores&lt;/dt>
&lt;dd>We also want the subsystem to constrain allowed cores to the subset of allocated resources. This functionality makes
use of the cpuset subsystem.&lt;/dd>
&lt;/dl>
&lt;p>&lt;/p>
&lt;blockquote>
Due to a bug fixed in version 1.11.5 of HWLOC, the task/affinity plugin may be required in addition to task/cgroup
for this to function properly. The default value is &amp;quot;no&amp;quot;. &lt;a class="citation-reference" href="#slucgrp" id="id7">[SLUCGRP]&lt;/a>&lt;/blockquote>
&lt;p>Now we can start the slurmd service and start accepting jobs.&lt;/p>
&lt;div class="highlight">&lt;pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">&lt;code class="language-shell" data-lang="shell">root@compute-1# systemctl start slurmd&lt;/code>&lt;/pre>&lt;/div>
&lt;/div>
&lt;/div>
&lt;div class="section" id="checkpoint">
&lt;h2>Checkpoint&lt;/h2>
&lt;p>Let us check if everything is up and running and the slurm components are all communicating:&lt;/p>
&lt;div class="highlight">&lt;pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">&lt;code class="language-shell" data-lang="shell">root@controller# sinfo
PARTITION AVAIL TIMELIMIT NODES STATE NODELIST
base* up 3-00:00:00 &lt;span style="color:#ae81ff">1&lt;/span> idle compute-&lt;span style="color:#f92672">[&lt;/span>1-4&lt;span style="color:#f92672">]&lt;/span>
long up infinite &lt;span style="color:#ae81ff">1&lt;/span> idle compute-&lt;span style="color:#f92672">[&lt;/span>1-4&lt;span style="color:#f92672">]&lt;/span>
root@controller#&lt;/code>&lt;/pre>&lt;/div>
&lt;/div>
&lt;div class="section" id="next-steps">
&lt;h2>Next Steps&lt;/h2>
&lt;p>Now we can start accepting job submissions, in
&lt;a class="reference external" href="https://implement.pt/2018/09/slurm-in-ubuntu-clusters-pt2/">part 2 of Slurm in Ubuntu Clusters&lt;/a> we will see how to submit job scripts,
allocate resources and see the status of the cluster.&lt;/p>
&lt;/div>
&lt;div class="section" id="additional-notes">
&lt;h2>Additional Notes&lt;/h2>
&lt;p>In order for the e-mail to work properly, we need to have a Mailing Transport Agent (MTA), i.e: postfix, the
configuration of postfix is outside this post's scope.&lt;/p>
&lt;p>In order to use MPI capabilities, the configuration depends on which implementation of MPI you will be using in your
cluster. If you want to keep the default it should work, however performance wise you could be losing by not choosing
the right MPI API.&lt;/p>
&lt;p>There are three fundamentally different modes of operation used by different MPI implementations &lt;a class="citation-reference" href="#mpislu" id="id8">[MPISLU]&lt;/a>:&lt;/p>
&lt;ol class="arabic simple">
&lt;li>Slurm directly launches the tasks and performs initialization of communications through the PMI2 or PMIx APIs.
(Supported by most modern MPI implementations)&lt;/li>
&lt;li>Slurm creates a resource allocation for the job and then mpirun launches tasks using Slurm's infrastructure
(older versions of OpenMPI).&lt;/li>
&lt;li>Slurm creates a resource allocation for the job and then mpirun launches tasks using some mechanism other than Slurm,
such as SSH or RSH. These tasks initiated outside of Slurm's monitoring or control.&lt;/li>
&lt;/ol>
&lt;p>Links to instructions for using several varieties of MPI/PMI with Slurm are provided in the slurm documentation.&lt;/p>
&lt;blockquote>
Use of an MPI implementation without the appropriate Slurm plugin may result in application failure. If multiple MPI
implementations are used on a system then some users may be required to explicitly specify a suitable Slurm MPI plugin. &lt;a class="citation-reference" href="#mpislu" id="id9">[MPISLU]&lt;/a>&lt;/blockquote>
&lt;/div>
&lt;div class="section" id="references">
&lt;h2>References&lt;/h2>
&lt;table class="docutils citation" frame="void" id="cce2003" rules="none">
&lt;colgroup>&lt;col class="label" />&lt;col />&lt;/colgroup>
&lt;tbody valign="top">
&lt;tr>&lt;td class="label">&lt;a class="fn-backref" href="#id1">[CCE2003]&lt;/a>&lt;/td>&lt;td>Simple Linux Utility for Resource Management,
M. Jette and M. Grondona, Proceedings of ClusterWorld Conference and Expo, San Jose, California, June 2003 &lt;a class="reference external" href="https://slurm.schedmd.com/slurm_design.pdf">[PDF]&lt;/a>&lt;/td>&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;table class="docutils citation" frame="void" id="mun2018" rules="none">
&lt;colgroup>&lt;col class="label" />&lt;col />&lt;/colgroup>
&lt;tbody valign="top">
&lt;tr>&lt;td class="label">&lt;a class="fn-backref" href="#id2">[MUN2018]&lt;/a>&lt;/td>&lt;td>Munge Installation Guide &lt;a class="reference external" href="https://github.com/dun/munge/wiki/Installation-Guide#creating-a-secret-key">[link]&lt;/a>&lt;/td>&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;table class="docutils citation" frame="void" id="bmisc17" rules="none">
&lt;colgroup>&lt;col class="label" />&lt;col />&lt;/colgroup>
&lt;tbody valign="top">
&lt;tr>&lt;td class="label">&lt;a class="fn-backref" href="#id3">[BMISC17]&lt;/a>&lt;/td>&lt;td>Slurm Overview, Brian Christiansen, Marshall Garey, Isaac Hartung (SchedMD) &lt;a class="reference external" href="https://slurm.schedmd.com/SC17/SlurmOverviewSC17.pdf">[PDF]&lt;/a>&lt;/td>&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;table class="docutils citation" frame="void" id="mpislu" rules="none">
&lt;colgroup>&lt;col class="label" />&lt;col />&lt;/colgroup>
&lt;tbody valign="top">
&lt;tr>&lt;td class="label">[MPISLU]&lt;/td>&lt;td>&lt;em>(&lt;a class="fn-backref" href="#id8">1&lt;/a>, &lt;a class="fn-backref" href="#id9">2&lt;/a>)&lt;/em> Slurm MPI Guide &lt;a class="reference external" href="https://slurm.schedmd.com/mpi_guide.html">[link]&lt;/a>&lt;/td>&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;table class="docutils citation" frame="void" id="slucgrp" rules="none">
&lt;colgroup>&lt;col class="label" />&lt;col />&lt;/colgroup>
&lt;tbody valign="top">
&lt;tr>&lt;td class="label">[SLUCGRP]&lt;/td>&lt;td>&lt;em>(&lt;a class="fn-backref" href="#id5">1&lt;/a>, &lt;a class="fn-backref" href="#id6">2&lt;/a>, &lt;a class="fn-backref" href="#id7">3&lt;/a>)&lt;/em> Slurm cgroup guide &lt;a class="reference external" href="https://slurm.schedmd.com/cgroups.html">[link]&lt;/a>&lt;/td>&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;table class="docutils citation" frame="void" id="krncgrp" rules="none">
&lt;colgroup>&lt;col class="label" />&lt;col />&lt;/colgroup>
&lt;tbody valign="top">
&lt;tr>&lt;td class="label">&lt;a class="fn-backref" href="#id4">[KRNCGRP]&lt;/a>&lt;/td>&lt;td>Kernel cgroup Documentation, Paul Menage, Paul Jackson and Christoph Lameter &lt;a class="reference external" href="https://www.kernel.org/doc/Documentation/cgroup-v1/cgroups.txt">[link]&lt;/a>&lt;/td>&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;/div>
&lt;/div></content><category>High Performance Computing</category><category>linux</category><category>slurm</category><category>mysql</category><category>mariadb</category><category>munge</category><category>python</category><category>scheduler</category><category>hpc</category><category>cluster</category></item><item><title>Using Python Virtual Environments with Slurm</title><link>https://implement.pt/2018/09/using-python-virtual-environments-with-slurm/</link><pubDate>Mon, 10 Sep 2018 09:35:00 +0100</pubDate><guid>https://implement.pt/2018/09/using-python-virtual-environments-with-slurm/</guid><summary type="html">&lt;div class="document">
&lt;p>A guide to using Python virtual environments with Slurm&lt;/p>
&lt;/div></summary><content type="html">&lt;div class="document">
&lt;p>Using specific packages within a shared resources environment can sometimes be a hassle, some users may need different
package versions, or even packages that conflict with each other. Relinquishing the package installation location to
local user directories makes this process easier to manage.&lt;/p>
&lt;p>In this post we'll see how to use virtual environments to manage your Python packages, this is useful not only to
separate your Python packages from the system ones but also to be able to separate packages for each project, and end
up making these environments portable and replicable across platforms.&lt;/p>
&lt;p>As a plus we'll see how to create a Slurm batch file to submit a job to a Slurm queue&lt;/p>
&lt;div class="section" id="overview">
&lt;h2>Overview&lt;/h2>
&lt;ul class="simple">
&lt;li>&lt;strong>virtualenv&lt;/strong> - command used to create our virtual environments.&lt;/li>
&lt;li>&lt;strong>$HOME/.virtualenvs/&lt;/strong> - the chosen directory to contain our virtual environments.&lt;/li>
&lt;li>&lt;strong>$HOME/.virtualenvs/AdS/&lt;/strong> - the directory where this specific virtual environment will be placed in.&lt;/li>
&lt;li>&lt;strong>sympy&lt;/strong> - in this example we will be installing the &lt;em>sympy&lt;/em> Python package.&lt;/li>
&lt;li>&lt;strong>source bin/activate&lt;/strong> - to enter the virtual environment we have to &lt;em>source&lt;/em> its &lt;em>bin/activate&lt;/em> file.&lt;/li>
&lt;li>&lt;strong>deactivate&lt;/strong> - to exit the virtual environment we have to issue the &lt;em>deactivate&lt;/em> command.&lt;/li>
&lt;li>&lt;strong>AdS_sympy.sbatch&lt;/strong> - Slurm jobs use a batch file with specific headers.&lt;/li>
&lt;/ul>
&lt;/div>
&lt;div class="section" id="requirements">
&lt;h2>Requirements&lt;/h2>
&lt;p>To be able to replicate this guide will need:&lt;/p>
&lt;ul class="simple">
&lt;li>&lt;a class="reference external" href="https://www.python.org/downloads/">python&lt;/a>&lt;/li>
&lt;li>&lt;a class="reference external" href="https://pypi.org/project/pip/">pip&lt;/a>&lt;/li>
&lt;li>&lt;a class="reference external" href="https://virtualenv.pypa.io/en/stable/">virtualenv&lt;/a>&lt;/li>
&lt;/ul>
&lt;/div>
&lt;div class="section" id="virtual-environment">
&lt;h2>Virtual Environment&lt;/h2>
&lt;p>First let's create our virtual environment&lt;/p>
&lt;div class="highlight">&lt;pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">&lt;code class="language-fallback" data-lang="fallback">t0rrant@bastion01 $ mkdir $HOME/.virtualenvs
t0rrant@bastion01 $ virtualenv $HOME/.virtualenvs/AdS&lt;/code>&lt;/pre>&lt;/div>
&lt;p>Now let's get into the virtual environment&lt;/p>
&lt;div class="highlight">&lt;pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">&lt;code class="language-fallback" data-lang="fallback">t0rrant@bastion01 $ source $HOME/.virtualenvs/AdS/bin/activate
(AdS) t0rrant@bastion01 $&lt;/code>&lt;/pre>&lt;/div>
&lt;p>We can now install the sympy package&lt;/p>
&lt;div class="highlight">&lt;pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">&lt;code class="language-fallback" data-lang="fallback">(AdS) t0rrant@bastion01 $ pip install sympy&lt;/code>&lt;/pre>&lt;/div>
&lt;p>Now let's take a look at our test file:&lt;/p>
&lt;div class="highlight">&lt;div style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">
&lt;table style="border-spacing:0;padding:0;margin:0;border:0;width:auto;overflow:auto;display:block;">&lt;tr>&lt;td style="vertical-align:top;padding:0;margin:0;border:0;">
&lt;pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">&lt;code>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">1
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">2
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">3
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">4
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">5
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td style="vertical-align:top;padding:0;margin:0;border:0;;width:100%">
&lt;pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">&lt;code class="language-python" data-lang="python">&lt;span style="color:#75715e"># AdS_sympy_test.py&lt;/span>
&lt;span style="color:#75715e">#!/usr/bin/env python&lt;/span>
&lt;span style="color:#f92672">from&lt;/span> sympy &lt;span style="color:#f92672">import&lt;/span> test
test(&lt;span style="color:#e6db74">&amp;#34;_basic&amp;#34;&lt;/span>)&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>
&lt;p>We only need to be within the virtual environment to actually use the &lt;em>sympy&lt;/em> package (i.e: execute our test file)
or to install new packages. The virtual environment will be used by the Slurm job as we will see.&lt;/p>
&lt;p>So, let us exit the virtual environment:&lt;/p>
&lt;div class="highlight">&lt;pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">&lt;code class="language-fallback" data-lang="fallback">(AdS) t0rrant@bastion01 $ deactivate
t0rrant@bastion01 $&lt;/code>&lt;/pre>&lt;/div>
&lt;/div>
&lt;div class="section" id="slurm-batch-file">
&lt;h2>Slurm Batch File&lt;/h2>
&lt;p>A quick look into the batch file, we must activate the virtual environment before running our code (deactivating it here
is not mandatory as the process will end):&lt;/p>
&lt;div class="highlight">&lt;div style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">
&lt;table style="border-spacing:0;padding:0;margin:0;border:0;width:auto;overflow:auto;display:block;">&lt;tr>&lt;td style="vertical-align:top;padding:0;margin:0;border:0;">
&lt;pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">&lt;code>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 1
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 2
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 3
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 4
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 5
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 6
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 7
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 8
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 9
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">10
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">11
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">12
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">13
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">14
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td style="vertical-align:top;padding:0;margin:0;border:0;;width:100%">
&lt;pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">&lt;code class="language-bash" data-lang="bash">&lt;span style="color:#75715e"># AdS_sympy_test/AdS_sympy.sbatch&lt;/span>
&lt;span style="color:#75715e">#!/bin/bash&lt;/span>
&lt;span style="color:#75715e">#SBATCH --job-name=AdS_sympy_test&lt;/span>
&lt;span style="color:#75715e">#SBATCH --output=/home/t0rrant/AdS_sympy_test/AdS_sympy_test-%j.out&lt;/span>
&lt;span style="color:#75715e">#SBATCH --error=/home/t0rrant/AdS_sympy_test/AdS_sympy_test-%j.err&lt;/span>
&lt;span style="color:#75715e">#SBATCH --mail-user=&amp;lt;your e-mail should go here&amp;gt;&lt;/span>
&lt;span style="color:#75715e">#SBATCH --mail-type=ALL&lt;/span>
&lt;span style="color:#75715e">#SBATCH --time=1:00:00&lt;/span>
&lt;span style="color:#75715e">#SBATCH --mem=1G&lt;/span>
RUNPATH&lt;span style="color:#f92672">=&lt;/span>/home/t0rrant/AdS_sympy_test/
cd $RUNPATH
source $HOME/.virtualenvs/AdS/bin/activate
python AdS_sympy_test.py&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>
&lt;p>Slurm options:&lt;/p>
&lt;table class="docutils option-list" frame="void" rules="none">
&lt;col class="option" />
&lt;col class="description" />
&lt;tbody valign="top">
&lt;tr>&lt;td class="option-group">
&lt;kbd>&lt;span class="option">--job-name&lt;/span>&lt;/kbd>&lt;/td>
&lt;td>This will determine the job name which appears in commands
like &lt;em>sacct&lt;/em>, &lt;em>smap&lt;/em> or &lt;em>sview&lt;/em>.&lt;/td>&lt;/tr>
&lt;tr>&lt;td class="option-group">
&lt;kbd>&lt;span class="option">--output&lt;/span>&lt;/kbd>&lt;/td>
&lt;td>Determines the location of the file to which the &lt;em>stdout&lt;/em>
should be redirected to.&lt;/td>&lt;/tr>
&lt;tr>&lt;td class="option-group">
&lt;kbd>&lt;span class="option">--error&lt;/span>&lt;/kbd>&lt;/td>
&lt;td>Determines the location of the file to which the &lt;em>stderr&lt;/em>
should be redirected to.&lt;/td>&lt;/tr>
&lt;tr>&lt;td class="option-group">
&lt;kbd>&lt;span class="option">--mail-user&lt;/span>&lt;/kbd>&lt;/td>
&lt;td>The e-mail address to which job events should be sent to.&lt;/td>&lt;/tr>
&lt;tr>&lt;td class="option-group">
&lt;kbd>&lt;span class="option">--mail-type&lt;/span>&lt;/kbd>&lt;/td>
&lt;td>The types of events that should be sent, here we use all of them.
For more info see &lt;a class="reference external" href="https://slurm.schedmd.com/sbatch.html">[the docs]&lt;/a>.&lt;/td>&lt;/tr>
&lt;tr>&lt;td class="option-group">
&lt;kbd>&lt;span class="option">--time&lt;/span>&lt;/kbd>&lt;/td>
&lt;td>The amount of time we want allocated for this job.&lt;/td>&lt;/tr>
&lt;tr>&lt;td class="option-group">
&lt;kbd>&lt;span class="option">--mem&lt;/span>&lt;/kbd>&lt;/td>
&lt;td>The amount of memory we want allocated for this job.&lt;/td>&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;p>Looks good, let's submit it to the queue:&lt;/p>
&lt;div class="highlight">&lt;pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">&lt;code class="language-fallback" data-lang="fallback">t0rrant@bastion01 $ sbatch AdS_sympy.sbatch
Submitted batch job 27047&lt;/code>&lt;/pre>&lt;/div>
&lt;p>Now check for errors and output:&lt;/p>
&lt;div class="highlight">&lt;pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">&lt;code class="language-fallback" data-lang="fallback">t0rrant@bastion01 $ cat /home/t0rrant/AdS_sympy_test/AdS_sympy_test-27047.err&lt;/code>&lt;/pre>&lt;/div>
&lt;p>No errors! =)&lt;/p>
&lt;div class="highlight">&lt;pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">&lt;code class="language-fallback" data-lang="fallback">t0rrant@bastion01 $ cat /home/t0rrant/AdS_sympy_test/AdS_sympy_test-27047.out
============================= test process starts ==============================
executable: /home/t0rrant/.virtualenvs/AdS/bin/python (2.7.13-final-0) [CPython]
architecture: 64-bit
cache: yes
ground types: python
numpy: None
random seed: 7576360
hash randomization: on (PYTHONHASHSEED=1519633959)
sympy/core/tests/test_basic.py[16] ................ [OK]
================== tests finished: 16 passed, in 0.09 seconds ==================
t0rrant@bastion01 $&lt;/code>&lt;/pre>&lt;/div>
&lt;p>As we can see it used the python executable from our virtual environment, used the sympy package also from that virtual
environment and successfully ran the basic tests from the &lt;em>sympy&lt;/em> package.&lt;/p>
&lt;/div>
&lt;/div></content><category>High Performance Computing</category><category>linux</category><category>slurm</category><category>python</category><category>scheduler</category><category>virtualenv</category><category>sympy</category></item><item><title>UEFI via software RAID with mdadm in Ubuntu 16.04</title><link>https://implement.pt/2018/08/uefi-via-software-raid-with-mdadm-ubuntu-16-04/</link><pubDate>Fri, 24 Aug 2018 09:35:00 +0100</pubDate><guid>https://implement.pt/2018/08/uefi-via-software-raid-with-mdadm-ubuntu-16-04/</guid><summary type="html">&lt;div class="document">
&lt;p>A guide to UEFI booting with RAID 1 in Ubuntu 16.04&lt;/p>
&lt;/div></summary><content type="html">&lt;div class="document" id="introduction">
&lt;h1 class="title">Introduction&lt;/h1>
&lt;p>In this post we'll see how to install an UEFI system within a RAID 1 array using mdadm, in Ubuntu Server 16.04.&lt;/p>
&lt;p>For this guide we need two images:&lt;/p>
&lt;ul class="simple">
&lt;li>systemrescuecd-x86-5.2.2 &lt;a class="reference external" href="http://www.system-rescue-cd.org/">[download]&lt;/a>&lt;/li>
&lt;li>Ubuntu 16.04.5-server-amd64 &lt;a class="reference external" href="http://releases.ubuntu.com/16.04/">[download]&lt;/a>&lt;/li>
&lt;/ul>
&lt;p>Disks: 4x 4TB (4096 block native, 512 emulated)&lt;/p>
&lt;p>Disk Layout:&lt;/p>
&lt;ol class="arabic simple">
&lt;li>100MB EFI vfat /boot/efi&lt;/li>
&lt;li>16GB SWAP swap&lt;/li>
&lt;li>40GB ROOT ext4 /&lt;/li>
&lt;li>(remainder)&lt;/li>
&lt;/ol>
&lt;div class="section" id="prepare-the-disks">
&lt;h2>Prepare the disks&lt;/h2>
&lt;p>First we need to prepare the disks for the RAID, to do that we will use the System Rescue CD as it has all the tools we will need, we start with &lt;em>parted&lt;/em>:&lt;/p>
&lt;p>(here we use MB units to ensure partition alignment, which we can check for in the end, although the partition actual size may differ, for small sizes)&lt;/p>
&lt;div class="highlight">&lt;pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">&lt;code class="language-shell" data-lang="shell">root# parted /dev/sda
&lt;span style="color:#f92672">(&lt;/span>parted&lt;span style="color:#f92672">)&lt;/span> mklabel gpt
&lt;span style="color:#f92672">(&lt;/span>parted&lt;span style="color:#f92672">)&lt;/span> unit MB
&lt;span style="color:#f92672">(&lt;/span>parted&lt;span style="color:#f92672">)&lt;/span> mkpart ESP 0&lt;span style="color:#ae81ff">\%&lt;/span> 100MB
&lt;span style="color:#f92672">(&lt;/span>parted&lt;span style="color:#f92672">)&lt;/span> set &lt;span style="color:#ae81ff">1&lt;/span> esp on
&lt;span style="color:#f92672">(&lt;/span>parted&lt;span style="color:#f92672">)&lt;/span> mkpart primary linux-swap 100MB 16.1GB
&lt;span style="color:#f92672">(&lt;/span>parted&lt;span style="color:#f92672">)&lt;/span> mkpart primary 16.1GB 56.1GB
&lt;span style="color:#f92672">(&lt;/span>parted&lt;span style="color:#f92672">)&lt;/span> mkpart primary 56.1GB 100&lt;span style="color:#ae81ff">\%&lt;/span>
&lt;span style="color:#f92672">(&lt;/span>parted&lt;span style="color:#f92672">)&lt;/span> print
Number Start End Size File System Name Flags
&lt;span style="color:#ae81ff">1&lt;/span> 1049kB 99.6MB 98.6MB fat32 EFI msftdata
&lt;span style="color:#ae81ff">2&lt;/span> 99.6MB 16.1GB 16.0GB linux-swap Linux RAID
&lt;span style="color:#ae81ff">3&lt;/span> 16.1GB 56.1GB 40GB Linux RAID
&lt;span style="color:#ae81ff">4&lt;/span> 56.1GB 4001GB 3945GB Linux RAID
&lt;span style="color:#f92672">(&lt;/span>parted&lt;span style="color:#f92672">)&lt;/span> align-check optimal &lt;span style="color:#ae81ff">1&lt;/span>
&lt;span style="color:#ae81ff">1&lt;/span> aligned
&lt;span style="color:#f92672">(&lt;/span>parted&lt;span style="color:#f92672">)&lt;/span> align-check optimal &lt;span style="color:#ae81ff">2&lt;/span>
&lt;span style="color:#ae81ff">2&lt;/span> aligned
&lt;span style="color:#f92672">(&lt;/span>parted&lt;span style="color:#f92672">)&lt;/span> align-check optimal &lt;span style="color:#ae81ff">3&lt;/span>
&lt;span style="color:#ae81ff">3&lt;/span> aligned
&lt;span style="color:#f92672">(&lt;/span>parted&lt;span style="color:#f92672">)&lt;/span> align-check optimal &lt;span style="color:#ae81ff">4&lt;/span>
&lt;span style="color:#ae81ff">4&lt;/span> aligned&lt;/code>&lt;/pre>&lt;/div>
&lt;p>Copy partition layouts using sgdisk:&lt;/p>
&lt;div class="highlight">&lt;pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">&lt;code class="language-shell" data-lang="shell">root# sgdisk --replicate&lt;span style="color:#f92672">=&lt;/span>/dev/sd&lt;span style="color:#f92672">{&lt;/span>b..d&lt;span style="color:#f92672">}&lt;/span> /dev/sda&lt;/code>&lt;/pre>&lt;/div>
&lt;p>Randomize UUID for each partition:&lt;/p>
&lt;div class="highlight">&lt;pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">&lt;code class="language-shell" data-lang="shell">root# sgdisk -G /dev/sd&lt;span style="color:#f92672">{&lt;/span>b..d&lt;span style="color:#f92672">}&lt;/span>&lt;/code>&lt;/pre>&lt;/div>
&lt;p>Note that the recommended size for the EFI Partition ranges from &lt;a class="reference external" href="https://help.ubuntu.com/community/DiskSpace">100 to
250 MB&lt;/a>.&lt;/p>
&lt;p>Create RAID array:&lt;/p>
&lt;div class="highlight">&lt;pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">&lt;code class="language-shell" data-lang="shell">root# mdadm --create --metadata&lt;span style="color:#f92672">=&lt;/span>1.0 --name&lt;span style="color:#f92672">=&lt;/span>hostname:efi /dev/md/efi --level&lt;span style="color:#f92672">=&lt;/span>mirror --raid-devices&lt;span style="color:#f92672">=&lt;/span>&lt;span style="color:#ae81ff">4&lt;/span> /dev/sda1 /dev/sdb1 /dev/sdc1 /dev/sdd1
root# mdadm --create --metadata&lt;span style="color:#f92672">=&lt;/span>1.0 --name&lt;span style="color:#f92672">=&lt;/span>hostname:swap /dev/md/swap --level&lt;span style="color:#f92672">=&lt;/span>mirror --raid-devices&lt;span style="color:#f92672">=&lt;/span>&lt;span style="color:#ae81ff">4&lt;/span> /dev/sda2 /dev/sdb2 /dev/sdc2 /dev/sdd2
root# mdadm --create --metadata&lt;span style="color:#f92672">=&lt;/span>1.2 --name&lt;span style="color:#f92672">=&lt;/span>hostname:root /dev/md/root --level&lt;span style="color:#f92672">=&lt;/span>mirror --raid-devices&lt;span style="color:#f92672">=&lt;/span>&lt;span style="color:#ae81ff">4&lt;/span> /dev/sda3 /dev/sdb3 /dev/sdc3 /dev/sdd3&lt;/code>&lt;/pre>&lt;/div>
&lt;p>The other partition is left alone as it is out of the scope of this
guide.&lt;/p>
&lt;p>Time to reboot to the Ubuntu Server image.&lt;/p>
&lt;/div>
&lt;div class="section" id="install-ubuntu-16-04">
&lt;h2>Install Ubuntu 16.04&lt;/h2>
&lt;p>After choosing/preseeding the usual installer checkboxes, we arrive at
partman stage, here we can usually see our raid arrays already assemble.
In order to choose the right md device we can switch to a separate tty
(usually by Alt+F&amp;lt;2-6&amp;gt;, or Alt+&amp;lt;left/right&amp;gt;) and either check in
/etc/mdadm/mdadm.conf for the mdXXX name corresponding to our array, or
run:&lt;/p>
&lt;div class="highlight">&lt;pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">&lt;code class="language-shell" data-lang="shell">root# cat /proc/mdstat&lt;/code>&lt;/pre>&lt;/div>
&lt;p>followed by:&lt;/p>
&lt;div class="highlight">&lt;pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">&lt;code class="language-shell" data-lang="shell">root# mdadm --detail /dev/mdXXX&lt;/code>&lt;/pre>&lt;/div>
&lt;p>so that we can check the name of each md device.&lt;/p>
&lt;p>After we are sure of which is the ESP, the swap and the root partitions,
we can choose accordingly back in the installer menu (Alt+F1, or Alt+F7)&lt;/p>
&lt;p>Here we choose EFI System Partition for the efi, SWAP space for the
swap, and ext4 mounted on / for the root filesystem.&lt;/p>
&lt;p>All done!&lt;/p>
&lt;/div>
&lt;div class="section" id="troubleshooting">
&lt;h2>Troubleshooting&lt;/h2>
&lt;p>But wait, sometimes you can (likely) come across issues when the
installer tries to run grub-install.&lt;/p>
&lt;p>This may be because you did not boot the installer in UEFI.&lt;/p>
&lt;p>If you did and still get the error, keep reading.&lt;/p>
&lt;p>Let's reboot into rescue-mode from the Ubuntu installer by choosing
&lt;cite>rescue a broken system&lt;/cite>&lt;/p>
&lt;p>Now lets choose to execute a shell into the installed system, with the
root md device (see above to be sure how to sure the root md device) as
the root filesystem.&lt;/p>
&lt;div class="highlight">&lt;pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">&lt;code class="language-shell" data-lang="shell">root# mount /boot/efi&lt;/code>&lt;/pre>&lt;/div>
&lt;p>If you get an error, confirm that your UUID is correct in /etc/fstab&lt;/p>
&lt;p>Check for installed files in &lt;em>/boot&lt;/em> and &lt;em>/boot/efi&lt;/em>.&lt;/p>
&lt;p>If you have no files there:&lt;/p>
&lt;div class="highlight">&lt;pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">&lt;code class="language-shell" data-lang="shell">root# update-grub
root# grub-install --efi-directory&lt;span style="color:#f92672">=&lt;/span>/boot/efi --uefi-secure-boot&lt;/code>&lt;/pre>&lt;/div>
&lt;p>If your UEFI boots without NVRAM entries, you can disable the NVRAM
writing via the “Update NVRAM variables to automatically boot into
Debian?” debconf prompt when running
&lt;a class="reference external" href="https://outflux.net/blog/archives/2018/04/19/uefi-booting-and-raid1/">[ref]&lt;/a>:&lt;/p>
&lt;div class="highlight">&lt;pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">&lt;code class="language-shell" data-lang="shell">root# dpkg-reconfigure -p low grub-efi-amd64&lt;/code>&lt;/pre>&lt;/div>
&lt;p>If not, you can use efibootmgr to edit the EFI boot entries.&lt;/p>
&lt;p>In order to use efibootmgr the system has to be in EFI mode, run the
following command to check for the existence of efivarfs:&lt;/p>
&lt;div class="highlight">&lt;pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">&lt;code class="language-shell" data-lang="shell">root# mount | grep efivars
efivarfs on /sys/firmware/efi/efivars type efivarfs &lt;span style="color:#f92672">(&lt;/span>ro,relatime&lt;span style="color:#f92672">)&lt;/span>&lt;/code>&lt;/pre>&lt;/div>
&lt;p>In this case it is mounted read-only through the sysfs init script, so
it needs to be remounted rw manually using the following command:&lt;/p>
&lt;div class="highlight">&lt;pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">&lt;code class="language-shell" data-lang="shell">root# mount -o remount,rw -t efivarfs efivarfs /sys/firmware/efi/efivars&lt;/code>&lt;/pre>&lt;/div>
&lt;p>Let's take a look at the EFI entries&lt;/p>
&lt;div class="highlight">&lt;pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">&lt;code class="language-shell" data-lang="shell">root# efibootmgr -v
...&lt;/code>&lt;/pre>&lt;/div>
&lt;p>If there is no entry corresponding to /dev/disk/by-partuuid/** -&amp;gt;
/dev/sd{a..d}, we need to add them manually:&lt;/p>
&lt;div class="highlight">&lt;pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">&lt;code class="language-shell" data-lang="shell">root# &lt;span style="color:#66d9ef">for&lt;/span> i in a b c d; &lt;span style="color:#66d9ef">do&lt;/span> efibootmgr -c -d /dev/sd&lt;span style="color:#ae81ff">\$&lt;/span>&lt;span style="color:#f92672">{&lt;/span>i&lt;span style="color:#f92672">}&lt;/span> -p &lt;span style="color:#ae81ff">1&lt;/span> -L &lt;span style="color:#e6db74">&amp;#34;ubuntu&amp;#34;&lt;/span> -l &lt;span style="color:#e6db74">&amp;#34;\\efi\\ubuntu\\shimx64.efi&amp;#34;&lt;/span>; &lt;span style="color:#66d9ef">done&lt;/span>
root# efibootmgr&lt;/code>&lt;/pre>&lt;/div>
&lt;p>For more info about efibootmgr check out &lt;a class="reference external" href="https://wiki.gentoo.org/wiki/Efibootmgr">the Gentoo
wiki&lt;/a>&lt;/p>
&lt;p>Now we should be ready to go!&lt;/p>
&lt;p>But wait, there is more...&lt;/p>
&lt;p>... if the UEFI writes anything to one of the drives, it may lead to
corrupted results once Linux mounts the RAID (since the member drives
won’t have identical block-level copies of the FAT32 any more).&lt;/p>
&lt;p>To deal with this &amp;quot;external write&amp;quot; situation, and since mdadm has the
&amp;quot;--update=resync&amp;quot; assembly option, we can use the following approach, :&lt;/p>
&lt;p>Prefer one RAID member’s copy of /boot/efi and rebuild the RAID at every
boot. If there were no external writes, there’s no issue. My approach
here is I really do not care which version of the ESP gets copied over,
if there is something to be written in the ESP and it gets copied over
to all RAID members we should be fine.&lt;/p>
&lt;p>This requires updating &lt;em>/etc/mdadm/mdadm.conf&lt;/em>.&lt;/p>
&lt;p>I've tried adding on the RAID’s ARRAY line to keep it from
auto-starting:&lt;/p>
&lt;div class="highlight">&lt;div style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">
&lt;table style="border-spacing:0;padding:0;margin:0;border:0;width:auto;overflow:auto;display:block;">&lt;tr>&lt;td style="vertical-align:top;padding:0;margin:0;border:0;">
&lt;pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">&lt;code>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">1
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td style="vertical-align:top;padding:0;margin:0;border:0;;width:100%">
&lt;pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">&lt;code class="language-fallback" data-lang="fallback">ARRAY metadata=1.0 UUID=XXX&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>
&lt;p>however it seems that mdadm refuses to assemble the array as it is
explicitly ignored. So I just delete the ARRAY line corresponding to the
efi md device.&lt;/p>
&lt;p>We now add the efi partition to the fstab (or modify the existing one):&lt;/p>
&lt;div class="highlight">&lt;div style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">
&lt;table style="border-spacing:0;padding:0;margin:0;border:0;width:auto;overflow:auto;display:block;">&lt;tr>&lt;td style="vertical-align:top;padding:0;margin:0;border:0;">
&lt;pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">&lt;code>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">1
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td style="vertical-align:top;padding:0;margin:0;border:0;;width:100%">
&lt;pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">&lt;code class="language-fallback" data-lang="fallback">UUID=XXXX-XXXX /boot/efi vfat noauto,defaults 0 0&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>
&lt;p>And finally we can add a systemd oneshot service that will assemble the
RAID with resync and mount it:&lt;/p>
&lt;p>&lt;em>/lib/systemd/system/efimount.service&lt;/em>&lt;/p>
&lt;div class="highlight">&lt;div style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">
&lt;table style="border-spacing:0;padding:0;margin:0;border:0;width:auto;overflow:auto;display:block;">&lt;tr>&lt;td style="vertical-align:top;padding:0;margin:0;border:0;">
&lt;pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">&lt;code>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 1
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 2
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 3
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 4
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 5
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 6
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 7
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 8
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 9
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">10
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">11
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">12
&lt;/span>&lt;span style="margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">13
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td style="vertical-align:top;padding:0;margin:0;border:0;;width:100%">
&lt;pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">&lt;code class="language-systemd" data-lang="systemd">&lt;span style="color:#66d9ef">[Unit]&lt;/span>
&lt;span style="color:#a6e22e">Description&lt;/span>&lt;span style="color:#f92672">=&lt;/span>&lt;span style="color:#e6db74">Resync /boot/efi RAID&lt;/span>
&lt;span style="color:#a6e22e">DefaultDependencies&lt;/span>&lt;span style="color:#f92672">=&lt;/span>&lt;span style="color:#e6db74">no&lt;/span>
&lt;span style="color:#a6e22e">After&lt;/span>&lt;span style="color:#f92672">=&lt;/span>&lt;span style="color:#e6db74">local-fs.target&lt;/span>
&lt;span style="color:#66d9ef">[Service]&lt;/span>
&lt;span style="color:#a6e22e">Type&lt;/span>&lt;span style="color:#f92672">=&lt;/span>&lt;span style="color:#e6db74">oneshot&lt;/span>
&lt;span style="color:#a6e22e">ExecStart&lt;/span>&lt;span style="color:#f92672">=&lt;/span>&lt;span style="color:#e6db74">/sbin/mdadm -A /dev/md/efi --uuid=YY:Y:Y:Y:YYY --update=resync&lt;/span>
&lt;span style="color:#a6e22e">ExecStart&lt;/span>&lt;span style="color:#f92672">=&lt;/span>&lt;span style="color:#e6db74">/bin/mount /boot/efi&lt;/span>
&lt;span style="color:#a6e22e">RemainAfterExit&lt;/span>&lt;span style="color:#f92672">=&lt;/span>&lt;span style="color:#e6db74">yes&lt;/span>
&lt;span style="color:#66d9ef">[Install]&lt;/span>
&lt;span style="color:#a6e22e">WantedBy&lt;/span>&lt;span style="color:#f92672">=&lt;/span>&lt;span style="color:#e6db74">sysinit.target&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>
&lt;p>We also run:&lt;/p>
&lt;div class="highlight">&lt;pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">&lt;code class="language-shell" data-lang="shell">root# update-initramfs -u&lt;/code>&lt;/pre>&lt;/div>
&lt;p>so the initramfs has an updated copy of &lt;em>/etc/mdadm/mdadm.conf&lt;/em>&lt;/p>
&lt;p>NOTE: XXXX-XXXX is the MD device's uuid, however YY:Y:Y:Y:YYY is the
UUID common for each partition /dev/sd{a..d}1 (our ESP partition) that
you can get by running:&lt;/p>
&lt;div class="highlight">&lt;pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">&lt;code class="language-shell" data-lang="shell">root# blkid | egrep &lt;span style="color:#e6db74">&amp;#34;/dev/sd.?1&amp;#34;&lt;/span>&lt;/code>&lt;/pre>&lt;/div>
&lt;p>Now yes, we can reboot and provision the system!&lt;/p>
&lt;p>A big thanks to &lt;a class="reference external" href="https://outflux.net/blog/">Kees Cook&lt;/a> as his blog
was one of the very limited resources about this use case on the web.&lt;/p>
&lt;/div>
&lt;/div></content><category>System Administration</category><category>linux</category><category>mdadm</category><category>parted</category><category>partition</category><category>raid</category><category>sgdisk</category><category>ubuntu</category><category>uefi</category></item></channel></rss>