Skip to content
Snippets Groups Projects
Verified Commit a225ee51 authored by Karel van Klink's avatar Karel van Klink :smiley_cat:
Browse files

Initial commit

parents
Branches
Tags
No related merge requests found
Showing with 476 additions and 0 deletions
__pycache__
.coverage
.idea
.tox
coverage.xml
*.egg-info
docs/build
.vscode
config.json
library 'SWDPipeline'
SimplePythonBuild('goat-ansible-api');
# Ansible API
And API that allows for remotely executing Ansible playbooks
## Running locally
Build documentation using `build-docs.sh`, and check out the quickstart page.
"""
automatically invoked app factory
"""
import logging
from fastapi import FastAPI
from fastapi.middleware.cors import CORSMiddleware
from ansible_api import environment
from ansible_api import config
from ansible_api.routes import default
def create_app():
"""
overrides default settings with those found
in the file read from env var SETTINGS_FILENAME
:return: a new flask app instance
"""
app = FastAPI()
# app = FastAPI(dependencies=[Depends(get_query_token)])
app.add_middleware(
CORSMiddleware,
allow_origins=["*"],
allow_credentials=True,
allow_methods=["*"],
allow_headers=["*"],
)
app.include_router(
default.router,
prefix='/api',
# tags="default"],
# dependencies=[Depends(get_token_header)],
# responses={418: {"description": "I'm a teapot"}},
)
# app.include_router(
# interfaces.router,
# prefix='/api/interfaces',
# # tags=["trunk"],
# # dependencies=[Depends(get_token_header)],
# # responses={418: {"description": "I'm a teapot"}},
# )
# test that config params are available and can be loaded
config.load()
logging.info('FastAPI app initialized')
environment.setup_logging()
return app
"""
default app creation
"""
import ansible_api
app = ansible_api.create_app()
if __name__ == "__main__":
import uvicorn
uvicorn.run(
'ansible_api.app:app',
host='0.0.0.0',
port=44444,
log_level='debug')
import json
import jsonschema
import os
CONFIG_SCHEMA = {
'$schema': 'http://json-schema.org/draft-07/schema#',
'definitions': {},
'type': 'object',
'properties': {
'playbook-dir': {'type': 'string'},
},
'required': ['playbook-dir'],
'additionalProperties': False
}
def load_from_file(f):
"""
Loads, validates and returns configuration parameters.
Input is validated against this jsonschema:
.. asjson:: resource_management.config.CONFIG_SCHEMA
:param f: file-like object that produces the config file
:return: a dict containing the parsed configuration parameters
"""
config = json.loads(f.read())
jsonschema.validate(config, CONFIG_SCHEMA)
return config
def load():
assert 'SETTINGS_FILENAME' in os.environ
with open(os.environ['SETTINGS_FILENAME']) as f:
return load_from_file(f)
import json
import logging.config
import os
LOGGING_DEFAULT_CONFIG = {
'version': 1,
'disable_existing_loggers': False,
'formatters': {
'simple': {
'format': '%(asctime)s - %(name)s '
'(%(lineno)d) - %(levelname)s - %(message)s'
}
},
'handlers': {
'console': {
'class': 'logging.StreamHandler',
'level': 'DEBUG',
'formatter': 'simple',
'stream': 'ext://sys.stdout'
}
},
'loggers': {
'resource_management': {
'level': 'DEBUG',
'handlers': ['console'],
'propagate': False
}
},
'root': {
'level': 'INFO',
'handlers': ['console']
}
}
def setup_logging():
"""
set up logging using the configured filename
if LOGGING_CONFIG is defined in the environment, use this for
the filename, otherwise use LOGGING_DEFAULT_CONFIG
"""
logging_config = LOGGING_DEFAULT_CONFIG
if 'LOGGING_CONFIG' in os.environ:
filename = os.environ['LOGGING_CONFIG']
with open(filename) as f:
logging_config = json.loads(f.read())
logging.config.dictConfig(logging_config)
import pkg_resources
import pydantic
from fastapi import APIRouter
API_VERSION = '0.1'
VERSION_STRING = pydantic.constr(regex=r'\d+\.\d+')
router = APIRouter()
class Version(pydantic.BaseModel):
api: VERSION_STRING
module: VERSION_STRING
@router.get('/version')
def version() -> Version:
return {
'api': API_VERSION,
'module':
pkg_resources.get_distribution('goat-ansible-api').version
}
python docs/dump-openapi-spec.py
sphinx-build -b html docs/source docs/build
{
"playbook-dir": "/absolute/path/to/playbooks"
}
\ No newline at end of file
# Minimal makefile for Sphinx documentation
#
# You can set these variables from the command line, and also
# from the environment for the first two.
SPHINXOPTS ?=
SPHINXBUILD ?= sphinx-build
SOURCEDIR = source
BUILDDIR = build
# Put it first so that "make" without argument is like "make help".
help:
@$(SPHINXBUILD) -M help "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O)
.PHONY: help Makefile
# Catch-all target: route all unknown targets to Sphinx using the new
# "make mode" option. $(O) is meant as a shortcut for $(SPHINXOPTS).
%: Makefile
@$(SPHINXBUILD) -M $@ "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O)
import json
import os
from fastapi.testclient import TestClient
import ansible_api
config_filename = os.path.join(
os.path.dirname(__file__),
'..', 'config-example.json')
output_filename = os.path.join(
os.path.dirname(__file__),
'source', '_static', 'openapi.json')
os.environ['SETTINGS_FILENAME'] = config_filename
app = ansible_api.create_app()
client = TestClient(app)
rsp = client.get('/openapi.json')
openapi_doc = json.dumps(rsp.json(), indent=2)
with open(output_filename, 'w') as f:
f.write(openapi_doc)
print(f'wrote {output_filename}')
# Configuration file for the Sphinx documentation builder.
#
# For the full list of built-in configuration values, see the documentation:
# https://www.sphinx-doc.org/en/master/usage/configuration.html
# -- Path setup --------------------------------------------------------------
# If extensions (or modules to document with autodoc) are in another directory,
# add these directories to sys.path here. If the directory is relative to the
# documentation root, use os.path.abspath to make it absolute, like shown here.
from importlib import import_module
from docutils.parsers.rst import Directive
from docutils import nodes
from sphinx import addnodes
import json
import os
import sys
sys.path.insert(0, os.path.abspath(
os.path.join(
os.path.dirname(__file__),
'..', '..', 'resource_management')))
class RenderAsJSON(Directive):
# cf. https://stackoverflow.com/a/59883833
required_arguments = 1
def run(self):
module_path, member_name = self.arguments[0].rsplit('.', 1)
member_data = getattr(import_module(module_path), member_name)
code = json.dumps(member_data, indent=2)
literal = nodes.literal_block(code, code)
literal['language'] = 'json'
return [
addnodes.desc_name(text=member_name),
addnodes.desc_content('', literal)
]
def setup(app):
app.add_directive('asjson', RenderAsJSON)
# -- Project information -----------------------------------------------------
project = 'Ansible API'
copyright = '2023, GÉANT'
author = 'GÉANT Orchestration & Automation Team'
# -- General configuration ---------------------------------------------------
extensions = [
'sphinx_rtd_theme',
'sphinx.ext.autodoc',
'sphinx.ext.coverage',
'sphinx.ext.todo'
]
templates_path = ['templates']
exclude_patterns = []
# -- Options for HTML output -------------------------------------------------
html_theme = 'sphinx_rtd_theme'
html_static_path = ['static']
# Both the class' and the __init__ method's docstring
# are concatenated and inserted.
autoclass_content = 'both'
autodoc_typehints = 'none'
# Display todos by setting to True
todo_include_todos = True
GOAT Ansible API
==========================
Documentation for the GOAT Ansible API
.. toctree::
:maxdepth: 1
:caption: Contents:
quickstart
`Swagger API Docs <_static/openapi.html>`__
Quick Start
==================
*This is a quick setup guide for running standalone on your local machine*
Install the Module
--------------------
*One of these should be what you're looking for:*
* Install the latest module snapshot
.. code-block::
python3 -m venv my-venv-directory
. my-venv-directory/bin/activate
pip install --pre --extra-index-url https://artifactory.software.geant.org/artifactory/api/pypi/geant-swd-pypi/simple goat-ansible-api
* Install the source code
.. code-block::
python3 -m venv my-venv-directory
. my-venv-directory/bin/activate
git clone https://gitlab.geant.org/goat/gap/ansible-api.git
cd ansible-api
pip install -e .
# for a full dev environment
pip install -r requirements.txt
Running the App
-------------------
* Create a settings file, see `config.json.example` for an example.
* Run the app like this (`app.py` starts the server on port 44444):
.. code-block:: bash
SETTINGS_FILENAME=/absolute/path/to/config.json python -m ansible_api.app
Examples
* Get the version
.. code-block:: bash
curl http://localhost:44444/api/version
* List all available playbooks
.. code-block:: bash
curl http://localhost:44444/api/playbook
* View the docs by loading `http://localhost:44444/docs` in your browser
<html lang="en">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<meta
name="description"
content="SwaggerUI"
/>
<title>SwaggerUI</title>
<link rel="stylesheet" href="https://unpkg.com/swagger-ui-dist@4.5.0/swagger-ui.css" />
</head>
<body>
<div id="swagger-ui"></div>
<script src="https://unpkg.com/swagger-ui-dist@4.5.0/swagger-ui-bundle.js" crossorigin></script>
<script>
window.onload = () => {
window.ui = SwaggerUIBundle({
url: 'openapi.json',
dom_id: '#swagger-ui',
});
};
</script>
</body>
</html>
setup.py 0 → 100644
from setuptools import setup, find_packages
setup(
name='goat-ansible-api',
version='0.0.1',
author='GÉANT Orchestration & Automation Team',
author_email='TBD',
description='GOAT Ansible API',
url='https://gitlab.geant.org/goat/gap/ansible-api',
packages=find_packages(),
install_requires=[
'jsonschema',
'requests',
]
)
tox.ini 0 → 100644
[flake8]
exclude = obsolete,.tox,venv
[testenv]
passenv = XDG_CACHE_HOME,USE_COMPOSE
deps =
coverage
flake8
-r requirements.txt
commands =
coverage erase
coverage run --source ansible_api -m pytest
coverage xml
coverage html
coverage report --fail-under 80
flake8
python docs/dump-openapi-spec.py
sphinx-build -b html docs/source docs/build
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment