From 9a3225d07f6b917ce91596477987abf79540494e Mon Sep 17 00:00:00 2001 From: Amir Chatur Date: Thu, 26 Feb 2026 11:16:13 -0600 Subject: [PATCH] feat: add storage network server delete automation This change implements automated storage network cleanup triggered by OpenStack server delete events. When an instance is deleted via 'openstack server delete', the system now automatically removes the corresponding storage network configuration through an event-driven workflow. The automation flow works as follows: 1. Nova emits Oslo notification events for instance delete operations 2. Argo Events captures these notifications via RabbitMQ event source 3. Sensor filters trigger the storage cleanup workflow for delete events 4. Ansible playbooks update Nautobot to reflect the removed instance 5. Nautobot Golden Config generates updated switch configurations 6. Switch update playbook applies the network changes to remove storage network access This completes the storage network automation lifecycle (create was previously merged), enabling zero-touch storage network deprovisioning that keeps network state synchronized with compute instance lifecycle without manual intervention. Changes include: - Added Nova Oslo event source and RabbitMQ user configuration - Created sensor for Nova instance delete events with event filtering - Enabled IronicUnderstackDriver in Nova configuration for storage network integration - Updated site-workflows kustomization to include new event sources and sensors --- components/nova/values.yaml | 4 + .../eventsource-openstack-nova.yaml | 44 ++++++++ .../eventsources/rabbitmq-user-argo-nova.yaml | 25 +++++ components/site-workflows/kustomization.yaml | 3 + .../sensors/sensor-nova-oslo-event.yaml | 102 ++++++++++++++++++ 5 files changed, 178 insertions(+) create mode 100644 components/site-workflows/eventsources/eventsource-openstack-nova.yaml create mode 100644 components/site-workflows/eventsources/rabbitmq-user-argo-nova.yaml create mode 100644 components/site-workflows/sensors/sensor-nova-oslo-event.yaml diff --git a/components/nova/values.yaml b/components/nova/values.yaml index bb11bff25..75006c117 100644 --- a/components/nova/values.yaml +++ b/components/nova/values.yaml @@ -93,6 +93,10 @@ conf: uwsgi: processes: 2 + # Enable oslo.messaging notifications for Argo Events integration + oslo_messaging_notifications: + driver: messagingv2 + console: # we are working with baremetal nodes and not QEMU so we don't need novnc or spice # connected to QEMU diff --git a/components/site-workflows/eventsources/eventsource-openstack-nova.yaml b/components/site-workflows/eventsources/eventsource-openstack-nova.yaml new file mode 100644 index 000000000..4a1956074 --- /dev/null +++ b/components/site-workflows/eventsources/eventsource-openstack-nova.yaml @@ -0,0 +1,44 @@ +apiVersion: argoproj.io/v1alpha1 +kind: EventSource +metadata: + name: openstack-nova +spec: + amqp: + # this is our eventName + notifications: + # amqp server url + url: amqp://rabbitmq-server-0.rabbitmq-nodes.openstack.svc.cluster.local:5672/nova + # jsonBody specifies that all event body payload coming from this + # source will be JSON + jsonBody: true + # name of the exchange. + exchangeName: nova + exchangeType: topic + exchangeDeclare: + durable: true + # routing key for messages within the exchange + routingKey: 'notifications.info' + # queue settings, if not provided defaults are used + queueDeclare: + name: argo_notifications + durable: true + exclusive: false + autoDelete: false + noWait: true + + # optional consume settings + # if not provided, default values will be used + consume: + consumerTag: "argo-events" + autoAck: true + exclusive: false + noLocal: false + # username and password for authentication + # use secret selectors + auth: + username: + name: argo-nova-user-credentials + key: username + password: + name: argo-nova-user-credentials + key: password diff --git a/components/site-workflows/eventsources/rabbitmq-user-argo-nova.yaml b/components/site-workflows/eventsources/rabbitmq-user-argo-nova.yaml new file mode 100644 index 000000000..fb8be5818 --- /dev/null +++ b/components/site-workflows/eventsources/rabbitmq-user-argo-nova.yaml @@ -0,0 +1,25 @@ +--- +apiVersion: rabbitmq.com/v1beta1 +kind: User +metadata: + name: argo-nova +spec: + rabbitmqClusterReference: + name: rabbitmq # rabbitmqCluster must exist in the same namespace as this resource + namespace: openstack +--- +apiVersion: rabbitmq.com/v1beta1 +kind: Permission +metadata: + name: argo-nova +spec: + vhost: "nova" + userReference: + name: "argo-nova" # name of a user.rabbitmq.com in the same namespace; must specify either spec.userReference or spec.user + permissions: + write: ".*" + configure: ".*" + read: ".*" + rabbitmqClusterReference: + name: rabbitmq # rabbitmqCluster must exist in the same namespace as this resource + namespace: openstack diff --git a/components/site-workflows/kustomization.yaml b/components/site-workflows/kustomization.yaml index 686a6844c..33256fbcb 100644 --- a/components/site-workflows/kustomization.yaml +++ b/components/site-workflows/kustomization.yaml @@ -5,9 +5,11 @@ resources: - eventbus/eventbus-default.yaml - eventbus/poddisruptionbudget-eventbus-default-pdb.yaml - eventsources/eventsource-openstack-ironic.yaml + - eventsources/eventsource-openstack-nova.yaml - eventsources/eventsource-openstack-keystone.yaml - eventsources/eventsource-openstack-neutron.yaml - eventsources/rabbitmq-user-argo-ironic.yaml + - eventsources/rabbitmq-user-argo-nova.yaml - eventsources/rabbitmq-user-argo-keystone.yaml - eventsources/rabbitmq-user-argo-neutron.yaml - eventsources/eventsource-k8s-openstack-secrets.yaml @@ -26,6 +28,7 @@ resources: - sensors/sensor-ironic-oslo-event.yaml # CronWorkflows - cronworkflows/resync-ironic-nautobot.yaml + - sensors/sensor-nova-oslo-event.yaml helmCharts: - name: nautobot-token diff --git a/components/site-workflows/sensors/sensor-nova-oslo-event.yaml b/components/site-workflows/sensors/sensor-nova-oslo-event.yaml new file mode 100644 index 000000000..0920bce08 --- /dev/null +++ b/components/site-workflows/sensors/sensor-nova-oslo-event.yaml @@ -0,0 +1,102 @@ +--- +apiVersion: argoproj.io/v1alpha1 +kind: Sensor +metadata: + name: nova-oslo-event + annotations: + workflows.argoproj.io/title: Process oslo_events for nova compute + workflows.argoproj.io/description: |+ + Triggers on the following Nova Events: + + - compute.instance.delete.end which happens when a server is deleted + AND the server has metadata.storage == "wanted" + + This sensor uses data filters to check the storage metadata and directly + triggers the ansible playbook without requiring a Python handler. + + The sensor extracts the following parameters from the event: + - device_id: from payload.node (the Baremetal Node ID) + - project_id: from payload.tenant_id (UUID without dashes) + + These are passed directly to the storage_on_server_delete.yml ansible playbook. + + Defined in `components/site-workflows/sensors/sensor-nova-oslo-event.yaml` +spec: + dependencies: + - eventName: notifications + eventSourceName: openstack-nova + name: nova-dep + transform: + # the event is a string-ified JSON so we need to decode it + # replace the whole event body and add a storage_wanted flag + jq: | + .body = (.body["oslo.message"] | fromjson) | + .body.storage_wanted = (.body.payload.metadata.storage // "none") + filters: + # applies each of the items in data with 'and' + dataLogicalOperator: "and" + data: + - path: "body.event_type" + type: "string" + value: + - "compute.instance.delete.end" + # Only process if storage was wanted + - path: "body.storage_wanted" + type: "string" + value: + - "wanted" + template: + serviceAccountName: sensor-submit-workflow + triggers: + - template: + name: nova-instance-delete + k8s: + operation: create + parameters: + - dest: spec.arguments.parameters.0.value + src: + dataKey: body.payload.node + dependencyName: nova-dep + - dest: spec.arguments.parameters.1.value + src: + dataKey: body.payload.tenant_id + dependencyName: nova-dep + source: + # create a workflow in argo-events prefixed with nova-delete- + resource: + apiVersion: argoproj.io/v1alpha1 + kind: Workflow + metadata: + generateName: nova-delete- + namespace: argo-events + spec: + serviceAccountName: workflow + entrypoint: main + # defines the parameters extracted from the event + arguments: + parameters: + - name: device_id + - name: project_id + templates: + - name: main + steps: + - - name: ansible-delete-server-storage + templateRef: + name: ansible-workflow-template + template: ansible-run + arguments: + parameters: + - name: playbook + value: storage_on_server_delete.yml + - name: extra_vars + value: device_id={{workflow.parameters.device_id}} project_id={{workflow.parameters.project_id}} + - - name: ansible-update-switches + templateRef: + name: ansible-workflow-template + template: ansible-run + arguments: + parameters: + - name: playbook + value: storage_update_switches.yml + - name: extra_vars + value: plan_mode=remediation