How to activate python virtual environment on remote machine with ansible? - ansible

I'm learning Vagrant and Ansible, I'm trying to setup a local development environment for a basic flask app in ubuntu20.04 with Nginx.
my vagrantfile looks like this:
Vagrant.configure("2") do |config|
config.vm.define :ubuntuserver do | ubuntuserver |
ubuntuserver.vm.box = "bento/ubuntu-20.04"
ubuntuserver.vm.hostname = "ubuntuserver"
ubuntuserver.vm.provision :ansible do | ansible |
ansible.playbook = "development.yml"
end
ubuntuserver.vm.network "private_network", ip:"10.11.1.105"
ubuntuserver.vm.network "forwarded_port", guest: 80, host: 8080
ubuntuserver.vm.network "public_network", bridge: "en1: Wi-Fi (AirPort)"
ubuntuserver.vm.provider :virtualbox do |vb|
vb.memory = "1024"
end
ubuntuserver.vm.synced_folder "./shared", "/var/www"
end
end
my ansible-playbook like so:
-
name: local env
hosts: ubuntuserver
tasks:
- name: update and upgrade apt packages
become: yes
apt:
upgrade: yes
update_cache: yes
- name: install software properties common
apt:
name: software-properties-common
state: present
- name: install nginx
become: yes
apt:
name: nginx
state: present
update_cache: yes
- name: ufw allow http
become: yes
community.general.ufw:
rule: allow
name: "Nginx HTTP"
- name: installing packages for python env
become: yes
apt:
name:
- python3-pip
- python3-dev
- build-essential
- libssl-dev
- libffi-dev
- python3-setuptools
- python3-venv
update_cache: yes
- name: Create app directory if it does not exist
ansible.builtin.file:
path: /var/www/app
state: directory
mode: '0774'
- name: Install virtualenv via pip
become: yes
pip:
name: virtualenv
executable: pip3
- name: Set python virual env
command:
cmd: virtualenv /var/www/app/ -p python3
creates: "/var/www/app/"
- name: Install requirements
pip:
requirements: /var/www/requirements.txt
virtualenv: /var/www/app/appenv
virtualenv_python: python3
My playbook fails at the next task with error:
- name: Activate /var/www/app/appenv
become: yes
command: source /var/www/app/appenv/bin/activate
fatal: [ubuntuserver]: FAILED! => {"changed": false, "cmd": "source /var/www/app/appenv/bin/activate", "msg": "[Errno 2] No such file or directory: b'source'", "rc": 2}
Rest of the playbook
- name: ufw allow 5000
become: yes
community.general.ufw:
rule: allow
to_port: 5000
- name: Run app
command: python3 /var/www/app/appenv/app.py
From what I understand from this thread, The "source" command must be used from inside the vagrant machine. (I tried solutions from the thread but couldn't get it to work)
If I ssh into the vagrant machine and execute the three last commands of my playbook manually:
source /var/www/app/appenv/bin/activate
sudo ufw allow 5000
python3 /var/www/app/appenv/app.py
my basic flask app is running on port 5000 at the IP set in the vagrantfile 10.11.1.105
My questions are:
How can I get the playbook to work and not have to ssh into the machine to accomplish the same?
Is my approach even correct, knowing that my end goal is to replicate in the vagrant machine a similar environment to what would be the production environment and develop the flask app from my local machine in the synced folder?
to give a maximum of information, if one wants to reproduce this.
I also have a shared/app/appenv/app.py file containing the basic flask app
from flask import Flask
app = Flask(__name__)
#app.route("/")
def hello():
return "<h1 style='color:blue'>Hello There!</h1>"
if __name__ == "__main__":
app.run(host='0.0.0.0')
and shared/requirements.txt file
wheel
uwsgi
flask

This question was coming from a misconception about python venv.
I thought that in order to install packages inside the virtual env it had to be activated.
for example:
source env/bin/activate
pip install package_name
I understood later that I can install packages in the venv without activating it by doing:
env/bin/pip install package_name
So the solution with ansible is not to activate the venv to install packages but instead
- name: "Install python packages with the local instance of pip"
shell: "{{virtualenv_path}}/bin/pip3 install package_name"
become: no
or better even with the pip module and the packages in a requirements.txt file:
- name: Install project requirements in venv
pip:
requirements: '{{project_path}}/requirements.txt'
virtualenv: '{{virtualenv_path}}'
virtualenv_python: python3

I wrote this article for a Linux computer with Python 3.x. In this scenario, this is your Ansible development machine. First, verify the installed Python version and path:
# check Python version
$ python3 -V
Python 3.6.8
$ which python3
/usr/bin/python3
I recommend setting up a directory for the virtual environment:
$ mkdir python-venv
$ cd !$
Create a new virtual environment
$ python3 -m venv ansible2.9
$ ls
ansible2.9
activate python venv
$ source ansible2.9/bin/activate
(ansible2.9)$ python3 -V
Python 3.6.8
upgrade pip
(ansible2.9)$ python3 -m pip install --upgrade pip
Install Ansible in a virtual environment
(ansible2.9)$ python3 -m pip install ansible==2.9
(ansible2.9)$ which ansible
~/python-venv/ansible2.9/bin/ansible
Verify your new installation:
(ansible2.9)$ ansible --version
ansible 2.9.0
config file = /etc/ansible/ansible.cfg
configured module search path = ['/home/devops/.ansible/plugins/modules', '/usr/share/ansible/plugins/modules']
ansible python module location = /home/devops/python-venv/ansible2.9/lib64/python3.6/site-packages/ansible
executable location = /home/devops/python-venv/ansible2.9/bin/ansible
python version = 3.6.8 (default, Jan 09 2021, 10:57:11) [GCC 8.3.1 20191121 (Red Hat 8.3.1-5)]
Install Ansible roles or collections
(ansible2.9)$ ansible-galaxy collection install \
kubernetes.core:==1.2.1 -p collections
Deactivate a Python virtual environment
(ansible2.9)$ deactivate
Create another Python virtual environment for Ansible 3.0
$ python3 -m venv ansible3.0
$ ls -1
ansible2.9
ansible3.0
$ source ansible3.0/bin/activate
(ansible3.0)$ which python
~/python-venv/ansible3.0/bin/python
(ansible3.0)$ python3 -m pip install --upgrade pip
(ansible3.0)$ python3 -m pip install ansible==3.0

Related

ansible throwing error when using pip command

When I am trying to run pip command through ansible I land up in error.
{"changed": false, "msg": "Unable to find any of pip to use. pip needs to be installed."}
When I debugged on my machine I found pip was installed in latest version. I realized my file uses sudo to run the pip.
So if I do which pip
I get the path of pip but if I do sudo which pip I get nothing.
I don't know how to change my file so instead of sudo it take
- name: "Allow newuser for new super user without SUDO password for using rsync:"
lineinfile:
path: /etc/sudoers
state: present
insertafter: '^%sudo'
line: "{{ user }} ALL=(ALL:ALL) NOPASSWD: /usr/bin/rsync"
- pip:
name: opencv-python
state: forcereinstall
executable: pip
I have no idea how to fix this issue
At the remote host find out which pip is used by the remote user. Use the executable and become the remote user. For example,
- name: Install opencv-python
become_user: admin
become: true
pip:
name: opencv-python
state: forcereinstall
executable: /home/admin/.local/bin/pip
See Becoming an Unprivileged User. Pipelining should solve the problems of becoming an unprivileged user.
shell> grep pipe ansible.cfg
pipelining = true
Notes
Whatever you install by pip this way will be available to this particular remote user only.
Preferably distro packages should be used. For example
shell> apt-cache search python-opencv
python-opencv - Python bindings for the computer vision library
python-opencv-apps - opencv_apps Robot OS package - Python 2 bindings
See The pip module isn't always idempotent #28952 and the Conclusions in particular.

Ansible - pip not found

I am getting this error:
TASK [pip] *********************************************************************
failed: [default] (item=urllib3) =>
{"changed": false, "item": "urllib3",
"msg": "Unable to find any of pip2, pip to use. pip needs to be installed."}
Upon a suggestion I run following command:
ansible default -a "which pip"
I get an error:
default | FAILED | rc=1 >>
non-zero return code
So I guess that means no pip installed. I tried installing pip using:
ansible default -a "easy_install pip"
I get the following error:
default | FAILED | rc=2 >>
[Errno 2] No such file or directory
Any ideas?
UPDATE
In play_local.yaml, I have the following task:
- name: Prepare system
hosts: default
become: yes
gather_facts: false
pre_tasks:
- raw: sudo apt-get -y install python python-setuptools python-pip build-essential libssl-dev libffi-dev python-dev easyinstall pip
- file: path=/etc/sudoers.d/ssh-auth-sock state=touch mode=0440
#- lineinfile: line='Defaults env_keep += "SSH_AUTH_SOCK"' path=/etc/sudoers.d/ssh-auth-sock
- replace:
path: /etc/apt/sources.list
regexp: 'br.'
replace: ''
Shouldn't this task install pip?
Seems like pip is not installed, you can use the following task to install it:
- name: Install pip
apt:
name: python-pip
update_cache: yes
state: present
May be pip is hashed. Meaning pip is installed at path x (may be /usr/local/bin/pip), however, cached at path y (may be /usr/bin/pip). You can confirm that from - ansible default -m shell -a ‘type pip’. To resolve this you’ll need to run - ansible default -m shell -a ‘hash -r’.
BTW, you can also use command module instead of shell.
I've just met the same problem on a brand new CentOS 7. Solved through installing setuptools with yum first and then pip with easy_install as below :
ansible default -b -m yum -a "name=python-setuptools state=present"
ansible default -b -m easy_install -a "name=pip state=present"
For Debian Based systems (RUN THIS ON CLIENT SYSTEMS):
first install package python-is-python3 then add alias for pip echo alias pip=pip3 >> ~/.bashrc
I know my solution would be dumb but It works.
# pip-fix.yml
- name: pip fix
hosts: all
become: true
tasks:
- name: install python-is-python3
apt: name=python-is-python3 update_cache=yes state=present
- name: creating alias
shell: echo alias pip=pip3 >> ~/.bashrc
- name: test and upgrade pip
pip: name=pip state=latest
tags:
- packages
run using ansible-playbook pip-fix.yml
Ansible Not finding / Installing Pip
While not using the author's original details, i think this might help them or others.
I was getting the following error for installing pip:
via Ansible:
FAILED! => {"changed": false, "cmd": "/bin/easy_install --upgrade pip", "failed": true, "msg": "Couldn't find index page for 'pip' (maybe misspelled?)
directly on the host:
Searching for pip
Reading https://pypi.python.org/simple/pip/
Couldn't find index page for 'pip' (maybe misspelled?)
Scanning index of all packages (this may take a while)
Reading https://pypi.python.org/simple/
No local packages or download links found for pip
error: Could not find suitable distribution for
Requirement.parse('pip')
I attempted to use the above hash -r answer:
(NOTE: help hash... hash -r == forget all remembered locations)
- name: forget easy_install path
shell: hash -r
become: true
however this was not a solve for me.
I found via another post: 'pip install' fails for every package ("Could not find a version that satisfies the requirement") that this was the final fix:
curl https://bootstrap.pypa.io/get-pip.py | python
or
- name: manually install pip
shell: curl https://bootstrap.pypa.io/pip/2.7/get-pip.py | python
become: true
NOTE: i changed the pip version. Also, there is a better way to do a curl in ansible, this is simply an example
I then followed with the easy_install pip latest. to ensure it was up to date.
below fix worked for me
#ln -s /usr/local/bin/pip /usr/bin/pip

pexpect version on vagrant box and on host is both 4.6.0, but when installing over ansible, 3.1 is the newest version

I was using ansible to install pexpect on both my vagrant box and also onto my host. When I installed pexpect onto both computers, the version is 4.6.0, but when using ansible to install using apt-get, the maximum version is only 3.1. The error message thrown is: "Insufficient version of pexpect installed (3.1), this module requires pexpect>=3.3. Error was __init__() got an unexpected keyword argument 'echo'"}How am I able to install pexpect in order to use the expect module for ansible?
The code for downloading pexpect is
- hosts: all
become: yes
become_user: root
gather_facts: no
#can use sudo/sudo_user instead of become, but thats depreceated in ansible 2.6 or later
tasks:
- name: download pip
apt: name=python3-pip state=latest
- name: update pexpect
command: pip3 install pexpect
command: pip3 install --upgrade pip3
command: pip3 install --upgrade pexpect
Run a task per command and try to use Ansible pip module.
- name: Install Pexpect
hosts: all
become: True
become_user: root
gather_facts: False
tasks:
- name: download pip
apt:
name: python3-pip
state: latest
- name: Install Pexpect
pip:
name: pexpect
state: latest
- name: Upgrade pip - Force reinstall to the latest version
pip:
name: pip
state: forcereinstall

Can't create gitlab project with ansible [duplicate]

I'm just getting started with ansible, and I'm trying to get it to work with the linode module from OSX Yosemite. I'm using brew-installed Python, and pip-installed ansible. So I have done the following:
$ brew install python
$ PYCURL_SSL_LIBRARY=nss pip install pycurl
$ CFLAGS=-Qunused-arguments CPPFLAGS=-Qunused-arguments pip install ansible
To test:
$ which python
/usr/local/bin/python
$ python --version
Python 2.7.9
$ which ansible
/usr/local/bin/ansible
$ ansible --version
ansible 1.8.2
configured module search path = None
When I run my playbook I get:
failed=True msg='pycurl required for this module'
So somehow ansible is not seeing the pycurl library.
Here is the playbook:
---
- hosts: 127.0.0.1
connection: local
gather_facts: no
tasks:
- name: "provision a server"
local_action:
module: linode
api_key: FOO
name: linode-test1
plan: 1
datacenter: 1
distribution: 99
password: 'superSecureRootPassword'
ssh_pub_key: ssh-rsa FOO
swap: 768
wait: yes
wait_timeout: 600
state: present
It's happening on ubuntu as well, finally figured out the solution. You need to explicity pass path to python executable
Change Ansible hosts file from
[local]
localhost
to
[local]
localhost ansible_python_interpreter=/usr/local/bin/python
or to your virtual environment python path.
I was able to fix this on OS X El Capitan 10.11.6 by editing hosts:
[local]
localhost ansible_python_interpreter=/usr/local/bin/python
You may then need additional Python dependencies:
pip install pycurl
pip install linode-python

Using Ansible, how can I install PythonBrew system-wide?

I'm trying to create a playbook with Ansible (v 1.3.3) to install Pythonbrew system-wide on a Debian server following the instructions in the Pythonbrew readme file.
I am able to get Pythonbrew installed but I cannot install the specific version of Python that I want with it. I suspect the issue has to do with the shell environment Ansible is running under.
Here's my playbook script:
- name: Install and configure PythonBrew
hosts: dev
user: root
vars_files:
- vars.yml
gather_facts: false
tasks:
- name: Install PythonBrew Debian packages
apt: pkg=${item} state=installed update-cache=yes
with_items: ${pythonbrew_packages}
- name: Install PythonBrew system-wide
shell: curl -kL http://xrl.us/pythonbrewinstall | bash creates=/usr/local/pythonbrew executable=/bin/bash
- name: Update bashrc for PythonBrew
lineinfile:
dest=~/.bashrc
regexp='^'
line='[[ -s $HOME/.pythonbrew/etc/bashrc ]] && source $HOME/.pythonbrew/etc/bashrc'
state=present
create=True
- name: Install python binary
shell: pythonbrew install -v ${python_version} executable=/bin/bash
When I run this playbook, it fails with the following output
failed: [devserver] => {"changed": true, "cmd": "pythonbrew
install -v 2.7.3 ", "delta": "0:00:00.016639", "end": "2013-10-11
15:21:40.989677", "rc": 127, "start": "2013-10-11 15:21:40.973038"}
stderr: /bin/bash: pythonbrew: command not found
I've been tweaking things for the last hour or so to no avail. Does anybody have any suggestions for fixing this?
By peeking at the PythonBrew install script, I was able to figure this out. (And just in time for the deprecation of PythonBrew!)
Here's the playbook that installs PythonBrew without manual intervention. This may be of interest to anyone trying to script PythonBrew to install automatically.
vars.yml
#
# Python/PythonBrew Settings
# TODO: replace old-style Ansible ${vars} with jinja-style {{ vars }}
#
project_name: MY_PROJECT
python:
version: 2.7.3
pythonbrew:
root: /usr/local/pythonbrew
bashrc_path: $HOME/.pythonbrew/etc/bashrc
packages:
- curl
- zlib1g-dev
- libsqlite3-dev
- libssl-dev
- libxml2
- libxml2-dev
- libxslt1-dev
- libmysqlclient-dev
- libbz2-dev
pythonbrew.yml
---
#
# Install and Configure PythonBrew
#
- name: Install and configure PythonBrew
hosts: MY_HOST
user: root
vars_files:
- vars.yml
gather_facts: false
tasks:
- name: Install PythonBrew Debian packages
apt: pkg=${item} state=installed update-cache=yes
with_items: ${pythonbrew.packages}
- name: Install PythonBrew system-wide
shell: curl -kL http://xrl.us/pythonbrewinstall | bash
executable=/bin/bash
creates=${pythonbrew.root}
- name: Update bashrc for PythonBrew
lineinfile:
dest=/root/.bashrc
regexp='^'
line='[[ -s ${pythonbrew.bashrc_path} ]] && source ${pythonbrew.bashrc_path}'
state=present
create=True
# This step allows install to continue without new shell. Pulled from:
# https://github.com/utahta/pythonbrew/blob/master/pythonbrew/installer/pythonbrewinstaller.py#L91
- name: Install python binary
shell: export PYTHONBREW_ROOT=${pythonbrew.root}; source ${pythonbrew.root}/etc/bashrc; pythonbrew install ${python.version}
executable=/bin/bash
- name: Switch to python version
shell: export PYTHONBREW_ROOT=${pythonbrew.root}; source ${pythonbrew.root}/etc/bashrc; pythonbrew switch ${python.version}
executable=/bin/bash

Resources