Skip to content

Commit 0290447

Browse files
committed
Add os_ratings role for registering resources in Cloudkitty
1 parent 147f216 commit 0290447

File tree

7 files changed

+309
-0
lines changed

7 files changed

+309
-0
lines changed

README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,4 +15,5 @@ This collection includes content for interacting with OpenStack clouds.
1515
- os_openstackclient
1616
- os_openstacksdk
1717
- os_projects
18+
- os_ratings
1819
- os_volumes

roles/os_ratings/README.md

Lines changed: 88 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,88 @@
1+
OpenStack Cloudkitty Ratings
2+
============================
3+
4+
This role can be used to register ratings in OpenStack Cloudkitty.
5+
6+
Requirements
7+
------------
8+
9+
The OpenStack Cloudkitty API should be accessible from the target host.
10+
11+
Role Variables
12+
--------------
13+
14+
`os_ratings_venv` is a path to a directory in which to create a
15+
virtual environment.
16+
17+
`os_ratings_upper_constraints_file` is a file or URL containing Python
18+
upper constraints.
19+
20+
`os_ratings_environment` is a dict of environment variables for use with
21+
OpenStack CLI. Default is empty.
22+
23+
`os_ratings_hashmap_field_mappings` is a list of mappings associated with a
24+
field. Each item is a dict with the following fields:
25+
* `service`
26+
* `name`
27+
* `mappings`
28+
The mappings field is a list, where each item is a dict with the following
29+
fields:
30+
* `value`
31+
* `cost`
32+
* `group` (optional)
33+
* `type`
34+
35+
`os_ratings_hashmap_service_mappings` is a list of mappings not associated with
36+
a field. Each item is a dict with the following fields:
37+
* `service`
38+
* `cost`
39+
* `group` (optional)
40+
* `type`
41+
42+
Dependencies
43+
------------
44+
45+
This role depends on the `stackhpc.openstack.os_openstackclient` role.
46+
47+
Example Playbook
48+
----------------
49+
50+
The following playbook registers a Cloudkitty flavor field with two mappings
51+
for different Nova flavors. It also registers a service mapping based on the
52+
size of images stored in Glance.
53+
54+
```
55+
---
56+
- name: Ensure Cloudkitty ratings are registered
57+
hosts: os-client
58+
tasks:
59+
- import_role:
60+
name: stackhpc.openstack.os_ratings
61+
vars:
62+
os_ratings_venv: "~/os-ratings-venv"
63+
os_ratings_environment:
64+
OS_AUTH_URL: "{{ lookup('env', 'OS_AUTH_URL') }}"
65+
...
66+
os_ratings_hashmap_field_mappings:
67+
- service: instance
68+
name: flavor_id
69+
mappings:
70+
- value: small
71+
cost: 1.0
72+
group: instance_uptime_flavor_id
73+
type: flat
74+
- value: large
75+
cost: 2.0
76+
group: instance_uptime_flavor_id
77+
type: flat
78+
os_ratings_hashmap_service_mappings:
79+
- service: image.size
80+
cost: 0.1
81+
group: volume_ceph
82+
type: flat
83+
```
84+
85+
Author Information
86+
------------------
87+
88+
- Mark Goddard (<[email protected]>)

roles/os_ratings/defaults/main.yml

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
---
2+
# Path to a directory in which to create a virtualenv.
3+
os_ratings_venv:
4+
# Upper constraints file for installation of Python dependencies.
5+
os_ratings_upper_constraints_file: https://releases.openstack.org/constraints/upper/2023.1
6+
7+
# Environment variables for use with OpenStack CLI.
8+
os_ratings_environment: {}
9+
# Mappings associated with a field.
10+
# Each item is a dict with the following fields:
11+
# * service
12+
# * name
13+
# * mappings
14+
# The mappings field is a list, where each item is a dict with the following fields:
15+
# * value
16+
# * cost
17+
# * group (optional)
18+
# * type
19+
# For example, for per-instance rating:
20+
# - service: instance
21+
# name: flavor_id
22+
# mappings:
23+
# - value: small
24+
# cost: 1.0
25+
# group: instance_uptime_flavor_id
26+
# type: flat
27+
# - value: large
28+
# cost: 2.0
29+
# group: instance_uptime_flavor_id
30+
# type: flat
31+
os_ratings_hashmap_field_mappings: []
32+
# Mappings not associated with a field.
33+
# Each item is a dict with the following fields:
34+
# * service
35+
# * cost
36+
# * group (optional)
37+
# * type
38+
# For example, for image image storage (MB)
39+
# - service: image.size
40+
# cost: 0.1
41+
# group: volume_ceph
42+
# type: flat
43+
os_ratings_hashmap_service_mappings: []

roles/os_ratings/meta/main.yml

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
dependencies:
3+
- role: stackhpc.openstack.os_openstackclient
4+
os_openstackclient_venv: "{{ os_ratings_venv }}"
5+
os_openstackclient_upper_constraints_file: "{{ os_ratings_upper_constraints_file | default(None) }}"
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
---
2+
# Task file for a single field and its mappings.
3+
4+
- name: Create hashmap field
5+
ansible.builtin.command: >
6+
{{ openstack }} rating hashmap field create {{ service_id }} {{ field.name }}
7+
when: field.name not in fields | map(attribute='Name') | list
8+
changed_when: true
9+
10+
# List again to get ID of created mapping.
11+
- name: List hashmap fields
12+
ansible.builtin.command: >
13+
{{ openstack }} rating hashmap field list -f json {{ service_id }}
14+
register: hashmap_field
15+
changed_when: false
16+
17+
- name: List hashmap field mappings
18+
vars:
19+
field_id: "{{ (hashmap_field.stdout | from_json | selectattr('Name', 'equalto', field.name) | first)['Field ID'] }}"
20+
ansible.builtin.command: >
21+
{{ openstack }} rating hashmap mapping list -f json --field-id {{ field_id }}
22+
register: hashmap_mappings
23+
changed_when: false
24+
25+
- name: Create hashmap field mappings
26+
vars:
27+
field_id: "{{ (hashmap_field.stdout | from_json | selectattr('Name', 'equalto', field.name) | first)['Field ID'] }}"
28+
group_id: >-
29+
{{ (hashmap_groups.stdout | from_json | selectattr('Name', 'equalto', item.group) | first)['Group ID'] | default('') if item.group is defined else '' }}
30+
ansible.builtin.command: >
31+
{{ openstack }} rating hashmap mapping create
32+
{{ item.cost }}
33+
--field-id {{ field_id }}
34+
--value {{ item.value }}
35+
{% if group_id | length > 0 %}--group-id {{ group_id }}{% endif %}
36+
--type {{ item.type }}
37+
loop: "{{ field.mappings }}"
38+
# Condition could be better, but should work with current values.
39+
when: item.value not in (hashmap_mappings.stdout | from_json | map(attribute='Value') | list)
40+
changed_when: true

roles/os_ratings/tasks/main.yml

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
---
2+
- name: Ensure Cloudkitty client is installed # noqa package-latest
3+
ansible.builtin.pip:
4+
name:
5+
- python-cloudkittyclient
6+
state: latest
7+
extra_args: "{% if os_ratings_upper_constraints_file %}-c {{ os_ratings_upper_constraints_file }}{% endif %}"
8+
virtualenv: "{{ os_ratings_venv }}"
9+
run_once: true
10+
11+
- name: Set a fact about the Ansible python interpreter
12+
ansible.builtin.set_fact:
13+
old_ansible_python_interpreter: "{{ ansible_python_interpreter | default('/usr/bin/python3') }}"
14+
15+
- name: Import ratings.yml
16+
ansible.builtin.import_tasks: ratings.yml
17+
vars:
18+
ansible_python_interpreter: "{{ os_ratings_venv ~ '/bin/python' if os_ratings_venv != None else old_ansible_python_interpreter }}"
19+
openstack: "{{ os_ratings_venv ~ '/bin/' if os_ratings_venv else '' }}openstack"
20+
os_ratings_hashmap_field_mapping_services: "{{ os_ratings_hashmap_field_mappings | map(attribute='service') | list }}"
21+
os_ratings_hashmap_service_mapping_services: "{{ os_ratings_hashmap_service_mappings | map(attribute='service') | list }}"
22+
environment: "{{ os_ratings_environment }}"

roles/os_ratings/tasks/ratings.yml

Lines changed: 110 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,110 @@
1+
---
2+
- name: List modules
3+
ansible.builtin.command: >
4+
{{ openstack }} rating module list -f json
5+
register: modules
6+
changed_when: false
7+
8+
- name: Enable hashmap module
9+
ansible.builtin.command: >
10+
{{ openstack }} rating module enable hashmap
11+
when: not (modules.stdout | from_json | selectattr('Module', 'equalto', 'hashmap') | first)['Enabled'] | bool
12+
changed_when: true
13+
14+
- name: List hashmap services
15+
ansible.builtin.command: >
16+
{{ openstack }} rating hashmap service list -f json
17+
register: hashmap_services
18+
changed_when: false
19+
20+
- name: Create hashmap services
21+
vars:
22+
existing_services: "{{ hashmap_services.stdout | from_json | map(attribute='Name') | list }}"
23+
ansible.builtin.command: >
24+
{{ openstack }} rating hashmap service create {{ item }}
25+
loop: "{{ (os_ratings_hashmap_field_mapping_services + os_ratings_hashmap_service_mapping_services) | unique | list }}"
26+
when: item not in existing_services
27+
changed_when: true
28+
29+
- name: List hashmap groups
30+
ansible.builtin.command: >
31+
{{ openstack }} rating hashmap group list -f json
32+
register: hashmap_groups
33+
changed_when: false
34+
35+
- name: Create hashmap groups
36+
vars:
37+
existing_groups: "{{ hashmap_groups.stdout | from_json | map(attribute='Name') | list }}"
38+
field_mapping_groups: "{{ query('subelements', os_ratings_hashmap_field_mappings, 'mappings') | map(attribute='1.group') | select('defined') | list }}"
39+
service_mapping_groups: "{{ os_ratings_hashmap_service_mappings | map(attribute='group') | select('defined') | list }}"
40+
ansible.builtin.command: >
41+
{{ openstack }} rating hashmap group create {{ item }}
42+
loop: "{{ (field_mapping_groups + service_mapping_groups) | unique | list }}"
43+
when:
44+
- item is not none and item | length > 0
45+
- item not in existing_groups
46+
changed_when: true
47+
48+
# List again to get IDs of created services.
49+
- name: List hashmap services
50+
ansible.builtin.command: >
51+
{{ openstack }} rating hashmap service list -f json
52+
register: hashmap_services
53+
changed_when: false
54+
55+
# List again to get IDs of created groups.
56+
- name: List hashmap groups
57+
ansible.builtin.command: >
58+
{{ openstack }} rating hashmap group list -f json
59+
register: hashmap_groups
60+
changed_when: false
61+
62+
- name: List hashmap fields
63+
vars:
64+
service_id: "{{ (hashmap_services.stdout | from_json | selectattr('Name', 'equalto', item) | first)['Service ID'] }}"
65+
ansible.builtin.command: >
66+
{{ openstack }} rating hashmap field list {{ service_id }} -f json
67+
loop: "{{ os_ratings_hashmap_field_mapping_services }}"
68+
register: hashmap_fields
69+
changed_when: false
70+
71+
# Field mappings
72+
73+
- name: Include field mappings
74+
ansible.builtin.include_tasks: field-mappings.yml
75+
vars:
76+
fields_result: "{{ hashmap_fields.results | selectattr('item', 'equalto', field.service) | first }}"
77+
fields: "{{ fields_result.stdout | from_json }}"
78+
service_id: "{{ (hashmap_services.stdout | from_json | selectattr('Name', 'equalto', field.service) | first)['Service ID'] }}"
79+
loop: "{{ os_ratings_hashmap_field_mappings }}"
80+
loop_control:
81+
loop_var: field
82+
83+
# Service mappings
84+
85+
- name: List hashmap service mappings
86+
vars:
87+
service_id: "{{ (hashmap_services.stdout | from_json | selectattr('Name', 'equalto', item) | first)['Service ID'] }}"
88+
ansible.builtin.command: >
89+
{{ openstack }} rating hashmap mapping list -f json --service-id {{ service_id }}
90+
loop: "{{ os_ratings_hashmap_service_mapping_services }}"
91+
register: hashmap_mappings
92+
changed_when: false
93+
94+
- name: Create hashmap service mappings
95+
vars:
96+
mappings_result: "{{ hashmap_mappings.results | selectattr('item', 'equalto', item.service) | first }}"
97+
mappings: "{{ mappings_result.stdout | from_json }}"
98+
service_id: "{{ (hashmap_services.stdout | from_json | selectattr('Name', 'equalto', item.service) | first)['Service ID'] }}"
99+
group_id: "{{ (hashmap_groups.stdout | from_json | selectattr('Name', 'equalto', item.group) | first)['Group ID'] | default('') if item.group is defined else
100+
'' }}"
101+
ansible.builtin.command: >
102+
{{ openstack }} rating hashmap mapping create
103+
{{ item.cost }}
104+
--service-id {{ service_id }}
105+
{% if group_id | length > 0 %}--group-id {{ group_id }}{% endif %}
106+
--type {{ item.type }}
107+
loop: "{{ os_ratings_hashmap_service_mappings }}"
108+
# Condition could be better, but should work with current values.
109+
when: mappings | length == 0
110+
changed_when: true

0 commit comments

Comments
 (0)