GithubHelp home page GithubHelp logo

ansible-runner-service's Introduction

ansible-runner-service

This project wraps the ansible_runner interface inside a REST API enabling ansible playbooks to be executed and queried from other platforms.

The incentive for this is two-fold;

  • provide Ansible integration to non-python projects
  • provide a means of programmatically running playbooks where the ansible engine is running on a separate host or in a separate container

Features

The core of this project is ansible_runner, so first of all, a quick call out to those folks for such an awesome tool!

Security

  • https support (http not supported)
    • production version:
      • uses TLS mutual authentication. (<misc/nginx> folder provides a container to be used in production)
      • Valid client and server certificates must be used to access the API (See documentation in <misc/nginx> folder)
    • test version:
      • uses self-signed if existing crt/key files are not present (<misc/docker> provides a container to be used in test systems)
      • if not present, generates self-signed on first start up
  • creates or reuses ssh pub/priv keys for communication with target hosts

Monitoring

  • /metrics endpoint provides key metrics for monitoring the instance with Prometheus
  • a sample Grafana dashboard is provided in the misc/dashboards directory to track activity

Playbook Execution

  • exposes playbooks by name found within the project folder
  • supports Ansible environments that use private libraries (ie. the library directory is stored within the project folder)
  • playbooks can be run with tags to change execution behavior
  • playbooks can use limit to restrict actions to a specific host
  • playbooks can use check parameter to run the ansible-runner in check mode
  • running playbooks may be cancelled
  • supports execution of concurrent playbooks

Playbook State

  • playbook state and output can be queried during and after execution
  • playbook state shows overall status, with current active task name
  • the caller can request all events associated with current or past playbook runs
  • events may be filtered for specific output e.g. ?task=RSEULTS to show events with a taskname of RESULTS
  • playbook state is cached to improve API response times

Inventory management

  • hosts and ansible groups are managed through the API /groups and /hosts endpoints
  • Before a host can be added to the inventory, it is checked for dns, and passwordless ssh
  • missing public keys on 'candidate' hosts, result in the instance's public key being returned to the caller. The requester can then arrange for this key to be installed on the candidate host.
  • host and group vars supported either inside the 'hosts' file, or in the host_vars/group_vars sub-directories

Developer Friendly

  • simple to use REST API allowing playbooks to be run, and results/state queried
  • provides a /api endpoint describing each endpoint
  • /api content is automatically generated and has no external dependencies
  • each description includes an curl command example, together with output

Deployment

  • supports docker - Dockerfile and README included
  • cross platform support (docker image uses CentOS7 base, build process executes against Ubuntu)
  • can be packaged as an rpm or run as a container
  • designed to offer core ansible functionality, supplemented by a users set of playbooks/roles/library
  • supports configuration options through a specific /etc directory
  • configuration options may be overridden at the command line for diagnostics
  • all relevant activity is logged

Prerequisites

So far, testing has been mainly against Fedora (28) and the CentOS7 for the docker image. Other distros may work fine (Travis build uses Ubuntu Trusty for example!).

Package Dependencies

  • Python 3.6
  • pyOpenSSL (python3-pyOpenSSL on Fedora, CentOS pyOpenSSL)
  • ansible_runner 1.1.1 or above

(see requirements.txt for a more complete list of the python dependencies)

if in doubt, look in the <misc/docker> folder and build the container!

Installation

Try before you buy...assuming you have an environment that meets the python3 dependencies, simply unzip the archive and run :)

python3 ansible_runner_service.py

When you run from any directory outside of /usr, the script regards this as 'dev' mode. In this mode, all files and paths are relative to the path that you've unzipped the project into.

For 'prod' mode, a setup.py is provided. Once the package is installed and called from /usr/*/bin, the script will expect config and output files to be found in all the normal 'production' locations (see proposed file layout below)

sudo python3 setup.py install --record installed_files --single-version-externally-managed

Once this is installed, you may start the service with

ansible_runner_service

Production ready container

A container suitable for production systems can be build using the 'Dockerfile' present in the project root folder. It uses nginx with mutual TLS authentication to provide the Ansible Runner Service API.

Check documentation in <misc/nginx/README.md> folder for more information.

API Endpoints

Once the service is running, you can point your browser at https://localhost:5001/api to show which endpoints are available. Each endpoint is described along with a curl example showing invocation and output.

API endpoints

You may click on any row to expand the description of the API route and show the curl example. The app uses a self-signed certificate, so all examples use the -k parameter (insecure mode).

Note: It is not the intent of this API to validate the parameters passed to it. It is assumed that parameter selection and validation happen prior to the API call.

Here's a quick 'cheat sheet' of the API endpoints.

API Route Description
/api Show available API endpoints (this page)
/api/v1/groups List all the defined groups in the inventory
/api/v1/groups/<group_name> Manage groups within the inventory
/api/v1/groupvars/<group_name> Manage group variables
/api/v1/hosts Return a list of hosts from the inventory
/api/v1/hosts/<host_name> Show group membership for a given host
/api/v1/hosts/<host_name>/groups/<group_name> Manage ansible control of a given host
/api/v1/hostvars/<host_name>/groups/<group_name> Manage host variables for a specific group within the inventory
/api/v1/jobs/<play_uuid>/events Return a list of events within a given playbook run (job)
/api/v1/jobs/<play_uuid>/events/<event_uuid> Return the output of a specific task within a playbook
/api/v1/playbooks Return the names of all available playbooks
/api/v1/playbooks/<play_uuid> Query the state or cancel a playbook run (by uuid)
/api/v1/playbooks/<playbook_name> Start a playbook by name, returning the play's uuid
/api/v1/playbooks/<playbook_name>/tags/ Start a playbook using tags to control which tasks run
/metrics Provide prometheus compatible statistics which describe playbook activity

Testing

Testing to date has all been lab based, so please bear this in mind if considering using this tool for production use cases (bug reports welcome!). Playbook integration with Ceph and Gluster has been the primary focus together with the probe-disks.yml playbook. Did you spot the theme?..It's all about the storage™ :)

For example, with ceph the osd-configure.yml playbook has been tested successfully.

Manual Testing

The archive, downloaded from github, contains a simple playbook that just uses the bash sleep command - enabling you to quickly experiment with the API.

Use the steps below (test mode/test container version <misc/docker>), to quickly exercise the API

  1. Get the list of available playbooks (should just be test.yml) curl -k -i https://localhost:5001/api/v1/playbooks -X GET
  2. Run the runnertest.yml playbook, passing the time_delay parameter (30 secs should be enough). curl -k -i -H "Content-Type: application/json" --data '{"time_delay": 30}' https://localhost:5001/api/v1/playbooks/runnertest.yml -X POST
  3. The previous command will return the playbooks UUID. Use this identifier to query the state or progress of the run. curl -k -i https://localhost:5001/api/v1/playbooks/f39069aa-9f3d-11e8-852f-c85b7671906d -X GET
  4. Get a list of all the events in a playbook. The return list consists of all the job event ID's curl -k -i https://localhost:5001/api/v1/jobs/f39069aa-9f3d-11e8-852f-c85b7671906d/events -X GET
  5. To get specific output from a job event, you can query the job event curl -k -i https://localhost:5001/api/v1/jobs/f39069aa-9f3d-11e8-852f-c85b7671906d/events/13-c85b7671-906d-e52d-d421-000000000008 -X GET

Obviously you'll need to change the playbook uuid and job uuids for your run :)

Tips & Tricks

  1. Tweaking the environment:The script uses a configuration module which is accessible across the different modules within the project. There are two ways that settings in the configuration module can be overridden;

    • by using a config.yaml file
    • by providing a setting value when starting the ansible_runner_service program
  2. Overriding configuration at run time, lets you do quick tests like this;

    • start the service, but don't perform any passwordless ssh tests
    $ ssh_checks=false python3 ansible_runner_service
    
    • change the target user when validating ssh connection is in place
    $ target_user=root python3 ansible_runner_service
    

Automated Build & Testing

The project uses Travis CI integration to check the following;

  • Installation
  • code style (using flake8)
  • Ansible inventory management (groups/hosts)
  • API endpoints using test data and a test playbook

For more info, look at the .travis.yml file.

File Layout (Proposed)

/etc/ansible-runner-service

  • logging.yaml
  • config.yaml
  • ansible-runner-service.crt (used only with the development Flask server)
  • ansible-runner-service.key (used only with the development Flask server)
  • certs
    • client (optional placement for store authorized client certificates)
    • server
      • server.crt (server certificate issued by )
      • server.key (server certificate key)
      • ca.crt (certificate authority cert to validate client certificates) /usr/share/ansible-runner-service
  • artifacts
  • inventory
  • env
  • project
    • roles (optional)
    • library (optional)
    • test.yaml
  • roles

/var/log/ansible-runner-service.log

/usr/share/doc/ansible-runner-service

  • README.md
  • LICENSE.md

/etc/systemd/system

  • ansible-runner-service.service

/usr/bin/ or /usr/local/bin

  • ansible_runner_service

ansible-runner-service's People

Contributors

benthomasson avatar dangel101 avatar irosenzw avatar it-praktyk avatar jmolmo avatar machacekondra avatar mnecas avatar mwperina avatar pcuzner avatar tomricardobola avatar usrbinkat avatar vasylherman avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

ansible-runner-service's Issues

`cb_event_handler` is called before `runner_cache` is initialized with `play_uuid` data

It may happen that cb_event_handler is called before runner_cache is initialized with play_uuid data.

[Wed Aug 07 16:32:56.800146 2019] [wsgi:error] [pid 4584:tid 140036868404992] cb_event_handler event_data={'uuid': 'dfe405a3-b457-4a4b-9534-fdaca27e0b90', 'stdout': u'Identity added:
/usr/share/ansible-runner-service/artifacts/3f86b4f2-b920-11e9-995c-001a4a013fc1/ssh_key_data (/usr/share/ansible-runner-service/artifacts/3f86b4f2-b920-11e9-995c-001a4a013fc1/ssh_key
_data)', 'counter': 1, 'end_line': 1, 'runner_ident': '3f86b4f2-b920-11e9-995c-001a4a013fc1', 'start_line': 0, 'event': 'verbose'}
[Wed Aug 07 16:32:56.804457 2019] [wsgi:error] [pid 4584:tid 140036868404992] Exception in thread Thread-6:
[Wed Aug 07 16:32:56.804506 2019] [wsgi:error] [pid 4584:tid 140036868404992] Traceback (most recent call last):
[Wed Aug 07 16:32:56.804524 2019] [wsgi:error] [pid 4584:tid 140036868404992]   File "/usr/lib64/python2.7/threading.py", line 804, in __bootstrap_inner
[Wed Aug 07 16:32:56.804561 2019] [wsgi:error] [pid 4584:tid 140036868404992]     self.run()
[Wed Aug 07 16:32:56.804580 2019] [wsgi:error] [pid 4584:tid 140036868404992]   File "/usr/lib64/python2.7/threading.py", line 757, in run
[Wed Aug 07 16:32:56.804595 2019] [wsgi:error] [pid 4584:tid 140036868404992]     self.__target(*self.__args, **self.__kwargs)
[Wed Aug 07 16:32:56.804611 2019] [wsgi:error] [pid 4584:tid 140036868404992]   File "/usr/lib/python2.7/site-packages/ansible_runner/runner.py", line 177, in run
[Wed Aug 07 16:32:56.804626 2019] [wsgi:error] [pid 4584:tid 140036868404992]     searchwindowsize=100)
[Wed Aug 07 16:32:56.804641 2019] [wsgi:error] [pid 4584:tid 140036868404992]   File "/usr/lib/python2.7/site-packages/pexpect/spawnbase.py", line 341, in expect
[Wed Aug 07 16:32:56.804657 2019] [wsgi:error] [pid 4584:tid 140036868404992]     timeout, searchwindowsize, async_)
[Wed Aug 07 16:32:56.804672 2019] [wsgi:error] [pid 4584:tid 140036868404992]   File "/usr/lib/python2.7/site-packages/pexpect/spawnbase.py", line 369, in expect_list
[Wed Aug 07 16:32:56.804688 2019] [wsgi:error] [pid 4584:tid 140036868404992]     return exp.expect_loop(timeout)
[Wed Aug 07 16:32:56.804711 2019] [wsgi:error] [pid 4584:tid 140036868404992]   File "/usr/lib/python2.7/site-packages/pexpect/expect.py", line 111, in expect_loop
[Wed Aug 07 16:32:56.804727 2019] [wsgi:error] [pid 4584:tid 140036868404992]     incoming = spawn.read_nonblocking(spawn.maxread, timeout)
[Wed Aug 07 16:32:56.804743 2019] [wsgi:error] [pid 4584:tid 140036868404992]   File "/usr/lib/python2.7/site-packages/pexpect/pty_spawn.py", line 500, in read_nonblocking
[Wed Aug 07 16:32:56.804758 2019] [wsgi:error] [pid 4584:tid 140036868404992]     return super(spawn, self).read_nonblocking(size)
[Wed Aug 07 16:32:56.804774 2019] [wsgi:error] [pid 4584:tid 140036868404992]   File "/usr/lib/python2.7/site-packages/pexpect/spawnbase.py", line 179, in read_nonblocking
[Wed Aug 07 16:32:56.804789 2019] [wsgi:error] [pid 4584:tid 140036868404992]     self._log(s, 'read')
[Wed Aug 07 16:32:56.804804 2019] [wsgi:error] [pid 4584:tid 140036868404992]   File "/usr/lib/python2.7/site-packages/pexpect/spawnbase.py", line 130, in _log
[Wed Aug 07 16:32:56.804820 2019] [wsgi:error] [pid 4584:tid 140036868404992]     second_log.write(s)
[Wed Aug 07 16:32:56.804835 2019] [wsgi:error] [pid 4584:tid 140036868404992]   File "/usr/lib/python2.7/site-packages/ansible_runner/utils.py", line 293, in write
[Wed Aug 07 16:32:56.804896 2019] [wsgi:error] [pid 4584:tid 140036868404992]     self._emit_event(line)
[Wed Aug 07 16:32:56.804913 2019] [wsgi:error] [pid 4584:tid 140036868404992]   File "/usr/lib/python2.7/site-packages/ansible_runner/utils.py", line 329, in _emit_event
[Wed Aug 07 16:32:56.804929 2019] [wsgi:error] [pid 4584:tid 140036868404992]     self._event_callback(event_data)
[Wed Aug 07 16:32:56.804944 2019] [wsgi:error] [pid 4584:tid 140036868404992]   File "/usr/lib/python2.7/site-packages/ansible_runner/runner.py", line 68, in event_callback
[Wed Aug 07 16:32:56.804959 2019] [wsgi:error] [pid 4584:tid 140036868404992]     should_write = self.event_handler(event_data)
[Wed Aug 07 16:32:56.804974 2019] [wsgi:error] [pid 4584:tid 140036868404992]   File "/usr/lib/python2.7/site-packages/ansible_runner_service-0.9-py2.7.egg/runner_service/services/pla
ybook.py", line 163, in cb_event_handler
[Wed Aug 07 16:32:56.804990 2019] [wsgi:error] [pid 4584:tid 140036868404992]     runner_cache[ident]['last_task_num'] = event_data['counter']
[Wed Aug 07 16:32:56.805005 2019] [wsgi:error] [pid 4584:tid 140036868404992] KeyError: '3f86b4f2-b920-11e9-995c-001a4a013fc1'
[Wed Aug 07 16:32:56.805036 2019] [wsgi:error] [pid 4584:tid 140036868404992]
[Wed Aug 07 16:32:56.853016 2019] [wsgi:error] [pid 4584:tid 140037111437056] [remote 127.0.0.1:38508] Playbook 3f86b4f2-b920-11e9-995c-001a4a013fc1 started in 0.1s
[Wed Aug 07 16:32:56.853619 2019] [wsgi:error] [pid 4584:tid 140037111437056] [remote 127.0.0.1:38508] Playbook ovirt-host-upgrade.yml, UUID=3f86b4f2-b920-11e9-995c-001a4a013fc1 initi
ated : status=running

empty inventory file can cause 409 errors

If the initial inventory exists, but is an empty file (created by a touch) the inventory load method will not seed it, and the loaded property will return False, since the inventory is None.

service is running, but can not access

2021-11-16 20:18:14,121 - root - INFO - Analysing local configuration options from /Users/eightzero/ansible/ansible-runner-service/config.yaml
2021-11-16 20:18:14,125 - root - INFO - - setting target_user to root
2021-11-16 20:18:14,128 - root - INFO - Analysing runtime overrides from environment variables
2021-11-16 20:18:14,129 - root - INFO - No configuration settings overridden
2021-11-16 20:18:14,134 - root - INFO - Loaded logging configuration from /Users/eightzero/ansible/ansible-runner-service/logging.yaml
2021-11-16 20:18:14,134 - root - INFO - Run mode is: dev
2021-11-16 20:18:14,134 - root - INFO - SSH keys present in /Users/eightzero/ansible/ansible-runner-service/samples/env
2021-11-16 20:18:14,135 - runner_service.utils - DEBUG - Checking for the SSL keys in /Users/eightzero/ansible/ansible-runner-service
2021-11-16 20:18:14,135 - runner_service.utils - INFO - Using existing SSL files in /Users/eightzero/ansible/ansible-runner-service
2021-11-16 20:18:14,188 - werkzeug - INFO -  * Running on https://127.0.0.1:5001/ (Press CTRL+C to quit)
~ curl https://127.0.0.1:5001/api
curl: (60) SSL certificate problem: self signed certificate
More details here: https://curl.haxx.se/docs/sslcerts.html

curl failed to verify the legitimacy of the server and therefore could not
establish a secure connection to it. To learn more about this situation and
how to fix it, please visit the web page mentioned above.

Wirting to log file

Hello,

Very nice service and job you are doing, I have one question.
When running ansible from cli it does write to the log file, but when I run it through the API it does not.
Is there a way to change that and log actions from the API?

Thank you!

Docker build fails (cryptography)

While trying to build :

 => ERROR [ 3/13] RUN /usr/bin/python3 -m pip install ansible cryptography docutils psutil PyYAML     pyOpenSSL flask flask-restful uwsgi netaddr notario &&     /usr/bin/python3 -m pip install -  137.1s 
------                                                                                                                                                                                                     
 > [ 3/13] RUN /usr/bin/python3 -m pip install ansible cryptography docutils psutil PyYAML     pyOpenSSL flask flask-restful uwsgi netaddr notario &&     /usr/bin/python3 -m pip install --no-cache-dir ansible-runner==1.4.6 &&     rm -rf /var/cache/yum:                                                                                                                                                          
#6 1.750 WARNING: Running pip install with root privileges is generally not a good idea. Try `__main__.py install --user` instead.                                                                         
#6 1.893 Collecting ansible                                                                                                                                                                                
#6 2.918   Downloading https://files.pythonhosted.org/packages/fd/f8/071905c6a67592d0852a9f340f6ab9226861eeeb97fdf4068642b22edcf3/ansible-4.10.0.tar.gz (36.8MB)                                           
#6 129.4 Collecting cryptography
#6 131.9   Downloading https://files.pythonhosted.org/packages/6d/0c/5e67831007ba6cd7e52c4095f053cf45c357739b0a7c46a45ddd50049019/cryptography-38.0.1.tar.gz (599kB)
#6 133.6     Complete output from command python setup.py egg_info:
#6 133.6     
#6 133.6             =============================DEBUG ASSISTANCE==========================
#6 133.6             If you are seeing an error here please try the following to
#6 133.6             successfully install cryptography:
#6 133.6     
#6 133.6             Upgrade to the latest pip and try again. This will fix errors for most
#6 133.6             users. See: https://pip.pypa.io/en/stable/installing/#upgrading-pip
#6 133.6             =============================DEBUG ASSISTANCE==========================
#6 133.6     
#6 133.6     Traceback (most recent call last):
#6 133.6       File "<string>", line 1, in <module>
#6 133.6       File "/tmp/pip-build-guiafx4y/cryptography/setup.py", line 17, in <module>
#6 133.6         from setuptools_rust import RustExtension
#6 133.6     ModuleNotFoundError: No module named 'setuptools_rust'
#6 133.6     
#6 133.6     ----------------------------------------
#6 136.6 Command "python setup.py egg_info" failed with error code 1 in /tmp/pip-build-guiafx4y/cryptography/

Maybe related to #84 ?

I'm building it on Docker for mac, tried both docker build and docker build --platform x86_64

add ipv6 support to ansible hosts

ansible-runner-service fails to resolve host if its running on ipv6 controller host ,
this issue can be reproduced with this command:

curl -v -H "Accept: application/json" -H "Content-type: application/json" -X POST -d "{}" http://localhost:50001/api/v1/hosts/XXXX/groups/ovirt
* About to connect() to localhost port 50001 (#0)
*   Trying ::1...
* Connected to localhost (::1) port 50001 (#0)
> POST /api/v1/hosts/caracal04.lab.eng.tlv2.redhat.com/groups/ovirt HTTP/1.1
> User-Agent: curl/7.29.0
> Host: localhost:50001
> Accept: application/json
> Content-type: application/json
> Content-Length: 2
> 
* upload completely sent off: 2 out of 2 bytes
< HTTP/1.1 404 NOT FOUND
< Date: Thu, 13 Feb 2020 12:08:29 GMT
< Server: Apache/2.4.6 (Red Hat Enterprise Linux) OpenSSL/1.0.2k-fips mod_wsgi/4.6.4 Python/2.7
< Content-Length: 173
< Content-Type: application/json
< 
{"status": "NOCONN", "msg": "SSH error - 'XXXX' not found; check DNS or /etc/hosts", "data": {"hostname": "XXXX"}}

": No such file or directory" while running the build image

Followed all the step mentioned at misc/docker/readme and build the image.

While trying to run the image to create container, I am getting error : ": No such file or directory"

I doubted that may be the file defined in ENTRYPOINT ie. "/usr/local/bin/ansible_runner_service" was not present so I replaced "/usr/local/bin/ansible_runner_service" with "/bin/bash" and tried running the image and /usr/local/bin/ansible_runner_service was present there.

So I tried executing the command from bash inside the container "ansible_runner_service" from "/usr/local/bin" dir and got the same error : ": No such file or directory"

image

Client to API authentication should use mutual TLS

Current client to API auth is based on basic http auth and web token. This design is problematic since it requires user credentials to be stored in a secure manner on the api server.

An alternate approach is to use TLS server + client authentication (mutual TLS). Implementing a new authentication mechanism also provides an opportunity to review the role of the flask dev server within the solution.

Options Considered

  1. Stay with flask, and override the werkzeug request handler to provide the client cert object within the stand request handling process
  2. remove the embedded server, and run the flask app within an app container (uwsgi) fronted by a production grade web server (nginx)

Provide credentials in new hosts

In order to execute playbooks in new hosts we require first to have password-less access to these new hosts.
In this moment we are doing this in a manual way, copying the Ansible Runner Service root user ssh public key to the new hosts.

Probably the procedure basically is to execute:
ssh-copy-id -i ~/.ssh/mykey.pub user@host

we will need to provide "user" password in "host" to complete the operation, but once the operation has been executed we will have the kind of access required.

Add new API endpoints to manage playbooks

Provide the capability of adding new playbooks to the "project" folder using the REST API directly .

Now we have an endpoint for listing Playbooks:
GET /api/v1/playbooks

Probably It will be needed to add a new endpoint for playbooks management

/api/v1/project/playbooks

with the following operations:

GET .../api/v1/project/playbooks
Retuns a list of available playbooks ( Move this from the current end-point
"/api/v1/project/playbooks"

POST .../api/v1/project/playbooks/<new_playbook>
Use the http request payload to store the content of the playbook <new_playbook>.
Creates the file <new_playbook> with the content stored in http payload in the projects folder.

DELETE .../api/v1/project/playbooks/<playbook>
Delete the file from the project folder

Support for winrm connection to Windows hosts

We were looking at creating an API wrapper around ansible runner when we found this project. 🤗

Our use case primarily revolves around managing Windows servers using winrm rather than SSH. Does ansible runner service inventory support Windows hosts that use winrm connection?

How do I enable CORS headers on ansible-runner-service

I use Ansible Runner Service to automate the execution of ansible playbooks. Am now trying to build a simple React UI to run some of my play book manually. But the request gets blocked by browser due to CORS policy.

Access to XMLHttpRequest at 'https://ansiblerunnerservice.lan:5001/api' from origin 'http://mywebserver.lan:3000' has been blocked by CORS policy: No 'Access-Control-Allow-Origin' header is present on the requested resource.

The ansible-runner-service is written using Python & my skills on python is very basic. Am wondering how could I add the 'Access-Control-Allow-Origin' to whitelist the web app.

Config file

Hello,

I am having a bit of a hard time to figure out where I can place ansible config file.
It seems it ignores the one in /usr/share/ansible-runner-service/project
Where should I place it?

Ubuntu20 Dockerfile

Hello,
I try a pull request but got Acess Denied.
Is it possible to add support for Ubuntu20?
Example of Dockerfile :

FROM ubuntu:20.04

# Ansible Runner Version
ARG ANSIBLE_RUNNER_VERSION="1.4.6"
ARG SERVICE_DIR="/root/ansible-runner-service"

# RPM package list
ARG DEB_PKGS="\
              wget unzip nginx supervisor python3-psutil \
              python3-pexpect python3-daemon  bubblewrap gcc \
              bzip2  python3 python3-pip python3-dev vim \
              python3-setuptools locales locales-all \
"
ARG PIP_PKGS="\
              ansible cryptography docutils psutil PyYAML \
              pyOpenSSL flask flask-restful uwsgi netaddr notario \
"

# APT Install Dependencies
RUN DEBIAN_FRONTEND="noninteractive" apt update && apt-get -y install tzdata
RUN apt update \
    && apt-get install -y ${DEB_PKGS}

RUN /usr/bin/python3 -m pip install ${PIP_PKGS} \
    && /usr/bin/python3 -m \
          pip install --no-cache-dir ansible-runner==${ANSIBLE_RUNNER_VERSION} \
    && echo

# Prepare folders for shared access and ssh
RUN mkdir -p /etc/ansible-runner-service && \
    mkdir -p /root/.ssh && \
    mkdir -p /usr/share/ansible-runner-service/artifacts && \
    mkdir -p usr/share/ansible-runner-service/env && \
    mkdir -p usr/share/ansible-runner-service/project && \
    mkdir -p usr/share/ansible-runner-service/inventory && \
    mkdir -p usr/share/ansible-runner-service/client_cert

# Install Ansible Runner
COPY ./*.py ${SERVICE_DIR}/
COPY ./*.yaml ${SERVICE_DIR}/
COPY ./runner_service ${SERVICE_DIR}/runner_service
COPY ./samples {SERVICE_DIR}/samples

# Put configuration files in the right places
# Nginx configuration
COPY misc/nginx/nginx.conf /etc/nginx/
# Ansible Runner Service nginx virtual server
COPY misc/nginx/ars_site_nginx.conf /etc/nginx/conf.d
# Ansible Runner Service uwsgi settings
COPY misc/nginx/uwsgi.ini ${SERVICE_DIR}
# Supervisor start sequence
COPY misc/nginx/supervisord.conf ${SERVICE_DIR}

EXPOSE 5001
ENV LC_ALL en_US.UTF-8
ENV LANG en_US.UTF-8
ENV LANGUAGE en_US.UTF-8
# Start services
CMD ["/usr/bin/supervisord", "-c", "/root/ansible-runner-service/supervisord.conf"]

Thank you

KeyError: 'event_data' - git clone "install"

Hi,

is this related to ansible-runner-service or ansible-runner?
Its clean installation (clone) from git, i just tweaked config.yaml to bind to specific IP:PORT.
Running on Centos7, package python36 from epel.

[*** ~]# pip list
Package        Version
-------------- -------
aniso8601      6.0.0  
ansible-runner 1.3.0  
asn1crypto     0.24.0 
bcrypt         3.1.6  
cffi           1.12.2 
Click          7.0    
cryptography   2.6.1  
docutils       0.14   
Flask          1.0.2  
Flask-RESTful  0.3.7  
itsdangerous   1.1.0  
Jinja2         2.10   
lockfile       0.12.2 
MarkupSafe     1.1.1  
paramiko       2.4.2  
pexpect        4.6.0  
pip            19.0.3 
psutil         5.6.1  
ptyprocess     0.6.0  
pyasn1         0.4.5  
pycparser      2.19   
PyJWT          1.7.1  
PyNaCl         1.3.0  
pyOpenSSL      19.0.0 
python-daemon  2.2.3  
pytz           2018.9 
PyYAML         5.1    
setuptools     39.2.0 
six            1.12.0 
Werkzeug       0.15.1 

[*** ~]# pip -V
pip 19.0.3 from /usr/local/lib/python3.6/site-packages/pip (python 3.6)

[*** ansible-runner-service]# python3.6 ansible_runner_service.py 
Starting ansible-runner-service
Analysing local configuration options from ./config.yaml
/opt/zbynek/ansible-runner-service/runner_service/configuration.py:96: YAMLLoadWarning: calling yaml.load() without Loader=... is deprecated, as the default Loader is unsafe. Please read https://msg.pyyaml.org/load for full details.
  local_config = yaml.load(_cfg.read())
- setting target_user to root
- setting passwords to {'admin': 'admin'}
- setting token_secret to secret
- setting token_hours to 24
Analysing runtime overrides from environment variables
No configuration settings overridden
2019-03-27 13:24:43,818 - root - INFO - Loaded logging configuration from ./logging.yaml
2019-03-27 13:24:43,819 - root - INFO - Run mode is: dev
2019-03-27 13:24:43,819 - runner_service.utils - DEBUG - Checking svctoken
2019-03-27 13:24:43,821 - runner_service.utils - INFO - svctoken created
2019-03-27 13:24:43,822 - root - DEBUG - No SSH keys present in ./samples/env
2019-03-27 13:24:43,822 - root - INFO - Creating SSH keys
2019-03-27 13:24:49,216 - runner_service.utils - INFO - Created SSH public key @ './samples/env/ssh_key.pub'
2019-03-27 13:24:49,217 - runner_service.utils - INFO - Created SSH private key @ './samples/env/ssh_key'
2019-03-27 13:24:49,220 - runner_service.utils - DEBUG - Checking for the SSL keys in .
2019-03-27 13:24:49,220 - runner_service.utils - INFO - Existing SSL files not found in .
2019-03-27 13:24:49,220 - runner_service.utils - INFO - Self-signed cert will be created - expiring in 3 years
2019-03-27 13:24:49,359 - runner_service.utils - DEBUG - Writing crt file to ./ansible_runner_service.crt
2019-03-27 13:24:49,360 - runner_service.utils - DEBUG - Writing key file to ./ansible_runner_service.key
 * Serving Flask app "runner_service" (lazy loading)
 * Environment: 
 * Debug mode: on
2019-03-27 13:24:49,414 - werkzeug - INFO -  * Running on https://0.0.0.0:5001/ (Press CTRL+C to quit)
2019-03-27 13:24:59,108 - runner_service.controllers.utils - INFO - 10.10.120.105 made a request without a valid token
2019-03-27 13:24:59,111 - werkzeug - INFO - 10.10.120.105 - - [27/Mar/2019 13:24:59] "GET /api/v1/jobs/9d3f0766-5089-11e9-837c-aade9b8475b0/events HTTP/1.1" 401 -
2019-03-27 13:25:08,039 - runner_service.controllers.login - DEBUG - Request received, content-type :None
2019-03-27 13:25:08,040 - runner_service.controllers.login - INFO - 10.10.120.105 - GET /api/v1/login
2019-03-27 13:25:08,042 - werkzeug - INFO - 10.10.120.105 - - [27/Mar/2019 13:25:08] "get /api/v1/login HTTP/1.1" 200 -
2019-03-27 13:25:40,469 - runner_service.controllers.playbooks - DEBUG - Request received, content-type :application/json
2019-03-27 13:25:40,471 - runner_service.controllers.playbooks - INFO - 10.10.120.105 - POST /api/v1/playbooks/runnertest.yml, parms={'time_delay': 30}
2019-03-27 13:25:40,471 - runner_service.controllers.playbooks - INFO - Playbook run request for runnertest.yml, from 10.10.120.105, parameters: {'time_delay': 30}
2019-03-27 13:25:40,473 - runner_service.services.playbook - DEBUG - Clearing up old env directory
2019-03-27 13:25:40,480 - runner_service.services.playbook - DEBUG - Playbook 6f04223a-508b-11e9-87ab-aade9b8475b0 started in 0.0s
2019-03-27 13:25:40,481 - runner_service.controllers.playbooks - INFO - Playbook runnertest.yml, UUID=6f04223a-508b-11e9-87ab-aade9b8475b0 initiated : status=starting
2019-03-27 13:25:40,483 - werkzeug - INFO - 10.10.120.105 - - [27/Mar/2019 13:25:40] "POST /api/v1/playbooks/runnertest.yml HTTP/1.1" 202 -
Exception in thread Thread-5:
Traceback (most recent call last):
  File "/usr/lib64/python3.6/threading.py", line 916, in _bootstrap_inner
    self.run()
  File "/usr/lib64/python3.6/threading.py", line 864, in run
    self._target(*self._args, **self._kwargs)
  File "/usr/local/lib/python3.6/site-packages/ansible_runner/runner.py", line 141, in run
    searchwindowsize=100)
  File "/usr/local/lib/python3.6/site-packages/pexpect/spawnbase.py", line 341, in expect
    timeout, searchwindowsize, async_)
  File "/usr/local/lib/python3.6/site-packages/pexpect/spawnbase.py", line 369, in expect_list
    return exp.expect_loop(timeout)
  File "/usr/local/lib/python3.6/site-packages/pexpect/expect.py", line 111, in expect_loop
    incoming = spawn.read_nonblocking(spawn.maxread, timeout)
  File "/usr/local/lib/python3.6/site-packages/pexpect/pty_spawn.py", line 485, in read_nonblocking
    return super(spawn, self).read_nonblocking(size)
  File "/usr/local/lib/python3.6/site-packages/pexpect/spawnbase.py", line 179, in read_nonblocking
    self._log(s, 'read')
  File "/usr/local/lib/python3.6/site-packages/pexpect/spawnbase.py", line 130, in _log
    second_log.write(s)
  File "/usr/local/lib/python3.6/site-packages/ansible_runner/utils.py", line 254, in write
    event_data = self._emit_event(value[:match.start()], event_data)
  File "/usr/local/lib/python3.6/site-packages/ansible_runner/utils.py", line 307, in _emit_event
    self._event_callback(event_data)
  File "/usr/local/lib/python3.6/site-packages/ansible_runner/runner.py", line 65, in event_callback
    should_write = self.event_handler(event_data)
  File "/opt/zbynek/ansible-runner-service/runner_service/services/playbook.py", line 160, in cb_event_handler
    runner_cache[ident]['role'] = event_data['event_data'].get('role', '')
KeyError: 'event_data'

Race condition in start playbook

When there are two parallel calls to start playbook, it may happen that the first and also the second call passes the line which cleans the artifacts directory. So later whatever playbook first created this directory is executed correctly, but the second playbook is not because it's using the data of the first playbook.

There are few options how to solve it:

  1. Make sure the start playbook is synchronized.
  2. Use different private_data_dir for each ansible playbook call. Internally ansible-runner can use the temp dir, if this directory is not specified, which could be the solution.

Collection of spec files

It would be nice to have a collection of spec files to build the rpm with different "flavours" :
For example:

  • Including systemd unit files to manage the service:
    • A spec file for use the Flask dev server
    • A spec file for use gunicorn http server: See @machacekondra spec file as example

sample runner-service docker image needs updating

ceph-ansible now hardcodes an ansible 2.8 dependency, and the current example container is using 2.7. This mismatch causes any testing with ceph-ansible to fail.

Can you please refresh the container image with ansible 2.8

Remove Paramiko dependency

We are using Paramiko just to check if contact with external servers is possible or not. Probably this can be done using a "ping" playbook, and thus avoid this dependency (security problems) and just rely in what Ansible provides to make this kind of check.

Sending limit to API service

Hey,

I am testing the service, but it seems to me it is not possible to send "limit" parameter.
When added to the payload the service does not send it to ansible and roles are applied to all the invetory.

POST /api/v1/playbooks/base.yml, parms={'limit': '127.0.0.1'}
This is what I am seeing in the logs coming to the service, and that playbook run on every host in the inventory.

How can I send either invetory or limit to the playbooks to target specific hosts?

Docker build fail

Hello, I am trying to run the Docker container on Ubuntu 20.04 with Docker version 19.03.12, build 48a66213fe
It seems the image can't build because of easy_install error, this is the output:

 ---> Running in 995ce457e1c7
Searching for pip
Best match: pip 9.0.3
Adding pip 9.0.3 to easy-install.pth file
Installing pip script to /usr/lib/python3.6/site-packages
error: [Errno 21] Is a directory: '/usr/lib/python3.6/site-packages/pip'
The command '/bin/sh -c easy_install-3.6 -d /usr/lib/python3.6/site-packages pip &&     ln -s /usr/lib/python3.6/site-packages/pip3 /usr/local/bin/pip3' returned a non-zero code: 1

I manged to make it work with the following Dockerfile, I have not tested the service with playbooks yet, but the /api endpoint seems to work. If you think it is good enough please use it or I can make pull request:

FROM centos:7
#RUN easy_install-3.6 -d /usr/lib/python3.6/site-packages pip && \
#    ln -s /usr/lib/python3.6/site-packages/pip3 /usr/local/bin/pip3
# python2 CentOS packages
# python2-flask-restful python-flask python-crypto pyOpenSSL
# python2-psutil python-pip
# python-daemon (pulled in by pip3 install of ansible-runner)
# python-wheel
# PyYAML


# Install Ansible Runner
RUN yum -y install epel-release  && \
    yum -y install bash wget unzip ansible \
           pexpect python-daemon  bubblewrap gcc \
           bzip2  openssh openssh-clients python2-psutil\
           python36 python36-devel python36-setuptools && \
           localedef -c -i en_US -f UTF-8 en_US.UTF-8
RUN /usr/bin/pip3 install cryptography docutils psutil PyYAML \
                 pyOpenSSL flask flask-restful && \
    /usr/bin/pip3 install --no-cache-dir ansible-runner==1.3.2 && \
    rm -rf /var/cache/yum

RUN mkdir -p /etc/ansible-runner-service && \
    mkdir -p /root/.ssh && \
    mkdir -p /usr/share/ansible-runner-service/{artifacts,env,project,inventory}

COPY ./ansible-runner-service.tar.gz /root/.
WORKDIR /root
RUN tar xvzf ansible-runner-service.tar.gz && \
    cd ansible-runner-service && \
    python3.6 setup.py install --record installed_files \
           --single-version-externally-managed

ENTRYPOINT ["/usr/local/bin/ansible_runner_service"]

Passing hosts through Post to run a playbook

anisble-runner allows you to send hosts
--hosts HOSTS Define the set of hosts to execute against
I'd like to be able to pass these in as well as part of the POST to run a playbook.

I know i can modify/add groups and hosts but having to manage this when multiple entities can call into the REST server at the same time doesn't seem possible.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.