confd: ethtool: Condense logic around autoneg configuration

Rely on the interface diff to figure out if any autoneg related config
has changed; if so, generate the resulting configuration based on the
current interface config.

- Remove loopback check, the model only allows ethernet config on
  interfaces of type "ethernet".

- Split up the input validation of speed/duplex to give the user an
  error message with as much specificity as possible.

- Validate that the configured speed/duplex is supported.

- Move the generated script to execute earlier to avoid unnecessary
  link flapping.

- Use negative POSIX error codes, these are (or at least should be)
  mapped to sysrepo errors in netdag_gen_iface.
This commit is contained in:
Tobias Waldekranz
2024-01-17 09:39:02 +01:00
parent b41a1b337a
commit e511076060
+49 -166
View File
@@ -653,191 +653,74 @@ skip_mtu:
return err;
}
static int netdag_write_ethtool(struct dagger *net, const char *ifname,
const char *fmt, ...)
static int netdag_gen_ethtool_autoneg(struct dagger *net, struct lyd_node *cif)
{
va_list ap;
struct lyd_node *eth = lydx_get_child(cif, "ethernet");
struct lyd_node *aneg = lydx_get_child(eth, "auto-negotiation");
const char *ifname = lydx_get_cattr(cif, "name");
const char *speed, *duplex;
int mbps, err = 0;
FILE *fp;
fp = dagger_fopen_next(net, "init", ifname, 65, "ethtool.sh");
fp = dagger_fopen_next(net, "init", ifname, 10, "ethtool-aneg.sh");
if (!fp)
return -EIO;
va_start(ap, fmt);
vfprintf(fp, fmt, ap);
va_end(ap);
fprintf(fp, "ethtool --change %s autoneg ", ifname);
if (!aneg || lydx_is_enabled(aneg, "enable")) {
fputs("on\n", fp);
} else {
speed = lydx_get_cattr(eth, "speed");
if (!speed) {
sr_session_set_error_message(net->session, "%s: "
"\"speed\" must be specified "
"when auto-negotiation is disabled", ifname);
err = -EINVAL;
goto out;
}
mbps = (int)(atof(speed) * 1000.);
if (!((mbps == 10) || (mbps == 100))) {
sr_session_set_error_message(net->session, "%s: "
"\"speed\" must be either 0.01 or 0.1 "
"when auto-negotiation is disabled", ifname);
err = -EINVAL;
goto out;
}
duplex = lydx_get_cattr(eth, "duplex");
if (!duplex || (strcmp(duplex, "full") && strcmp(duplex, "half"))) {
sr_session_set_error_message(net->session, "%s: "
"\"duplex\" must be either "
"\"full\" or \"half\" "
"when auto-negotiation is disabled", ifname);
err = -EINVAL;
goto out;
}
fprintf(fp,"off speed %d duplex %s\n", mbps, duplex);
}
out:
fclose(fp);
return err;
return 0;
}
static const char *lyd_get_autoneg(struct lyd_node *cif)
{
struct lydx_diff nd;
struct lyd_node *node;
node = lydx_get_descendant(lyd_child(cif), "ethernet", "auto-negotiation", "enable", NULL);
if (node && lydx_get_diff(node, &nd) && nd.new)
return (strcmp(nd.val, "true") == 0) ? "on" : "off";
return NULL;
}
static const char *lyd_get_duplex(struct lyd_node *cif)
{
struct lydx_diff nd;
struct lyd_node *node;
node = lydx_get_descendant(lyd_child(cif), "ethernet", "duplex", NULL);
if (node && lydx_get_diff(node, &nd) && nd.new)
return nd.val;
return NULL;
}
static int lyd_get_speed(struct lyd_node *cif)
{
struct lydx_diff nd;
struct lyd_node *n;
n = lydx_get_descendant(lyd_child(cif), "ethernet", "speed", NULL);
if (n && lydx_get_diff(n, &nd) && nd.new)
return (int)(atof(nd.val) * 1000);
return 0;
}
static int lyd_speed_has_changed(struct lyd_node *dif)
{
struct lydx_diff nd;
struct lyd_node *n;
n = lydx_get_descendant(lyd_child(dif), "ethernet", "speed", NULL);
return (n && lydx_get_diff(n, &nd));
}
static int lyd_duplex_has_changed(struct lyd_node *dif)
{
struct lydx_diff nd;
struct lyd_node *n;
n = lydx_get_descendant(lyd_child(dif), "ethernet", "duplex", NULL);
return (n && lydx_get_diff(n, &nd));
}
static int lyd_autoneg_has_changed(struct lyd_node *dif)
{
struct lydx_diff nd;
struct lyd_node *n;
n = lydx_get_descendant(lyd_child(dif), "ethernet", "auto-negotiation", "enable", NULL);
return (n && lydx_get_diff(n, &nd));
}
static int lyd_autoneg_is_on(struct lyd_node *cif)
{
const char *val;
val = lyd_get_autoneg(cif);
if (!val) {
/* TODO Change this to error when auto-neg is true in factory config */
return 1;
}
return (strcmp(val, "on") == 0);
}
static int netdag_set_autoneg_off(struct dagger *net, const char *ifname,
const char *duplex, int speed)
{
if (!speed || !duplex) {
sr_session_set_error_message(net->session,
"Autoneg off requires explicit speed and duplex");
return SR_ERR_INVAL_ARG;
}
return netdag_write_ethtool(net, ifname,
"ethtool -s %s autoneg off duplex %s speed %d\n",
ifname, duplex, speed);
}
static int netdag_set_autoneg_on(struct dagger *net, const char *ifname)
{
return netdag_write_ethtool(net, ifname, "ethtool -s %s autoneg on\n", ifname);
}
static int netdag_set_autoneg(struct dagger *net, const char *ifname, struct lyd_node *cif)
{
const char *autoneg = lyd_get_autoneg(cif);
const char *duplex = lyd_get_duplex(cif);
int speed = lyd_get_speed(cif);
if (!autoneg)
return SR_ERR_INVAL_ARG;
if (strcmp(autoneg, "off") == 0)
return netdag_set_autoneg_off(net, ifname, duplex, speed);
return netdag_set_autoneg_on(net, ifname);
}
static int netdag_set_speed(struct dagger *net, const char *ifname, struct lyd_node *cif)
{
int speed = lyd_get_speed(cif);
if (!speed && !lyd_autoneg_is_on(cif)) {
sr_session_set_error_message(net->session,
"Can't remove speed when autoneg is disabled");
return SR_ERR_INVAL_ARG;
}
return netdag_write_ethtool(net, ifname, "ethtool -s %s speed %d\n",
ifname, speed);
}
static int netdag_set_duplex(struct dagger *net, const char *ifname, struct lyd_node *cif)
{
const char *duplex = lyd_get_duplex(cif);
/**
* Duplex has a default value, no need to check if autoneg is off and
* the duplex is removed, which would be a problem otherwise.
*/
return netdag_write_ethtool(net, ifname, "ethtool -s %s duplex %s\n",
ifname, duplex);
}
static int netdag_gen_ethtool(struct dagger *net, struct lyd_node *cif, struct lyd_node *dif)
{
const char *ifname = lydx_get_cattr(cif, "name");
struct lyd_node *eth = lydx_get_child(dif, "ethernet");
int err;
if (!strcmp(ifname, "lo"))
return SR_ERR_OK;
/**
* Autoneg on means duplex and speed doesn't matter.
* Autoneg off requires and set both speed and duplex.
*
* Either way, if autoneg has changed duplex and speed are handled.
*/
if (lyd_autoneg_has_changed(dif))
return netdag_set_autoneg(net, ifname, cif);
if (lyd_duplex_has_changed(dif)) {
err = netdag_set_duplex(net, ifname, cif);
if (lydx_get_descendant(lyd_child(eth), "auto-negotiation", "enable", NULL) ||
lydx_get_child(eth, "speed") ||
lydx_get_child(eth, "duplex")) {
err = netdag_gen_ethtool_autoneg(net, cif);
if (err)
return err;
}
if (lyd_speed_has_changed(dif)) {
err = netdag_set_speed(net, ifname, cif);
if (err)
return err;
}
return SR_ERR_OK;
return 0;
}
static int bridge_diff_vlan_port(struct dagger *net, FILE *br, const char *brname, int vid,