1. Introduction to Foreman API
With the Representational State Transfer (REST) API of Foreman, you can control your Foreman environment outside of the standard web interface. That way, you can integrate your Foreman with custom scripts or external applications over HTTP.
1.1. Overview of the Foreman API
Integrate with enterprise systems, automate repetitive tasks, and manage your infrastructure programmatically using the resource-based REST model of the Foreman API. You can use any HTTP client to interact with Foreman.
The benefits of using the REST API are:
-
Broad client support – any programming language, framework, or system with support for HTTP protocol can use the API.
-
Self-descriptive – client applications require minimal knowledge of the Foreman infrastructure because a user discovers many details at runtime.
-
Resource-based model – the resource-based REST model provides a natural way to manage a virtualization platform.
You can use the REST API to perform the following tasks:
-
Integrate with enterprise IT systems.
-
Integrate with third-party applications.
-
Perform automated maintenance or error checking tasks.
-
Automate repetitive tasks with scripts.
1.2. Foreman API compared to Hammer CLI
Hammer serves as a human-friendly interface to Foreman API. Use Hammer for interactive tasks and testing API calls, but use the API directly for better performance when executing many commands in scripts.
For example, to test responses to API calls before applying them in a script, use the --debug option to inspect API calls that Hammer issues: hammer --debug organization list.
In the background, each Hammer command first establishes a binding to the API, then sends a request. This can have performance implications when executing a large number of Hammer commands in sequence. In contrast, a script communicating directly with the API establishes the binding only once.
1.3. Getting help with Foreman API
You can use the API reference on your Foreman server to view information about API endpoints supported for Foreman API integrations.
View the full API reference on your Foreman server at the following URL:
https://foreman.example.com/apidoc/
Replace foreman.example.com with the FQDN of your Foreman server.
2. API syntax
Understanding the basic syntax of API requests and JSON responses enables you to construct valid API calls and interpret the returned data.
|
Important
|
Even though versions 1 and 2 of the Foreman API are available, Foreman community only supports version 2. |
2.1. API request composition
You can use the built-in API reference documentation to compose valid API requests.
This example shows how to compose a curl API request.
The built-in API reference shows the API route, or path, preceded by an HTTP method:
HTTP_METHOD API_ROUTE
To work with the API, construct a command by using the curl command syntax and the API route from the reference document:
$ curl \ --data @My_Input_File.json \ --header "Accept: application/json" \ --header "Content-Type: application/json" \ --output My_Output_File --request HTTP_METHOD \ --user "My_User_Name:My_Password" \ API_ROUTE \ | python3 -m json.tool
--data-
For
POSTandPUTrequests, use the--dataoption to pass JSON-formatted data. --header-
When passing the JSON data with the
--dataoption, you must specify the following headers with the--headeroption. --output-
When downloading content from Foreman server, specify the output file with the
--outputoption. --request-
To use
curlfor the API call, specify an HTTP method with the--requestoption. For example,--request POST. --user-
Provide Foreman user credentials with the
--useroption. API_ROUTE-
Use the API route in the following format:
\https://foreman.example.com/api/architectures. In Foreman, version 2 of the API is the default. Therefore, it is not necessary to usev2in the URL for API calls. json.tool-
Redirect the output to the Python
json.toolmodule to make the output easier to read.
2.1.1. Using the GET HTTP method
Use the GET HTTP method to get data from the API about an existing entry or resource. This example requests the number of registered hosts.
-
Submit a GET request by using
curl.Example API request:
$ curl \ --request GET \ --user My_User_Name:My_Password \ https://foreman.example.com/api/hosts \ | python3 -m json.tool
Example API response:
{ "total": 2, "subtotal": 2, "page": 1, "per_page": 20, "search": null, "sort": { "by": null, "order": null }, "results": output truncated }The response from the API indicates that there are two results in total, this is the first page of the results, and the maximum results per page is set to 20. For more information, see JSON response format.
2.1.2. Using the POST HTTP method
Use the POST HTTP method to create new resources such as activation keys or hosts.
You must submit the data in JSON format. For more information, see Passing JSON data to the API request.
-
Create a test file, for example,
activation-key.json, with the following content:{"organization_id":1, "name":"TestKey", "description":"Just for testing"} -
Create an activation key by applying the data in the
activation-key.jsonfile.Example API request:
$ curl \ --header "Accept: application/json" \ --header "Content-Type: application/json" \ --request POST \ --user My_User_Name:My_Password \ --data @activation-key.json \ https://foreman.example.com/katello/api/activation_keys \ | python3 -m json.tool
Example API response:
{ "id": 2, "name": "TestKey", "description": "Just for testing", "unlimited_hosts": true, "auto_attach": true, "content_view_id": null, "environment_id": null, "usage_count": 0, "user_id": 3, "max_hosts": null, "release_version": null, "service_level": null, "content_overrides": [ ], "organization": { "name": "Default Organization", "label": "Default_Organization", "id": 1 }, "created_at": "2024-02-16 12:37:47 UTC", "updated_at": "2024-02-16 12:37:48 UTC", "content_view": null, "environment": null, "products": null, "host_collections": [ ], "permissions": { "view_activation_keys": true, "edit_activation_keys": true, "destroy_activation_keys": true } }
-
In the Foreman web UI, navigate to Content > Lifecycle > Activation Keys to view your activation keys.
2.1.3. Using the PUT HTTP method
Use the PUT HTTP method to update existing resources such as changing activation key properties or host configurations.
You must submit the data in JSON format. For more information, see Passing JSON data to the API request.
-
Edit the
activation-key.jsonfile created previously as follows:{"organization_id":1, "name":"TestKey", "description":"Just for testing","max_hosts":"10" } -
Apply the changes in the JSON file.
Example API request:
$ curl \ --request PUT \ --header "Accept: application/json" \ --header "Content-Type: application/json" \ --user My_User_Name:My_Password \ --data @activation-key.json \ https://foreman.example.com/katello/api/activation_keys/2 \ | python3 -m json.tool
Example API response:
{ "id": 2, "name": "TestKey", "description": "Just for testing", "unlimited_hosts": false, "auto_attach": true, "content_view_id": null, "environment_id": null, "usage_count": 0, "user_id": 3, "max_hosts": 10, "release_version": null, "service_level": null, "content_overrides": [ ], "organization": { "name": "Default Organization", "label": "Default_Organization", "id": 1 }, "created_at": "2024-02-16 12:37:47 UTC", "updated_at": "2024-02-16 12:46:17 UTC", "content_view": null, "environment": null, "products": null, "host_collections": [ ], "permissions": { "view_activation_keys": true, "edit_activation_keys": true, "destroy_activation_keys": true } }
-
In the Foreman web UI, navigate to Content > Lifecycle > Activation Keys to view the updated activation keys.
2.1.4. Using the DELETE HTTP method
Use the DELETE HTTP method with an API route to permanently remove resources such as activation keys or hosts from Foreman.
This example deletes the TestKey activation key, whose ID is 2.
-
Submit a DELETE request by using
curl.Example API request:
$ curl \ --header "Accept: application/json" \ --header "Content-Type: application/json" \ --request DELETE \ --user My_User_Name:My_Password \ https://foreman.example.com/katello/api/activation_keys/2 \ | python3 -m json.tool
Example API response:
output omitted "started_at": "2024-02-16 12:58:17 UTC", "ended_at": "2024-02-16 12:58:18 UTC", "state": "stopped", "result": "success", "progress": 1.0, "input": { "activation_key": { "id": 2, "name": "TestKey" output truncated
2.2. JSON response format
Foreman API returns results in JSON format, which you can parse programmatically to extract data from single-object responses or collections responses.
2.2.1. JSON response format for single objects
You can use single-object JSON responses to work with a single object.
API requests to a single object require the unique identifier :id of the object.
This is an example of the format for a single-object request for the Foreman domain which ID is 23:
- API request
-
$ curl \ --request GET \ --user My_User_Name:My_Password \ https://foreman.example.com/api/domains/23 \ | python3 -m json.tool
- API response
-
{ "id": 23, "name": "qa.lab.example.com", "fullname": "QA", "dns_id": 10, "created_at": "2024-08-13T09:02:31Z", "updated_at": "2024-08-13T09:02:31Z" }
2.2.2. JSON response format for collections
Collections return lists of objects, such as hosts and domains. Each response includes pagination metadata and a results section containing the data you can iterate through.
This is an example of the format for a collection request for a list of Foreman domains:
- Example API request
-
$ curl \ --request GET \ --user My_User_Name:My_Password \ https://foreman.example.com/api/domains \ | python3 -m json.tool
- Example API response
-
{ "total": 3, "subtotal": 3, "page": 1, "per_page": 20, "search": null, "sort": { "by": null, "order": null }, "results": [ { "id": 23, "name": "qa.lab.example.com", "fullname": "QA", "dns_id": 10, "created_at": "2024-08-13T09:02:31Z", "updated_at": "2024-08-13T09:02:31Z" }, { "id": 25, "name": "dev.lab.example.com", "fullname": "DEVEL", "dns_id": 8, "created_at": "2024-08-13T08:32:48Z", "updated_at": "2024-08-14T07:04:03Z" }, { "id": 32, "name": "hr.lab.example.com", "fullname": "HR", "dns_id": 8, "created_at": "2024-08-16T08:32:48Z", "updated_at": "2024-08-16T07:04:03Z" } ] }
2.2.3. JSON response metadata
Review the metadata fields in Foreman API responses to understand pagination, search results, and collection information when working with the API.
Foreman API responses contain the following metadata fields:
total-
The total number of objects without any search parameters.
subtotal-
The number of objects returned with the given search parameters. If there is no search, then subtotal is equal to total.
page-
The page number.
per_page-
The maximum number of objects returned per page.
limit-
The specified number of objects to return in a collection response.
offset-
The number of objects skipped before returning a collection.
search-
The search string based on
scoped_scopedsyntax. sort-
-
by– Specifies by what field the API sorts the collection. -
order– The sort order, either ASC for ascending or DESC for descending.
-
results-
The collection of objects.
2.3. Relating API error messages to the API reference
You can map API error messages to the corresponding sections in the API reference documentation by translating the RAILS error format to the reference format. This helps you locate the relevant documentation when troubleshooting API errors.
The API uses a RAILs format to indicate an error:
Nested_Resource.Attribute_Name
This translates to the following format used in the API reference:
Resource[Nested_Resource_attributes][Attribute_Name_id]
3. API call authentication
You must authenticate API calls to Foreman server by using SSL certificates and valid user credentials, with options for HTTP basic authentication, SSL client certificates, or personal access tokens.
3.1. SSL authentication overview
Foreman requires HTTPS for all API communications to provide encryption and identity verification. You can use either self-signed certificates or custom SSL certificates you configure.
Foreman 3.18 does not support non-SSL communications.
By default, Foreman server uses a self-signed certificate. This certificate acts as both the server certificate to verify the encryption key and the certificate authority (CA) to trust the identity of Foreman server.
3.2. Configuring SSL authentication
Configure SSL authentication to enable secure API requests to Foreman by installing and trusting the CA certificate.
-
Obtain a certificate from your Foreman server by using one of the following options:
-
If you plan to call the API from a remote server, download the CA certificate:
$ curl \ --output /etc/pki/ca-trust/source/anchors/foreman.example.com-katello-server-ca.crt \ http://foreman.example.com/pub/katello-server-ca.crt
-
If you plan to call the API directly on your Foreman server, copy the certificate to the
/etc/pki/ca-trust/source/anchorsdirectory:# cp /var/www/html/pub/katello-server-ca.crt /etc/pki/ca-trust/source/anchors/foreman.example.com-katello-server-ca.crt
-
-
Add the certificate to the list of trusted CAs:
$ update-ca-trust extract
-
Verify that your client trusts the Foreman SSL certificate by entering the API request without the
--cacertoption:$ curl \ --request GET \ --user My_User_Name:My_Password \ https://foreman.example.com/api/v2/hosts
3.3. HTTP authentication overview
You must provide valid Foreman credentials to access the Foreman API.
The API uses HTTP Basic authentication to send encoded user credentials in the Authorization header.
If a request does not include an appropriate Authorization header, the API returns a 401 Authorization Required error.
|
Important
|
Basic authentication involves potentially sensitive information, for example, it sends passwords as plain text. The REST API requires HTTPS for transport-level encryption of plain text requests. |
Some base64 libraries break encoded credentials into multiple lines and terminate each line with a newline character.
This invalidates the header and causes a faulty request.
The Authorization header requires the encoded credentials to be on a single line within the header.
3.4. Token authentication overview
You can use Personal Access Tokens to authenticate API requests instead of passwords. Additionally, you can manage your security by setting expiration dates or revoking tokens at any time.
3.4.1. Creating a Personal Access Token
Create a Personal Access Token to authenticate API requests without sharing your password.
-
Your user account has a role that grants the
create_personal_access_tokenspermission.
-
In the Foreman web UI, navigate to Administer > Users.
-
Select a user for which you want to create a Personal Access Token.
-
On the Personal Access Tokens tab, click Add Personal Access Token.
-
Enter a Name for you Personal Access Token.
-
Optional: Select the Expires date to set an expiration date. If you do not set an expiration date, your Personal Access Token will never expire unless revoked.
-
Click Submit. You now have the Personal Access Token available to you on the Personal Access Tokens tab.
ImportantEnsure to store your Personal Access Token as you will not be able to access it again after you leave the page or create a new Personal Access Token. You can click Copy to clipboard to copy your Personal Access Token.
-
Make an API request to your Foreman server and authenticate with your Personal Access Token:
$ curl \ --user My_Username:My_Personal_Access_Token \ https://foreman.example.com/api/status
-
You should receive a response with status
200, for example:{"foreman_version":"3.18.0","result":"ok","status":200,"version":"3.5.1.10","api_version":2}If you go back to Personal Access Tokens tab, you can see the updated Last Used time next to your Personal Access Token.
3.4.2. Revoking a Personal Access Token
Revoke a Personal Access Token before its expiration date when a token is compromised or no longer needed for API access.
-
In the Foreman web UI, navigate to Administer > Users.
-
Select a user for which you want to revoke the Personal Access Token.
-
On the Personal Access Tokens tab, locate the Personal Access Token you want to revoke.
-
Click Revoke in the Actions column next to the Personal Access Token you want to revoke.
-
Make an API request to your Foreman server and try to authenticate with the revoked Personal Access Token:
$ curl \ --user My_Username:My_Personal_Access_Token \ https://foreman.example.com/api/status
-
You receive the following error message:
{ "error": {"message":"Unable to authenticate user My_Username"} }
4. API requests in various languages
You can interact with Foreman API using curl for quick testing, Ruby for automation scripts with apipie bindings, or Python for integration with existing Python-based workflows.
4.1. Calling the API in curl
You can use curl to test API endpoints, debug requests, and perform quick operations without writing scripts.
You can use curl to manipulate resources on your Foreman server.
API calls to Foreman require data in json format.
Foreman requires the use of HTTPS, and by default, a certificate for host identification.
For user authentication, you can use the --user option to provide Foreman user credentials in the form --user My_User_Name:My_Password.
If you do not include the password, the command prompts you to enter it.
To reduce security risks, do not include the password as part of the command, because it then becomes part of your shell history.
For simplicity, the examples in this section include the password.
Be aware that if you use the --silent option, curl does not display a progress meter or any error messages.
Examples in this chapter use the Python json.tool module to format the output.
4.1.1. Passing JSON data to the API request
You can pass JSON-formatted data to Foreman API requests either as inline strings or external files.
When specifying JSON data with the --data option, you must set the following HTTP headers with the --header option:
--header "Accept: application/json" \
--header "Content-Type: application/json"
Use one of the following options to include data with the --data option.
- JSON-formatted string
-
Enclose the quoted JSON-formatted data in curly braces
{}. When passing a value for a JSON type parameter, you must escape quotation marks"with backslashes\. For example, within curly braces, you must format"Example JSON Variable"as\"Example JSON Variable\":--data {"id":44, "smart_class_parameter":{"override":"true", "parameter_type":"json", "default_value":"{\"GRUB_CMDLINE_LINUX\": {\"audit\":\"1\",\"crashkernel\":\"true\"}}"}} - JSON-formatted file
-
The unquoted JSON-formatted data enclosed in a file and specified by the
@sign and the filename. For example:--data @file.json
Using external files for JSON formatted data has the following advantages:
-
You can use your favorite text editor.
-
You can use syntax checker to find and avoid mistakes.
-
You can use tools to check the validity of JSON data or to reformat it.
Use the json_verify tool to check the validity of the JSON file:
$ json_verify < file.json
4.1.2. Retrieving a list of resources
You can retrieve lists of Foreman resources such as users, hosts, or organizations using GET requests, with responses containing both metadata for pagination and the actual resource data.
This example is a basic request that returns a list of Foreman resources, Foreman users in this case. Such requests return a list of data wrapped in metadata, while other request types only return the actual object.
- Example API request
-
$ curl \ --request GET \ --user My_User_Name:My_Password \ https://foreman.example.com/api/users \ | python3 -m json.tool
- Example API response
-
{ "page": 1, "per_page": 20, "results": [ { "admin": false, "auth_source_id": 1, "auth_source_name": "Internal", "created_at": "2024-09-21 08:59:22 UTC", "default_location": null, "default_organization": null, "description": "", "effective_admin": false, "firstname": "", "id": 5, "last_login_on": "2024-09-21 09:03:25 UTC", "lastname": "", "locale": null, "locations": [], "login": "test", "mail": "test@example.com", "organizations": [ { "id": 1, "name": "Default Organization" } ], "ssh_keys": [], "timezone": null, "updated_at": "2024-09-21 09:04:45 UTC" }, { "admin": true, "auth_source_id": 1, "auth_source_name": "Internal", "created_at": "2024-09-20 07:09:41 UTC", "default_location": null, "default_organization": { "description": null, "id": 1, "name": "Default Organization", "title": "Default Organization" }, "description": "", "effective_admin": true, "firstname": "Admin", "id": 4, "last_login_on": "2024-12-07 07:31:09 UTC", "lastname": "User", "locale": null, "locations": [ { "id": 2, "name": "Default Location" } ], "login": "admin", "mail": "admin@example.com", "organizations": [ { "id": 1, "name": "Default Organization" } ], "ssh_keys": [], "timezone": null, "updated_at": "2024-11-14 08:19:46 UTC" } ], "search": null, "sort": { "by": null, "order": null }, "subtotal": 2, "total": 2 }
4.1.3. Creating a user
You can create Foreman user accounts using POST requests.
This example adds a user account named test_user.
- Example API request
-
$ curl \ --header "Accept: application/json" \ --header "Content-Type: application/json" \ --request POST \ --user My_User_Name:My_Password \ --data "{\"firstname\":\"Test Name\",\"mail\":\"test@example.com\",\"login\":\"test_user\",\"password\":\"password123\",\"auth_source_id\":1}" \ https://foreman.example.com/api/users \ | python3 -m json.tool
4.1.4. Modifying a user
You can modify Foreman user accounts using PUT requests by specifying the user ID and the fields you want to update, such as login name or email address.
This example changes the given name and login for the test_user account.
- Example API request
-
$ curl \ --header "Accept: application/json" \ --header "Content-Type: application/json" \ --request PUT \ --user My_User_Name:My_Password \ --data "{\"firstname\":\"New Test Name\",\"mail\":\"test@example.com\",\"login\":\"new_test_user\",\"password\":\"password123\",\"auth_source_id\":1}" \ https://foreman.example.com/api/users/8 \ | python3 -m json.tool
4.2. Calling the API in Ruby
You can use Ruby to automate complex Foreman workflows. Use REST client libraries or apipie bindings to get type-safe API calls with built-in validation.
|
Important
|
These are example scripts and commands. Ensure you review these scripts carefully before use, and replace any variables, user names, passwords, and other information to suit your own deployment. |
4.2.1. Creating objects by using Ruby
You can use Ruby-based REST clients to create Foreman organizations and lifecycle environments programmatically, with built-in error handling for existing resources.
This script connects to the Foreman API, creates an organization, and then creates three lifecycle environments in the organization. If the organization already exists, the script uses that organization. If any of the lifecycle environments already exist in the organization, the script returns an error and exits.
#!/usr/bin/ruby
require 'rest-client'
require 'json'
url = 'https://foreman.example.com/api/v2/'
katello_url = "#{url}/katello/api/v2/"
$username = '_My_User_Name_'
$password = '_My_Password_'
org_name = "_My_Organization_"
environments = [ "Development", "Testing", "Production" ]
# Performs a GET by using the passed URL location
def get_json(location)
response = RestClient::Request.new(
:method => :get,
:url => location,
:user => $username,
:password => $password,
:headers => { :accept => :json,
:content_type => :json }
).execute
JSON.parse(response.to_str)
end
# Performs a POST and passes the data to the URL location
def post_json(location, json_data)
response = RestClient::Request.new(
:method => :post,
:url => location,
:user => $username,
:password => $password,
:headers => { :accept => :json,
:content_type => :json},
:payload => json_data
).execute
JSON.parse(response.to_str)
end
# Creates a hash with ids mapping to names for an array of records
def id_name_map(records)
records.inject({}) do |map, record|
map.update(record['id'] => record['name'])
end
end
# Get list of existing organizations
orgs = get_json("#{katello_url}/organizations")
org_list = id_name_map(orgs['results'])
if !org_list.has_value?(org_name)
# If our organization is not found, create it
puts "Creating organization: \t#{org_name}"
org_id = post_json("#{katello_url}/organizations", JSON.generate({"name"=> org_name}))["id"]
else
# Our organization exists, so let's grab it
org_id = org_list.key(org_name)
puts "Organization \"#{org_name}\" exists"
end
# Get list of organization's lifecycle environments
envs = get_json("#{katello_url}/organizations/#{org_id}/environments")
env_list = id_name_map(envs['results'])
prior_env_id = env_list.key("Library")
# Exit the script if at least one life cycle environment already exists
environments.each do |e|
if env_list.has_value?(e)
puts "ERROR: One of the Environments is not unique to organization"
exit
end
end
# Create life cycle environments
environments.each do |environment|
puts "Creating environment: \t#{environment}"
prior_env_id = post_json("#{katello_url}/organizations/#{org_id}/environments", JSON.generate({"name" => environment, "organization_id" => org_id, "prior_id" => prior_env_id}))["id"]
end
4.2.2. Using apipie bindings with Ruby
You can use apipie bindings in Ruby to automatically fetch and cache Foreman API definitions. This way, you can write type-safe API calls with built-in validation and reduce manual endpoint configuration.
#!/usr/bin/ruby
require 'apipie-bindings'
org_name = "_My_Organization_"
environments = [ "Development", "Testing", "Production" ]
# Create an instance of apipie bindings
@api = ApipieBindings::API.new({
:uri => 'https://foreman.example.com/',
:username => 'admin',
:password => 'changeme',
:api_version => 2
})
# Performs an API call with default options
def call_api(resource_name, action_name, params = {})
http_headers = {}
apipie_options = { :skip_validation => true }
@api.resource(resource_name).call(action_name, params, http_headers, apipie_options)
end
# Creates a hash with IDs mapping to names for an array of records
def id_name_map(records)
records.inject({}) do |map, record|
map.update(record['id'] => record['name'])
end
end
# Get list of existing organizations
orgs = call_api(:organizations, :index)
org_list = id_name_map(orgs['results'])
if !org_list.has_value?(org_name)
# If our organization is not found, create it
puts "Creating organization: \t#{org_name}"
org_id = call_api(:organizations, :create, {'organization' => { :name => org_name }})['id']
else
# Our organization exists, so let's grab it
org_id = org_list.key(org_name)
puts "Organization \"#{org_name}\" exists"
end
# Get list of organization's life cycle environments
envs = call_api(:lifecycle_environments, :index, {'organization_id' => org_id})
env_list = id_name_map(envs['results'])
prior_env_id = env_list.key("Library")
# Exit the script if at least one life cycle environment already exists
environments.each do |e|
if env_list.has_value?(e)
puts "ERROR: One of the Environments is not unique to organization"
exit
end
end
# Create life cycle environments
environments.each do |environment|
puts "Creating environment: \t#{environment}"
prior_env_id = call_api(:lifecycle_environments, :create, {"name" => environment, "organization_id" => org_id, "prior_id" => prior_env_id })['id']
end
4.3. Calling the API in Python
You can use Python with the requests module to integrate Foreman API calls into existing Python automation workflows, data processing pipelines, or DevOps tooling.
|
Important
|
These are example scripts and commands. Ensure you review these scripts carefully before use, and replace any variables, user names, passwords, and other information to suit your own deployment. |
Example scripts in this section do not use SSL verification for interacting with the REST API.
4.3.1. Creating objects by using Python
You can use the Python requests module to create Foreman organizations and lifecycle environments programmatically.
This script connects to the Foreman API, creates an organization, and then creates three lifecycle environments in the organization. If the organization already exists, the script uses that organization. If any of the lifecycle environments already exist in the organization, the script returns an error and exits.
#!/usr/bin/python3
import json
import sys
try:
import requests
except ImportError:
print("Please install the python-requests module.")
sys.exit(-1)
# URL to your Foreman server
URL = "https://foreman.example.com"
FOREMAN_API = f"{URL}/api/"
KATELLO_API = f"{URL}/katello/api/"
POST_HEADERS = {'content-type': 'application/json'}
# Default credentials to login to Foreman
USERNAME = "admin"
PASSWORD = "changeme"
# Ignore SSL for now
SSL_VERIFY = False
# Name of the organization to be either created or used
ORG_NAME = "MyOrg"
# Name for life cycle environments to be either created or used
ENVIRONMENTS = ["Development", "Testing", "Production"]
def get_json(location):
"""
Performs a GET by using the passed URL location
"""
r = requests.get(location, auth=(USERNAME, PASSWORD), verify=SSL_VERIFY)
return r.json()
def post_json(location, json_data):
"""
Performs a POST and passes the data to the URL location
"""
result = requests.post(
location,
data=json_data,
auth=(USERNAME, PASSWORD),
verify=SSL_VERIFY,
headers=POST_HEADERS
)
return result.json()
def main():
"""
Main routine that creates or re-uses an organization and
life cycle environments.
If life cycle environments already
exist, exit out.
"""
# Check if our organization already exists
org = get_json(f"{FOREMAN_API}/organizations/{ORG_NAME}")
# If our organization is not found, create it
if org.get('error', None):
org_id = post_json(
f"{FOREMAN_API}/organizations/",
json.dumps({"name": ORG_NAME})
)["id"]
print("Creating organization:\t" + ORG_NAME)
else:
# Our organization exists, so let's grab it
org_id = org['id']
print(f"Organization '{ORG_NAME}' exists.")
# Now, let's fetch all available life cycle environments for this org...
envs = get_json(
f"{KATELLO_API}/organizations/{org_id}/environments/"
)
# ...and add them to a dictionary, with respective 'Prior' environment
prior_env_id = 0
env_list = {}
for env in envs['results']:
env_list[env['id']] = env['name']
prior_env_id = env['id'] if env['name'] == "Library" else prior_env_id
# Exit the script if at least one life cycle environment already exists
if all(environment in env_list.values() for environment in ENVIRONMENTS):
print("ERROR: One of the Environments is not unique to organization")
sys.exit(-1)
# Create life cycle environments
for environment in ENVIRONMENTS:
new_env_id = post_json(
f"{KATELLO_API}/organizations/{org_id}/environments/",
json.dumps({
"name": environment,
"organization_id": org_id,
"prior": prior_env_id
})
)["id"]
print("Creating environment:\t" + environment)
prior_env_id = new_env_id
if __name__ == "__main__":
main()
4.3.2. Retrieving resource information by using Python
You can use Python to retrieve and display Foreman resource information such as host details, facts, and subscriptions. With this script, you can then format the Foreman API response specifically for your reporting or monitoring workflows.
#!/usr/bin/env python3
import json
import sys
try:
import requests
except ImportError:
print("Please install the python-requests module.")
sys.exit(-1)
HOSTNAME = "foreman.example.com"
# URL for the API to your Foreman server
FOREMAN_API = f"https://{HOSTNAME}/api/"
KATELLO_API = f"https://{HOSTNAME}/katello/api/v2/"
POST_HEADERS = {'content-type': 'application/json'}
# Default credentials to login to Foreman
USERNAME = "admin"
PASSWORD = "password"
# Ignore SSL for now
SSL_VERIFY = False
#SSL_VERIFY = "./path/to/CA-certificate.crt" # Put the path to your CA certificate here to allow SSL_VERIFY
def get_json(url):
# Performs a GET by using the passed URL location
r = requests.get(url, auth=(USERNAME, PASSWORD), verify=SSL_VERIFY)
return r.json()
def get_results(url):
jsn = get_json(url)
if jsn.get('error'):
print("Error: " + jsn['error']['message'])
else:
if jsn.get('results'):
return jsn['results']
elif 'results' not in jsn:
return jsn
else:
print("No results found")
return None
def display_all_results(url):
results = get_results(url)
if results:
print(json.dumps(results, indent=4, sort_keys=True))
def display_info_for_hosts(url):
hosts = get_results(url)
if hosts:
print(f"{'ID':10}{'Name':40}{'IP':30}{'Operating System':30}")
for host in hosts:
print(f"{str(host['id']):10}{host['name']:40}{str(host['ip']):30}{str(host['operatingsystem_name']):30}")
def display_info_for_subs(url):
subs = get_results(url)
if subs:
print(f"{'ID':10}{'Name':90}{'Start Date':30}")
for sub in subs:
print(f"{str(sub['id']):10}{sub['name']:90}{str(sub['start_date']):30}")
def main():
host = HOSTNAME
print(f"Displaying all info for host {host} ...")
display_all_results(FOREMAN_API + 'hosts/' + host)
print(f"Displaying all facts for host {host} ...")
display_all_results(FOREMAN_API + f'hosts/{host}/facts')
host_pattern = 'example'
print(f"Displaying basic info for hosts matching pattern '{host_pattern}'...")
display_info_for_hosts(FOREMAN_API + 'hosts?per_page=1&search=name~' + host_pattern)
print(f"Displaying basic info for subscriptions")
display_info_for_subs(KATELLO_API + 'subscriptions')
environment = 'production'
print(f"Displaying basic info for hosts in environment {environment}...")
display_info_for_hosts(FOREMAN_API + 'hosts?search=environment=' + environment)
if __name__ == "__main__":
main()
5. API cheat sheet
Use the Foreman API to automate host management, lifecycle environments, content uploads, and searches.
You can use the API on Foreman server via HTTPS on port 443.
For example, in Ruby, you can specify the Foreman server URL as follows:
url = 'https://foreman.example.com/api/v2/'
katello_url = 'https://foreman.example.com/katello/api/v2/'
You can use these values to fully automate your scripts, removing any need to verify which ports to use.
The following examples use curl for sending API requests.
5.1. Working with hosts
You can use API requests to manage hosts. Namely, you can list, query, search, and delete hosts programmatically.
5.1.1. Listing hosts
You can retrieve a list of all registered hosts from Foreman.
This example returns a list of registered hosts.
- Example API request
-
$ curl \ --request GET \ --user My_User_Name:My_Password \ https://foreman.example.com/api/v2/hosts \ | python3 -m json.tool
- API response
-
{ ... "total" => 2, "subtotal" => 2, "page" => 1, "per_page" => 1000, "search" => nil, "sort" => { "by" => nil, "order" => nil }, "results" => [ ... }
5.1.2. Requesting information for a host
You can retrieve detailed information about a specific host, including architecture, build status, and capabilities.
This request returns information for host.example.com.
- Example API request
-
$ curl \ --request GET \ --user My_User_Name:My_Password \ https://foreman.example.com/api/v2/hosts/host.example.com \ | python3 -m json.tool
- Example API response
-
{ "all_puppetclasses": [], "architecture_id": 1, "architecture_name": "x86_64", "build": false, "capabilities": [ "build" ], "certname": "host.example.com", "comment": null, "compute_profile_id": null, ... }
5.1.3. Listing host facts
You can retrieve system facts such as hardware details, BIOS version, and installed software for a specific host.
This request returns all facts for the host foreman.example.com.
- Example API request
-
$ curl \ --request GET \ --user My_User_Name:My_Password \ https://foreman.example.com/api/v2/hosts/foreman.example.com/facts \ | python3 -m json.tool
- Example API response
-
{ ... "results": { "foreman.example.com": { "augeasversion": "1.0.0", "bios_release_date": "01/01/2007", "bios_version": "0.5.1", "blockdevice_sr0_size": "1073741312", "facterversion": "1.7.6", ... }
5.1.4. Searching for hosts with matching patterns
You can filter hosts by hostname patterns or other matching criteria.
This query returns all hosts that match the pattern "example".
- Example API request
-
$ curl \ --request GET \ --user My_User_Name:My_Password \ https://foreman.example.com/api/v2/hosts?search=example \ | python3 -m json.tool
- Example API response
-
{ ... "results": [ { "name": "foreman.example.com", ... } ], "search": "example", ... }
5.1.5. Searching for hosts in an environment
You can filter hosts by environment to retrieve all hosts assigned to a specific environment.
This query returns all hosts in the production environment.
- Example API request
-
$ curl \ --request GET \ --user My_User_Name:My_Password \ https://foreman.example.com/api/v2/hosts?search=environment=production \ | python3 -m json.tool
- Example API response
-
{ ... "results": [ { "environment_name": "production", "name": "foreman.example.com", ... } ], "search": "environment=production", ... }
5.1.6. Searching for hosts with a specific fact value
You can filter hosts by specific fact values such as host group, operating system, or custom facts.
This query returns all hosts with the host group My Host Group.
- Example API request
-
$ curl \ --request GET \ --user My_User_Name:My_Password \ https://foreman.example.com/api/v2/hosts?search=hostgroup=%22My+Host+Group%22 \ | python3 -m json.tool
- Example API response
-
{ ... "results": [ { ... "hostgroup_id": 1, "hostgroup_name": "My Host Group", "name": "my-host.example.com", ... } ], "search": "hostgroup=\"My Host Group\"", ... }
5.1.7. Deleting a host
You can permanently remove a host from Foreman when decommissioning a server, reclaiming software subscriptions, or clearing out stale inventory data to maintain accurate compliance reporting.
This example request deletes a host with a name host1.example.com.
- Example API request
-
$ curl \ --request DELETE \ --user My_User_Name:My_Password \ https://foreman.example.com/api/v2/hosts/host1.example.com \ | python3 -m json.tool
5.1.8. Downloading a full-host boot disk image
You can download a bootable ISO image for a host to handle provisioning or recovery scenarios.
This request downloads a full boot disk image for a host by its ID.
- Example API request
-
$ curl \ --request GET \ --user My_User_Name:My_Password \ --output My_Image.iso \ https://foreman.example.com/api/bootdisk/hosts/host_ID?full=true
5.2. Working with lifecycle environments
Lifecycle environments represent stages in your application life cycle such as development, testing, and production, enabling controlled content promotion through linked lifecycle environment paths.
To create linked lifecycle environments with the API, use the prior_id parameter.
You can find the built-in API reference for lifecycle environments at https://foreman.example.com/apidoc/v2/lifecycle_environments.html.
The API routes include /katello/api/environments and /katello/api/organizations/:organization_id/environments.
5.2.1. Listing lifecycle environments
You can retrieve all lifecycle environments for an organization to view your content promotion paths.
- Example API request
-
$ curl \ --header "Accept: application/json" \ --header "Content-Type: application/json" \ --request GET \ --user My_User_Name:My_Password \ https://foreman.example.com/katello/api/organizations/1/environments \ | python3 -m json.tool`
- Example API response
-
output omitted "description": null, "id": 1, "label": "Library", "library": true, "name": "Library", "organization": { "id": 1, "label": "Default_Organization", "name": "Default Organization" }, "permissions": { "destroy_lifecycle_environments": false, "edit_lifecycle_environments": true, "promote_or_remove_content_views_to_environments": true, "view_lifecycle_environments": true }, "prior": null, "successor": null, output truncated
5.2.2. Creating linked lifecycle environments
Create a linked path of lifecycle environments to establish a controlled content promotion workflow from development through testing to production stages.
This procedure uses the default Library environment with ID 1 as the starting point for creating lifecycle environments.
-
Choose an existing lifecycle environment that you want to use as a starting point. List the environment by using its ID. In this case, the environment with ID
1:Example request:
$ curl \ --request GET \ --user My_User_Name:My_Password \ https://foreman.example.com/katello/api/environments/1 \ | python3 -m json.tool
Example response:
output omitted "id": 1, "label": "Library", output omitted "prior": null, "successor": null, output truncated -
Create a JSON file, for example,
life-cycle.json, with the following content:{"organization_id":1,"label":"api-dev","name":"API Development","prior":1} -
Create a lifecycle environment by using the
prioroption set to1.Example request:
$ curl \ --header "Accept: application/json" \ --header "Content-Type: application/json" \ --request POST \ --user My_User_Name:My_Password \ --data @life-cycle.json \ https://foreman.example.com/katello/api/environments \ | python3 -m json.tool
Example response:
output omitted "description": null, "id": 2, "label": "api-dev", "library": false, "name": "API Development", "organization": { "id": 1, "label": "Default_Organization", "name": "Default Organization" }, "permissions": { "destroy_lifecycle_environments": true, "edit_lifecycle_environments": true, "promote_or_remove_content_views_to_environments": true, "view_lifecycle_environments": true }, "prior": { "id": 1, "name": "Library" }, output truncatedIn the command output, you can see the ID for this lifecycle environment is
2, and the lifecycle environment before this one is1. Use the lifecycle environment with ID2to create a successor to this environment. -
Edit the previously created
life-cycle.jsonfile to update thelabel,name, andpriorvalues.{"organization_id":1,"label":"api-qa","name":"API QA","prior":2} -
Create a lifecycle environment using the
prioroption set to2.Example request:
$ curl \ --header "Accept: application/json" \ --header "Content-Type: application/json" \ --request POST \ --user My_User_Name:My_Password \ --data @life-cycle.json \ https://foreman.example.com/katello/api/environments \ | python3 -m json.tool
Example response:
output omitted "description": null, "id": 3, "label": "api-qa", "library": false, "name": "API QA", "organization": { "id": 1, "label": "Default_Organization", "name": "Default Organization" }, "permissions": { "destroy_lifecycle_environments": true, "edit_lifecycle_environments": true, "promote_or_remove_content_views_to_environments": true, "view_lifecycle_environments": true }, "prior": { "id": 2, "name": "API Development" }, "successor": null, output truncatedIn the command output, you can see the ID for this lifecycle environment is
3, and the lifecycle environment before this one is2.
5.2.3. Updating a lifecycle environment
You can modify lifecycle environment attributes such as name or description using a POST request.
This example request updates a description of the lifecycle environment with ID 3.
- Example API request
-
$ curl \ --header "Accept: application/json" \ --header "Content-Type: application/json" \ --request POST \ --user My_User_Name:My_Password \ --data '{"description":"Quality Acceptance Testing"}' \ https://foreman.example.com/katello/api/environments/3 \ | python3 -m json.tool - Example API response
-
output omitted "description": "Quality Acceptance Testing", "id": 3, "label": "api-qa", "library": false, "name": "API QA", "organization": { "id": 1, "label": "Default_Organization", "name": "Default Organization" }, "permissions": { "destroy_lifecycle_environments": true, "edit_lifecycle_environments": true, "promote_or_remove_content_views_to_environments": true, "view_lifecycle_environments": true }, "prior": { "id": 2, "name": "API Development" }, output truncated
5.2.4. Deleting a lifecycle environment
You can remove lifecycle environments that have no successors to clear unused promotion paths.
When deleting lifecycle environments, delete them in reverse order.
- Example API request
-
$ curl \ --request DELETE \ --user My_User_Name:My_Password \ https://foreman.example.com/katello/api/environments/:id
5.3. Uploading Content to Foreman server
You can upload and import files up to 2 MB to repositories on Foreman, automate content management for packages, scripts, and other repository artifacts.
This process involves the following steps:
-
Create an upload request.
-
Upload the content.
-
Import the content.
-
Delete the upload request.
The maximum file size that you can upload is 2 MB. For information about uploading larger content, see Uploading content larger than 2 MB.
-
Assign the package name to the variable
name:Example request:
$ export name=jq-1.6-2.el7.x86_64.rpm
-
Assign the checksum of the file to the variable
checksum:Example request:
$ export checksum=$(sha256sum $name|cut -c 1-65)
-
Assign the file size to the variable
size:Example request:
$ export size=$(du -bs $name|cut -f 1)
-
Create an upload request that returns the upload ID of the request by using
sizeandchecksum.Example request:
$ curl \ --header 'Content-Type: application/json' \ --request POST \ --user My_User_Name:My_Password \ --data "{\"size\": \"$size\", \"checksum\":\"$checksum\"}" \ https://foreman.example.com/katello/api/v2/repositories/76/content_uploadswhere 76, in this case, is an example Repository ID.
Example request:
{"upload_id":"37eb5900-597e-4ac3-9bc5-2250c302fdc4"} -
Assign the upload ID to the variable
upload_id:$ export upload_id=37eb5900-597e-4ac3-9bc5-2250c302fdc4
-
Assign the path of the package you want to upload to the variable
path:$ export path=/root/jq/jq-1.6-2.el7.x86_64.rpm
-
Upload your content. Ensure you use the correct MIME type when you upload data. The API uses the
application/jsonMIME type for the requests to Foreman unless stated otherwise. Combine the upload ID, MIME type, and other parameters to upload content.Example request:
$ curl \ --user My_User_Name:My_Password \ --header Accept:application/json \ --header Content-Type:multipart/form-data \ --request PUT \ --data-urlencode size=$size \ --data-urlencode offset=0 \ --data-urlencode content@${path} \ https://foreman.example.com/katello/api/v2/repositories/76/content_uploads/$upload_id -
After you have uploaded the content to your Foreman server, you need to import it into the appropriate repository. Until you complete this step, Foreman server does not detect the new content.
Example request:
$ curl \ --header "Content-Type: application/json" \ --request PUT \ --user My_User_Name:My_Password \ --data "{\"uploads\":[{\"id\": \"$upload_id\", \"name\": \"$name\", \"checksum\": \"$checksum\" }]}" \ https://foreman.example.com/katello/api/v2/repositories/76/import_uploads -
After you have successfully uploaded and imported your content, you can delete the upload request. This frees any temporary disk space that data is using during the upload.
Example request:
$ curl \ --header 'Content-Type: application/json' \ --request DELETE \ --user My_User_Name:My_Password \ --data "{}" \ https://foreman.example.com/katello/api/v2/repositories/76/content_uploads/$upload_id
5.3.1. Uploading content larger than 2 MB
To upload packages and ISO images that are larger than 2 MB, split them into smaller chunks before uploading each piece to repositories in Foreman sequentially.
Note that this example uses sample content, host names, user names, repository ID, and file names.
-
Assign the package name to the variable
name:$ export name=bpftool-3.10.0-1160.2.1.el7.centos.plus.x86_64.rpm
-
Assign the checksum of the file to the variable
checksum:$ export checksum=$(sha256sum $name|cut -c 1-65)
-
Assign the file size to the variable
size:$ export size=$(du -bs $name|cut -f 1)
-
The following command creates a new upload request and returns the upload ID of the request using
sizeandchecksum.Example request:
$ curl \ --header 'Content-Type: application/json' \ --request POST \ --user My_User_Name:My_Password \ --data "{\"size\": \"$size\", \"checksum\":\"$checksum\"}" \ https://foreman.example.com/katello/api/v2/repositories/76/content_uploadswhere 76, in this case, is an example Repository ID.
Example output
{"upload_id":"37eb5900-597e-4ac3-9bc5-2250c302fdc4"} -
Assign the upload ID to the variable
upload_id:$ export upload_id=37eb5900-597e-4ac3-9bc5-2250c302fdc4
-
Split the file in 2MB chunks:
$ split \ --bytes 2MB \ --numeric-suffixes \ --suffix-length=1 \ bpftool-3.10.0-1160.2.1.el7.centos.plus.x86_64.rpm bpftool
View the file chunks:
$ ls -l bpftool[0-9]
Example output:
-rw-r--r--. 1 root root 2000000 Mar 31 14:15 bpftool0 -rw-r--r--. 1 root root 2000000 Mar 31 14:15 bpftool1 -rw-r--r--. 1 root root 2000000 Mar 31 14:15 bpftool2 -rw-r--r--. 1 root root 2000000 Mar 31 14:15 bpftool3 -rw-r--r--. 1 root root 868648 Mar 31 14:15 bpftool4 -
Assign the prefix of the split files to the variable path.
$ export path=/root/tmp/bpftool
-
Upload the file chunks. The offset starts at 0 bytes for the first chunk and increases by 2000000 bytes for each file. Note the use of the offset parameter and how it relates to the file size. Note also that the indexes are used after the path variable, for example, ${path}0, ${path}1.
Example requests:
$ curl \ --user My_User_Name:My_Password \ --header Accept:application/json \ --header Content-Type:multipart/form-data \ --request PUT \ --data-urlencode size=$size \ --data-urlencode offset=0 \ --data-urlencode content@${path}0 \ https://foreman.example.com/katello/api/v2/repositories/76/content_uploads/$upload_id $ curl \ --user My_User_Name:My_Password \ --header Accept:application/json \ --header Content-Type:multipart/form-data \ --request PUT \ --data-urlencode size=$size \ --data-urlencode offset=2000000 \ --data-urlencode content@${path}1 \ https://foreman.example.com/katello/api/v2/repositories/76/content_uploads/$upload_id $ curl \ --user My_User_Name:My_Password \ --header Accept:application/json \ --header Content-Type:multipart/form-data \ --request PUT \ --data-urlencode size=$size \ --data-urlencode offset=4000000 \ --data-urlencode content@${path}2 \ https://foreman.example.com/katello/api/v2/repositories/76/content_uploads/$upload_id $ curl \ --user My_User_Name:My_Password \ --header Accept:application/json \ --header Content-Type:multipart/form-data \ --request PUT \ --data-urlencode size=$size \ --data-urlencode offset=6000000 --data-urlencode content@${path}3 \ https://foreman.example.com/katello/api/v2/repositories/76/content_uploads/$upload_id $ curl \ --user My_User_Name:My_Password \ --header Accept:application/json \ --header Content-Type:multipart/form-data \ --request PUT \ --data-urlencode size=$size \ --data-urlencode offset=8000000 \ --data-urlencode content@${path}4 \ https://foreman.example.com/katello/api/v2/repositories/76/content_uploads/$upload_id -
Import the complete upload to the repository:
$ curl \ --header "Content-Type: application/json" \ --request PUT \ --user My_User_Name:My_Password \ --data "{\"uploads\":[{\"id\": \"$upload_id\", \"name\": \"$name\", \"checksum\": \"$checksum\" }]}" \ https://foreman.example.com/katello/api/v2/repositories/76/import_uploads -
Delete the upload request:
$ curl \ --header 'Content-Type: application/json' \ --request DELETE \ --user My_User_Name:My_Password \ --data "{}" \ https://foreman.example.com/katello/api/v2/repositories/76/content_uploads/$upload_id
5.3.2. Uploading duplicate content
Reuse existing content when uploading files with matching checksums to save storage space and transfer time.
-
Upload content to Foreman:
$ curl \ --header 'Content-Type: application/json' \ --request POST \ --user My_User_Name:My_Password \ --data "{\"size\": \"$size\", \"checksum\":\"$checksum\"}" \ https://foreman.example.com/katello/api/v2/repositories/76/content_uploadsThe call will return a content unit ID instead of an upload ID, similar to this:
{"content_unit_href":"/pulp/api/v3/content/file/files/c1bcdfb8-d840-4604-845e-86e82454c747/"}You can copy this output and call import uploads directly to add the content to a repository.
Example API response:
$ curl \ --header "Content-Type: application/json" \ --request PUT \ --user My_User_Name:My_Password \ --data "{\"uploads\":[{\"content_unit_id\": \"/pulp/api/v3/content/file/files/c1bcdfb8-d840-4604-845e-86e82454c747/\", \"name\": \"$name\", \ \"checksum\": \"$checksum\" }]}" \ https://foreman.example.com/katello/api/v2/repositories/76/import_uploadsNote that the call changes from using
upload_idto usingcontent_unit_id.
5.4. Using extended searches
You can combine search parameters in your API queries to filter hosts. This way you can target specific hosts by their operating system or hardware model.
For more information, see Working efficiently with Foreman web UI in Administering Foreman.
For example, you can search for hosts.
-
In the Foreman web UI, navigate to Hosts > All Hosts.
-
Click the Search field to display a list of search parameters.
-
Locate the search parameters that you want to use. For this example, locate os_title and model.
-
Combine the search parameters in your API query as follows:
Example request:
$ curl \ --user My_User_Name:My_Password \ https://foreman.example.com/api/v2/hosts?search=os_title=\"RedHat+7.7\",model=\"PowerEdge+R330\" \ | python3 -m json.tool
Example response:
{ ... "results": [ { "model_id": 1, "model_name": "PowerEdge R330", "name": "foreman.example.com", "operatingsystem_id": 1, "operatingsystem_name": "RedHat 7.7", ... } ], "search": "os_title=\"RedHat 7.7\",model=\"PowerEdge R330\"", "subtotal": 1, "total": 11 }
5.5. Using searches with pagination control
You can control how many results an API query returns per page and which page to retrieve in order to manage large datasets efficiently.
The per_page parameter specifies the number of results per page and the page parameter specifies which page, as calculated by the per_page parameter, to return.
The number of items to be presented is set using the setting entries_per_page, which defaults to 20.
However, you can change it per request by using the parameter per_page.
This example returns a list of architectures for an organization with ID 1 in pages.
The list contains 5 entries per page and returns the second page.
- Example API request
-
$ curl \ --request GET \ --user My_User_Name:My_Password \ https://foreman.example.com/katello/api/architectures?organization_id=1&per_page=5&page=2
5.5.1. Returning multiple pages
You can iterate through multiple pages of API results to retrieve complete datasets when the total number of items exceeds the per-page limit.
- Bash script
-
$ for i inseq 1 3; do \ curl \ --request GET \ --user My_User_Name:My_Password \ https://foreman.example.com/katello/api/content_views?per_page=5&page=$i; \ done
5.6. Overriding Smart Class parameters
You can override Smart Class parameters to customize Puppet configuration values for specific hosts or host groups. This way you customize different environments without changing the original Puppet classes.
You can find the full list of attributes that you can modify in the built-in API reference at https://foreman.example.com/apidoc/v2/smart_class_parameters/update.html.
-
Find the ID of the Smart Class parameter you want to change:
-
List all Smart Class Parameters.
Example request:
$ curl \ --request GET \ --user My_User_Name:My_Password \ https://foreman.example.com/api/smart_class_parameters
-
If you know the Puppet class ID, for example 5, you can restrict the scope: Example request:
$ curl \ --request GET \ --user My_User_Name:My_Password \ https://foreman.example.com/api/puppetclasses/5/smart_class_parameters
Both calls accept a search parameter. You can view the full list of searchable fields in the Foreman web UI. Navigate to Configure > Smart variables and click in the search query box to reveal the list of fields.
Two particularly useful search parameters are
puppetclass_nameandkey, which you can use to search for a specific parameter. For example, use the--dataoption to pass URL encoded data.Example request:
$ curl \ --request GET \ --user My_User_Name:My_Password \ --data 'search=puppetclass_name = access_insights_client and key = authmethod' \ https://foreman.example.com/api/smart_class_parameters
Foreman supports standard scoped-search syntax.
-
-
When you find the ID of the parameter, list the full details including current override values.
Example request:
$ curl \ --request GET \ --user My_User_Name:My_Password \ https://foreman.example.com/api/smart_class_parameters/63
-
Enable overriding of parameter values.
Example request:
$ curl \ --header "Accept: application/json" \ --header "Content-Type: application/json" \ --request PUT \ --user My_User_Name:My_Password \ --data '{"smart_class_parameter":{"override":true}}' \ https://foreman.example.com/api/smart_class_parameters/63Note that you cannot create or delete the parameters manually. You can only modify their attributes. Foreman creates and deletes parameters only upon class import from Smart Proxies.
-
Add custom override matchers.
Example request:
$ curl \ --header "Accept: application/json" \ --header "Content-Type: application/json" \ --request PUT \ --user My_User_Name:My_Password \ --data '{"smart_class_parameter":{"override_value":{"match":"hostgroup=Test","value":"2.4.6"}}}' \ https://foreman.example.com/api/smart_class_parameters/63For more information about override values, see
https://foreman.example.com/apidoc/v2/override_values.html. -
You can delete override values.
Example request:
$ curl \ --request DELETE \ --user My_User_Name:My_Password \ https://foreman.example.com/api/smart_class_parameters/63/override_values/3
5.7. Modifying a Smart Class parameter by using an external file
You can modify Puppet Smart Class parameters by using external JSON files to simplify working with complex parameter values. That way, you can enable syntax highlighting and error detection in your editor.
This example uses a MOTD Puppet manifest.
-
Search for the Puppet Class by name,
motdin this case.Example request:
$ curl \ --header "Accept: application/json" \ --header "Content-Type: application/json" \ --request GET \ --user My_User_Name:My_Password \ https://foreman.example.com/api/smart_class_parameters?search=puppetclass_name=motd \ | python3 -m json.tool
-
Examine the following output. Each Smart Class Parameter has an ID that is global for the same Foreman instance. The
contentparameter of themotdclass hasid=3. Do not confuse this with the Puppet Class ID that displays before the Puppet Class name.Example response:
{ "avoid_duplicates": false, "created_at": "2024-02-06 12:37:48 UTC", # Remove this line. "default_value": "", # Add a new value here. "description": "", "hidden_value": "", "hidden_value?": false, "id": 3, "merge_default": false, "merge_overrides": false, "override": false, # Set the override value totrue. "override_value_order": "fqdn\nhostgroup\nos\ndomain", "override_values": [], # Remove this line. "override_values_count": 0, "parameter": "content", "parameter_type": "string", "puppetclass_id": 3, "puppetclass_name": "motd", "required": false, "updated_at": "2024-02-07 11:56:55 UTC", # Remove this line. "use_puppet_default": false, "validator_rule": null, "validator_type": "" } -
Use the parameter ID
3to get the information specific to themotdparameter and redirect the output to a file, for example, output_file.json.Example request:
$ curl \ --header "Accept: application/json" \ --header "Content-Type: application/json" \ --request GET \ --user My_User_Name:My_Password \ https://foreman.example.com/api/smart_class_parameters/3 \ | python3 -m json.tool > output_file.json
-
Copy the file created in the previous step to a new file for editing, for example,
changed_file.json:$ cp output_file.json changed_file.json
-
Modify the required values in the file. In this example, change the content parameter of the
motdmodule, which requires changing theoverrideoption fromfalsetotrue:{ "avoid_duplicates": false, "created_at": "2024-02-06 12:37:48 UTC", # Remove this line. "default_value": "", # Add a new value here. "description": "", "hidden_value": "", "hidden_value?": false, "id": 3, "merge_default": false, "merge_overrides": false, "override": false, # Set the override value totrue. "override_value_order": "fqdn\nhostgroup\nos\ndomain", "override_values": [], # Remove this line. "override_values_count": 0, "parameter": "content", "parameter_type": "string", "puppetclass_id": 3, "puppetclass_name": "motd", "required": false, "updated_at": "2024-02-07 11:56:55 UTC", # Remove this line. "use_puppet_default": false, "validator_rule": null, "validator_type": "" } -
After editing the file, verify that it looks as follows and then save the changes:
{ "avoid_duplicates": false, "default_value": "No Unauthorized Access Allowed", "description": "", "hidden_value": "", "hidden_value?": false, "id": 3, "merge_default": false, "merge_overrides": false, "override": true, "override_value_order": "fqdn\nhostgroup\nos\ndomain", "override_values_count": 0, "parameter": "content", "parameter_type": "string", "puppetclass_id": 3, "puppetclass_name": "motd", "required": false, "use_puppet_default": false, "validator_rule": null, "validator_type": "" } -
Submit the file to Foreman:
$ curl \ --header "Accept: application/json" \ --header "Content-Type: application/json" \ --request PUT \ --user My_User_Name:My_Password \ --data @changed_file.json \ https://foreman.example.com/api/smart_class_parameters/3
5.8. Deleting OpenSCAP reports
Delete OpenSCAP compliance reports to free up storage space or remove outdated scan results, either individually by ID or in bulk using a bash script for large-scale cleanup.
-
List all OpenSCAP reports. Note the IDs of the reports that you want to delete.
Example request:
$ curl \ --user My_User_Name:My_Password \ https://foreman.example.com/api/v2/compliance/arf_reports/ \ | python3 -m json.tool
Example response:
{ "page": 1, "per_page": 20, "results": [ { "created_at": "2024-05-16 13:27:09 UTC", "failed": 0, "host": "host1.example.com", "id": 404, "othered": 0, "passed": 0, "updated_at": "2024-05-16 13:27:09 UTC" }, { "created_at": "2024-05-16 13:26:07 UTC", "failed": 0, "host": "host2.example.com, "id": 405, "othered": 0, "passed": 0, "updated_at": "2024-05-16 13:26:07 UTC" }, { "created_at": "2024-05-16 13:25:07 UTC", "failed": 0, "host": "host3.example.com", "id": 406, "othered": 0, "passed": 0, "updated_at": "2024-05-16 13:25:07 UTC" }, { "created_at": "2024-05-16 13:24:07 UTC", "failed": 0, "host": "host4.example.com", "id": 407, "othered": 0, "passed": 0, "updated_at": "2024-05-16 13:24:07 UTC" }, ], "search": null, "sort": { "by": null, "order": null }, "subtotal": 29, "total": 29 -
Using an ID from the previous step, delete the OpenSCAP report. Repeat for each ID that you want to delete.
Example request:
$ curl \ --user My_User_Name:My_Password \ --header "Content-Type: application/json" \ --request DELETE \ https://foreman.example.com/api/v2/compliance/arf_reports/405
Example response:
HTTP/1.1 200 OK Date: Thu, 18 May 2024 07:14:36 GMT Server: Apache/2.4.6 (Enterprise Linux) X-Frame-Options: SAMEORIGIN X-XSS-Protection: 1; mode=block X-Content-Type-Options: nosniff Foreman_version: 3.11.0.76 Foreman_api_version: 2 Apipie-Checksum: 2d39dc59aed19120d2359f7515e10d76 Cache-Control: max-age=0, private, must-revalidate X-Request-Id: f47eb877-35c7-41fe-b866-34274b56c506 X-Runtime: 0.661831 X-Powered-By: Phusion Passenger 4.0.18 Set-Cookie: request_method=DELETE; path=/ Set-Cookie: _session_id=d58fe2649e6788b87f46eabf8a461edd; path=/; secure; HttpOnly ETag: "2574955fc0afc47cb5394ce95553f428" Status: 200 OK Vary: Accept-Encoding Transfer-Encoding: chunked Content-Type: application/json; charset=utf-8
#!/bin/bash
# this script removes all ARF reports from your Foreman server
# settings
USER="My_User_Name"
PASS="My_Password"
URI="https://foreman.example.com"
# check amount of reports
while [ $(curl --user $USER:$PASS $URI/api/v2/compliance/arf_reports/ | python3 -m json.tool | grep \"\total\": | cut --fields=2 --delimiter":" | cut --fields=1 --delimiter"," | sed "s/ //g") -gt 0 ]; do
# fetch reports
for i in $(curl --user $USER:$PASS $URI/api/v2/compliance/arf_reports/ | python3 -m json.tool | grep \"\id\": | cut --fields=2 --delimiter":" | cut --fields=1 --delimiter"," | sed "s/ //g")
# delete reports
do
curl --user $USER:$PASS --header "Content-Type: application/json" --request DELETE $URI/api/v2/compliance/arf_reports/$i
done
done
Appendix A: API response codes
The Foreman API provides HTTP response status codes for API calls. The following codes are common for all resources in the Foreman API.
| Response | Explanation |
|---|---|
200 OK |
For a successful request action: show, index, update, or delete (GET, PUT, DELETE requests). |
201 Created |
For a successful create action (POST request). |
301 Moved Permanently |
Redirect when Foreman is restricted to use HTTPS and HTTP is attempted. |
400 Bad Request |
A required parameter is missing or the search query has invalid syntax. |
401 Unauthorized |
Failed to authorize the user, for example, due to incorrect credentials. |
403 Forbidden |
The user has insufficient permissions to perform the action or read the resource, or the action is unsupported in general. |
404 Not Found |
The record with the given ID does not exist. It can appear in show and delete actions when the requested record does not exist; or in create, update and delete actions when one of the associated records does not exist. |
409 Conflict |
Could not delete the record due to existing dependencies, for example, host groups that still contain hosts. |
415 Unsupported Media Type |
The content type of the HTTP request is not JSON. |
422 Unprocessable Entity |
Failed to create an entity due to some validation errors. Applies to create or update actions only. |
500 Internal Server Error |
Unexpected internal server error. |
503 Service Unavailable |
The server is not running. |
Appendix B: Creating a complete permission table
Generate a complete permission table from the Foreman console to review all available permissions and actions on your Foreman.
-
The
foreman-consolepackage is installed on Foreman server.
-
Start the Foreman console:
# foreman-rake console
-
Insert the following code into the console:
f = File.open('/tmp/table.html', 'w') result = Foreman::AccessControl.permissions {|a,b| a.security_block <=> b.security_block}.collect do |p| actions = p.actions.collect { |a| "<li>#{a}</li>" } "<tr><td>#{p.name}</td><td><ul>#{actions.join('')}</ul></td><td>#{p.resource_type}</td></tr>" end.join("\n") f.write("<table border=\"1\"><tr><td>Permission name</td><td>Actions</td><td>Resource type</td></tr>\n") f.write(result) f.write("</table>\n")The above syntax creates a table of permissions and saves it to the
/tmp/table.htmlfile. -
Press
Ctrl+Dto exit the Foreman console. -
Open
/tmp/table.htmlin a web browser to view the table.