Skip to content

Commit

Permalink
Check host network connectivity (#128)
Browse files Browse the repository at this point in the history
* Check host network connectivity

Check host network connectivity and offer DNS alternatives in case host
internet is not available. This allows users with broken DNS setup
to configure the host network with a working DNS.

Requires home-assistant/supervisor#5321.

* Apply suggestions from code review

Co-authored-by: Bram Kragten <[email protected]>

* Remove network issue warning once resolved

* Make const var again

All consts need to be assigned on creation. This is not possible in this
case.

* Improve wording

* Replace try with use

* Improve network issue wording

* Rename function, call schedule outside of then()

The idea is to call schedule() always, no matter if there is an error
or not. So call it outside of the then() block.

* Use let

---------

Co-authored-by: Bram Kragten <[email protected]>
  • Loading branch information
agners and bramkragten authored Oct 7, 2024
1 parent 06b1fb1 commit 67abf8b
Show file tree
Hide file tree
Showing 3 changed files with 168 additions and 7 deletions.
31 changes: 30 additions & 1 deletion rootfs/usr/share/www/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ <h2>This may take 20 minutes or more</h2>
</div>
</div>
</div>
<div class="state-error">
<div id="state-error" class="error">
<h1>Error installing Home Assistant</h1>
<div role="alert" class="alert">
<svg class="alert-icon" preserveAspectRatio="xMidYMid meet" focusable="false" role="img" aria-hidden="true"
Expand All @@ -42,6 +42,35 @@ <h1>Error installing Home Assistant</h1>
</div>
</div>
</div>
<div id="state-network-issue" class="warning">
<h1>Networking issue detected</h1>
<div role="alert" class="alert warning">
<svg class="alert-icon" preserveAspectRatio="xMidYMid meet" focusable="false" role="img" aria-hidden="true"
viewBox="0 0 24 24">
<g>
<path
d="M11,15H13V17H11V15M11,7H13V13H11V7M12,2C6.47,2 2,6.5 2,12A10,10 0 0,0 12,22A10,10 0 0,0 22,12A10,10 0 0,0 12,2M12,20A8,8 0 0,1 4,12A8,8 0 0,1 12,4A8,8 0 0,1 20,12A8,8 0 0,1 12,20Z">
</path>
</g>
</svg>
<div class="alert-content">
<p>
Home Assistant OS detected a networking issue in your setup. As part of the initial
setup, Home Assistant OS downloads the latest version of Home Assistant Core. This
networking issue prevents this download. The network issue might be DNS related.
The currently used DNS service is: <span id="current_dns"></span>.
</p>
<p>
To resolve this, you can try a different DNS server. Select one of the options below.
Alternatively, change your router configuration to use your own custom DNS server.
</p>
</div>
</div>
<div class="actions">
<button id="use_cloudflare_dns" class="button">Use Cloudflare DNS</button>
<button id="use_google_dns" class="button">Use Google DNS</button>
</div>
</div>
<pre id="log"></pre>
<button id="show_logs">Show details</button>
</div>
Expand Down
110 changes: 107 additions & 3 deletions rootfs/usr/share/www/static/scripts.js
Original file line number Diff line number Diff line change
Expand Up @@ -81,7 +81,7 @@ function fetchLogs() {
res.text().then(function (text) {
var logElement = document.getElementById("log");
if (errorCheck.test(text)) {
document.body.classList.add("error");
document.body.classList.add("supervisor-error");
document.getElementById("show_logs").innerText = "Download raw logs";
logElement.showFull = true;
}
Expand All @@ -98,22 +98,60 @@ function fetchLogs() {
}
});
}
}, scheduleFetchLogs());
});
scheduleFetchLogs();
}

function scheduleTry() {
setTimeout(testAvailable, 5000);
}

var scheduleTimeout;
let scheduleTimeout;

function scheduleFetchLogs() {
clearTimeout(scheduleTimeout);
scheduleTimeout = setTimeout(fetchLogs, 5000);
}

function fetchSupervisorInfo() {
fetch("/supervisor/network/info").then(function (res) {
if (!res.ok)
return;

res.json().then(function (data) {
if (!data.data.host_internet) {
document.body.classList.add("network-issue");
}
else
{
document.body.classList.remove("network-issue");
}

if (document.body.classList.contains("network-issue")) {
const primaryInterface = data.data.interfaces.find(intf => intf.primary);
const dnsElement = document.getElementById("current_dns");
if (!primaryInterface) {
dnsElement.innerText = "(no primary interface)";
} else {
dnsElement.innerText = [...(primaryInterface.ipv4?.nameservers || []), ...(primaryInterface.ipv6?.nameservers || [])].join(', ');
}
}

});
});
scheduleFetchSupervisorInfo();
}

let scheduleSupervisorTimeout;

function scheduleFetchSupervisorInfo() {
clearTimeout(scheduleSupervisorTimeout);
scheduleSupervisorTimeout = setTimeout(fetchSupervisorInfo, 5000);
}

scheduleTry();
fetchLogs();
fetchSupervisorInfo();

document.getElementById("show_logs").addEventListener("click", toggleLogs);
function toggleLogs(event) {
Expand All @@ -139,6 +177,72 @@ function toggleLogs(event) {
}
}

document.getElementById("use_cloudflare_dns").addEventListener("click", function() {
setDns(["1.1.1.1", "1.0.0.1"], ["2606:4700:4700::1111", "2606:4700:4700::1001"]);
});

document.getElementById("use_google_dns").addEventListener("click", function() {
setDns(["8.8.8.8", "8.8.4.4"], ["2001:4860:4860::8888", "2001:4860:4860::8844"]);
});

function setDns(ipv4nameservers, ipv6nameservers) {
// Step 1: Fetch the primary network interface from the /network/info endpoint
fetch("/supervisor/network/info", {
method: 'GET',
headers: {
'Content-Type': 'application/json',
}
})
.then(response => {
if (!response.ok) {
throw new Error('Failed to fetch network info');
}
return response.json();
})
.then(data => {
// Step 2: Find the primary interface
const primaryInterface = data.data.interfaces.find(intf => intf.primary && intf.enabled);
if (!primaryInterface) {
throw new Error('No primary interface found');
}

// Step 3: Update the DNS settings for the primary interface
const payload = {
ipv4: {
method: "auto",
nameservers: ipv4nameservers
},
ipv6: {
method: "auto",
nameservers: ipv6nameservers
}
};

return fetch(`/supervisor/network/interface/${primaryInterface.interface}/update`, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify(payload)
});
})
.then(response => {
if (!response.ok) {
throw new Error('Failed to update the interface');
}
fetchSupervisorInfo();
return response.json();
})
.then(data => {
console.log('Success:', data);
// Optionally handle the success case, e.g., updating the UI or showing a message
})
.catch((error) => {
console.error('Error:', error);
// Optionally handle the error case, e.g., showing an error message
});
}

var dialogs = document.querySelectorAll('dialog');
dialogs.forEach(dialog => {
dialogPolyfill.registerDialog(dialog);
Expand Down
34 changes: 31 additions & 3 deletions rootfs/usr/share/www/static/styles.css
Original file line number Diff line number Diff line change
Expand Up @@ -30,15 +30,15 @@ body {
white-space: nowrap;
}

.error .state-normal {
.supervisor-error .state-normal {
display: none;
}

.state-error {
#state-error {
display: none;
}

.error .state-error {
.supervisor-error #state-error {
display: block;
}

Expand All @@ -60,6 +60,9 @@ body {
pointer-events: none;
content: "";
border-radius: 4px;
}

.error .alert::after {
background-color: #db4437;
}

Expand All @@ -76,6 +79,31 @@ body {
margin-right: 0;
}

#state-network-issue {
display: none;
}

.network-issue #state-network-issue {
display: block;
}

.network-issue .state-normal {
display: none;
}

.warning .alert-icon {
fill: #ffa600;
}

.warning .alert::after {
background-color: #ffa600;
}

.warning .actions {
display: flex;
margin-bottom: 16px;
}

.header {
text-align: center;
margin-top: 32px;
Expand Down

0 comments on commit 67abf8b

Please sign in to comment.