From 8d02c28e5a2ba034a47f85b87d025cf15d5dae1a Mon Sep 17 00:00:00 2001 From: Will Miller <willm@stackhpc.com> Date: Thu, 6 Sep 2018 16:53:20 +0000 Subject: [PATCH] Add Nova flavor registration --- ansible/deploy.yml | 3 ++ ansible/host_vars/localhost | 10 +++-- ansible/register_flavors.yml | 11 +++++ ansible/roles/nova-flavors/README.md | 21 +++++++++ ansible/roles/nova-flavors/defaults/main.yml | 25 +++++++++++ .../roles/nova-flavors/files/requirements.txt | 4 ++ ansible/roles/nova-flavors/tasks/main.yml | 44 +++++++++++++++++++ 7 files changed, 115 insertions(+), 3 deletions(-) create mode 100644 ansible/register_flavors.yml create mode 100644 ansible/roles/nova-flavors/README.md create mode 100644 ansible/roles/nova-flavors/defaults/main.yml create mode 100644 ansible/roles/nova-flavors/files/requirements.txt create mode 100644 ansible/roles/nova-flavors/tasks/main.yml diff --git a/ansible/deploy.yml b/ansible/deploy.yml index 0197bc3..f5a78ff 100644 --- a/ansible/deploy.yml +++ b/ansible/deploy.yml @@ -7,3 +7,6 @@ - name: Enrol nodes in Ironic import_playbook: enrol_nodes.yml + +- name: Register flavors in Nova + import_playbook: register_flavors.yml diff --git a/ansible/host_vars/localhost b/ansible/host_vars/localhost index dc100a4..bd4dc62 100644 --- a/ansible/host_vars/localhost +++ b/ansible/host_vars/localhost @@ -51,20 +51,24 @@ node_types: {} specs: [] # nova_flavors is a list of Nova flavors to be created. Each flavor must -# specify the resource class it is associated with. This resource class must -# be referenced in `specs`. -# +# specify the resource class it is associated with, as well as the Tenks node +# type whose hardware specs should be used. # For example: # # nova_flavors: # # Required. # - resource_class: my_rc +# # Required. +# node_type: type0 # # Defaults to `resource_class`. # name: my_flavor # # Optional, defaults to []. # required_traits: [] # # Optional, defaults to []. # forbidden_traits: [] +# # Extra key-value pairs to add to the flavor's specs. Optional, defaults +# # to {}. +# custom_specs: {} nova_flavors: [] # The Glance UUID of the image to use for the deployment kernel. diff --git a/ansible/register_flavors.yml b/ansible/register_flavors.yml new file mode 100644 index 0000000..f7a7314 --- /dev/null +++ b/ansible/register_flavors.yml @@ -0,0 +1,11 @@ +--- +- hosts: localhost + tasks: + - name: Register Nova flavors + include_role: + name: nova-flavors + vars: + flavors_virtualenv_path: "{{ virtualenv_path }}" + flavors_python_upper_constraints_url: >- + {{ python_upper_constraints_url }} + flavors: "{{ nova_flavors }}" diff --git a/ansible/roles/nova-flavors/README.md b/ansible/roles/nova-flavors/README.md new file mode 100644 index 0000000..88cb76e --- /dev/null +++ b/ansible/roles/nova-flavors/README.md @@ -0,0 +1,21 @@ +Nova Flavors +============ + +This role creates flavors in Nova. + +Requirements +------------ + +- *OS_\** environment variables for the OpenStack cloud in question present in + the shell environment. These can be sourced from an OpenStack RC file, for + example. + +Role Variables +-------------- + +- `flavors`: A list of dicts of details for flavors that are to be created. The + format for this is detailed in `defaults/main.yml`. +- `flavors_virtualenv_path`: The path to the virtualenv in which to install the + OpenStack clients. +- `flavors_python_upper_constraints_url`: The URL of the upper constraints file + to pass to pip when installing Python packages. diff --git a/ansible/roles/nova-flavors/defaults/main.yml b/ansible/roles/nova-flavors/defaults/main.yml new file mode 100644 index 0000000..7c6aeb5 --- /dev/null +++ b/ansible/roles/nova-flavors/defaults/main.yml @@ -0,0 +1,25 @@ +--- +# A list of Nova flavors to create. +# For example: +# +# flavors: +# # Required. +# - resource_class: my_rc +# # Required. +# node_type: type0 +# # Defaults to `resource_class`. +# name: my_flavor +# # Optional, defaults to []. +# required_traits: [] +# # Optional, defaults to []. +# forbidden_traits: [] +# # Extra key-value pairs to add to the flavor's specs. Optional, defaults +# # to {}. +# custom_specs: {} +flavors: [] + +# The path to the virtualenv in which to install the OpenStack clients. +flavors_virtualenv_path: +# The URL of the upper constraints file to pass to pip when installing Python +# packages. +flavors_python_upper_constraints_url: diff --git a/ansible/roles/nova-flavors/files/requirements.txt b/ansible/roles/nova-flavors/files/requirements.txt new file mode 100644 index 0000000..34eb0da --- /dev/null +++ b/ansible/roles/nova-flavors/files/requirements.txt @@ -0,0 +1,4 @@ +# This file contains the Python packages that are needed in the Tenks virtual +# env. + +openstacksdk>=0.17.2 # Apache diff --git a/ansible/roles/nova-flavors/tasks/main.yml b/ansible/roles/nova-flavors/tasks/main.yml new file mode 100644 index 0000000..aad5134 --- /dev/null +++ b/ansible/roles/nova-flavors/tasks/main.yml @@ -0,0 +1,44 @@ +--- + - name: Ensure Python requirements are installed + pip: + requirements: "{{ '/'.join([role_path, 'files', 'requirements.txt']) }}" + extra_args: >- + -c {{ flavors_python_upper_constraints_url }} + virtualenv: "{{ flavors_virtualenv_path }}" + + - name: Register Nova flavors + os_nova_flavor: + auth_type: password + name: "{{ item.name | default(item.resource_class) }}" + # FIXME(w-miller): don't necessarily assume the first disk? + disk: "{{ node_types[item.node_type].volumes.0.capacity | default('0') + | size_string_to_gb }}" + ram: "{{ node_types[item.node_type].memory_mb }}" + vcpus: "{{ node_types[item.node_type].vcpus }}" + # NOTE(w-miller): I'm not quite sure whether this is janky or beautiful. + # + # * Set hardware specs to zero here for scheduling purposes. + # * Add the resource class name. + # * Add required and forbidden traits. + # * Add any custom specs from the user. + extra_specs: >- + {{ { + "resources:DISK_GB": 0, + "resources:MEMORY_MB": 0, + "resources:VCPU": 0, + "resources:CUSTOM_" ~ ( + item.resource_class | upper + | regex_replace('[^A-Za-z0-9]', '_')): 1 + } + | combine(dict(item.required_traits | default([]) + | map('regex_replace', '(.*)', 'trait:\1') + | zip_longest([], fillvalue='required'))) + | combine(dict(item.forbidden_traits | default([]) + | map('regex_replace', '(.*)', 'trait:\1') + | zip_longest([], fillvalue='forbidden'))) + | combine(item.custom_specs | default({})) + }} + vars: + ansible_python_interpreter: >- + {{ '/'.join([flavors_virtualenv_path, 'bin', 'python']) }} + loop: "{{ flavors }}"