Skip to content
Draft
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
33 changes: 32 additions & 1 deletion homeassistant/components/proxmoxve/config_flow.py
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,19 @@ def _get_nodes_data(data: dict[str, Any]) -> list[dict[str, Any]]:
verify_ssl=data.get(CONF_VERIFY_SSL, DEFAULT_VERIFY_SSL),
**auth_kwargs,
)
except AuthenticationError as err:
raise ProxmoxAuthenticationError from err
except SSLError as err:
raise ProxmoxSSLError from err
except ConnectTimeout as err:
raise ProxmoxConnectTimeout from err
except ResourceException as err:
_LOGGER.debug("Error during Proxmox client initialisation", exc_info=True)
raise ProxmoxInitFailed from err
except requests.exceptions.ConnectionError as err:
raise ProxmoxConnectionError from err

try:
nodes = client.nodes.get()
except AuthenticationError as err:
raise ProxmoxAuthenticationError from err
Expand All @@ -105,6 +118,7 @@ def _get_nodes_data(data: dict[str, Any]) -> list[dict[str, Any]]:
except ConnectTimeout as err:
raise ProxmoxConnectTimeout from err
except ResourceException as err:
_LOGGER.debug("Error fetching nodes", exc_info=True)
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The attack trace is outputted by default, right, when an exception is caught?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If so both users on the issue would have had logs, so I doubt it's by default (also looking at other config_flows), but I might be mistaken

raise ProxmoxNoNodesFound from err
Comment thread
CoMPaTech marked this conversation as resolved.
Comment thread
CoMPaTech marked this conversation as resolved.
except requests.exceptions.ConnectionError as err:
raise ProxmoxConnectionError from err
Expand All @@ -115,7 +129,10 @@ def _get_nodes_data(data: dict[str, Any]) -> list[dict[str, Any]]:
vms = client.nodes(node["node"]).qemu.get()
containers = client.nodes(node["node"]).lxc.get()
except ResourceException as err:
raise ProxmoxNoNodesFound from err
_LOGGER.debug(
"Error fetching VMs/LXC for node %s", node["node"], exc_info=True
)
raise ProxmoxNoVMLXCFound from err
Comment thread
CoMPaTech marked this conversation as resolved.
except requests.exceptions.ConnectionError as err:
raise ProxmoxConnectionError from err

Expand Down Expand Up @@ -298,9 +315,15 @@ async def _validate_input(
except ProxmoxSSLError as exc:
errors["base"] = "ssl_error"
err = exc
except ProxmoxInitFailed as exc:
errors["base"] = "api_error_no_details"
err = exc
except ProxmoxNoNodesFound as exc:
errors["base"] = "no_nodes_found"
err = exc
except ProxmoxNoVMLXCFound as exc:
errors["base"] = "no_vmlxc_found"
Comment on lines +318 to +325
Comment thread
CoMPaTech marked this conversation as resolved.
Comment thread
CoMPaTech marked this conversation as resolved.
Comment thread
CoMPaTech marked this conversation as resolved.
err = exc
except ProxmoxConnectionError as exc:
errors["base"] = "cannot_connect"
err = exc
Expand Down Expand Up @@ -370,6 +393,14 @@ class ProxmoxNoNodesFound(ProxmoxError):
"""Error to indicate no nodes found."""


class ProxmoxNoVMLXCFound(ProxmoxError):
"""Error to indicate no lxc or vm found."""
Comment thread
CoMPaTech marked this conversation as resolved.
Outdated


class ProxmoxInitFailed(ProxmoxError):
"""Error to indicate API initialisation failure."""


class ProxmoxConnectTimeout(ProxmoxError):
"""Error to indicate a connection timeout."""

Expand Down
7 changes: 6 additions & 1 deletion homeassistant/components/proxmoxve/strings.json
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,12 @@
"reconfigure_successful": "[%key:common::config_flow::abort::reconfigure_successful%]"
},
"error": {
"api_error_no_details": "An error occurred while communicating with the Proxmox VE instance",
"cannot_connect": "Cannot connect to Proxmox VE server",
"connect_timeout": "[%key:common::config_flow::error::timeout_connect%]",
"invalid_auth": "[%key:common::config_flow::error::invalid_auth%]",
"no_nodes_found": "No active nodes found",
"no_nodes_found": "No active nodes were found on the Proxmox VE server.",
"no_vmlxc_found": "No LXC or VM were found on the Proxmox VE server.",
Comment thread
CoMPaTech marked this conversation as resolved.
"ssl_error": "SSL check failed. Check the SSL settings"
},
"step": {
Expand Down Expand Up @@ -324,6 +326,9 @@
"no_permission_vm_lxc_power": {
"message": "The configured Proxmox VE user does not have permission to manage the power state of VMs and containers. Please grant the user the 'VM.PowerMgmt' permission and try again."
},
"no_vmlxc_found": {
"message": "No LXC or VM were found on the Proxmox VE server."
Comment thread
CoMPaTech marked this conversation as resolved.
},
"permissions_error": {
"message": "Failed to retrieve Proxmox VE permissions. Please check your credentials and try again."
},
Expand Down
7 changes: 5 additions & 2 deletions tests/components/proxmoxve/conftest.py
Original file line number Diff line number Diff line change
Expand Up @@ -85,8 +85,8 @@ def mock_setup_entry() -> Generator[AsyncMock]:
with patch(
"homeassistant.components.proxmoxve.async_setup_entry",
return_value=True,
) as mock_setup_entry:
yield mock_setup_entry
) as mock_setup:
yield mock_setup


@pytest.fixture
Expand All @@ -104,6 +104,9 @@ def mock_proxmox_client():
mock_api.return_value = mock_instance
mock_api_cf.return_value = mock_instance

mock_instance._mock_api = mock_api
mock_instance._mock_api_cf = mock_api_cf

mock_instance.access.ticket.post.return_value = load_json_object_fixture(
"access_ticket.json", DOMAIN
)
Expand Down
71 changes: 68 additions & 3 deletions tests/components/proxmoxve/test_config_flow.py
Original file line number Diff line number Diff line change
Expand Up @@ -147,8 +147,8 @@ async def test_form(
"connect_timeout",
),
(
ResourceException("404", "status_message", "content"),
"no_nodes_found",
ResourceException("500", "status_message", "content"),
"api_error_no_details",
),
(
requests.exceptions.ConnectionError("Connection error"),
Expand All @@ -161,6 +161,71 @@ async def test_form_exceptions(
mock_proxmox_client: MagicMock,
exception: Exception,
reason: str,
) -> None:
"""Test we handle all exceptions."""
mock_proxmox_client._mock_api_cf.side_effect = exception
result = await hass.config_entries.flow.async_init(
DOMAIN, context={"source": SOURCE_USER}
)

assert result["type"] is FlowResultType.FORM
assert result["step_id"] == "user"

result = await hass.config_entries.flow.async_configure(
result["flow_id"],
user_input=MOCK_USER_STEP,
)

assert result["type"] is FlowResultType.FORM
assert result["step_id"] == "user_auth"

result = await hass.config_entries.flow.async_configure(
result["flow_id"],
user_input=MOCK_USER_AUTH_STEP_PASSWORD,
)

assert result["type"] is FlowResultType.FORM
assert result["errors"] == {"base": reason}

mock_proxmox_client._mock_api_cf.side_effect = None

result = await hass.config_entries.flow.async_configure(
result["flow_id"], user_input=MOCK_USER_AUTH_STEP_PASSWORD
)

assert result["type"] is FlowResultType.CREATE_ENTRY


@pytest.mark.parametrize(
("exception", "reason"),
[
(
AuthenticationError("Invalid credentials"),
"invalid_auth",
),
(
SSLError("SSL handshake failed"),
"ssl_error",
),
(
ConnectTimeout("Connection timed out"),
"connect_timeout",
),
(
ResourceException("400", "status_message", "content"),
"no_nodes_found",
),
(
requests.exceptions.ConnectionError("Connection error"),
"cannot_connect",
),
],
)
async def test_form_node_exceptions(
hass: HomeAssistant,
mock_proxmox_client: MagicMock,
exception: Exception,
reason: str,
) -> None:
"""Test we handle all exceptions."""
mock_proxmox_client.nodes.get.side_effect = exception
Expand Down Expand Up @@ -201,7 +266,7 @@ async def test_form_exceptions(
[
(
ResourceException("404", "status_message", "content"),
"no_nodes_found",
"no_vmlxc_found",
),
(
requests.exceptions.ConnectionError("Connection error"),
Expand Down