/Proxmox_Docker_Ansible

Install openuc/uniteme in Proxmox using a docker container to run playbooks

Primary LanguagePython

Complete instructions and diagrams will be published here:
http://sipxcom.org/blog/
and 
http://blog.ezuce.com/


Automating QA/testbed deployments with Proxmox, Docker and Ansible


Proxmox Virtual Environment (https://www.proxmox.com/en/) is a complete server virtualization management solution, based on KVM (http://www.linux-kvm.org/page/Main_Page) and container virtualization, and is the weapon of choice for our QA team. This article should be seen as a follow up of the Ansible (https://www.ansible.com/) AWS blog posted earlier. We will not review the topics already discussed there. If you need a refresher please see the following {{link to Ansible AWS blog series }}


Since our QA team is using Windows (https://www.microsoft.com/en-us/windows) to manage Proxmox, I’ve encountered some difficulties in running Ansible on their PC’s so I’ve decided to take advantage of the incredibly versatile tool called Docker (https://www.docker.com/). 


Instructions on how to install Docker on Windows can be found at: https://docs.docker.com/engine/installation/windows/
Overview


On the management PC after we install Docker we will create a dockerfile that will install Ansible inside an Ubuntu (https://www.ubuntu.com/) 16.04 image and we will create an entry point that will execute a script which will call a Proxmox API wrapper to start a VM and then will invoke an Ansible playbook that will help us install/create/populate a Uniteme/sipXcom instance.


We’ll be able to check the status of the VM from the web browser by accessing Proxmox URL from our management machine.
Getting Started
Step 1. Dockerfile
The following is the Dockerfile we’ll use:
$ cat Dockerfile
FROM ubuntu:latest
LABEL ansible_management=v1.0
RUN echo "Installing ansible"
RUN apt-get update -y
RUN apt-get install software-properties-common -y
RUN apt-add-repository ppa:ansible/ansible -y
RUN apt-add-repository universe -y
RUN apt-add-repository main -y
RUN apt-get update -y
RUN apt-get install ansible -y
RUN apt-get install python-pip python-dev build-essential -y
RUN apt-get install python-selinux -y
RUN apt-get install rsync -y
RUN pip install --upgrade pip
RUN pip install configparser
RUN pip install requests
RUN pip install pyproxmox
ENTRYPOINT  ./start.py


As mentioned we will use latest Ubuntu (16.04 at the time of writing this blog), we will label that image as “ansible_management=v1.0” to be able to identify with ease newly created image. We will then install some dependencies needed to execute ‘ansible’ and ‘pyproxmox’ (Python wrapper for Proxmox API’s).


At the end of the the Dockerfile we set the entry point to a startup script written in Python (https://www.python.org/) that will invoke Ansible commands and pyproxmox.


To build your docker image you will need to run the following in the path where you have Dockerfile the following command:
$ docker build -t management_machine:v1 .


Wait for the command to complete and check your image with docker images:
 [mcostache@localhost ~]$ docker images
REPOSITORY                                                               TAG                 IMAGE ID            CREATED             SIZE
management_machine                                                       v1                  34c09e467bc5        6 days ago          577.8 MB


The final command that you will need to run will start a container that uses the above image after all the files are in place:
$ docker run -v `pwd`:`pwd` -w `pwd` -i -t management_machine:v1
Step 2.  Create startup.py
If you simply want to copy and paste the script I created make sure you take caution with regards to indentation, as you may (or may not) know python is very sensitive to that.


cat start.py
#! /usr/bin/env python


#Created by Mihai for QA and for fun
#this is the main entry point for configuring and populating a full openuc server on proxmox
#for how to use -README and blog entry on ezuce.com or follow code it is commented:)
#you need python and pip installed
#you need to ssh-copy-id for the ip of template you use centos/uniteme


try:
 import pip
except ImportError, e:
 pass # pip module doesn't exist, deal with it.




#Step1 Create an inventory.ini file to be used by Ansible
# we need to import a few python modules first




import sys,requests,os
sys.path.append('files/scripts')  # needed to import local modules in that path
import create_inventory , start_proxmox_machine
create_inventory.readConfig("files/configuration/configuration.yml")
create_inventory.readCredentials("files/configuration/proxcred.yml")
create_inventory.createInventory()


#Step 2 make sure pypromox (proxmox API wrapper is installed) on the current machine


try:
        from pyproxmox import *
except ImportError:
        pip.main(['install', '--user', 'pyproxmox'])
        from pyproxmox import *




#Step 3 prompt user for upgrade/ install or populate
os.system('clear')
action = raw_input('Do you want to Upgrade/Install/Stop or Populate a new uniteme machine? Answer with Upgrade/Install/Stop/Populate\n')
action=action.lower()


if action == "install":


#    - Start proxmox vm with proxmox API call ----
    start_proxmox_machine.readCredentials("files/configuration/proxcred.yml")
    start_proxmox_machine.readNode("files/configuration/configuration.yml")
    start_proxmox_machine.startMachine()


#    - Run ansible playbook to install openuc ----
    os.system('ansible-playbook install.yml')






elif action == "upgrade":
    #    - Start proxmox vm with proxmox API call ----
    start_proxmox_machine.readCredentials("files/configuration/proxcred.yml")
    start_proxmox_machine.readNode("files/configuration/configuration.yml")
    start_proxmox_machine.startMachine()
    #    - Run ansible playbook to upgrade openuc ----
    os.system('ansible-playbook upgrade.yml')




elif action == "stop":
    #    - Stopping proxmox vm with proxmox API call ----
    start_proxmox_machine.readCredentials("files/configuration/proxcred.yml")
    start_proxmox_machine.readNode("files/configuration/configuration.yml")
    start_proxmox_machine.stopMachine()
    exit()


elif action == "populate":
    os.system('ansible-playbook populate.yml')
    exit()


else:
    print "You can only chose install;upgrade or stop!!!"
    exit()


action2 = raw_input('Do you want to setup your server? Y/N\n')
action2=action2.lower()
if action2 == 'y':
    os.system('ansible-playbook populate.yml')
else:
    exit()


The above code is commented and self explanatory so I will mention only that I needed to create some Python modules from the original pyproxmox wrapper to make it easy to follow. Also note that we have 2 files one for credentials and one for the initial config that you will need to adapt to your own setup since with these 2 files we are creating some dependencies for pyproxmox and Ansible (create_inventory.createInventory() for example ).






Step 3. A look at inventory.ini and script that creates it 
Inventory.ini file is created by the create_inventory Python module. This is based on the proxcred file from which it will extract VM user and password and from the configuration file from which it will extract IP address of the VM:
cat files/configuration/proxcred.yml


proxuser: root@pam
proxpass: qwe123
vmuser: root
superadmin: superadmin
vmpass: 12345678


cat files/configuration/configuration.yml


node: px3
vmid: 9000
vmIP: 10.3.0.60
oucVersion: 16.04
sip_domain: mihai.test
sip_realm: mihai.test
net_domain: mihai.test
net_host: uc1


cat files/scripts/create_inventory.py


#! /usr/bin/env python


import configparser, requests, yaml, os, sys, argparse
from pprint import pprint
from ConfigParser import ConfigParser
import os.path


vmIP=""
proxuser=""
proxpass=""
def readConfig(arg):
    try:
        os.path.isfile(arg)
                
        with open(arg, 'r') as stream:
            global vmIP
                vmIP=yaml.load(stream)['vmIP']
            
            
    except:
        print "EXCEPTION::Oops!  Something must be wrong with your configuration file (./files/configuration/configuration.yml.  Try again after you check that file..."  




def readCredentials(arg):


    try:
        os.path.isfile(arg)
        with open(arg, 'r') as stream:
            global proxuser
                proxuser=yaml.load(stream)['vmuser']
                          
        with open(arg, 'r') as stream:
            global proxpass
                   proxpass =str(yaml.load(stream)['vmpass'])
           
            
    except:
         print "EXCEPTION::Oops!  Something must be wrong with your proxmox credential file (./files/configuration/proxcred.yml.  Try again after you check that file..."


def createInventory():


    with open('inventory.ini', 'w') as outfile:
            outfile.write("[proxmox]\n")
            outfile.write(vmIP)
                  outfile.write(" ansible_ssh_user=" + "\"" + proxuser +"\"")
              outfile.write(" ansible_ssh_pass=" + "\"" + proxpass + "\"")




$ cat inventory.ini
[proxmox]
10.3.0.60 ansible_ssh_user="root" ansible_ssh_pass="12345678"
Step 4. Create Ansible playbooks 
Install Playbook
First let’s have a look at the install playbook:
cat install.yml
---
- hosts: localhost
  strategy: debug
  vars_files:
      - files/configuration/proxcred.yml
      - files/configuration/configuration.yml
  tasks:
  - name: Wait for proxmox machine SSHD to start
    wait_for: host={{ vmIP  }} port=22 delay=10 timeout=320


- hosts: proxmox
  strategy: debug
  gather_facts: no
  remote_user: root
  vars_files:
      - files/configuration/proxcred.yml
      - files/configuration/configuration.yml
  tasks:
   - name: Install rsync
     yum: name=rsync state=present
  - name: Print url
    debug: msg="https://download.ezuce.com/openuc-stage/{{oucVersion}}-unstable/openuc-{{oucVersion}}.0-centos.repo"
  - name: Download desired openuc.repo
    get_url:
      force: yes
      url: https://download.ezuce.com/openuc-stage/{{oucVersion}}-unstable/openuc-{{oucVersion}}.0-centos.repo
      dest: /etc/yum.repos.d/openuc.repo
      url_password: "{{ouc_download_user}}"
      url_username: "{{ouc_download_pass}}"
    register: get_url_log
  - name: Print get_url_log_output
    debug: var=get_url_log


  - name: Installing openuc. Go get a coffee. It will take a while
    script:  install_OUC.sh


We will first use configuration and credential files defined a priori:
vars_files:
      - files/configuration/proxcred.yml
      - files/configuration/configuration.yml


Since startup script handles VM startup with Proxmox API calls  start_proxmox_machine.startMachine():, we will wait for sshd to start on the remote host   
wait_for: host={{ vmIP  }} port=22 delay=10 timeout=320


Using the get_url:  module we will download Uniteme stage repo since this is the one used by our QA team to test latest commits:
url: https://download.ezuce.com/openuc-stage/{{oucVersion}}-unstable/openuc-{{oucVersion}}.0-centos.repo


Note that we are using openuc repository credentials added in proxcred file
url_password: "{{ouc_download_user}}"
url_username: "{{ouc_download_pass}}"


After repo file is downloaded we will install Uniteme (openuc) by executing: script:  install_OUC.sh
We chose this  way of installing so we can have some output on the managed machine:
cat install_OUC.sh
#!/bin/bash
yum clean all && yum install openuc -y


Populate playbook
Now let’s examine the populate.yml playbook


This playbook will be used to configure Uniteme (openuc) and to add users. On the git link I’ve provided some sample phones; gateways and dialplan json files that  you can use in the same manner seen here for adding users:




$ cat populate.yml
---
- hosts: proxmox


  vars_files:
      - files/configuration/proxcred.yml
      - files/configuration/configuration.yml
  gather_facts: no
  remote_user: root
  tasks:
  - name: Configure openuc
    shell: sipxecs-setup --noui --sip_domain "{{ sip_domain }}" --sip_realm "{{ sip_realm }}" --net_domain "{{ net_domain }}" --net_host "{{ net_host }}"
 
  - name: Stop Iptables
    command: service iptables stop
 
 - name: Start services
   script: files/scripts/start_services.sh




  - name: copy superadmin.sql function to remote machine
    copy: src="superadmin.sql"  dest="/var/log/superadmin.sql"




   - name: Execute psql script
     shell: psql -U postgres SIPXCONFIG -f /var/log/superadmin.sql


- hosts: localhost
   tasks:
 - name: Use REST API to populate servers
    script: files/scripts/add_users_API.py


In this playbook we will connect to Proxmox host defined in inventory.ini where we will execute locally the configuration script:
shell: sipxecs-setup --noui --sip_domain "{{ sip_domain }}" --sip_realm "{{ sip_realm }}" --net_domain "{{ net_domain }}" --net_host "{{ net_host }}" using variables defined in configuration file.


We will then execute a psql stored procedure that will add the ‘superadmin’ user and a script that will start services on the remote node.


Finally from the local machine we will execute a python script that will use REST API calls to populate openuc server with users, as seen here:
$ cat files/scripts/add_users_API.py
#!/usr/bin/env python
import json, requests, yaml,configparser, requests, yaml, os, sys, argparse, os.path
from pprint import pprint
from ConfigParser import ConfigParser


# to import local modules
sys.path.append('files/scripts')
import create_url
output_json = yaml.load(open('files/json/users.json'))       # parse json file to extract entities  
headers = {'content-type': 'application/json'}


url= create_url.return_url()+'/users'
for i in output_json:
    for k in output_json[i]:
        data=json.dumps(k,ensure_ascii=False)
        new_data=data.encode("utf-8")
        r = requests.post(url,data=new_data,verify=False,headers=headers) # POST data to REST API url


This script is invoking a module created by me to define the url where to make API calls
$ cat files/scripts/create_url.py
#!/usr/bin/env python
import json, requests, yaml,configparser, requests, yaml, os, sys, argparse, os.path
from pprint import pprint
from ConfigParser import ConfigParser


vmIP=''
superadmin=''
vmpass=''


def return_ip ():
    raw_config_settins= open('files/configuration/configuration.yml', 'r')
    config_settings = yaml.safe_load(raw_config_settins)
    vmIP = str(config_settings['vmIP'])
    return vmIP


def return_superadmin ():
    raw_cred_settings = open('files/configuration/proxcred.yml','r')
    cred_settings = config_settings = yaml.safe_load(raw_cred_settings)
    superadmin = str(cred_settings['superadmin'])
    return superadmin


def return_superadmin_pass():
    raw_cred_settings = open('files/configuration/proxcred.yml','r')
    cred_settings = config_settings = yaml.safe_load(raw_cred_settings)
    vmpass = str(cred_settings['vmpass'])
    return vmpass


def return_url():
    url = "https://"+return_superadmin()+":"+return_superadmin_pass()+"@"+return_ip()+"/sipxconfig/api"
    return url


After you have all these files in place and you change the configuration file according to your setup, run the initial docker command and follow the prompts:
$  docker run -v `pwd`:`pwd` -w `pwd` -i -t management_machine:v1


Do you want to Upgrade/Install/Stop or Populate a new uniteme machine? Answer with Upgrade/Install/Stop/Populate
 
install
>>>>>>Starting proxmox VM:  9000 >>>>>>


PLAY [localhost] ***************************************************************


TASK [setup] *******************************************************************
ok: [localhost]


As always you can download PoC (Proof of Concept files) with:
git clone https://github.com/Mihai-CMM/Proxmox_Docker_Ansible.git


If you improve it please share back with the community.