Skip to content

Commit

Permalink
Add os_ratings role for registering resources in Cloudkitty
Browse files Browse the repository at this point in the history
  • Loading branch information
markgoddard committed Mar 19, 2024
1 parent 147f216 commit 0290447
Show file tree
Hide file tree
Showing 7 changed files with 309 additions and 0 deletions.
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,4 +15,5 @@ This collection includes content for interacting with OpenStack clouds.
- os_openstackclient
- os_openstacksdk
- os_projects
- os_ratings
- os_volumes
88 changes: 88 additions & 0 deletions roles/os_ratings/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
OpenStack Cloudkitty Ratings
============================

This role can be used to register ratings in OpenStack Cloudkitty.

Requirements
------------

The OpenStack Cloudkitty API should be accessible from the target host.

Role Variables
--------------

`os_ratings_venv` is a path to a directory in which to create a
virtual environment.

`os_ratings_upper_constraints_file` is a file or URL containing Python
upper constraints.

`os_ratings_environment` is a dict of environment variables for use with
OpenStack CLI. Default is empty.

`os_ratings_hashmap_field_mappings` is a list of mappings associated with a
field. Each item is a dict with the following fields:
* `service`
* `name`
* `mappings`
The mappings field is a list, where each item is a dict with the following
fields:
* `value`
* `cost`
* `group` (optional)
* `type`

`os_ratings_hashmap_service_mappings` is a list of mappings not associated with
a field. Each item is a dict with the following fields:
* `service`
* `cost`
* `group` (optional)
* `type`

Dependencies
------------

This role depends on the `stackhpc.openstack.os_openstackclient` role.

Example Playbook
----------------

The following playbook registers a Cloudkitty flavor field with two mappings
for different Nova flavors. It also registers a service mapping based on the
size of images stored in Glance.

```
---
- name: Ensure Cloudkitty ratings are registered
hosts: os-client
tasks:
- import_role:
name: stackhpc.openstack.os_ratings
vars:
os_ratings_venv: "~/os-ratings-venv"
os_ratings_environment:
OS_AUTH_URL: "{{ lookup('env', 'OS_AUTH_URL') }}"
...
os_ratings_hashmap_field_mappings:
- service: instance
name: flavor_id
mappings:
- value: small
cost: 1.0
group: instance_uptime_flavor_id
type: flat
- value: large
cost: 2.0
group: instance_uptime_flavor_id
type: flat
os_ratings_hashmap_service_mappings:
- service: image.size
cost: 0.1
group: volume_ceph
type: flat
```

Author Information
------------------

- Mark Goddard (<[email protected]>)
43 changes: 43 additions & 0 deletions roles/os_ratings/defaults/main.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
---
# Path to a directory in which to create a virtualenv.
os_ratings_venv:
# Upper constraints file for installation of Python dependencies.
os_ratings_upper_constraints_file: https://releases.openstack.org/constraints/upper/2023.1

# Environment variables for use with OpenStack CLI.
os_ratings_environment: {}
# Mappings associated with a field.
# Each item is a dict with the following fields:
# * service
# * name
# * mappings
# The mappings field is a list, where each item is a dict with the following fields:
# * value
# * cost
# * group (optional)
# * type
# For example, for per-instance rating:
# - service: instance
# name: flavor_id
# mappings:
# - value: small
# cost: 1.0
# group: instance_uptime_flavor_id
# type: flat
# - value: large
# cost: 2.0
# group: instance_uptime_flavor_id
# type: flat
os_ratings_hashmap_field_mappings: []
# Mappings not associated with a field.
# Each item is a dict with the following fields:
# * service
# * cost
# * group (optional)
# * type
# For example, for image image storage (MB)
# - service: image.size
# cost: 0.1
# group: volume_ceph
# type: flat
os_ratings_hashmap_service_mappings: []
5 changes: 5 additions & 0 deletions roles/os_ratings/meta/main.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
dependencies:
- role: stackhpc.openstack.os_openstackclient
os_openstackclient_venv: "{{ os_ratings_venv }}"
os_openstackclient_upper_constraints_file: "{{ os_ratings_upper_constraints_file | default(None) }}"
40 changes: 40 additions & 0 deletions roles/os_ratings/tasks/field-mappings.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
---
# Task file for a single field and its mappings.

- name: Create hashmap field
ansible.builtin.command: >
{{ openstack }} rating hashmap field create {{ service_id }} {{ field.name }}
when: field.name not in fields | map(attribute='Name') | list
changed_when: true

# List again to get ID of created mapping.
- name: List hashmap fields
ansible.builtin.command: >
{{ openstack }} rating hashmap field list -f json {{ service_id }}
register: hashmap_field
changed_when: false

- name: List hashmap field mappings
vars:
field_id: "{{ (hashmap_field.stdout | from_json | selectattr('Name', 'equalto', field.name) | first)['Field ID'] }}"
ansible.builtin.command: >
{{ openstack }} rating hashmap mapping list -f json --field-id {{ field_id }}
register: hashmap_mappings
changed_when: false

- name: Create hashmap field mappings
vars:
field_id: "{{ (hashmap_field.stdout | from_json | selectattr('Name', 'equalto', field.name) | first)['Field ID'] }}"
group_id: >-
{{ (hashmap_groups.stdout | from_json | selectattr('Name', 'equalto', item.group) | first)['Group ID'] | default('') if item.group is defined else '' }}
ansible.builtin.command: >
{{ openstack }} rating hashmap mapping create
{{ item.cost }}
--field-id {{ field_id }}
--value {{ item.value }}
{% if group_id | length > 0 %}--group-id {{ group_id }}{% endif %}
--type {{ item.type }}
loop: "{{ field.mappings }}"
# Condition could be better, but should work with current values.
when: item.value not in (hashmap_mappings.stdout | from_json | map(attribute='Value') | list)
changed_when: true
22 changes: 22 additions & 0 deletions roles/os_ratings/tasks/main.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
---
- name: Ensure Cloudkitty client is installed # noqa package-latest
ansible.builtin.pip:
name:
- python-cloudkittyclient
state: latest
extra_args: "{% if os_ratings_upper_constraints_file %}-c {{ os_ratings_upper_constraints_file }}{% endif %}"
virtualenv: "{{ os_ratings_venv }}"
run_once: true

- name: Set a fact about the Ansible python interpreter
ansible.builtin.set_fact:
old_ansible_python_interpreter: "{{ ansible_python_interpreter | default('/usr/bin/python3') }}"

- name: Import ratings.yml
ansible.builtin.import_tasks: ratings.yml
vars:
ansible_python_interpreter: "{{ os_ratings_venv ~ '/bin/python' if os_ratings_venv != None else old_ansible_python_interpreter }}"
openstack: "{{ os_ratings_venv ~ '/bin/' if os_ratings_venv else '' }}openstack"
os_ratings_hashmap_field_mapping_services: "{{ os_ratings_hashmap_field_mappings | map(attribute='service') | list }}"
os_ratings_hashmap_service_mapping_services: "{{ os_ratings_hashmap_service_mappings | map(attribute='service') | list }}"
environment: "{{ os_ratings_environment }}"
110 changes: 110 additions & 0 deletions roles/os_ratings/tasks/ratings.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,110 @@
---
- name: List modules
ansible.builtin.command: >
{{ openstack }} rating module list -f json
register: modules
changed_when: false

- name: Enable hashmap module
ansible.builtin.command: >
{{ openstack }} rating module enable hashmap
when: not (modules.stdout | from_json | selectattr('Module', 'equalto', 'hashmap') | first)['Enabled'] | bool
changed_when: true

- name: List hashmap services
ansible.builtin.command: >
{{ openstack }} rating hashmap service list -f json
register: hashmap_services
changed_when: false

- name: Create hashmap services
vars:
existing_services: "{{ hashmap_services.stdout | from_json | map(attribute='Name') | list }}"
ansible.builtin.command: >
{{ openstack }} rating hashmap service create {{ item }}
loop: "{{ (os_ratings_hashmap_field_mapping_services + os_ratings_hashmap_service_mapping_services) | unique | list }}"
when: item not in existing_services
changed_when: true

- name: List hashmap groups
ansible.builtin.command: >
{{ openstack }} rating hashmap group list -f json
register: hashmap_groups
changed_when: false

- name: Create hashmap groups
vars:
existing_groups: "{{ hashmap_groups.stdout | from_json | map(attribute='Name') | list }}"
field_mapping_groups: "{{ query('subelements', os_ratings_hashmap_field_mappings, 'mappings') | map(attribute='1.group') | select('defined') | list }}"
service_mapping_groups: "{{ os_ratings_hashmap_service_mappings | map(attribute='group') | select('defined') | list }}"
ansible.builtin.command: >
{{ openstack }} rating hashmap group create {{ item }}
loop: "{{ (field_mapping_groups + service_mapping_groups) | unique | list }}"
when:
- item is not none and item | length > 0
- item not in existing_groups
changed_when: true

# List again to get IDs of created services.
- name: List hashmap services
ansible.builtin.command: >
{{ openstack }} rating hashmap service list -f json
register: hashmap_services
changed_when: false

# List again to get IDs of created groups.
- name: List hashmap groups
ansible.builtin.command: >
{{ openstack }} rating hashmap group list -f json
register: hashmap_groups
changed_when: false

- name: List hashmap fields
vars:
service_id: "{{ (hashmap_services.stdout | from_json | selectattr('Name', 'equalto', item) | first)['Service ID'] }}"
ansible.builtin.command: >
{{ openstack }} rating hashmap field list {{ service_id }} -f json
loop: "{{ os_ratings_hashmap_field_mapping_services }}"
register: hashmap_fields
changed_when: false

# Field mappings

- name: Include field mappings
ansible.builtin.include_tasks: field-mappings.yml
vars:
fields_result: "{{ hashmap_fields.results | selectattr('item', 'equalto', field.service) | first }}"
fields: "{{ fields_result.stdout | from_json }}"
service_id: "{{ (hashmap_services.stdout | from_json | selectattr('Name', 'equalto', field.service) | first)['Service ID'] }}"
loop: "{{ os_ratings_hashmap_field_mappings }}"
loop_control:
loop_var: field

# Service mappings

- name: List hashmap service mappings
vars:
service_id: "{{ (hashmap_services.stdout | from_json | selectattr('Name', 'equalto', item) | first)['Service ID'] }}"
ansible.builtin.command: >
{{ openstack }} rating hashmap mapping list -f json --service-id {{ service_id }}
loop: "{{ os_ratings_hashmap_service_mapping_services }}"
register: hashmap_mappings
changed_when: false

- name: Create hashmap service mappings
vars:
mappings_result: "{{ hashmap_mappings.results | selectattr('item', 'equalto', item.service) | first }}"
mappings: "{{ mappings_result.stdout | from_json }}"
service_id: "{{ (hashmap_services.stdout | from_json | selectattr('Name', 'equalto', item.service) | first)['Service ID'] }}"
group_id: "{{ (hashmap_groups.stdout | from_json | selectattr('Name', 'equalto', item.group) | first)['Group ID'] | default('') if item.group is defined else
'' }}"
ansible.builtin.command: >
{{ openstack }} rating hashmap mapping create
{{ item.cost }}
--service-id {{ service_id }}
{% if group_id | length > 0 %}--group-id {{ group_id }}{% endif %}
--type {{ item.type }}
loop: "{{ os_ratings_hashmap_service_mappings }}"
# Condition could be better, but should work with current values.
when: mappings | length == 0
changed_when: true

0 comments on commit 0290447

Please sign in to comment.