Automating OS NAS Storage Management
Tags: flask, puppet, hiera, nas
Published: 2018-08-09 16:21:05

---
title: Automating OS NAS Storage Management
published: true
description: With the growing storage industry, automating the creation and mounting of network storage, becomes a critical function to maintaining large environments. Using Puppet Hiera and Flask, its now possible to manage NFS mounts via an API.
tags: flask, puppet, hiera, nas
---

According to [Forbes](https://www.forbes.com/sites/andrewcave/2017/04/13/what-will-we-do-when-the-worlds-data-hits-163-zettabytes-in-2025/#47dac9d5349a), by 2025 the Worlds Data is estimated to hit 163 Zettabytes of storage.  As you can imagine, this number continues to grow, from the enormous amount of applications spawning to our favorite puppy videos, data is everywhere. Behind that data is storage, and that storage connects to an operating system. This is where my latest project fits in.

# The Problem Statement
How can we make Network Attached Storage (NAS) easily manageable and self service to our customers?

# Context
Within my environment, there are three business logics that are key. The first is our configuration management tool, Puppet, which manages tens of thousands of servers across several productions and non-production environments. The second is Bitbucket, which houses code for the entire company and syncs self-created modules to all puppet masters on a cron-style basis. Third is the initiative to allow for system “devineers” to automate the infrastructure into a completely self-servicing environment.

Within this problem statement, there are several key players. For simplicity, I will break them down into a role, rather than their actual title – storage, systems, and customer.


# Background
In the environment when a dev group, or a customer, requests storage the request is filtered through to the systems team. The systems team then requests the storage to be provisioned from storage, which today is through an automated API call or self-service page. Once the storage is provisioned and presented to the servers, the systems teams is required to mount, provide permissions, and alert the customer that the new network storage is ready for them to use. Today the systems team task is very manual, and although processes are sync, the paperwork and hands on keyboard could push the request out longer.

# Architecture 
![bitbucket-puppet-servers](https://www.lucidchart.com/publicSegments/view/9fa476b2-3fd1-4c03-ae4a-a3be26cac1d1/image.png)

# Solution
Create a containerized API that dynamically scales and manages definition-based files that are then pushed out through configuration management. The Flask based Python API, reaches out to Bitbucket, to grab the latest version of mounts, for both hosts and hostgroups, adds the modification from the API, then commits and pushes back to git. From there Bitbucket syncs with the Puppet, and within thirty minutes (or the next manual puppet run), the new mount appears on the OS for the customer to use.

# Details
### API
<table>
    <tr>
        <th>HTTP method</th>
        <th>URI path</th>
        <th>Description</th>
    </tr>
    <tr>
        <td>GET</td>
        <td>/</td>
        <td>Returns health status to show node is active</td>
    </tr>
    <tr>
        <td>GET</td>
        <td>/mounts</td>
        <td>Retrieves all mounts</td>
    </tr>
    <tr>
        <td>GET</td>
        <td>/mounts/hosts, /mounts/hostgroups</td>
        <td>Retrieves just [hostname/hostgroup] mount.</td>
    </tr>
    <tr>
        <td>GET</td>
        <td>/mounts/[hosts/hostgroups]/[servername/hostgroupname]</td>
        <td>Retrieves servers/hostgroups mounts</td>
    </tr>
    <tr>
        <td>GET</td>
        <td>/mounts/[hosts/hostgroups]/[servername/hostgroupname]/uuid</td>
        <td> Retrieves invidiual mount point for given UUID</td>
    </tr>
    <tr>
        <td>POST/PUT</td>
        <td>/mounts/[hosts/hostgroups]</td>
        <td>Create new NAS Point for a particular host or hostgroup</td>
    </tr>
    <tr>
        <td>PATCH</td>
        <td>/mounts/[hosts/hostgroups]/[servername/hostgroupname]/uuid</td>
        <td>Modifies a NAS Point for a particular host or hostgroup</td>
    </tr>
    <tr>
        <td>DELETE</td>
        <td>/mounts/[hosts/hostgroups]/[servername/hostgroupname]</td>
        <td>Removes the management for a particular host or hostgroup</td>
    </tr>
    <tr>
        <td>DELETE</td>
        <td>/mounts/[hosts/hostgroups]/[servername/hostgroupname]/uuid</td>
        <td>Removes the management of a NAS Point for a particular host or hostgroup</td>
    </tr>
</table>

### YAML File:
The strucute of the yaml defines an array of mount objects, that live on a host/hostgroup.
```
nfs_mounts::Hosts:
   Hostname.company.com:
    -	uuid
        share_path
        local_path
        options
        owner
        group
   Hostname2.company.com:
    -	...
nfs_mounts::Hostgroups:
    Group1:
    -	uuid
        share_path
        local_path
        options
        owner
        group
    -	...
     Group2
    ...
```
### Puppet Code
The following snippet, loads the yaml, then loops through host and hostgroup, and at runtime checks to see if the host or hostgroup match.
```
# == Class: nfs_mounts ()
#
# Base Class for NAS Mounts
#
class nfs_mounts(
  $hosts,
  $hostgroups
) {
    include '::stdlib'
    # Load in Dynamic Yaml file
    if is_hash($hosts) and has_key($hosts, $::fqdn) {
        # Check if the server name matches item in loop
        $hosts[$::fqdn].each | $mount | {
            if (is_hash($mount) and
                has_key($mount, 'local_path') and
                has_key($mount, 'share_path') and
                has_key($mount, 'owner')  and
                has_key($mount, 'group') and
                has_key($mount, 'options') ){
                # Create the directory to be mounted
                exec { $name :
                    command => "/bin/mkdir -p ${mount[local_path]}",
                    unless  => "/usr/bin/test -d ${mount[local_path]}",
                }
                # Set Ownership
                file { $mount[local_path] :
                    ensure  => directory,
                    owner   => $mount[owner],
                    group   => $mount[group],
                    require => Exec[$name],
                }
                # Mount the share
                mount { $mount[local_path] :
                    ensure   => mounted,
                    atboot   => 'yes',
                    device   => $mount[share_path],
                    remounts => false ,
                    fstype   => 'nfs',
                    options  => $mount[options],
                    dump     => '0',
                    pass     => '0',
                    require  => Exec[ $name ],
                }
            }
        }
    }

    if is_hash($hostgroups) and has_key($hostgroups, $::hostgroup) {
        $hostgroups[$::hostgroup].each | $mount | {
            ...
        }
    }
}
```



To see the fully scripted api, check out the project over on my [github](https://github.com/danquack/NFS-API.git) and let me know what you think in the comments below. 

{% github https://github.com/danquack/NFS-API %}