Skip to content

Commit 0704c1a

Browse files
committed
Add enroll-netdev Argo workflow to enroll network devices
1 parent 288f0b9 commit 0704c1a

5 files changed

Lines changed: 474 additions & 0 deletions

File tree

python/understack-workflows/pyproject.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@ dependencies = [
3232
[project.scripts]
3333
bmc-kube-password = "understack_workflows.main.bmc_display_password:main"
3434
bmc-password = "understack_workflows.main.print_bmc_password:main"
35+
enroll-netdev = "understack_workflows.main.enroll_netdev:main"
3536
enroll-server = "understack_workflows.main.enroll_server:main"
3637
netapp-configure-interfaces = "understack_workflows.main.netapp_configure_net:main"
3738
netapp-create-svm = "understack_workflows.main.netapp_create_svm:main"
Lines changed: 165 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,165 @@
1+
import logging
2+
from types import SimpleNamespace
3+
from unittest.mock import MagicMock
4+
from unittest.mock import call
5+
6+
from understack_workflows import ironic_node
7+
from understack_workflows.main import enroll_netdev
8+
9+
10+
def make_ironic_client():
11+
fake_client = MagicMock()
12+
node = SimpleNamespace(uuid="node-123")
13+
fake_client.node.create.return_value = node
14+
fake_client.port.create.side_effect = [
15+
SimpleNamespace(uuid="port-1"),
16+
SimpleNamespace(uuid="port-2"),
17+
]
18+
return fake_client, node
19+
20+
21+
def test_enroll_creates_node_ports_logs_and_makes_available(mocker, caplog):
22+
caplog.set_level(logging.INFO)
23+
fake_ironic, node = make_ironic_client()
24+
mocker.patch(
25+
"understack_workflows.ironic.client.get_ironic_client",
26+
return_value=fake_ironic,
27+
)
28+
29+
enroll_netdev.enroll(
30+
name="leaf01",
31+
physical_network="f20-1-network",
32+
port1_mac="00:11:22:33:44:55",
33+
port1_switch="spine01.example.net",
34+
port1_intf="Ethernet1/1",
35+
port2_mac="00:11:22:33:44:66",
36+
port2_switch="spine02.example.net",
37+
port2_intf="Ethernet1/2",
38+
resource_class=None,
39+
)
40+
41+
fake_ironic.node.create.assert_called_once_with(
42+
automated_clean=False,
43+
driver="netdev",
44+
name="leaf01",
45+
resource_class="generic",
46+
)
47+
fake_ironic.port.create.assert_has_calls(
48+
[
49+
call(
50+
address="00:11:22:33:44:55",
51+
category="network",
52+
local_link_connection={
53+
"switch_id": "00:00:00:00:00:00",
54+
"switch_info": "spine01.example.net",
55+
"port_id": "Ethernet1/1",
56+
},
57+
name="leaf01:port1",
58+
node_uuid=node.uuid,
59+
physical_network="f20-1-network",
60+
),
61+
call(
62+
address="00:11:22:33:44:66",
63+
category="network",
64+
local_link_connection={
65+
"switch_id": "00:00:00:00:00:00",
66+
"switch_info": "spine02.example.net",
67+
"port_id": "Ethernet1/2",
68+
},
69+
name="leaf01:port2",
70+
node_uuid=node.uuid,
71+
physical_network="f20-1-network",
72+
),
73+
]
74+
)
75+
fake_ironic.node.set_provision_state.assert_has_calls(
76+
[
77+
call(
78+
node.uuid,
79+
"manage",
80+
cleansteps=None,
81+
runbook=None,
82+
disable_ramdisk=None,
83+
),
84+
call(
85+
node.uuid,
86+
"provide",
87+
cleansteps=None,
88+
runbook=None,
89+
disable_ramdisk=None,
90+
),
91+
]
92+
)
93+
fake_ironic.node.wait_for_provision_state.assert_has_calls(
94+
[
95+
call(
96+
node.uuid,
97+
"manageable",
98+
timeout=ironic_node.NODE_STATE_TIMEOUT_SECS,
99+
),
100+
call(
101+
node.uuid,
102+
"available",
103+
timeout=ironic_node.NODE_STATE_TIMEOUT_SECS,
104+
),
105+
]
106+
)
107+
assert "Starting enroll-netdev workflow name=leaf01" in caplog.text
108+
assert "Created netdev Ironic node name=leaf01 uuid=node-123" in caplog.text
109+
assert "[node:node-123] Created baremetal port name=leaf01:port1" in caplog.text
110+
assert "[node:node-123] Node is available" in caplog.text
111+
112+
113+
def test_enroll_records_external_cmdb_id_and_custom_resource_class(mocker):
114+
fake_ironic, _ = make_ironic_client()
115+
mocker.patch(
116+
"understack_workflows.ironic.client.get_ironic_client",
117+
return_value=fake_ironic,
118+
)
119+
120+
enroll_netdev.enroll(
121+
name="leaf01",
122+
physical_network="f20-1-network",
123+
port1_mac="00:11:22:33:44:55",
124+
port1_switch="spine01.example.net",
125+
port1_intf="Ethernet1/1",
126+
port2_mac="00:11:22:33:44:66",
127+
port2_switch="spine02.example.net",
128+
port2_intf="Ethernet1/2",
129+
external_cmdb_id="cmdb-1",
130+
resource_class="switch",
131+
)
132+
133+
fake_ironic.node.create.assert_called_once_with(
134+
automated_clean=False,
135+
driver="netdev",
136+
name="leaf01",
137+
resource_class="switch",
138+
extra={"external_cmdb_id": "cmdb-1"},
139+
)
140+
141+
142+
def test_argument_parser_defaults_resource_class_to_generic():
143+
args = enroll_netdev.argument_parser().parse_args(
144+
[
145+
"--name",
146+
"leaf01",
147+
"--physical-network",
148+
"f20-1-network",
149+
"--port1-mac",
150+
"00:11:22:33:44:55",
151+
"--port1-switch",
152+
"spine01.example.net",
153+
"--port1-intf",
154+
"Ethernet1/1",
155+
"--port2-mac",
156+
"00:11:22:33:44:66",
157+
"--port2-switch",
158+
"spine02.example.net",
159+
"--port2-intf",
160+
"Ethernet1/2",
161+
]
162+
)
163+
164+
assert args.resource_class == "generic"
165+
assert args.external_cmdb_id == ""

0 commit comments

Comments
 (0)