[ATMOSPHERE-308] Add Ironic (#1697) (#1725)
Manual cherry-pick for stable/2023.1
Reviewed-by: Mohammed Naser <mnaser@vexxhost.com>
diff --git a/roles/ironic/README.md b/roles/ironic/README.md
new file mode 100644
index 0000000..03dfba0
--- /dev/null
+++ b/roles/ironic/README.md
@@ -0,0 +1 @@
+# `ironic`
diff --git a/roles/ironic/defaults/main.yml b/roles/ironic/defaults/main.yml
new file mode 100644
index 0000000..158e06b
--- /dev/null
+++ b/roles/ironic/defaults/main.yml
@@ -0,0 +1,38 @@
+# Copyright (c) 2023 VEXXHOST, Inc.
+#
+# Licensed under the Apache License, Version 2.0 (the "License"); you may
+# not use this file except in compliance with the License. You may obtain
+# a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations
+# under the License.
+
+ironic_helm_release_name: ironic
+ironic_helm_chart_path: "../../charts/ironic/"
+ironic_helm_chart_ref: /usr/local/src/ironic
+
+ironic_helm_release_namespace: openstack
+ironic_helm_values: {}
+
+# List of annotations to apply to the Ingress
+ironic_ingress_annotations: {}
+
+# Ironic bare metal network used for PXE
+ironic_bare_metal_network_manage: true
+ironic_bare_metal_network_name: baremetal
+ironic_bare_metal_network_provider_physical_network: external
+ironic_bare_metal_network_provider_network_type: flat # vlan
+# ironic_bare_metal_network_provider_segmentation_id:
+ironic_bare_metal_subnet_name: baremetal
+ironic_bare_metal_subnet_cidr: 172.24.6.0/24
+
+# Ironic Python Agent images
+ironic_python_agent_deploy_kernel_name: ipa-centos9-zed.kernel
+ironic_python_agent_deploy_kernel_url: https://tarballs.opendev.org/openstack/ironic-python-agent/dib/files/ipa-centos9-stable-zed.kernel
+ironic_python_agent_deploy_ramdisk_name: ipa-centos9-zed.initramfs
+ironic_python_agent_deploy_ramdisk_url: https://tarballs.opendev.org/openstack/ironic-python-agent/dib/files/ipa-centos9-stable-zed.initramfs
diff --git a/roles/ironic/meta/main.yml b/roles/ironic/meta/main.yml
new file mode 100644
index 0000000..4140469
--- /dev/null
+++ b/roles/ironic/meta/main.yml
@@ -0,0 +1,34 @@
+# Copyright (c) 2023 VEXXHOST, Inc.
+#
+# Licensed under the Apache License, Version 2.0 (the "License"); you may
+# not use this file except in compliance with the License. You may obtain
+# a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations
+# under the License.
+
+galaxy_info:
+ author: VEXXHOST, Inc.
+ description: Ansible role for OpenStack Ironic
+ license: Apache-2.0
+ min_ansible_version: 5.5.0
+ platforms:
+ - name: Ubuntu
+ versions:
+ - focal
+
+dependencies:
+ - role: defaults
+ - role: openstack_helm_endpoints
+ vars:
+ openstack_helm_endpoints_chart: ironic
+ - role: openstacksdk
+ - role: vexxhost.kubernetes.upload_helm_chart
+ vars:
+ upload_helm_chart_src: "{{ ironic_helm_chart_path }}"
+ upload_helm_chart_dest: "{{ ironic_helm_chart_ref }}"
diff --git a/roles/ironic/tasks/main.yml b/roles/ironic/tasks/main.yml
new file mode 100644
index 0000000..09cc9c6
--- /dev/null
+++ b/roles/ironic/tasks/main.yml
@@ -0,0 +1,96 @@
+# Copyright (c) 2022 VEXXHOST, Inc.
+#
+# Licensed under the Apache License, Version 2.0 (the "License"); you may
+# not use this file except in compliance with the License. You may obtain
+# a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations
+# under the License.
+
+- name: Create bare metal network
+ when: ironic_bare_metal_network_manage | bool
+ ansible.builtin.import_tasks: network/create.yml
+
+- name: Lookup existing bare metal network
+ when: not (ironic_bare_metal_network_manage | bool)
+ ansible.builtin.import_tasks: network/lookup.yml
+
+# - name: Create bare metal network ports
+# # noqa: args[module]
+# openstack.cloud.port:
+# cloud: atmosphere
+# name: "ironic-{{ inventory_hostname_short }}"
+# device_owner: ironic:conductor
+# network: "{{ ironic_bare_metal_network.id }}"
+# fixed_ips: >-
+# {{
+# [
+# {
+# "ip_address": ironic_bare_metal_network_ip
+# }
+# ]
+# if ironic_bare_metal_network_ip is defined else omit
+# }}
+
+# - name: Set binding for ports
+# changed_when: false
+# ansible.builtin.shell: |
+# openstack port set \
+# --host {{ ansible_fqdn }} \
+# ironic-{{ inventory_hostname_short }}
+# environment:
+# OS_CLOUD: atmosphere
+
+- name: Upload images
+ ansible.builtin.include_role:
+ name: glance_image
+ loop:
+ - name: "{{ ironic_python_agent_deploy_kernel_name }}"
+ url: "{{ ironic_python_agent_deploy_kernel_url }}"
+ format: aki
+ - name: "{{ ironic_python_agent_deploy_ramdisk_name }}"
+ url: "{{ ironic_python_agent_deploy_ramdisk_url }}"
+ format: ari
+ vars:
+ glance_image_name: "{{ item.name }}"
+ glance_image_url: "{{ item.url }}"
+ glance_image_container_format: "{{ item.format }}"
+ glance_image_disk_format: "{{ item.format }}"
+
+- name: Get details on the kernel image
+ run_once: true
+ openstack.cloud.image_info:
+ cloud: atmosphere
+ image: "{{ ironic_python_agent_deploy_kernel_name }}"
+ register: ironic_python_agent_deploy_kernel
+
+- name: Get details on the ramdisk image
+ run_once: true
+ openstack.cloud.image_info:
+ cloud: atmosphere
+ image: "{{ ironic_python_agent_deploy_ramdisk_name }}"
+ register: ironic_python_agent_deploy_ramdisk
+
+- name: Deploy Helm chart
+ run_once: true
+ kubernetes.core.helm:
+ name: "{{ ironic_helm_release_name }}"
+ chart_ref: "{{ ironic_helm_chart_ref }}"
+ release_namespace: "{{ ironic_helm_release_namespace }}"
+ create_namespace: true
+ kubeconfig: /etc/kubernetes/admin.conf
+ values: "{{ _ironic_helm_values | combine(ironic_helm_values, recursive=True) }}"
+
+- name: Create Ingress
+ ansible.builtin.include_role:
+ name: openstack_helm_ingress
+ vars:
+ openstack_helm_ingress_endpoint: baremetal
+ openstack_helm_ingress_service_name: ironic-api
+ openstack_helm_ingress_service_port: 6385
+ openstack_helm_ingress_annotations: "{{ ironic_ingress_annotations }}"
diff --git a/roles/ironic/tasks/network/create.yml b/roles/ironic/tasks/network/create.yml
new file mode 100644
index 0000000..8032983
--- /dev/null
+++ b/roles/ironic/tasks/network/create.yml
@@ -0,0 +1,35 @@
+# Copyright (c) 2022 VEXXHOST, Inc.
+#
+# Licensed under the Apache License, Version 2.0 (the "License"); you may
+# not use this file except in compliance with the License. You may obtain
+# a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations
+# under the License.
+
+- name: Create bare metal network
+ run_once: true
+ openstack.cloud.network:
+ cloud: atmosphere
+ # Network settings
+ name: "{{ ironic_bare_metal_network_name }}"
+ provider_physical_network: "{{ ironic_bare_metal_network_provider_physical_network }}"
+ provider_network_type: "{{ ironic_bare_metal_network_provider_network_type }}"
+ provider_segmentation_id: "{{ ironic_bare_metal_network_provider_segmentation_id }}"
+ register: ironic_bare_metal_network
+
+- name: Create bare metal network subnet
+ run_once: true
+ openstack.cloud.subnet:
+ cloud: atmosphere
+ # Subnet settings
+ network_name: "{{ ironic_bare_metal_subnet_name }}"
+ name: "{{ ironic_bare_metal_subnet_name }}"
+ cidr: "{{ ironic_bare_metal_subnet_cidr }}"
+ allocation_pool_start: "{{ ironic_bare_metal_subnet_allocation_pool_start | default(omit) }}"
+ allocation_pool_end: "{{ ironic_bare_metal_subnet_allocation_pool_end | default(omit) }}"
diff --git a/roles/ironic/tasks/network/lookup.yml b/roles/ironic/tasks/network/lookup.yml
new file mode 100644
index 0000000..8838ac9
--- /dev/null
+++ b/roles/ironic/tasks/network/lookup.yml
@@ -0,0 +1,33 @@
+# Copyright (c) 2022 VEXXHOST, Inc.
+#
+# Licensed under the Apache License, Version 2.0 (the "License"); you may
+# not use this file except in compliance with the License. You may obtain
+# a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations
+# under the License.
+
+- name: Gather information about a bare metal network
+ run_once: true
+ openstack.cloud.networks_info:
+ cloud: atmosphere
+ # Network settings
+ name: "{{ ironic_bare_metal_network_name }}"
+ register: ironic_bare_metal_networks_info
+
+- name: Assert that we match a single network only
+ ansible.builtin.assert:
+ that:
+ - ironic_bare_metal_networks_info.openstack_networks | length == 1
+ fail_msg: "Expected exactly one network, but found {{ ironic_bare_metal_networks_info.openstack_networks | length }}"
+ success_msg: "Successfully matched a single network"
+ run_once: true
+
+- name: Set fact with bare metal network information
+ ansible.builtin.set_fact:
+ ironic_bare_metal_network: "{{ ironic_bare_metal_networks_info.openstack_networks[0] }}"
diff --git a/roles/ironic/vars/main.yml b/roles/ironic/vars/main.yml
new file mode 100644
index 0000000..a965b1b
--- /dev/null
+++ b/roles/ironic/vars/main.yml
@@ -0,0 +1,78 @@
+# Copyright (c) 2023 VEXXHOST, Inc.
+#
+# Licensed under the Apache License, Version 2.0 (the "License"); you may
+# not use this file except in compliance with the License. You may obtain
+# a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations
+# under the License.
+
+_ironic_helm_values:
+ endpoints: "{{ openstack_helm_endpoints }}"
+ images:
+ tags: "{{ atmosphere_images | vexxhost.atmosphere.openstack_helm_image_tags('ironic') }}"
+ bootstrap:
+ image:
+ enabled: false
+ network:
+ enabled: false
+ object_store:
+ enabled: false
+ dependencies:
+ static:
+ api:
+ jobs:
+ - ironic-db-sync
+ - ironic-ks-user
+ - ironic-ks-endpoints
+ - ironic-rabbit-init
+ # NOTE(mnaser): We're managing all the networks via Ansible.
+ # - ironic-manage-cleaning-network
+ conductor:
+ jobs:
+ - ironic-db-sync
+ - ironic-ks-user
+ - ironic-ks-endpoints
+ - ironic-rabbit-init
+ # NOTE(mnaser): We're managing all the networks via Ansible.
+ # - ironic-manage-cleaning-network
+ conf:
+ ironic:
+ DEFAULT:
+ log_config_append: null
+ enabled_network_interfaces: flat,neutron
+ default_network_interface: flat
+ conductor:
+ clean_step_priority_override: deploy.erase_devices_express:5
+ deploy_kernel: "{{ ironic_python_agent_deploy_kernel.openstack_image.id }}"
+ deploy_ramdisk: "{{ ironic_python_agent_deploy_ramdisk.openstack_image.id }}"
+ deploy:
+ erase_devices_priority: 0
+ erase_devices_metadata_priority: 0
+ neutron:
+ cleaning_network: "{{ ironic_bare_metal_network_name }}"
+ inspection_network: "{{ ironic_bare_metal_network_name }}"
+ provisioning_network: "{{ ironic_bare_metal_network_name }}"
+ rescuing_network: "{{ ironic_bare_metal_network_name }}"
+ pxe:
+ kernel_append_params: "ipa-insecure=true systemd.journald.forward_to_console=yes"
+ service_catalog:
+ valid_interfaces: public
+ pod:
+ affinity:
+ anti:
+ type:
+ conductor: requiredDuringSchedulingIgnoredDuringExecution
+ replicas:
+ api: 3
+ conductor: 3
+ manifests:
+ ingress_api: false
+ service_ingress_api: false
+ # NOTE(mnaser): We're managing all the networks via Ansible.
+ job_manage_cleaning_network: false