Dell OpenManage Enterprise Operations with Ansible Part 2: Templates and Deployment
Mon, 04 Dec 2023 16:30:25 -0000
|Read Time: 0 minutes
In case you missed it, check out the first part of this blog series for some background on the openmanage Ansible collection by Dell. In this post, we’ll take a look at template based deployment in OME driving from Ansible.
Template based deployment
Templates in OME are a great way to define the exact configuration that you would like to replicate on a group of servers. You can collect devices into multiple groups based on the workload profile and apply templates on these groups to achieve identical configurations based on security, performance, and other considerations.
To retrieve template information, you can use the dellemc.openmanage.ome_template_info module to query templates based on a variety of system_query _options. You can pass filter parameters as shown here:
- name: Get filtered template info based on name. dellemc.openmanage.ome_template_info: hostname: "{{ hostname }}" username: "{{ username }}" password: "{{ password }}" validate_certs: no system_query_options: filter: "Name eq 'empty_template'" register: template_info - name: print template info debug: msg: "{{template_info}}"
Template creation
One way to create a template is by using an existing device configuration. You can also create a template by cloning an existing template and then modifying the parameters as necessary. Following are the Ansible tasks for each respective method:
- name: Create a template from a reference device. dellemc.openmanage.ome_template: hostname: "{{ hostname }}" username: "{{ username }}" password: "{{ password }}" validate_certs: no device_service_tag: "{{device_service_tag}}" attributes: Name: "{{device_service_tag}}-template" Description: "ideal Template description" - name: Clone a template dellemc.openmanage.ome_template: hostname: "{{ hostname }}" username: "{{ username }}" password: "{{ password }}" validate_certs: no command: "clone" template_name: "empty_template" attributes: Name: "deploy_clone" delegate_to: localhost
Template Attribute list
Very often as part of day-2 operations you may have to change a set of attributes, which can be difficult given that a template is a very detailed object with thousands of parameters. To see what parameters are available to modify in a template, we must get the complete list of parameters using a REST API call. In the following example, we first establish an API connection and then make a call to api/TemplateService/Templates(11)/Views(1)/AttributeViewDetails. We then store this information in a JSON file for further exploration and parsing:
- name: Get PowerScale API Session Token ansible.builtin.uri: url: "https://{{ hostname }}/api/SessionService/Sessions" method: post body_format: json validate_certs: false status_code: 200,201 body: | { "UserName": "{{ username }}", "Password": "{{ password }}", "SessionType":"API" } register: api_response tags: "api-call" - name: Store API auth token ansible.builtin.set_fact: ome_auth_token: "{{ api_response.x_auth_token }}" tags: "api-call" - name: Get attribute details uri: url: "https://{{ hostname }}/api/TemplateService/Templates(11)/Views(1)/AttributeViewDetails" validate_certs: false method: get #body_format: json #body: | # {"privileges":{{ admin_priv.json.privileges }}} headers: X-Auth-Token: "{{ ome_auth_token }}" status_code: 200,201,204,409 register: api_output - name: Save device_info to a file copy: content: "{{ api_output | to_nice_json }}" dest: "./output-json/api_output.json"
Once we have the JSON file with the complete set of attributes, we can find the exact attribute we want to modify. Given the attribute JSON file can span thousands of lines, we can use a simple python script to run a quick search of the attributes file based on keywords. Here are a few lines that can retrieve all the attributes containing Email:
import json with open('./output-json/api_output.json') as f: data = json.load(f) for item in data['json']['AttributeGroups'][0]['SubAttributeGroups']: if item['DisplayName'].find("Email") >-1: print('\n') print(item['DisplayName']) print('-------------------------') for subitem in item['Attributes']: print(subitem['DisplayName'])
Once we have the attribute that needs to be modified, we can use the ome_template module with (a) command set to modify and (b) attribute name and value set as follows:
- name: Modify template dellemc.openmanage.ome_template: hostname: "{{ hostname }}" username: "{{ username }}" password: "{{ password }}" validate_certs: no command: "modify" template_name: "deploy_clone" attributes: Attributes: - DisplayName: 'iDRAC, RAC Email Alert, EmailAlert 1 Email Alert Address' Value: "world123@test.com"
Device grouping and deployment
To apply templates to multiple devices, we can create device groups and then apply the deployment template for the entire group. To add devices to a group, you can create an array of devices. Here, I am passing the entire set of devices that I queried using ome_device_info:
- name: Retrieve basic inventory of all devices. dellemc.openmanage.ome_device_info: hostname: "{{ hostname }}" username: "{{ username }}" password: "{{ password }}" validate_certs: no register: device_info_result - name: get all service tags set_fact: service_tags: "{{ service_tags + [item.DeviceServiceTag] }}" loop: "{{ device_info_result.device_info.value }}" no_log: true - name: Create a device group dellemc.openmanage.ome_groups: hostname: "{{ hostname }}" username: "{{ username }}" password: "{{ password }}" validate_certs: no name: "demo-group-all" - name: Add devices to a static device group dellemc.openmanage.ome_device_group: hostname: "{{ hostname }}" username: "{{ username }}" password: "{{ password }}" validate_certs: no name: "demo-group-all" device_service_tags: "{{service_tags}}"
Now, we are ready to deploy our template to the device group we created using the same ome_template module but with command set to deploy:
- name: Deploy template on groups dellemc.openmanage.ome_template: hostname: "{{ hostname }}" username: "{{ username }}" password: "{{ password }}" validate_certs: no command: "deploy" template_name: "deploy_clone" device_group_names: - "deploy_group"
You can watch the following video to see in depth how the different steps of the workflow are run:
Conclusion
To recap, we’ve covered how to create templates, query and find the available attributes we can modify, and then modify them in a template, as well as how to group devices and deploy templates to those groups. You can find the code mentioned in this blog on GitHub as part of this Automation examples repo.
Author: Parasar Kodati, Engineering Technologist, Dell ISG
Related Blog Posts
Dell OpenManage Enterprise Operations with Ansible Part 3: Compliance, Reporting, and Remediation
Fri, 08 Dec 2023 15:37:49 -0000
|Read Time: 0 minutes
In case you missed it, check out the first post of this series for some background information on the openmanage Ansible collection by Dell and inventory management, as well as the second post to learn more about template-based deployment. In this blog, we’ll take a look at automating compliance and remediation workflows in Dell OpenManage Enterprise (OME) with Ansible.
Compliance baselines
Compliance baselines in OME are reports that show the ‘delta’ or difference between the specified desired configuration and the actual configuration of the various devices in the inventory. The desired configuration is specified as a compliance template, which can be cloned from either a deployment template or a device using the ome_template covered in the deployment section of this series. Following are task examples for creating compliance templates:
- name: Create a compliance template from deploy template dellemc.openmanage.ome_template: hostname: "{{ hostname }}" username: "{{ username }}" password: "{{ password }}" validate_certs: no command: "clone" template_name: "email_deploy_template" template_view_type: "Compliance" attributes: Name: "email_compliance_template"
- name: Create a compliance template from reference device dellemc.openmanage.ome_template: hostname: "{{ hostname }}" username: "{{ username }}" password: "{{ password }}" validate_certs: no command: "create" device_service_tag: - "SVTG123" template_view_type: "Compliance" attributes: Name: "Configuration Compliance" Description: "Configuration Compliance Template" Fqdds: "BIOS"
Once we have the template ready, we can create the baseline, which is the main step where OME compares the template configuration to devices. Devices can be specified as a list or a device group. Depending on the number of devices, this step can be time-consuming. The following code uses a device group that has already been created, as shown in part 2 of this OME blog series:
- name: Create a configuration compliance baseline using an existing template dellemc.openmanage.ome_configuration_compliance_baseline: hostname: "{{ hostname }}" username: "{{ username }}" password: "{{ password }}" validate_certs: no command: create template_name: "email_compliance_template" description: "SNMP Email setting" names: "baseline_email" device_group_names: demo-group-all
Once the baseline task is run, we can retrieve the results, store them in a variable, and write the contents to a file for further analysis:
- name: Retrieve the compliance report of all of the devices in the specified configuration compliance baseline. dellemc.openmanage.ome_configuration_compliance_info: hostname: "{{ hostname }}" username: "{{ username }}" password: "{{ password }}" validate_certs: no baseline: "baseline_email" register: compliance_report delegate_to: localhost
- name: store the variable to json copy: content: "{{ compliance_report | to_nice_json }}" dest: "./output-json/compliance_report.json" delegate_to: localhost
Once the compliance details are stored in a variable, we can always extract details from it, like the list of non-compliant devices shown here:
- name: Extract service tags of devices with highest level compliance status set_fact: non_compliant_devices: "{{ non_compliant_devices | default([]) + [device.Id] }}" loop: "{{ compliance_report.compliance_info }}" loop_control: loop_var: device when: device.ComplianceStatus > 1 no_log: true
Remediatation
The remediation task brings all devices to a desired template configuration, much like the template deployment job. For remediation, we use the same baseline module with command set to remediate and pass all devices we would like to remediate, as well as the list of devices that are non-compliant:
- name: Remediate a specified non-complaint devices to a configuration compliance baseline using device IDs # noqa: args[module] dellemc.openmanage.ome_configuration_compliance_baseline: hostname: "{{ hostname }}" username: "{{ username }}" password: "{{ password }}" validate_certs: no command: "remediate" names: "baseline_email" device_ids: "{{ non_compliant_devices }}" when: "non_compliant_devices | length > 0" delegate_to: localhost
Watch the following video to see in-depth how the different steps of this workflow are run:
Conclusion
To recap, we’ve covered the creation of compliance templates and running baseline checks against your PowerEdge server inventory. We then saw how to retrieve detailed compliance reports and parse them in Ansible for further analysis. Finally, using the OME baseline Ansible, we ran a remediation job to correct any configuration drift in non-compliant devices. Don’t forget to check out the detailed documentation for openmanage Ansible modules including both OME and iDRAC/redfish modules and roles, as well as the complete code examples used here in this GitHub repository.
Author: Parasar Kodati, Engineering Technologist, Dell ISG
Dell OpenManage Enterprise Operations with Ansible Part 1: Inventory Management
Tue, 14 Nov 2023 14:36:09 -0000
|Read Time: 0 minutes
The OpenManage collection
Dell OpenManage Enterprise (OME) is a powerful fleet management tool for managing and monitoring Dell PowerEdge server infrastructure. Very recently, Dell announced OME 4.0, complete with a litany of new functionality that my colleague Mark detailed in another blog. Here, we'll explore how to automate inventory management of devices managed by OME using Ansible.
Prerequisites
Before we get started, ensure you have Ansible and Python installed on your system. Additionally, you will need to install Dell’s openmanage Ansible collection from Ansible Galaxy using the following command:
ansible-galaxy collection install dellemc.openmanage
The source code and examples for the openmanage collection can be found on GitHub as well. Note that this collection includes modules and roles for iDRAC/Redfish interfaces as well as modules for OpenManage Enterprise with complete fleet management workflows. In this blog, we will look at examples from the OME modules within the collection.
Figure 1. Dell openmanage ansible modules on GitHub
Inventory management workflows
Inventory management typically involves gathering details like the different devices under management, their health information, and so on. The dellemc.openmanage.ome_device_info is the optimal module for collecting the most information. Let’s dig into some tasks to get this information.
Retrieve basic inventory
This task retrieves basic inventory information for all devices managed by OME:
- name: Retrieve basic inventory of all devices dellemc.openmanage.ome_device_info: hostname: "{{ hostname }}" username: "{{ username }}" password: "{{ password }}" validate_certs: no register: device_info_result
Once we have the output of this captured in a variable like device_info_result, we can drill down into the object to retrieve data like the number of servers and their service tags and print such information using the debug task:
- name: Device count debug: msg: "Number of devices: {{ device_info_result.device_info.value | length }}"
- name: get all service tags set_fact: service_tags: "{{ service_tags + [item.DeviceServiceTag] }}" loop: "{{ device_info_result.device_info.value }}" no_log: true
- name: List service tags of devices debug: msg: "{{ service_tags }}"
Note that device_info_result is a huge object. To view all the information that is available to extract, write the contents of the variable to a JSON file:
- name: Save device_info to a file copy: content: "{{ device_info_result | to_nice_json }}" dest: "./output-json/device_info_result.json"
Subsystem health
Subsystem health information is another body of information that is extremely granular. This information is not part of the default module task. To get this data, we need to explicitly set the fact_subsystem option to subsystem_health. Following is the task to retrieve subsystem health information for devices identified by their service tags. We pass the entire array of service tags to get all the information at once:
- name: Retrieve subsystem health of specified devices identified by service tags. dellemc.openmanage.ome_device_info: hostname: "{{ hostname }}" username: "{{ username }}" password: "{{ password }}" validate_certs: no fact_subset: "subsystem_health" system_query_options: device_service_tag: "{{ service_tags }}" register: health_info_result
Using the register directive, we loaded the subsystem health information into the variable health_info_result. Once again, we recommend writing this information to a JSON file using the following code in order to see the level of granularity that you can extract:
- name: Save device health info to a file copy: content: "{{ health_info_result | to_nice_json }}" dest: "./output-json/health_info_result.json"
To identify device health issues, we loop through all the devices with the service_tags variable and check if there are any faults reported for each device. When faults are found, we store the fault information into a dictionary variable, shown as the inventory_issues variable in the following code. The dictionary variable has three fields: service tag, fault summary, and the fault list. Note that the fault list itself is an array containing all the faults for the device:
- name: Gather information for devices with issues set_fact: inventory_issues: > {{ inventory_issues + [{ 'service_tag': item, 'fault_summary': health_info_result['device_info']['device_service_tag'][service_tags[index]]['value'] | json_query('[?FaultSummaryList].FaultSummaryList[]'), 'fault_list': health_info_result['device_info']['device_service_tag'][service_tags[index]]['value'] | json_query('[?FaultList].FaultList[]') }] }} loop: "{{ service_tags }}" when: " (health_info_result['device_info']['device_service_tag'][service_tags[index]]['value'] | json_query('[?FaultList].FaultList[]') | length) > 0" loop_control: index_var: index no_log: true
In the next task, we loop through the devices with issues and gather more detailed fault information for each. The tasks to perform this extraction are included in an external task file named device_issues.yml which is run for every member of the inventory_issues dictionary. Note that we are passing device_item and device_index as variables for each iteration of device_issues.yml:
- name: Gather fault details include_tasks: device_issues.yml vars: device_item: "{{ item }}" device_index: "{{ index }}" loop: "{{ inventory_issues }}" loop_control: index_var: index no_log: true
Within the device_issues.yml, we first initialize a dictionary variable that can gather information about the faults for the device. The variable captures the subsystem, fault message, and the recommended action:
- name: Initialize specifics structure set_fact: current_device: { 'service_tag': '', 'subsystem': [], 'Faults': [], 'Recommendations':[] }
We loop through all the faults for the device and populate the objects of the dictionary variable:
- name: Assign fault specifics set_fact: current_device: service_tag: "{{ device_item.service_tag }}" Faults: "{{ current_device.Faults + [fault.Message] }}" Recommendations: "{{ current_device.Recommendations + [fault.RecommendedAction] }}" loop: "{{ device_item.fault_list }}" loop_control: loop_var: fault when: device_item.fault_list is defined no_log: true
We then append to a global variable that is aggregating the information for all the devices:
- name: Append current device to all_faults set_fact: fault_details: "{{ fault_details + [current_device] }}"
Back to the main YML script, once we have all the information captured in fault_details, we can print the information we need to store to a file:
- name: Print fault details debug: msg: "Fault details: {{ item.Faults }}" loop: "{{ fault_details }}" loop_control: label: "{{ item.service_tag }}"
- name: Print recommendations debug: msg: "Recommended actions: {{ item.Recommendations }}" loop: "{{ fault_details }}" loop_control: label: "{{ item.service_tag }}"
Check out the following video to see how the different steps of the workflow are run:
Conclusion
To recap, we looked at the various information gathering tasks within inventory management of a large PowerEdge server footprint. Note that I used health information objects to demonstrate how to drill down to find the information you need, however you can do this with any fact subset that can retrieved using the dellemc.openmanage.ome_device_info module. You can find the code from this blog on GitHub as part of this Automation examples repo.
Author: Parasar Kodati, Engineering Technologist, Dell ISG