Setup Salt Minion in a Python3 Virtual Environment on Windows

This blog is a third in series in continuation to my previous blogs viz Ansible Playbook to automate Salt Minion deployment and Setup Salt Minion in a Python3 Virtual Environment on a Redhat or Centos. In this blog, get to know how to implement Salt Minion in python 3 virtual environment running on windows servers using Ansible 🙂

To communicate between windows manage nodes andAnsible control nodes, WinRM is used to establish connection and execute playbooks or commands using powershell. To do the required configuration, please follow the steps provided here.

If you have followed the above steps you should be able to reach windows hosts using the ansible command ansible windows -m win_ping. If the response succeeds then proceed with the below steps.

Ansible playbook to install python3.7, salt and setup salt-minion to run in virtual environment on windows

# vi: set shiftwidth=2 tabstop=2 softtabstop=-1 expandtab:
# Pre-requisites:
# - All target hosts are added to the domain
# - User with domain admin rights required to run the playbooks
# - Existing Salt minion and service will be removed
---
- name: Ansible Playbook to install Salt Minion in a Python Virtual Environment
  hosts: windows
  tasks:
  # Step 1 : Remove standard salt minion service
  - name: Remove standardsalt-minion service 
    win_service:
      name: salt-minion
      state: absent
    ignore_errors: true  

  # Step 2 : Check if salt directory exists
  - name: Check if salt directory exists
    win_stat: path='C:\salt\scripts'
    register: saltstatus
  - debug:
      var: saltstatus

  # Step 3 : Uninstall salt minion if exists
  - name: Uninstall standard salt-minion
    win_package:
      path: C:\salt\uninst.exe
      product_id: salt
      state: absent
      arugments: /S
    when: not saltstatus.stat.exists

  # Step 4 : Remove standard salt minion's salt directory
  - name: Remove c:\salt directory 
    win_file:
      path: C:\salt
      state: absent
    when: not saltstatus.stat.exists

  # Step 5 : Create directory structure to deploy salt-minion
  - name: Create directory structure
    win_file:
      path: C:\salt-deploy
      state: directory

  # Step 6 : Copy python 3.7, salt repo and virtual env files
  - name: Copy python installer, salt repo and virtual env files
    win_copy:
      src: /salt-deploy/files/win
      dest: C:\salt-deploy\

  # Step 7 : Check if python already installed by this playbook previously
  - name: Check to see if Python is installed
    win_stat: path='C:\python37'
    register: pythonstatus
  - debug: 
      var: pythonstatus

  # Step 8 : Install python silently using the options provided in the 
  # unttend.xml file. Python is installed to c:\python37 directory
  - name: Install python 
    raw: 'C:\salt-deploy\win\python-3.7.6-amd64.exe unattend.xml /quiet'
    when: not pythonstatus.stat.exists
    register: pythonsetup
  - debug:
      var: pythonsetup
  
  
  # Step 9 : Extract salt from the zip archive to c:\salt directory
  - name: Extract salt to c:\salt
    win_unzip:
      src: 'C:\salt-deploy\win\salt.zip'
      dest: 'C:\'
    when: not saltstatus.stat.exists
  
 
  # Step 10 : Check if python virtual environment exists
  - name: Check salt-minion Venv exists
    win_stat: path='C:\min_venv'
    register: minionstatus
  - debug:
      var: minionstatus

  # Step 11 : Extract python virtual environment to c:\min_venv directory
  - name: Setup salt-minion Venv
    win_unzip:
      src: 'C:\salt-deploy\win\min_venv.zip'
      dest: 'C:\'
    when: not minionstatus.stat.exists
  
  # Step 12 : Update the hosts file with salt master ip address and hostname
  - name: Add salt-master IP and Hostname to hosts files 
    win_lineinfile:
      path: C:\Windows\System32\drivers\etc\hosts
      regex: '# 127\.0\.0\.1'
      insertafter: '^127\.0\.0\.1'
      line: 10.100.249.87   salt

  # Step 13 : Check if Salt Minion Service is already installed
  - name: Check Salt Minion service installed
    win_service:
      name: Minion-Venv
    register: service_info
  - debug:
      var: service_info
    
  # Step 14 : Install Salt Minion as Service from start.bat batch file
  # using nssm utility https://nssm.cc/ if not installed already
  - name: Install Minion Service using NSSM
    raw: 'C:\salt-deploy\win\nssm.exe install Minion-Venv C:\salt-deploy\win\start.bat'
    register: minionsvc
    when: not service_info.exists == true
  - debug:
      var: minionsvc

  # Step 15 : Start Salt Minion Service if not running already
  - name: Start Minion Service 
    raw: 'C:\salt-deploy\win\nssm.exe start Minion-Venv'
    register: startminion
    when: not service_info.state == 'running'
  - debug:
      var: startminion

Hope you’ve followed all the steps and able to configure Salt Minion in a Python virtual environment on windows successfully.

What am I missing here? Let me know in the comments and I’ll add it in!

Advertisement

Ansible Playbook to automate Salt Minion deployment

This Ansible playbook is written to automate the steps provided in my previous blog to setup Salt Minion in a Python3 Virtual Environment. Ansible is so beautiful yet cool to complementing SALT to be deployed across all hosts 🙂

This playbook is self explanatory for those who know Ansible at an intermediate level. Please feel free to reach out to me for help, comments and suggestions.

# vi: set shiftwidth=2 tabstop=2 softtabstop=-1 expandtab:
# Pre-requisites:
# - User 'ansible_admin' to be created on all target hosts
# - User 'ansible_admin' to be added to wheel group with no password prompt
# - SSH keys to be configured for User 'ansible_admin' from Ansible server to all hosts

---
- name: Ansible Playbook to install Salt Minion in a Python Virtual Environment
  hosts: minions
  become: yes
  tasks:
  
  # Step 1: Stop and disable default system's salt minion service. 
  # Service module used for backward compatibility.
  - name: Stop and disable Salt Minion service 
    service:
      name: salt-minion
      state: stopped
      enabled: no
    ignore_errors: true

  # Step 2: To build Python from source code gcc, gcc-c++, zlib, openssl-devel
  # are mandatory. Remaining packages are required for automated install of salt
  # minion in the virtual environment.
  - name: Install dependency packages to build Python3.6
    yum:
      name: "{{ packages }}"
      state: present
    vars:
      packages:
      - libselinux-python
      - libffi-devel
      - gcc
      - gcc-c++
      - zlib
      - zlib-devel
      - readline-devel
      - openssl-devel
      - bzip2-devel
      - sqlite-devel
      - git
      - logrotate
    register: install_result
  - debug:
      var: install_result.stdout

  # Step 3: Check if Python3.6 is already installed on the host.
  - name: Check Python3.6 availability
    stat:
      path: /usr/local/bin/python3.6
    register: out
  - debug:
      msg: "Python3.6 not installed"
    when: out.stat.exists == false

  - debug:
      msg: "REPO Host: {{ url_repo }}"
  # Step 4: Download python and salt source code. salt-minion is the venv
  # which has salt-minion configuration and python modules required to run 
  # salt minion in venv.
  - name: Download files from repo     
    get_url:
      url: "{{ url_repo }}{{ item }}"
      dest: /tmp/{{ item }}
      use_proxy: no 
    with_items:
      - salt-minion.tgz
      - Python-3.6.10.tgz
      - salt.tgz
    when: out.stat.exists == false

  # Step 5: Extract Python and place it in /tmp folder.
  - name: Extract Python3.6
    unarchive:
      src: /tmp/Python-3.6.10.tgz
      dest: /tmp
      remote_src: yes
    when: out.stat.exists == false
  
  # Step 6: Build Python3.6 from source. "make altinstall" is used
  # so that build is not merged with system's default python.
  - name: Build Python3.6 from source
    command: chdir=/tmp/Python-3.6.10 {{ item }}
    with_items:
      - ./configure
      - /usr/bin/make
      - /usr/bin/make altinstall
    register: result
    when: out.stat.exists == false
  
  # Step 7: Validate Python 3.6 build and installed successfully
  - name: Validate Python 3.6 build and installed successfully
    debug:
      msg: "Python3.6 is installed successfully"
    when: out.stat.exists == true

  # Step 8: Check Salt folder exists on the host. It won't exists unless 
  # this playbook was run earlier.
  - name: Check Salt folder exists on the host
    stat:
      path: /salt
    register: salt
  - debug:
      msg: "Salt folder not exists"
    when: salt.stat.exists == false

  # Step 9: Check Salt minionfolder exists on the host. It won't exists  
  # unless this playbook was run earlier.
  - name: Check Salt Minion folder exists
    stat:
      path: /salt-minion
    register: minion
  - debug:
      msg: "salt-minion folder not exists"
    when: minion.stat.exists == false

  # Step 10: Extract salt to root partition on the host. 
  - name: Extract salt to root partition
    unarchive:
      src: /tmp/salt.tgz
      dest: /
      remote_src: yes
    when: salt.stat.exists == false
  
  # Step 11: Extract salt to root partition on the host. 
  - name: Extract salt-minion to root partition
    unarchive:
      src: /tmp/salt-minion.tgz
      dest: /
      remote_src: yes
    when: salt.stat.exists == false

  # Step 12: Add Salt Master and host's ip and hostname in 
  # /etc/hosts file. 
  - name: Add entries to /etc/hosts file
    blockinfile:
      path: /etc/hosts
      block: |
        {{ ansible_default_ipv4.address }}    {{ ansible_hostname }}
        {{ salt_master }}      salt
  
   # Step 13: Copy salt minion logrotate policy on the host.
  - name: Copy salt minion logrotate policy
    copy:
      src: logrotate-salt-minion
      dest: /etc/logrotate.d/salt-minion
      backup: yes
      mode: '0644'

  # Step 14: Validate salt minion logrotate configuration
  - name: Validate salt minion logrotate configuration
    command: logrotate -d /etc/logrotate.d/salt-minion
    register: valrotate
    ignore_errors: true
    changed_when: false
  - debug:
      var: valrotate.stdout_lines

  # Step 15: Check python modules in virtual environment /salt-minion
  - name: Check python modules in virtual environment 
    shell: /salt-minion/bin/python -m pip freeze | wc -l
    register: modcount
    changed_when: false
  - debug:
      msg: "Total number of modules {{ modcount.stdout_lines }}"

  # Step 16: Install missing modules in virtual environment /salt-minion
  - name: Install missing modules in virtual environment
    pip:
      virtualenv: /salt-minion
      requirements: /salt-minion/requirements.txt
      extra_args: "--no-index --find-links=/salt-minion/modules/"
    register: pip
    when: modcount.stdout_lines < 18
  - debug:
      var: pip

  # Step 17: Install salt module in virtual environment /salt-minion
  - name: Install salt module in virtual environment
    pip:
      virtualenv: /salt-minion     
      name: ''
      extra_args: " -e /salt"
    when: modcount.stdout_lines < 19

  # Step 18: Check Salt Minion daemon is already running
  - name: Check Salt Minion daemon is already running or not
    command: pgrep salt-minion  
    ignore_errors: true
    changed_when: false
    register: check_minion
  - debug:
      var: check_minion.stdout

  # Step 19: Start Salt Minion in virtual environment /salt-minion
  - name: Start Salt Minion 
    command: /salt-minion/bin/salt-minion -c /salt-minion/etc/salt -d
    register: start_minion    
    when: check_minion.stdout == ''
  - debug:
      var: start_minion.stdout

  # Step 20: Clean up files downloaded in the /tmp directory
  - name: Clean up
    file:
      path: "{{ item }}"
      state: absent
    with_items:
      - /tmp/Python-3.6.10.tgz
      - /tmp/salt.tgz
      - /tmp/salt-minion.tgz
      - /tmp/Python-3.6.10

Setup Salt Minion in a Python3 Virtual Environment on a Redhat or Centos

Salt Minion requires python 2.x (python 2.x retired on 1st of 2020) or python 3.x and set of modules which are not available on the default python which was shipped with the operating system.

Why?

By running Salt Minion in a Python3.x venv (virtual environment); All packages installed within this venv would not interfere with packages outside the environment and will be contained only inside this virtual environment. In simple terms this venv do not meddle with default python came along with the OS.

How and What?

Follow the below steps to

  • Build and configure Python 3.x
  • Download salt from GitHub and create directories
  • Update configuration files
    • /etc/hosts
    • minion_id
    • logrotate configuration
  • Activate venv
  • Start salt minion and verify
  • Validate Salt Master can manage newly added minion

Steps

1 Install the dependency and build packages before installing Python3

yum install -y libffi-devel gcc gcc-c++ zlib zlib-devel readline-devel openssl-devel bzip2-devel sqlite-devel wget curl git nc

2 Download Python and Copy Python 3.6.10 from local repo to target VM

scp Python-3.6.10.tgz root@target_vm:/root/

3 Run below commands to build, configure and install python

#cd /opt
#cp /root/Python-3.6.10.tgz /opt
#tar xvf Python-3.6.10.tgz
#cd Python-3.6.10
#./configure
#make 
#make altinstall
(alternate install where modules and libraries does not mix up with existing system default Python version)

4 Verify Python 3.6.10 installed successfully

#which python3.6
 /usr/local/bin/python3.6

5 Download, Copy and extract salt and salt-minion to target VM’s root partition

#cp salt.tgz root@target_vm:/root/
#cp salt-minion.tgz root@target_vm:/root/
#tar xvf /root/salt.tgz /
#tar xvf /root/salt-minion.tgz /

6 Update Configuration files

a. /etc/hosts file by adding below two lines at the end

# Salt Master IP - Change it to production SALT Master IP
10.10.10.100   salt     
# Target VM IP and host name - Change it to the production target IP and host name 
10.10.10.150    target_vm 

b. /salt-minion/etc/salt/minion_id file by adding the target VM hostname

[root@target_vm]#cat /salt-minion/etc/salt/minion_id
target_vm
[root@target_vm]#

c. Configure logrotate

i. Check logrotate is installed or not

 [root@target_vm]#yum list logrotate
 Loaded plugins: product-id, rhui-lb, search-disabled-repos, subscription-manager
 This system is not registered with an entitlement server. You can use subscription-manager to register.
 Installed Packages
 logrotate.x86_64 3.8.6-17.el7 @rhui-rhel-7-server-rhui-rpms

ii. If not installed, install logrotate

 [root@target_vm]#yum install -y logrotate

iii. Configure logrotate for Salt Minion by creating a config file

 [root@target_vm]# vi /etc/logrotate.d/salt-minion
 /salt-minion/var/log/salt/minion {
 size 100M
 missingok
 notifempty
 rotate 7
 daily
 sharedscripts
 delaycompress
 postrotate
     pkill salt-minion && salt-minion -c /salt-minion/etc/salt -d
 endscript
 }

iv. Verify and validate logrotate configured for salt minion

 [root@target_vm]# logrotate -d /etc/logrotate.d/salt-minion
 reading config file /etc/logrotate.d/salt-minion
 Allocating hash table for state file, size 15360 B
 Handling 1 logs
 rotating pattern: /salt-minion/var/log/salt/minion  after 1 days (7 rotations)
 empty log files are not rotated, old logs are removed
 considering log /salt-minion/var/log/salt/minion
   log does not need rotating (log has been already rotated)not running postrotate script, since no logs were rotated

7 Activate venv on the target VM. Notice that after running the command name of venv is added at the beginning of the prompt(salt-minion)

[root@target_vm]#source /salt-minion/bin/activate
(salt-minion)[root@target_vm]#

8 Health checks

a. Verify all required modules are installed in the venv using pip freeze or pip list command

(salt-minion)  [root@target_vm] # pip freeze
 certifi==2019.11.28
 chardet==3.0.4
 concurrent-log-handler==0.9.16
 distro==1.4.0
 futures==3.1.1
 idna==2.8
 Jinja2==2.10.3
 MarkupSafe==1.1.1
 msgpack==0.6.2
 msgpack-python==0.5.6
 portalocker==1.5.2
 psutil==5.6.7
 pycrypto==2.6.1
 PyYAML==5.2
 pyzmq==18.1.1
 requests==2.22.0
 -e git+https://github.com/saltstack/salt@4dd05bef806924fe0b878c839a2fa0fa5c0502ef#egg=salt
 tornado==4.5.3
 urllib3==1.25.7

b. Verify target VM hostname is updated in minion_id

(salt-minion) [root@target_vm] #cat /salt-minion/etc/salt/minion_id
target_vm

9 Start the salt minion as daemon and verify

(salt-minion)  [root@target_vm]#salt-minion -c /salt-minion/etc/salt -d
 (salt-minion)  [root@target_vm]# ps auxf | grep salt-minion
 root       852  0.0  0.0 112716   980 pts/0    S+   00:52   0:00          _ grep --color=auto salt-minion
 root       721 13.7  1.1 556612 46524 ?        Sl   00:51   0:03 /salt-minion/bin/python3.6 /salt-minion/bin/salt-minion -c /salt-minion/etc/salt -d
 (salt-minion)  [root@target_vm]#

10 Verify Salt Master accepted keys of target VM and latter is added as minion successfully

a. Login to salt master server and run below command to accept keys

[root@salt-master]#salt-key -c ./etc/salt -A

b. Verify target VM is added as minion successfully

[root@salt-master]#salt '*' test.ping
target_vm
True

Hope you’ve followed all the steps and able to configure Salt Minion in a Python virtual environment successfully.

If you enjoyed this post, I’d be very grateful if you’d help it spread by emailing it to a friend, or sharing it on Twitter or Facebook. Thank you!

What am I missing here? Let me know in the comments and I’ll add it in!

References:

https://docs.saltstack.com/en/latest/ref/configuration/minion.html

https://github.com/saltstack/salt

https://docs.saltstack.com/en/latest/topics/development/hacking.html

Image Courtesy: https://www.socallinuxexpo.org/

Many Thanks for stopping by!! Wish you Happy New Year 2020!