Skip to content

Commit 7321af5

Browse files
capturing baremetal.node.provision_set.end when baremetal node transitioning from inspect to manage
1 parent c93d546 commit 7321af5

File tree

9 files changed

+706
-5
lines changed

9 files changed

+706
-5
lines changed

components/site-workflows/kustomization.yaml

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,8 @@ resources:
2222
- sensors/sensor-neutron-olso-event.yaml
2323
- sensors/sensor-ironic-reclean.yaml
2424
- sensors/sensor-ironic-node-port.yaml
25-
- sensors/sensor-ironic-oslo-event.yaml
25+
- sensors/sensor-ironic-oslo-deploying-event.yaml
26+
- sensors/sensor-ironic-oslo-inspecting-event.yaml
2627

2728
helmCharts:
2829
- name: nautobot-token

components/site-workflows/sensors/sensor-ironic-oslo-event.yaml renamed to components/site-workflows/sensors/sensor-ironic-oslo-deploying-event.yaml

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
apiVersion: argoproj.io/v1alpha1
33
kind: Sensor
44
metadata:
5-
name: ironic-oslo-event
5+
name: ironic-oslo-deploying-event
66
annotations:
77
workflows.argoproj.io/title: Process oslo_events for ironic project
88
workflows.argoproj.io/description: |+
@@ -13,11 +13,11 @@ metadata:
1313
Resulting code should be very similar to:
1414
1515
```
16-
argo -n argo-events submit --from workflowtemplate/ironic-oslo-event \
16+
argo -n argo-events submit --from workflowtemplate/openstack-oslo-event \
1717
-p event-json "JSON-payload" -p device_id=<UUID> -p project_id=<UUID>
1818
```
1919
20-
Defined in `workflows/argo-events/sensors/sensor-ironic-oslo-event.yaml`
20+
Defined in `workflows/argo-events/sensors/sensor-sensor-ironic-oslo-deploying-event.yaml`
2121
spec:
2222
dependencies:
2323
- eventName: openstack
Lines changed: 84 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,84 @@
1+
---
2+
apiVersion: argoproj.io/v1alpha1
3+
kind: Sensor
4+
metadata:
5+
name: ironic-oslo-inspecting-event
6+
annotations:
7+
workflows.argoproj.io/title: Process oslo_events for ironic project
8+
workflows.argoproj.io/description: |+
9+
Triggers on the following Ironic Events:
10+
11+
- baremetal.node.provision_set.end which happens after a state change on the node
12+
13+
Resulting code should be very similar to:
14+
15+
```
16+
argo -n argo-events submit --from workflowtemplate/openstack-oslo-event \
17+
-p event-json "JSON-payload" -p device_id=<UUID> -p project_id=<UUID>
18+
```
19+
20+
Defined in `workflows/argo-events/sensors/sensor-ironic-oslo-inspecting-event.yaml`
21+
spec:
22+
dependencies:
23+
- eventName: openstack
24+
eventSourceName: openstack-ironic
25+
name: ironic-dep
26+
transform:
27+
# the event is a string-ified JSON so we need to decode it
28+
# replace the whole event body
29+
jq: |
30+
(
31+
.body = (.body["oslo.message"] | fromjson) |
32+
.body.ironic_object = .body.payload["ironic_object.data"]
33+
)
34+
filters:
35+
# applies each of the items in data with 'and' but there's only one
36+
dataLogicalOperator: "and"
37+
data:
38+
- path: "body.event_type"
39+
type: "string"
40+
value:
41+
- "baremetal.node.provision_set.end"
42+
- path: "body.ironic_object.previous_provision_state"
43+
type: "string"
44+
value:
45+
- "inspecting"
46+
template:
47+
serviceAccountName: sensor-submit-workflow
48+
triggers:
49+
- template:
50+
name: update-nautobot
51+
k8s:
52+
operation: create
53+
parameters:
54+
# first parameter is the parsed oslo.message
55+
- dest: spec.arguments.parameters.0.value # event-json
56+
src:
57+
dataKey: body
58+
dependencyName: ironic-dep
59+
source:
60+
# create a workflow in argo-events prefixed with ironic-prov-
61+
resource:
62+
apiVersion: argoproj.io/v1alpha1
63+
kind: Workflow
64+
metadata:
65+
generateName: update-nautobot-
66+
namespace: argo-events
67+
spec:
68+
serviceAccountName: workflow
69+
entrypoint: main
70+
# defines the parameters being replaced above
71+
arguments:
72+
parameters:
73+
- name: event-json
74+
templates:
75+
- name: main
76+
steps:
77+
- - name: oslo-events
78+
templateRef:
79+
name: update-nautobot-on-openstack-oslo-event
80+
template: main
81+
arguments:
82+
parameters:
83+
- name: event-json
84+
value: "{{workflow.parameters.event-json}}"

python/understack-workflows/pyproject.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@ sync-network-segment-range = "understack_workflows.main.sync_ucvni_group_range:m
4040
openstack-oslo-event = "understack_workflows.main.openstack_oslo_event:main"
4141
netapp-create-svm = "understack_workflows.main.netapp_create_svm:main"
4242
netapp-configure-interfaces = "understack_workflows.main.netapp_configure_net:main"
43+
update-nautobot-on-openstack-oslo-event = "understack_workflows.main.update_nautobot:main"
4344

4445
[dependency-groups]
4546
test = [

python/understack-workflows/understack_workflows/ironic/client.py

Lines changed: 51 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,31 @@
1+
from ironicclient.common.apiclient import exceptions as ironic_exceptions
2+
from ironicclient.v1.client import Client as IronicV1Client
3+
4+
from understack_workflows.helpers import setup_logger
15
from understack_workflows.openstack.client import get_ironic_client
26

7+
logger = setup_logger(__name__)
8+
39

410
class IronicClient:
511
def __init__(
612
self,
13+
ironic_client: IronicV1Client | None = None,
714
) -> None:
815
"""Initialize our ironicclient wrapper."""
916
self.logged_in = False
17+
self._client = ironic_client
18+
self._client_factory = get_ironic_client
19+
20+
@property
21+
def client(self) -> IronicV1Client:
22+
"""Get the ironic client, creating it lazily if needed."""
23+
if self._client is None:
24+
self._client = self._client_factory()
25+
return self._client
1026

1127
def login(self):
12-
self.client = get_ironic_client()
28+
self._client = get_ironic_client()
1329
self.logged_in = True
1430

1531
def create_node(self, node_data: dict):
@@ -66,3 +82,37 @@ def list_ports(self, node_id: str):
6682
def _ensure_logged_in(self):
6783
if not self.logged_in:
6884
self.login()
85+
86+
def get_node_inventory(self, node_ident: str) -> dict:
87+
"""Fetch node inventory data from Ironic API.
88+
89+
Args:
90+
node_ident: Node UUID, name, or other identifier
91+
92+
Returns:
93+
Dict containing node inventory data
94+
95+
Raises:
96+
ironic_exceptions.NotFound: If node doesn't exist
97+
ironic_exceptions.ClientException: For other API errors
98+
"""
99+
try:
100+
logger.info("Fetching inventory for node: %s", node_ident)
101+
102+
# Call the inventory API endpoint
103+
inventory = self.client.node.get_inventory(node_ident)
104+
105+
logger.info("Successfully retrieved inventory for node %s", node_ident)
106+
return inventory
107+
108+
except ironic_exceptions.NotFound:
109+
logger.error("Node not found: %s", node_ident)
110+
raise
111+
except ironic_exceptions.ClientException as e:
112+
logger.error("Ironic API error for node %s: %s", node_ident, e)
113+
raise
114+
except Exception as e:
115+
logger.error(
116+
"Unexpected error fetching inventory for %s: %s", node_ident, e
117+
)
118+
raise

0 commit comments

Comments
 (0)