From 76a1939af96d6ea2d6d66d9e9068dae516295e43 Mon Sep 17 00:00:00 2001 From: Christian Manivong Date: Wed, 10 Jun 2026 01:38:02 +0200 Subject: [PATCH] fix: AOS-Switch REST API session cookie auth and VLAN PUT body connect() never sent back the sessionId returned in the login response body, so all writes (POST/PUT/DELETE) were silently rejected with "Access is unauthorized" while reads worked fine. Also fix _api_set_vlan to PUT the full VLAN object (AOS-Switch v3 rejects partial PUT bodies with HTTP 400). Co-Authored-By: Claude Sonnet 4.6 --- napalm_procurve/api_client.py | 7 +++++++ napalm_procurve/procurve.py | 7 +++++-- 2 files changed, 12 insertions(+), 2 deletions(-) diff --git a/napalm_procurve/api_client.py b/napalm_procurve/api_client.py index 878123e..34a9f67 100644 --- a/napalm_procurve/api_client.py +++ b/napalm_procurve/api_client.py @@ -127,6 +127,13 @@ class ProcurveApiClient: raise ConnectAuthError( f"REST API login failed (HTTP {resp.status_code}) for {self.hostname}" ) + # AOS-Switch returns the session cookie in the JSON body rather than + # via Set-Cookie — it must be sent back as a Cookie header on every + # subsequent request, otherwise writes (POST/PUT/DELETE) are rejected + # with "Access is unauthorized" while reads still succeed. + cookie = resp.json().get("cookie") + if cookie: + self._session.headers.update({"Cookie": cookie}) logger.debug("REST API login OK for %s", self.hostname) def setup(self, api_version: str, proto: str) -> None: diff --git a/napalm_procurve/procurve.py b/napalm_procurve/procurve.py index 977fea9..d17b945 100644 --- a/napalm_procurve/procurve.py +++ b/napalm_procurve/procurve.py @@ -765,8 +765,11 @@ class ProcurveDriver(SwitchDriver): resp = self._api.post("vlans", json=payload) if not resp.ok: if resp.status_code in (400, 409): - # VLAN already exists — update name via PUT - resp2 = self._api.put(f"vlans/{vlan_id}", json={"name": name}) + # VLAN already exists — PUT requires the full VLAN object + existing = self._api.get(f"vlans/{vlan_id}") + existing.pop("uri", None) + existing["name"] = name + resp2 = self._api.put(f"vlans/{vlan_id}", json=existing) if not resp2.ok: raise ConnectionException( f"set_vlan({vlan_id}): API PUT returned HTTP {resp2.status_code}"