Skip to content

Commit

Permalink
Raise if an initial connection can not be established (#850)
Browse files Browse the repository at this point in the history
  • Loading branch information
farmio authored Jan 18, 2022
1 parent e85342d commit 6500162
Show file tree
Hide file tree
Showing 5 changed files with 47 additions and 6 deletions.
1 change: 1 addition & 0 deletions changelog.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@

### Connection

- Raise if an initial connection can not be established, auto-reconnect only when the connection was successful once
- Add support for TCP tunnel connections
- Optionally run KNXIPInterface in separate thread
- Handle separate Tunneling control and data endpoints
Expand Down
32 changes: 32 additions & 0 deletions test/xknx_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

import pytest
from xknx import XKNX
from xknx.exceptions import CommunicationError
from xknx.io import ConnectionConfig, ConnectionType


Expand Down Expand Up @@ -94,3 +95,34 @@ async def test_xknx_start_and_stop_with_dedicated_connection_config(
assert xknx.knxip_interface is None
assert xknx.telegram_queue._consumer_task.done()
assert not xknx.state_updater.started

@pytest.mark.parametrize(
"connection_config",
[
ConnectionConfig(
connection_type=ConnectionType.ROUTING, local_ip="127.0.0.1"
),
ConnectionConfig(
connection_type=ConnectionType.TUNNELING, gateway_ip="127.0.0.2"
),
],
)
@patch(
"xknx.io.transport.UDPTransport.connect",
new_callable=AsyncMock,
side_effect=OSError,
)
async def test_xknx_start_initial_connection_error(
self, transport_connect_mock, connection_config
):
"""Test xknx start raising when socket can't be set up."""
xknx = XKNX(
state_updater=True,
connection_config=connection_config,
)
with pytest.raises(CommunicationError):
await xknx.start()
transport_connect_mock.assert_called_once()
assert xknx.telegram_queue._consumer_task is None # not started
assert not xknx.state_updater.started
assert not xknx.started.is_set()
7 changes: 5 additions & 2 deletions xknx/io/knxip_interface.py
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@ def __init__(
self.connection_config = connection_config

async def start(self) -> None:
"""Start KNX/IP interface."""
"""Start KNX/IP interface. Raise `CommunicationError` if connection fails."""
await self._start()

async def _start(self) -> None:
Expand Down Expand Up @@ -309,7 +309,10 @@ def _find_default_gateway() -> ipaddress.IPv4Address:
logger.warning(
"No interface on same subnet as gateway found. Falling back to default gateway."
)
default_gateway = _find_default_gateway()
try:
default_gateway = _find_default_gateway()
except KeyError as err:
raise CommunicationError(f"No route to {gateway} found") from err
local_ip = _scan_interfaces(default_gateway)
assert isinstance(local_ip, str)
return local_ip
Expand Down
3 changes: 2 additions & 1 deletion xknx/io/routing.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
from typing import TYPE_CHECKING, Callable

from xknx.core import XknxConnectionState
from xknx.exceptions import CommunicationError
from xknx.knxip import (
HPAI,
CEMIFrame,
Expand Down Expand Up @@ -107,7 +108,7 @@ async def connect(self) -> bool:
)
# close udp transport to prevent open file descriptors
self.udp_transport.stop()
raise ex
raise CommunicationError("Routing could not be started") from ex
await self.xknx.connection_manager.connection_state_changed(
XknxConnectionState.CONNECTED
)
Expand Down
10 changes: 7 additions & 3 deletions xknx/io/tunnel.py
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,7 @@ def __init__(
self.sequence_number = 0
self.telegram_received_callback = telegram_received_callback
self._data_endpoint_addr: tuple[str, int] | None = None
self._initial_connection = True
self._is_reconnecting = False
self._reconnect_task: asyncio.Task[None] | None = None
self._src_address = xknx.own_address
Expand Down Expand Up @@ -109,12 +110,14 @@ async def connect(self) -> bool:
await self.xknx.connection_manager.connection_state_changed(
XknxConnectionState.DISCONNECTED
)
if self.auto_reconnect:
if not self._initial_connection and self.auto_reconnect:
self._reconnect_task = asyncio.create_task(self._reconnect())
return False
# close udp transport to prevent open file descriptors
# close transport to prevent open file descriptors
self.transport.stop()
raise ex
raise CommunicationError(
"Tunnel connection could not be established"
) from ex
else:
self._tunnel_established()
await self.xknx.connection_manager.connection_state_changed(
Expand All @@ -124,6 +127,7 @@ async def connect(self) -> bool:

def _tunnel_established(self) -> None:
"""Set up interface when the tunnel is ready."""
self._initial_connection = False
self.sequence_number = 0

def _tunnel_lost(self) -> None:
Expand Down

0 comments on commit 6500162

Please sign in to comment.