Skip to content

Commit d97e97f

Browse files
authored
Merge pull request #804 from rackerlabs/subport-dynamic-segment
feat: Subport dynamic segment
2 parents f0accfd + f90766b commit d97e97f

File tree

12 files changed

+747
-482
lines changed

12 files changed

+747
-482
lines changed

python/neutron-understack/neutron_understack/nautobot.py

Lines changed: 76 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -212,49 +212,87 @@ def add_tenant_vlan_tag_to_ucvni(self, network_uuid: str, vlan_tag: int) -> dict
212212
def subnet_delete(self, uuid: str) -> dict:
213213
return self.make_api_request("DELETE", f"/api/ipam/prefixes/{uuid}/")
214214

215-
def prep_switch_interface(
215+
def configure_port_status(self, interface_uuid: str, status: str) -> dict:
216+
url = f"/api/dcim/interfaces/{interface_uuid}/"
217+
payload = {"status": {"name": status}}
218+
return self.make_api_request("PATCH", url, payload)
219+
220+
def set_port_vlan_associations(
216221
self,
217-
connected_interface_id: str,
218-
ucvni_uuid: str,
219-
vlan_tag: int | None,
220-
modify_native_vlan: bool | None = True,
222+
interface_uuid: str,
223+
native_vlan_id: int | None,
224+
allowed_vlans_ids: set[int],
225+
vlan_group_name: str,
221226
) -> dict:
222-
"""Runs a Nautobot Job to update a switch interface for tenant mode.
223-
224-
The nautobot job will assign vlans as required and set the interface
225-
into the correct mode for "normal" tenant operation.
227+
"""Set the tagged and untagged vlan(s) on an interface."""
228+
url = f"/api/dcim/interfaces/{interface_uuid}/"
226229

227-
The dictionary with vlan group ID and vlan tag is returned.
228-
"""
229-
url = "/api/plugins/undercloud-vni/prep_switch_interface"
230-
payload = {
231-
"ucvni_id": str(ucvni_uuid),
232-
"connected_interface_id": str(connected_interface_id),
233-
"modify_native_vlan": modify_native_vlan,
234-
"vlan_tag": vlan_tag,
230+
payload: dict = {
231+
"tagged_vlans": [
232+
_vlan_payload(vlan_group_name, vlan_id) for vlan_id in allowed_vlans_ids
233+
],
235234
}
236-
return self.make_api_request("POST", url, payload)
237235

238-
def detach_port(self, connected_interface_id: str, ucvni_uuid: str) -> str:
239-
"""Runs a Nautobot Job to cleanup a switch interface.
236+
if native_vlan_id is not None:
237+
payload["untagged_vlan"] = _vlan_payload(vlan_group_name, native_vlan_id)
240238

241-
The nautobot job will find a VLAN that is bound to the UCVNI, remove it
242-
from the Interface and if the VLAN is unused it will delete it.
239+
return self.make_api_request("PATCH", url, payload)
240+
241+
def add_port_vlan_associations(
242+
self,
243+
interface_uuid: str,
244+
allowed_vlans_ids: set[int],
245+
vlan_group_name: str,
246+
) -> dict:
247+
"""Adds the specified vlan(s) to interface untagged/tagged vlans."""
248+
url = f"/api/dcim/interfaces/{interface_uuid}/"
249+
250+
current_state = self.make_api_request("GET", f"{url}?depth=1")
251+
252+
current_tagged_vlans = {
253+
tagged_vlan["vid"] for tagged_vlan in current_state.get("tagged_vlans", [])
254+
}
255+
256+
tagged_vlans = current_tagged_vlans.union(allowed_vlans_ids)
243257

244-
The vlan group ID is returned.
245-
"""
246-
url = "/api/plugins/undercloud-vni/detach_port"
247258
payload = {
248-
"ucvni_uuid": str(ucvni_uuid),
249-
"connected_interface_id": str(connected_interface_id),
259+
"tagged_vlans": [
260+
_vlan_payload(vlan_group_name, vlan_id) for vlan_id in tagged_vlans
261+
],
250262
}
251-
resp_data = self.make_api_request("POST", url, payload)
263+
return self.make_api_request("PATCH", url, payload)
252264

253-
return resp_data["vlan_group_id"]
265+
def remove_port_network_associations(
266+
self, interface_uuid: str, network_ids_to_remove: set[str]
267+
):
268+
query = """
269+
query($interface_id: ID!){
270+
interface(id: $interface_id){
271+
name
272+
untagged_vlan {id network: rel_ucvni_vlans { id }}
273+
tagged_vlans {id network: rel_ucvni_vlans { id }}
274+
}
275+
}
276+
"""
277+
variables = {"interface_id": interface_uuid}
278+
current = self.api.graphql.query(query, variables).json["data"]["interface"]
279+
LOG.debug("Nautobot %s query result: %s", variables, current)
254280

255-
def configure_port_status(self, interface_uuid: str, status: str) -> dict:
256281
url = f"/api/dcim/interfaces/{interface_uuid}/"
257-
payload = {"status": {"name": status}}
282+
payload = {}
283+
284+
if (
285+
current["untagged_vlan"]
286+
and current["untagged_vlan"]["id"] in network_ids_to_remove
287+
):
288+
payload["untagged_vlan"] = None
289+
290+
payload["tagged_vlans"] = [
291+
tagged_vlan["id"]
292+
for tagged_vlan in current["tagged_vlans"]
293+
if tagged_vlan["id"] not in network_ids_to_remove
294+
]
295+
258296
return self.make_api_request("PATCH", url, payload)
259297

260298
def fetch_vlan_group_uuid(self, device_uuid: str) -> str:
@@ -303,3 +341,10 @@ def create_vlan_and_associate_vlan_to_ucvni(self, vlan: VlanPayload):
303341
) from error
304342
else:
305343
return result
344+
345+
346+
def _vlan_payload(vlan_group_name: str, vlan_id: int) -> dict:
347+
return {
348+
"vlan_group": {"name": vlan_group_name},
349+
"vid": vlan_id,
350+
}

0 commit comments

Comments
 (0)