Chapter 4. Getting Started with the Red Hat Satellite API


This chapter provides a range of examples of how to use the Red Hat Satellite API to perform different tasks. These examples focus on the Satellite Server, using HTTPS on port 443. You can also access the API via the Satellite Capsule, but you need to use port 8443 or the API calls will fail.
You can address these different port requirements within the script itself. For example, in Ruby, you can specify the Satellite and Capsule URLs as follows:
url = 'https://satellite6.example.com/api/v2/'
capsule_url = 'https://capsule.example.com:8443/api/v2/'
katello_url = 'https://satellite6.example.com/katello/api/v2/'
If the host is subscribed to a Satellite Server or Capsule Server, you can determine the correct port required to access the API from the /etc/rhsm/rhsm.conf file, in the port entry of the [server] section. You can use these values to fully automate your scripts, removing any need to verify which ports to use.

4.1. API Examples Using Curl

This section describes how to use curl to perform various tasks using the Satellite API.

4.1.1. Performing Simple Queries

The following examples describe how to use curl to search for information about your Satellite deployment. These examples include both the actual command and some sample output, and example values for user names and passwords. Expect different results for each deployment. These examples also use the python -m json.tool command to format the output.

Note

Red Hat Satellite requires the use of HTTPS, and by default a certificate for host identification. If you have not added the Satellite Server certificate as described in Section 3.1, “Using SSL Authentication”, then you can use the -k (insecure) option to bypass certificate checks.
For user authentication, you can use the form -u username:password or, if you do not include the password, the command prompts you to enter it. Red Hat recommends that you do not include the password as part of the command, because it then becomes part of your shell history and might present a security risk. These examples include the password only for the sake of simplicity.
Be aware that if you use the -s (silent) option with curl that you will not see a progress meter or any error messages.
Retrieving a List of Resources

The following is a basic query that returns a list of resources. Such requests return a list of data wrapped in metadata, while other request types only return the actual object.

$ curl -X GET -s -k -u sat_username:sat_password https://satellite6.example.com/api/v2/hosts | python -m json.tool

{
       "total" => 2,
    "subtotal" => 2,
        "page" => 1,
    "per_page" => 1000,
      "search" => nil,
        "sort" => {
           "by" => nil,
        "order" => nil
    },
     "results" => [
      ...
}

Example 4.1. Listing Users

$ curl -X GET -s -k -u sat_username:sat_password https://satellite6.example.com/api/users
{
  "total": 1,
  "subtotal": 1,
  "page": 1,
  "per_page": 20,
  "search": null,
  "sort": {
    "by": null,
    "order": null
  },
  "results": [{"firstname":"Admin","lastname":"User","mail":"root@example.com","admin":true,"auth_source_id":1,"auth_source_name":"Internal","timezone":null,"locale":null,"last_login_on":"2017-02-08 23:25:51 UTC","created_at":"2017-01-09 12:10:02 UTC","updated_at":"2017-02-08 23:25:51 UTC","id":3,"login":"admin","default_location":null,"locations":[],"default_organization":{"id":1,"name":"Default Organization","title":"Default Organization","description":null},"organizations":[]}]
}
Running a Generic Host Query

The following query returns information for the host satellite6.example.com:

$  curl -X GET -s -k -u sat_username:sat_password https://satellite6.example.com/api/v2/hosts/satellite6.example.com | python -m json.tool
{
    "all_puppetclasses": [],
    "architecture_id": 1,
    "architecture_name": "x86_64",
    "build": false,
    "capabilities": [
        "build"
    ],
    "certname": "satellite6.example.com",
    "comment": null,
    "compute_profile_id": null,
    ...
}
Searching for Facts for a Specific Host

The following query returns all facts for the host satellite6.example.com:

$ curl -X GET -s -k -u sat_username:sat_password https://satellite6.example.com/api/v2/hosts/satellite6.example.com/facts | python -m json.tool
{
...
    "results": {
        "satellite6.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",
            ...
}
Searching all Hosts for Matching Patterns

The following query returns all hosts that match the pattern "example":

$ curl -X GET -s -k -u sat_username:sat_password https://satellite6.example.com/api/v2/hosts?search=example | python -m json.tool
{
    ...
    "results": [
        {
            "name": "satellite6.example.com",
            ...
        }
    ],
    "search": "example",
    ...
}
Searching for all Hosts in a Specific Environment

The following query returns all hosts in the "production" environment:

$ curl -X GET -s -k -u sat_username:sat_password https://satellite6.example.com/api/v2/hosts?search=environment=production | python -m json.tool
{
    ...
    "results": [
        {
            "environment_name": "production",
            "name": "satellite6.example.com",
            ...
        }
    ],
    "search": "environment=production",
    ...
}
Searching for all Hosts with a Specific Fact Value

The following query returns all hosts with a model name "RHEV Hypervisor":

$ curl -X GET -s -k -u sat_username:sat_password https://satellite6.example.com/api/v2/hosts?search=model=\"RHEV+Hypervisor\" | python -m json.tool
{
    ...
    "results": [
        {
            "model_id": 1,
            "model_name": "RHEV Hypervisor",
            "name": "satellite6.example.com",
            ...
        }
    ],
    "search": "model=\"RHEV Hypervisor\"",
    ...
}
Deleting a Host

The following query deletes a host with a name host1.example.com:

curl -k -u sat_username:sat_password -X DELETE https://satellite6.example.com/api/v2/hosts/host1.example.com

4.1.2. Creating and Modifying Resources

You can use the Satellite API to manipulate resources on the Satellite Server. These API calls require that you pass various parameters beyond the simple user name, password, and URI that you want to query. For example, to upload content to your Satellite Server, or to modify Satellite resources, you need to include extra information in the header when you construct your request.
You can specify the version of the API either in the header, as described in the following examples, or as part of the URL. For example, https://satellite6.example.com/api/v2/architectures is the equivalent of using Accept:version=2 in the request header. The URL specification takes precedence.
The following is the basic syntax for a POST request:
$ curl -H "Accept:application/json,version=2" \
       -H "Content-Type:application/json" -X POST \
       -u username:password -k \
       -d json-formatted-data https://satellite6.example.com
For example, to create a new architecture, you can use the following example request:
$ curl -H "Accept:application/json,version=2" \
       -H "Content-Type:application/json" -X POST -u sat_username:sat_password \
       -k -d "{\"architecture\":{\"name\":\"i686\"}}" \
       https://satellite6.example.com/api/architectures
This returns output similar to the following:
{"name":"i686","id":3,"created_at":"2015-10-29T13:21:09Z","updated_at":"2015-10-29T13:21:09Z","operatingsystems":[],"images":[]}
You can use the following command to verify that the architecture was created:
$ curl -X GET -u sat_username:sat_password -k https://satellite6.example.com/api/v2/architectures | python -m json.tool
  {
      "page": 1,
      "per_page": 20,
      "results": [
          {
              "created_at": "2015-04-02T05:29:46Z",
              "id": 2,
              "name": "i386",
              "updated_at": "2015-04-02T05:29:46Z"
          },
          {
              "created_at": "2015-04-02T05:29:46Z",
              "id": 1,
              "name": "x86_64",
              "updated_at": "2015-04-02T05:29:46Z"
          },
          {
              "created_at": "2015-11-04T19:40:15Z",
              "id": 3,
              "name": "i686",
              "updated_at": "2015-11-04T19:40:15Z"
          }
      ],
      "search": null,
      "sort": {
          "by": null,
          "order": null
      },
      "subtotal": 3,
      "total": 3
  }
You can also use hammer on the Satellite Server to verify the results:
$ hammer -u sat_username -p sat_password architecture list
---|-------
ID | NAME
---|-------
2  | i386
1  | x86_64
3  | i686
---|-------

Example 4.2. Creating a New User

$ curl -H "Accept:application/json,version=2" \
-H "Content-Type:application/json" -X POST \
-u sat_username:sat_password -k \
-d "{\"firstname\":\"Test\",\"lastname\":\"API-User\",\"mail\":\"test@example.com\",\"login\":\"test_api\",\"password\":\"123456\",\"auth_source_id\":1}" \
https://satellite6.example.com/api/users

4.1.2.1. Uploading Content to the Satellite Server

This section describes how to use curl with the Satellite 6 API to upload and import large files to your Satellite Server. This process involves four steps:
  1. Create an upload request.
  2. Upload the content.
  3. Import the content.
  4. Delete the upload request.
The maximum file size that you can upload is about 30 MB. To upload larger content, refer to Example 4.3, “Uploading Content Larger than 30 MB”.

Procedure 4.1. Uploading Content to the Satellite Server

  1. Create the upload request. Ensure you modify the example parameters to suit your deployment:
    $ curl -H "Accept:application/json,version=2" \
           -H "Content-Type:application/json" \
           -X POST \
           -u sat_username:sat_password -k -d "{}" \
           https://satellite6.example.com/katello/api/repositories/3/content_uploads
    This command returns the upload_id similar to the following:
    {"upload_id":"0be156b1-f373-4cad-89d0-924f8f4491d2","_href":"/pulp/api/v2/content/uploads/0be156b1-f373-4cad-89d0-924f8f4491d2/"}
  2. Upload your content. Ensure you use the correct MIME type when you upload data. The "application/json" MIME type is used for the majority of requests to Satellite 6. Combine the upload_id, MIME type, and other parameters to upload content:
    $ curl -H "Accept:application/json,version=2" \
           -H "Content-Type:multipart/form-data" \
           -X PUT \
           -u sat_username:sat_password \
           -k --data-urlencode "content@/home/sat6user/rpmbuild/RPMS/noarch/python-scripttest-1.1.1-1.fc21.noarch.rpm" \
           --data-urlencode offset=0 \
           https://satellite6.example.com/katello/api/repositories/3/content_uploads/0be156b1-f373-4cad-89d0-924f8f4491d2
  3. After you have uploaded the content to the Satellite Server, you need to import it into the appropriate repository. Until you complete this step, the Satellite Server will not be aware of the new content:
    $ curl -H "Accept:application/json,version=2" \
           -H "Content-Type:application/json" \
           -X PUT \
           -u sat_username:sat_password \
           -k -d "{\"upload_ids\":[\"0be156b1-f373-4cad-89d0-924f8f4491d2\"]}" \
           https://satellite6.example.com/katello/api/repositories/3/import_uploads
  4. After you have successfully uploaded and imported your content, you can delete the upload request. This frees any temporary disk space that was used during the upload:
    $ curl -H "Accept:application/json,version=2" \
           -H "Content-Type:application/json" \
           -X DELETE -d "{}" \
           -u sat_username:sat_password \
           -k https://satellite6.example.com/katello/api/repositories/3/content_uploads/0be156b1-f373-4cad-89d0-924f8f4491d2

Example 4.3. Uploading Content Larger than 30 MB

The following example demonstrates in full how to split a large file into chunks, create an upload request, upload the individual files, import them to Satellite, and then delete the upload request. Note that this example uses sample content, host names, user names, and file names.
  1. Download the sample module:
    $ wget https://forgeapi.puppetlabs.com/v3/files/theforeman-foreman-5.0.1.tar.gz?_ga=1.267255502.1792403825.1430297670 -O theforeman-foreman-5.0.1.tar.gz
    
    Split the module into 50,000 byte chunks:
    $ split --bytes 50000 --numeric-suffixes --suffix-length=1 theforeman-foreman-5.0.1.tar.gz foreman_module.
    
    View the resulting files:
    $ ls -la theforeman-foreman-5.0.1.tar.gz foreman_module.*
    -rw-r--r--. 1 root root 50000 Nov  4 04:42 foreman_module.0
    -rw-r--r--. 1 root root 32928 Nov  4 04:42 foreman_module.1
    -rw-r--r--. 1 root root 82928 Nov  4 04:41 theforeman-foreman-5.0.1.tar.gz
  2. Create a new upload request (this is the equivalent of cat on the Satellite Server).
    $ curl -H "Accept:application/json,version=2" \
           -H "Content-Type:application/json" \
           -X POST \
           -u sat_username:sat_password -k -d "{}" \
           https://ibm-vm01.example.com/katello/api/repositories/2/content_uploads
    
    The above command returns an upload ID:
    {"upload_id":"9585528f-07ad-4bb1-9c80-ccece249b2b7","_href":"/pulp/api/v2/content/uploads/9585528f-07ad-4bb1-9c80-ccece249b2b7/"}
  3. Upload the file chunks that you created in Step 1. Notice the use of the offset parameter in this example and how it relates to the file size:
    $ curl -H "Accept:application/json,version=2" \
           -H "Content-Type:multipart/form-data" \
           -X PUT \
           -u sat_username:sat_password \
           -k --data-urlencode "content@foreman_module.0" \
           --data-urlencode offset=0 \
           https://ibm-vm01.example.com/katello/api/repositories/2/content_uploads/9585528f-07ad-4bb1-9c80-ccece249b2b7
    
    $ curl -H "Accept:application/json,version=2" \
           -H "Content-Type:multipart/form-data" \
           -X PUT \
           -u sat_username:sat_password \
           -k --data-urlencode "content@foreman_module.1" \
           --data-urlencode offset=50000 \
           https://ibm-vm01.example.com/katello/api/repositories/2/content_uploads/9585528f-07ad-4bb1-9c80-ccece249b2b7
  4. Import the complete upload to the repository:
    $ curl -H "Accept:application/json,version=2" \
           -H "Content-Type:application/json" \
           -X PUT \
           -u sat_username:sat_password \
           -k -d "{\"upload_ids\":[\"9585528f-07ad-4bb1-9c80-ccece249b2b7\"]}" \
           https://ibm-vm01.example.com/katello/api/repositories/2/import_uploads
  5. Delete the upload request:
    $ curl -H "Accept:application/json,version=2" \
           -H "Content-Type:application/json" \
           -X DELETE -d "{}" \
           -u sat_username:sat_password \
           -k https://ibm-vm01.example.com/katello/api/repositories/2/content_uploads/9585528f-07ad-4bb1-9c80-ccece249b2b7
  6. Log in to the Satellite Server to check if the file was transferred correctly:
    $ ls -la /var/lib/pulp/content/puppet_module/theforeman-foreman-5.0.1.tar.gz
    -rw-r--r--. 1 apache apache 82928 Nov  4 04:55 /var/lib/pulp/content/puppet_module/theforeman-foreman-5.0.1.tar.gz
    
    Compare the files:
    $ cmp /var/lib/pulp/content/puppet_module/theforeman-foreman-5.0.1.tar.gz theforeman-foreman-5.0.1.tar.gz
    
    $ echo $?
    0
    

4.1.3. Overriding Smart Class Parameters

You can search for Smart Parameters using the API and supply a value to override a Smart Parameter in a Class. The full list of attributes that can be modified can be found in the built-in API reference at https://satellite6.example.com/apidoc/v2/smart_class_parameters/update.html.
For example, to list all Smart Class Parameters, the API route is shown as GET /api/smart_class_parameters. Using curl, the command is as follows:
$ curl -X GET -s -k -u sat_username:sat_password \
https://satellite6.example.com/api/smart_class_parameters
If you know the Puppet class ID, for example 5, you can restrict the scope as follows:
$ curl -X GET -s -k -u sat_username:sat_password https://satellite6.example.com/api/puppetclasses/5/smart_class_parameters
Both calls accept a search parameter. The full list of searchable fields is visible in the web UI in the search input box. 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_name and key, which enables you to search for a specific parameter. For example, using the -d, --data option to pass URL encoded data:
$ curl -X GET -s -k -u sat_username:sat_password https://satellite6.example.com/api/smart_class_parameters -d 'search=puppetclass_name = access_insights_client and key = authmethod'
Standard scoped-search syntax is supported.
Once you have found the ID of the parameter, you can proceed with listing the full details including current override values. For example, for an ID of 63, the API route is GET /api/smart_class_parameters/63. Using curl, the command would be:
$ curl -X GET -s -k -u sat_username:sat_password \
https://satellite6.example.com/api/smart_class_parameters/63
Now you can enable overriding of parameter values with a PUT call:
$ curl -H "Accept:application/json,version=2" \
-H "Content-Type:application/json" -X PUT \
-s -k -u sat_username:sat_password \
-d '{"smart_class_parameter":{"override":true}}' \
https://satellite6.example.com/api/smart_class_parameters/63
Note that there is no way to create or delete the parameters manually. Users can only modify their attributes. Parameters get created and deleted only upon class import from a proxy.
When override is enabled, you can add custom override matchers:
$ curl -H "Accept:application/json,version=2" \
-H "Content-Type:application/json" -X PUT \
-s -k -u sat_username:sat_password \
-d '{"smart_class_parameter":{"override_value":{"match":"hostgroup=Test","value":"2.4.6"}}}' \
https://satellite6.example.com/api/smart_class_parameters/63
Details about all parameters of the API call are here: https://satellite6.example.com/apidoc/v2/override_values.html.
To delete override values use a command as follows:
$ curl -X DELETE -s -u sat_username:sat_password \
https://satellite6.example.com/api/smart_class_parameters/63/override_values/3

4.1.3.1. Modifying a Smart Class Parameter Using an External File

Using external files simplifies working with JSON data. Using an editor with syntax highlighting can help you avoid and locate mistakes.

Procedure 4.2. Modifying a Smart Class Parameter Using an External File

For this example we will use a MOTD Puppet manifest.
  1. Search for the Puppet Class by name, in this case, motd:
    $ curl -H "Accept:application/json,version=2" \
    -H "Content-Type:application/json" -X GET \
    -u sat_user:sat_passwd -k \
    "https://satellite6.example.com/api/smart_class_parameters?search=puppetclass_name=motd” \
    | python -m json.tool
    {
    "page": 1,
    "per_page": 20,
    "results": [
    {
    "avoid_duplicates": false,
    "created_at": "2017-02-06 12:37:48 UTC",
    "default_value": "",
    "description": "",
    "hidden_value": "*****",
    "hidden_value?": false,
    "id": 3,
    "merge_default": false,
    "merge_overrides": false,
    "override": false,
    "override_value_order": "fqdn\nhostgroup\nos\ndomain",
    "override_values_count": 0,
    "parameter": "content",
    "parameter_type": "string",
    "puppetclass_id": 3,
    "puppetclass_name": "motd",
    "required": false,
    "updated_at": "2017-02-07 13:08:42 UTC",
    "use_puppet_default": false,
    "validator_rule": null,
    "validator_type": ""
    },
    {
    "avoid_duplicates": false,
    "created_at": "2017-02-06 12:37:48 UTC",
    "default_value": true,
    "description": "",
    "hidden_value": "*****",
    "hidden_value?": false,
    "id": 1,
    "merge_default": false,
    "merge_overrides": false,
    "override": false,
    "override_value_order": "fqdn\nhostgroup\nos\ndomain",
    "override_values_count": 0,"parameter": "dynamic_motd",
    "parameter_type": "boolean",
    "puppetclass_id": 3,
    "puppetclass_name": "motd",
    "required": false,
    "updated_at": "2017-02-06 15:21:06 UTC",
    "use_puppet_default": null,
    "validator_rule": null,
    "validator_type": null
    },
    {
    "avoid_duplicates": false,
    "created_at": "2017-02-06 12:37:48 UTC",
    "default_value": "",
    "description": "",
    "hidden_value": "*****",
    "hidden_value?": false,
    "id": 2,
    "merge_default": false,
    "merge_overrides": false,
    "override": false,
    "override_value_order": "fqdn\nhostgroup\nos\ndomain",
    "override_values_count": 0,
    "parameter": "template",
    "parameter_type": "string",
    "puppetclass_id": 3,
    "puppetclass_name": "motd",
    "required": false,
    "updated_at": "2017-02-06 15:21:06 UTC",
    "use_puppet_default": null,
    "validator_rule": null,
    "validator_type": null
    }
    ],
    "search": "puppetclass_name=motd",
    "sort": {
    "by": null,
    "order": null
    },
    "subtotal": 3,
    "total": 66
    }
    Each Smart Class Parameter has an ID that is global for the same Satellite instance. The content parameter of the motd class has id=3 in this Satellite Server. Do not confuse this with the Puppet Class ID which appears just before the Puppet Class name.
  2. Use the parameter ID 3 to get the information specific to the motd parameter and redirect the output to a file, for example, output_file.json:
    $ curl -H "Accept:application/json,version=2" \
    -H "Content-Type:application/json" -X GET \
    -u sat_user:sat_passwd -k \
    "https://satellite6.example.com/api/smart_class_parameters/3 \
    | python -m json.tool > output_file.json
  3. Copy the file created in the previous step to a new file for editing, for example, changed_file.json. Open the file in an editor and modify the desired values. In this example, we wish to change the content parameter of the motd module, which requires changing the override option from false to true:
    {
    "avoid_duplicates": false,
    "created_at": "2017-02-06 12:37:48 UTC", # This line must be removed.
    "default_value": "", # A new value should be supplied here.
    "description": "",
    "hidden_value": "*****",
    "hidden_value?": false,
    "id": 3,
    "merge_default": false,
    "merge_overrides": false,
    "override": false, # The override value must be set to true.
    "override_value_order": "fqdn\nhostgroup\nos\ndomain",
    "override_values": [], # This line must be removed.
    "override_values_count": 0,
    "parameter": "content",
    "parameter_type": "string",
    "puppetclass_id": 3,
    "puppetclass_name": "motd",
    "required": false,
    "updated_at": "2017-02-07 11:56:55 UTC", # This line must be removed.
    "use_puppet_default": false,
    "validator_rule": null,
    "validator_type": ""
    }
  4. 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": ""
    }
  5. Use a PUT command as follows to apply the changes to Satellite Server:
    $ curl -H "Accept:application/json,version=2" \
    -H "Content-Type:application/json" \
    -X PUT -u $user:$passwd \
    -d @changed_file.json \
    -k "https://satellite6.example.com/api/smart_class_parameters/3

4.1.4. Applying Errata to a Host or Host Collection

You can use curl with the PUT command to apply errata to a host, host group, or host collection. The following is the basic syntax of a PUT request:
$ curl -H "Accept:application/json,version=2" \
                   -H "Content-Type:application/json" -X PUT \
                   -u sat_username:sat_password -k \
                   -d json-formatted-data https://satellite6.example.com
You can browse the built in API doc (https://satellite6.example.com/apidoc/v2.html) to find a URL to use for applying Errata. You can use the Satellite web UI to help discover the format for the search query. Navigate to Hosts Host Collections and select a host collection. Go to Collection Actions Errata Installation and notice the search query box contents. For example, for a Host Collection called my-collection the search box contains host_collection="my-collection". This will be used in the example below for Host Collections.

Example 4.4. Applying Errata to a Host

In this example the API URL for bulk actions, /katello/api/hosts/bulk/install_content, is used to show the format required for a simple search.
$ curl -H "Accept:application/json,version=2" \
       -H "Content-Type:application/json" -X PUT \
       -u sat_username:sat_password -k \
       -d "{\"organization_id\":1,\"included\":{\"search\":\"my-host\"},\"content_type\":\"errata\",\"content\":[\"RHBA-2016:1981\"]}" https://satellite6.example.com/api/v2/hosts/bulk/install_content

Example 4.5. Applying Errata to a Host Collection

In this example, notice the level of escaping required to pass the search string host_collection="my-collection" as seen in the Satellite web UI.
$ curl -H "Accept:application/json,version=2" \
       -H "Content-Type:application/json" -X PUT \
       -u sat_username:sat_password -k \
       -d "{\"organization_id\":1,\"included\":{\"search\":\"host_collection=\\\"my-collection\\\"\"},\"content_type\":\"errata\",\"content\":[\"RHBA-2016:1981\"]}" https://satellite6.example.com/api/v2/hosts/bulk/install_content
Red Hat logoGithubRedditYoutubeTwitter

Learn

Try, buy, & sell

Communities

About Red Hat Documentation

We help Red Hat users innovate and achieve their goals with our products and services with content they can trust.

Making open source more inclusive

Red Hat is committed to replacing problematic language in our code, documentation, and web properties. For more details, see the Red Hat Blog.

About Red Hat

We deliver hardened solutions that make it easier for enterprises to work across platforms and environments, from the core datacenter to the network edge.

© 2024 Red Hat, Inc.