From 1f871bc70d6c2055f01a5d14e5735ce3f77c5986 Mon Sep 17 00:00:00 2001 From: stdweird Date: Mon, 18 Mar 2024 11:22:49 +0100 Subject: [PATCH 1/6] ncm-network: refactor network core schema --- .../main/pan/components/network/config.pan | 0 .../components/network/core-schema-legacy.pan | 473 ++++++++++++++++++ .../pan/components/network/core-schema.pan | 473 +----------------- .../main/pan/components/network/schema.pan | 0 .../pan/components/network/types/network.pan | 97 ++++ .../types/network/backend/initscripts.pan | 3 + .../network/types/network/backend/nmstate.pan | 3 + .../network/types/network/ethtool.pan | 68 +++ .../network/types/network/interface.pan | 165 ++++++ .../components/network/types/network/ovs.pan | 35 ++ .../network/types/network/route.pan | 55 ++ .../components/network/types/network/rule.pan | 35 ++ .../network/types/network/tunnel.pan | 75 +++ .../test/resources/simple_base_profile.pan | 4 + 14 files changed, 1016 insertions(+), 470 deletions(-) mode change 100755 => 100644 ncm-network/src/main/pan/components/network/config.pan create mode 100644 ncm-network/src/main/pan/components/network/core-schema-legacy.pan mode change 100755 => 100644 ncm-network/src/main/pan/components/network/schema.pan create mode 100644 ncm-network/src/main/pan/components/network/types/network.pan create mode 100644 ncm-network/src/main/pan/components/network/types/network/backend/initscripts.pan create mode 100644 ncm-network/src/main/pan/components/network/types/network/backend/nmstate.pan create mode 100644 ncm-network/src/main/pan/components/network/types/network/ethtool.pan create mode 100644 ncm-network/src/main/pan/components/network/types/network/interface.pan create mode 100644 ncm-network/src/main/pan/components/network/types/network/ovs.pan create mode 100644 ncm-network/src/main/pan/components/network/types/network/route.pan create mode 100644 ncm-network/src/main/pan/components/network/types/network/rule.pan create mode 100644 ncm-network/src/main/pan/components/network/types/network/tunnel.pan diff --git a/ncm-network/src/main/pan/components/network/config.pan b/ncm-network/src/main/pan/components/network/config.pan old mode 100755 new mode 100644 diff --git a/ncm-network/src/main/pan/components/network/core-schema-legacy.pan b/ncm-network/src/main/pan/components/network/core-schema-legacy.pan new file mode 100644 index 0000000000..b1745ee243 --- /dev/null +++ b/ncm-network/src/main/pan/components/network/core-schema-legacy.pan @@ -0,0 +1,473 @@ +declaration template components/network/core-schema-legacy; + +include 'pan/types'; +include 'quattor/functions/network'; + +type network_valid_routing_table = string with exists("/system/network/routing_table/" + SELF); + +type network_ip_cmd_prefix = string with {is_ipv4_netmask_pair(SELF) || is_ipv6_network_block(SELF)}; + +@documentation{ + Add route (IPv4 of IPv6) + Presence of ':' in any of the values indicates this is IPv6 related. +} +type structure_route = { + @{The ADDRESS in ADDRESS/PREFIX via GATEWAY} + "address" ? string with {SELF == 'default' || is_ip(SELF)} + @{The PREFIX in ADDRESS/PREFIX via GATEWAY} + "prefix" ? long + @{The GATEWAY in ADDRESS/PREFIX via GATEWAY} + "gateway" ? type_ip + @{alternative notation for prefix (cannot be combined with prefix)} + "netmask" ? type_ip + @{routing table} + "table" ? network_valid_routing_table + @{pretend that the nexthop is directly attached to this link} + "onlink" ? boolean + @{route add command options to use (cannot be combined with other options)} + "command" ? string with !match(SELF, '[;]') +} with { + if (exists(SELF['command'])) { + module = value('/software/components/network/ncm-module', ''); + if (module == 'nmstate') error("Command routes are not supported by the nmstate backend"); + if (length(SELF) != 1) error("Cannot use command and any of the other attributes as route"); + } else { + if (!exists(SELF['address'])) + error("Address is mandatory for route (in absence of command)"); + if (exists(SELF['prefix']) && exists(SELF['netmask'])) + error("Use either prefix or netmask as route"); + }; + + if (exists(SELF['prefix'])) { + pref = SELF['prefix']; + ipv6 = false; + foreach (k; v; SELF) { + if (match(to_string(v), ':')) { + ipv6 = true; + }; + }; + if (ipv6) { + if (!is_ipv6_prefix_length(pref)) { + error("Prefix %s is not a valid IPv6 prefix", pref); + }; + } else { + if (!is_ipv4_prefix_length(pref)) { + error("Prefix %s is not a valid IPv4 prefix", pref); + }; + }; + }; + + true; +}; + +@documentation{ + Add rule (IPv4 of IPv6) + Presence of ':' in any of the values indicates this is IPv6 related. +} +type structure_rule = { + @{to selector} + "to" ? network_ip_cmd_prefix + @{from selector} + "from" ? network_ip_cmd_prefix + @{not action (false value means no not action; also the default when not is not defined)} + "not" ? boolean + @{routing table action} + "table" ? network_valid_routing_table + @{priority, The priority of the rule over the others. Required by Network Manager when setting routing rules.} + "priority" ? long(0..0xffffffff) + @{rule add options to use (cannot be combined with other options)} + "command" ? string with !match(SELF, '[;]') +} with { + module = value('/software/components/network/ncm-module', ''); + if (exists(SELF['command'])) { + if (module == 'nmstate') error("Command rule are not supported by the nmstate backend"); + if (length(SELF) != 1) error("Cannot use command and any of the other attributes as rule"); + } else { + if (!exists(SELF['to']) && !exists(SELF['from'])) { + error("Rule requires selector to or from (or use command)"); + }; + if (!exists(SELF['table'])) { + error("Rule requires action table (or use command)"); + }; + }; + true; +}; + +@documentation{ + Interface alias +} +type structure_interface_alias = { + "ip" ? type_ip + "netmask" : type_ip + "broadcast" ? type_ip + "fqdn" ? type_fqdn +}; + + +############################################################ +# +# Defines virtual IP addresses as loopback interface +# +############################################################ +@documentation{ + Define vip interfaces for configuring loopback interface. Used with frr/zebra configuration +} + +type structure_vip = { + "interfaces" : valid_interface[] + "ip" : type_ip + "fqdn" ? type_fqdn + "netmask" ? type_ip + "broadcast" ? type_ip +}; + +@documentation{ + Describes the bonding options for configuring channel bonding on EL5 and similar. +} +type structure_bonding_options = { + "mode" : long(0..6) + "miimon" : long + "updelay" ? long + "downdelay" ? long + "primary" ? valid_interface + "lacp_rate" ? long(0..1) + "xmit_hash_policy" ? string with match (SELF, '^(0|1|2|layer(2|2\+3|3\+4))$') +} with { + if ( SELF['mode'] == 1 || SELF['mode'] == 5 || SELF['mode'] == 6 ) { + if ( ! exists(SELF["primary"]) ) { + error("Bonding configured but no primary is defined."); + }; + } else { + if ( exists(SELF["primary"]) ) { + error("Primary is defined but this is not allowed with this bonding mode."); + }; + }; + true; +}; + +@documentation{ + describes the bridging options + (the parameters for /sys/class/net/
/brport) +} +type structure_bridging_options = { + "bpdu_guard" ? long + "flush" ? long + "hairpin_mode" ? long + "multicast_fast_leave" ? long + "multicast_router" ? long + "path_cost" ? long + "priority" ? long + "root_block" ? long +}; + +@documentation{ + interface ethtool offload +} +type structure_ethtool_offload = { + "rx" ? string with match (SELF, '^(on|off)$') + "tx" ? string with match (SELF, '^(on|off)$') + @{Set the TCP segment offload parameter to "off" or "on"} + "tso" ? string with match (SELF, '^(on|off)$') + "gro" ? string with match (SELF, '^(on|off)$') + "gso" ? string with match (SELF, '^(on|off)$') +}; + +@documentation{ + Set the ethernet transmit or receive buffer ring counts. + See ethtool --show-ring for the values. +} +type structure_ethtool_ring = { + "rx" ? long + "tx" ? long + "rx-mini" ? long + "rx-jumbo" ? long +}; + +@documentation{ + Set the number of channels. + See ethtool --show-channels for the values. +} +type structure_ethtool_channels = { + "rx" ? long(0..) + "tx" ? long(0..) + "other" ? long(0..) + "combined" ? long(0..) +}; + +@documentation{ + ethtool wol p|u|m|b|a|g|s|d... + from the man page + Sets Wake-on-LAN options. Not all devices support this. The argument to this option is a string + of characters specifying which options to enable. + p Wake on phy activity + u Wake on unicast messages + m Wake on multicast messages + b Wake on broadcast messages + a Wake on ARP + g Wake on MagicPacket(tm) + s Enable SecureOn(tm) password for MagicPacket(tm) + d Disable (wake on nothing). This option clears all previous option +} +type structure_ethtool_wol = string with match (SELF, '^(p|u|m|b|a|g|s|d)+$'); + +@documentation{ + ethtool +} +type structure_ethtool = { + "wol" ? structure_ethtool_wol + "autoneg" ? string with match (SELF, '^(on|off)$') + "duplex" ? string with match (SELF, '^(half|full)$') + "speed" ? long + "channels" ? structure_ethtool_channels +}; + +@documentation{ + interface plugin for vxlan support via initscripts-vxlan +} +type structure_interface_plugin_vxlan = { + @{VXLAN Network Identifier (or VXLAN Segment ID); derived from devicename vxlan[0-9] if not defined} + 'vni' ? long(0..16777216) + @{multicast ip to join} + 'group' ? type_ip + @{destination IP address to use in outgoing packets} + 'remote' ? type_ip + @{source IP address to use in outgoing packets} + 'local' ? type_ip + @{UDP destination port} + 'dstport' ? long(2..65535) + @{Group Policy extension} + 'gbp' ? boolean +} with { + if (exists(SELF['group']) && exists(SELF['remote'])) { + error('Cannot define both group and remote for vxlan'); + }; + if (!exists(SELF['group']) && !exists(SELF['remote'])) { + error('Must define either group or remote for vxlan'); + }; + true; +}; + +@documentation{ + interface plugin via custom ifup/down[-pre]-local hooks +} +type structure_interface_plugin = { + @{VXLAN support via initscripts-vxlan} + "vxlan" ? structure_interface_plugin_vxlan +}; + +@documentation{ + interface +} +type structure_interface = { + "ip" ? type_ip + "gateway" ? type_ip + "netmask" ? type_ip + "broadcast" ? type_ip + "driver" ? string + "bootproto" ? string with match(SELF, '^(static|bootp|dhcp|none)$') + "onboot" ? boolean + "type" ? string with match(SELF, '^(Ethernet|Bridge|Tap|xDSL|IPIP|OVS(Bridge|Port|IntPort|Bond|Tunnel|PatchPort))$') + "device" ? string + "master" ? string + "mtu" ? long + @{Routes for this interface. + These values are used to generate the /etc/sysconfig/network-scripts/route[6]- files + as used by ifup-routes when using ncm-network. + This allows for mixed IPv4 and IPv6 configuration} + "route" ? structure_route[] + @{Rules for this interface. + These values are used to generate the /etc/sysconfig/network-scripts/rule[6]- files + as used by ifup-routes when using ncm-network. + This allows for mixed IPv4 and IPv6 configuration} + "rule" ? structure_rule[] + @{Aliases for this interface. + These values are used to generate the /etc/sysconfig/network-scripts/ifcfg-: files + as used by ifup-aliases when using ncm-network.} + "aliases" ? structure_interface_alias{} + @{Explicitly set the MAC address. The MAC address is taken from /hardware/cards/nic//hwaddr.} + "set_hwaddr" ? boolean + "bridge" ? valid_interface + "bonding_opts" ? structure_bonding_options + + "offload" ? structure_ethtool_offload + "ring" ? structure_ethtool_ring + "ethtool" ? structure_ethtool + + @{Is a VLAN device. If the device name starts with vlan, this is always true.} + "vlan" ? boolean + @{If the device name starts with vlan, this has to be set. + It is set (but ignored by ifup) if it the device is not named vlan} + "physdev" ? valid_interface + + "fqdn" ? string + "network_environment" ? string + "network_type" ? string + "nmcontrolled" ? boolean + @{Set DEFROUTE, is the default for ipv6_defroute} + "defroute" ? boolean + + "linkdelay" ? long # LINKDELAY + "stp" ? boolean # enable/disable stp on bridge (true: STP=on) + "delay" ? long # brctl setfd DELAY + "bridging_opts" ? structure_bridging_options + + "bond_ifaces" ? string[] + "ovs_bridge" ? valid_interface + "ovs_extra" ? string + "ovs_opts" ? string # See ovs-vswitchd.conf.db(5) for documentation + "ovs_patch_peer" ? string + "ovs_tunnel_opts" ? string # See ovs-vswitchd.conf.db(5) for documentation + "ovs_tunnel_type" ? string with match(SELF, '^(gre|vxlan)$') + + "ipv4_failure_fatal" ? boolean + "ipv6_autoconf" ? boolean + "ipv6_failure_fatal" ? boolean + "ipv6_mtu" ? long(1280..65536) + "ipv6_privacy" ? string with match(SELF, '^rfc3041$') + "ipv6_rtr" ? boolean + @{Set IPV6_DEFROUTE, defaults to defroute value} + "ipv6_defroute" ? boolean + "ipv6addr" ? type_network_name + "ipv6addr_secondaries" ? type_network_name[] + "ipv6init" ? boolean + + @{tunnel IP} + "my_inner_ipaddr" ? type_ip + @{tunnel IP netmask prefix} + "my_inner_prefix" ? long(0..32) + @{primary local IP address} + "my_outer_ipaddr" ? type_ip + @{remote peer primary IP address} + "peer_outer_ipaddr" ? type_ip + + "plugin" ? structure_interface_plugin +} with { + if ( exists(SELF['ovs_bridge']) && exists(SELF['type']) && SELF['type'] == 'OVSBridge') { + error("An OVSBridge interface cannot have the ovs_bridge option defined"); + }; + if ( exists(SELF['ovs_tunnel_type']) && (!exists(SELF['type']) || SELF['type'] != 'OVSTunnel')) { + error("ovs_tunnel_bridge is defined but the type of interface is not defined as OVSTunnel"); + }; + if ( exists(SELF['ovs_tunnel_opts']) && (!exists(SELF['type']) || SELF['type'] != 'OVSTunnel')) { + error("ovs_tunnel_opts is defined but the type of interface is not defined as OVSTunnel"); + }; + if ( exists(SELF['ovs_patch_peer']) && (!exists(SELF['type']) || SELF['type'] != 'OVSPatchPort')) { + error("ovs_patch_peer is defined but the type of interface is not defined as OVSPatchPort"); + }; + if ( exists(SELF['bond_ifaces']) ) { + if ( (!exists(SELF['type']) || SELF['type'] != 'OVSBond') ) { + error("bond_ifaces is defined but the type of interface is not defined as OVSBond"); + }; + foreach (i; iface; SELF['bond_ifaces']) { + if ( !exists("/system/network/interfaces/" + iface) ) { + error("The " + iface + " interface is used by bond_ifaces, but does not exist"); + }; + }; + }; + if (exists(SELF['ip']) && exists(SELF['netmask'])) { + if (exists(SELF['gateway']) && ! ip_in_network(SELF['gateway'], SELF['ip'], SELF['netmask'])) { + error('networkinterface has gateway %s not reachable from ip %s with netmask %s', + SELF['gateway'], SELF['ip'], SELF['netmask']); + }; + if (exists(SELF['broadcast']) && ! ip_in_network(SELF['broadcast'], SELF['ip'], SELF['netmask'])) { + error('networkinterface has broadcast %s not reachable from ip %s with netmask %s', + SELF['broadcast'], SELF['ip'], SELF['netmask']); + }; + }; + if (exists(SELF['plugin']) && exists(SELF['plugin']['vxlan']) && ! exists(SELF['physdev'])) { + error('vxlan plugin requires physdev'); + }; + + foreach (i; name; list('my_inner_ipaddr', 'my_inner_prefix', 'my_outer_ipaddr', 'peer_outer_ipaddr')) { + if ( exists(SELF[name]) && (!exists(SELF['type']) || SELF['type'] != 'IPIP')) { + error("%s is defined but the type of interface is not defined as IPIP", name); + }; + }; + + if ( exists(SELF['type']) && SELF['type'] == 'IPIP' ) { + foreach (i; name; list('my_inner_ipaddr', 'my_inner_prefix', 'my_outer_ipaddr')) { + if (!exists(SELF[name])) { + error("Type IPIP but %s is not defined.", name); + }; + }; + }; + + true; +}; + + +@documentation{ + router +} +type structure_router = string[]; + +@documentation{ + IPv6 global settings +} +type structure_ipv6 = { + "enabled" ? boolean + "default_gateway" ? type_ip + "gatewaydev" ? valid_interface # sets IPV6_DEFAULTDEV +}; + +@documentation{ + Host network configuration + + These values are used to generate /etc/sysconfig/network + when using ncm-network (unless specified otherwise). +} +type structure_network = { + "domainname" : type_fqdn + "hostname" : type_shorthostname + "realhostname" ? string with is_shorthostname(SELF) || is_fqdn(SELF) + "default_gateway" ? type_ip + @{When default_gateway is not set, the component will try to guess the default + gateway using the first configured gateway set on an interface. + The default is true for backward compatible behaviour.} + "guess_default_gateway" ? boolean + "gatewaydev" ? valid_interface + @{Per interface network settings. + These values are used to generate the /etc/sysconfig/network-scripts/ifcfg- files + when using ncm-network.} + "interfaces" : structure_interface{} + "nameserver" ? type_ip[] + "nisdomain" ? string(1..64) with match(SELF, '^\S+$') + @{Setting nozeroconf to true stops an interface from being assigned an automatic address in the 169.254.0.0 subnet.} + "nozeroconf" ? boolean + @{The default behaviour for all interfaces wrt setting the MAC address (see interface set_hwaddr attribute). + The component default is false.} + "set_hwaddr" ? boolean + "nmcontrolled" ? boolean + "allow_nm" ? boolean + @{let NetworkManager manage the dns (only for nmstate)} + "nm_manage_dns" : boolean = false + @{let ncm-network cleanup inactive connections (only for nmstate)} + "nm_clean_inactive_conn" : boolean = true + "primary_ip" ? string + "routers" ? structure_router{} + "ipv6" ? structure_ipv6 + "manage_vips" : boolean = false + "vips" ? structure_vip{} + @{Manage custom routing table entries; key is the name; value is the id} + "routing_table" ? long(1..252){} with { + if (exists(SELF['main']) || exists(SELF['local']) || exists(SELF['default']) || exists(SELF['unspec'])) { + error("No reserved names in routing table"); + }; + true; + } +} with { + if (exists(SELF['default_gateway'])) { + reachable = false; + # is there any interface that can reach it? + foreach (name; data; SELF['interfaces']) { + if (exists(data['ip']) && exists(data['netmask']) && + ip_in_network(SELF['default_gateway'], data['ip'], data['netmask'])) { + reachable = true; + }; + }; + if (!reachable) { + error("No interface with ip/mask found to reach default gateway"); + }; + }; + true; +}; diff --git a/ncm-network/src/main/pan/components/network/core-schema.pan b/ncm-network/src/main/pan/components/network/core-schema.pan index 0839ff6e3a..e74ef85ca8 100644 --- a/ncm-network/src/main/pan/components/network/core-schema.pan +++ b/ncm-network/src/main/pan/components/network/core-schema.pan @@ -2,474 +2,7 @@ declaration template components/network/core-schema; -include 'pan/types'; -include 'quattor/functions/network'; +final variable QUATTOR_TYPES_NETWORK_LEGACY ?= true; -type network_valid_routing_table = string with exists("/system/network/routing_table/" + SELF); - -type network_ip_cmd_prefix = string with {is_ipv4_netmask_pair(SELF) || is_ipv6_network_block(SELF)}; - -@documentation{ - Add route (IPv4 of IPv6) - Presence of ':' in any of the values indicates this is IPv6 related. -} -type structure_route = { - @{The ADDRESS in ADDRESS/PREFIX via GATEWAY} - "address" ? string with {SELF == 'default' || is_ip(SELF)} - @{The PREFIX in ADDRESS/PREFIX via GATEWAY} - "prefix" ? long - @{The GATEWAY in ADDRESS/PREFIX via GATEWAY} - "gateway" ? type_ip - @{alternative notation for prefix (cannot be combined with prefix)} - "netmask" ? type_ip - @{routing table} - "table" ? network_valid_routing_table - @{pretend that the nexthop is directly attached to this link} - "onlink" ? boolean - @{route add command options to use (cannot be combined with other options)} - "command" ? string with !match(SELF, '[;]') -} with { - if (exists(SELF['command'])) { - module = value('/software/components/network/ncm-module', ''); - if (module == 'nmstate') error("Command routes are not supported by the nmstate backend"); - if (length(SELF) != 1) error("Cannot use command and any of the other attributes as route"); - } else { - if (!exists(SELF['address'])) - error("Address is mandatory for route (in absence of command)"); - if (exists(SELF['prefix']) && exists(SELF['netmask'])) - error("Use either prefix or netmask as route"); - }; - - if (exists(SELF['prefix'])) { - pref = SELF['prefix']; - ipv6 = false; - foreach (k; v; SELF) { - if (match(to_string(v), ':')) { - ipv6 = true; - }; - }; - if (ipv6) { - if (!is_ipv6_prefix_length(pref)) { - error("Prefix %s is not a valid IPv6 prefix", pref); - }; - } else { - if (!is_ipv4_prefix_length(pref)) { - error("Prefix %s is not a valid IPv4 prefix", pref); - }; - }; - }; - - true; -}; - -@documentation{ - Add rule (IPv4 of IPv6) - Presence of ':' in any of the values indicates this is IPv6 related. -} -type structure_rule = { - @{to selector} - "to" ? network_ip_cmd_prefix - @{from selector} - "from" ? network_ip_cmd_prefix - @{not action (false value means no not action; also the default when not is not defined)} - "not" ? boolean - @{routing table action} - "table" ? network_valid_routing_table - @{priority, The priority of the rule over the others. Required by Network Manager when setting routing rules.} - "priority" ? long(0..0xffffffff) - @{rule add options to use (cannot be combined with other options)} - "command" ? string with !match(SELF, '[;]') -} with { - module = value('/software/components/network/ncm-module', ''); - if (exists(SELF['command'])) { - if (module == 'nmstate') error("Command rule are not supported by the nmstate backend"); - if (length(SELF) != 1) error("Cannot use command and any of the other attributes as rule"); - } else { - if (!exists(SELF['to']) && !exists(SELF['from'])) { - error("Rule requires selector to or from (or use command)"); - }; - if (!exists(SELF['table'])) { - error("Rule requires action table (or use command)"); - }; - }; - true; -}; - -@documentation{ - Interface alias -} -type structure_interface_alias = { - "ip" ? type_ip - "netmask" : type_ip - "broadcast" ? type_ip - "fqdn" ? type_fqdn -}; - - -############################################################ -# -# Defines virtual IP addresses as loopback interface -# -############################################################ -@documentation{ - Define vip interfaces for configuring loopback interface. Used with frr/zebra configuration -} - -type structure_vip = { - "interfaces" : valid_interface[] - "ip" : type_ip - "fqdn" ? type_fqdn - "netmask" ? type_ip - "broadcast" ? type_ip -}; - -@documentation{ - Describes the bonding options for configuring channel bonding on EL5 and similar. -} -type structure_bonding_options = { - "mode" : long(0..6) - "miimon" : long - "updelay" ? long - "downdelay" ? long - "primary" ? valid_interface - "lacp_rate" ? long(0..1) - "xmit_hash_policy" ? string with match (SELF, '^(0|1|2|layer(2|2\+3|3\+4))$') -} with { - if ( SELF['mode'] == 1 || SELF['mode'] == 5 || SELF['mode'] == 6 ) { - if ( ! exists(SELF["primary"]) ) { - error("Bonding configured but no primary is defined."); - }; - } else { - if ( exists(SELF["primary"]) ) { - error("Primary is defined but this is not allowed with this bonding mode."); - }; - }; - true; -}; - -@documentation{ - describes the bridging options - (the parameters for /sys/class/net/
/brport) -} -type structure_bridging_options = { - "bpdu_guard" ? long - "flush" ? long - "hairpin_mode" ? long - "multicast_fast_leave" ? long - "multicast_router" ? long - "path_cost" ? long - "priority" ? long - "root_block" ? long -}; - -@documentation{ - interface ethtool offload -} -type structure_ethtool_offload = { - "rx" ? string with match (SELF, '^(on|off)$') - "tx" ? string with match (SELF, '^(on|off)$') - @{Set the TCP segment offload parameter to "off" or "on"} - "tso" ? string with match (SELF, '^(on|off)$') - "gro" ? string with match (SELF, '^(on|off)$') - "gso" ? string with match (SELF, '^(on|off)$') -}; - -@documentation{ - Set the ethernet transmit or receive buffer ring counts. - See ethtool --show-ring for the values. -} -type structure_ethtool_ring = { - "rx" ? long - "tx" ? long - "rx-mini" ? long - "rx-jumbo" ? long -}; - -@documentation{ - Set the number of channels. - See ethtool --show-channels for the values. -} -type structure_ethtool_channels = { - "rx" ? long(0..) - "tx" ? long(0..) - "other" ? long(0..) - "combined" ? long(0..) -}; - -@documentation{ - ethtool wol p|u|m|b|a|g|s|d... - from the man page - Sets Wake-on-LAN options. Not all devices support this. The argument to this option is a string - of characters specifying which options to enable. - p Wake on phy activity - u Wake on unicast messages - m Wake on multicast messages - b Wake on broadcast messages - a Wake on ARP - g Wake on MagicPacket(tm) - s Enable SecureOn(tm) password for MagicPacket(tm) - d Disable (wake on nothing). This option clears all previous option -} -type structure_ethtool_wol = string with match (SELF, '^(p|u|m|b|a|g|s|d)+$'); - -@documentation{ - ethtool -} -type structure_ethtool = { - "wol" ? structure_ethtool_wol - "autoneg" ? string with match (SELF, '^(on|off)$') - "duplex" ? string with match (SELF, '^(half|full)$') - "speed" ? long - "channels" ? structure_ethtool_channels -}; - -@documentation{ - interface plugin for vxlan support via initscripts-vxlan -} -type structure_interface_plugin_vxlan = { - @{VXLAN Network Identifier (or VXLAN Segment ID); derived from devicename vxlan[0-9] if not defined} - 'vni' ? long(0..16777216) - @{multicast ip to join} - 'group' ? type_ip - @{destination IP address to use in outgoing packets} - 'remote' ? type_ip - @{source IP address to use in outgoing packets} - 'local' ? type_ip - @{UDP destination port} - 'dstport' ? long(2..65535) - @{Group Policy extension} - 'gbp' ? boolean -} with { - if (exists(SELF['group']) && exists(SELF['remote'])) { - error('Cannot define both group and remote for vxlan'); - }; - if (!exists(SELF['group']) && !exists(SELF['remote'])) { - error('Must define either group or remote for vxlan'); - }; - true; -}; - -@documentation{ - interface plugin via custom ifup/down[-pre]-local hooks -} -type structure_interface_plugin = { - @{VXLAN support via initscripts-vxlan} - "vxlan" ? structure_interface_plugin_vxlan -}; - -@documentation{ - interface -} -type structure_interface = { - "ip" ? type_ip - "gateway" ? type_ip - "netmask" ? type_ip - "broadcast" ? type_ip - "driver" ? string - "bootproto" ? string with match(SELF, '^(static|bootp|dhcp|none)$') - "onboot" ? boolean - "type" ? string with match(SELF, '^(Ethernet|Bridge|Tap|xDSL|IPIP|OVS(Bridge|Port|IntPort|Bond|Tunnel|PatchPort))$') - "device" ? string - "master" ? string - "mtu" ? long - @{Routes for this interface. - These values are used to generate the /etc/sysconfig/network-scripts/route[6]- files - as used by ifup-routes when using ncm-network. - This allows for mixed IPv4 and IPv6 configuration} - "route" ? structure_route[] - @{Rules for this interface. - These values are used to generate the /etc/sysconfig/network-scripts/rule[6]- files - as used by ifup-routes when using ncm-network. - This allows for mixed IPv4 and IPv6 configuration} - "rule" ? structure_rule[] - @{Aliases for this interface. - These values are used to generate the /etc/sysconfig/network-scripts/ifcfg-: files - as used by ifup-aliases when using ncm-network.} - "aliases" ? structure_interface_alias{} - @{Explicitly set the MAC address. The MAC address is taken from /hardware/cards/nic//hwaddr.} - "set_hwaddr" ? boolean - "bridge" ? valid_interface - "bonding_opts" ? structure_bonding_options - - "offload" ? structure_ethtool_offload - "ring" ? structure_ethtool_ring - "ethtool" ? structure_ethtool - - @{Is a VLAN device. If the device name starts with vlan, this is always true.} - "vlan" ? boolean - @{If the device name starts with vlan, this has to be set. - It is set (but ignored by ifup) if it the device is not named vlan} - "physdev" ? valid_interface - - "fqdn" ? string - "network_environment" ? string - "network_type" ? string - "nmcontrolled" ? boolean - @{Set DEFROUTE, is the default for ipv6_defroute} - "defroute" ? boolean - - "linkdelay" ? long # LINKDELAY - "stp" ? boolean # enable/disable stp on bridge (true: STP=on) - "delay" ? long # brctl setfd DELAY - "bridging_opts" ? structure_bridging_options - - "bond_ifaces" ? string[] - "ovs_bridge" ? valid_interface - "ovs_extra" ? string - "ovs_opts" ? string # See ovs-vswitchd.conf.db(5) for documentation - "ovs_patch_peer" ? string - "ovs_tunnel_opts" ? string # See ovs-vswitchd.conf.db(5) for documentation - "ovs_tunnel_type" ? string with match(SELF, '^(gre|vxlan)$') - - "ipv4_failure_fatal" ? boolean - "ipv6_autoconf" ? boolean - "ipv6_failure_fatal" ? boolean - "ipv6_mtu" ? long(1280..65536) - "ipv6_privacy" ? string with match(SELF, '^rfc3041$') - "ipv6_rtr" ? boolean - @{Set IPV6_DEFROUTE, defaults to defroute value} - "ipv6_defroute" ? boolean - "ipv6addr" ? type_network_name - "ipv6addr_secondaries" ? type_network_name[] - "ipv6init" ? boolean - - @{tunnel IP} - "my_inner_ipaddr" ? type_ip - @{tunnel IP netmask prefix} - "my_inner_prefix" ? long(0..32) - @{primary local IP address} - "my_outer_ipaddr" ? type_ip - @{remote peer primary IP address} - "peer_outer_ipaddr" ? type_ip - - "plugin" ? structure_interface_plugin -} with { - if ( exists(SELF['ovs_bridge']) && exists(SELF['type']) && SELF['type'] == 'OVSBridge') { - error("An OVSBridge interface cannot have the ovs_bridge option defined"); - }; - if ( exists(SELF['ovs_tunnel_type']) && (!exists(SELF['type']) || SELF['type'] != 'OVSTunnel')) { - error("ovs_tunnel_bridge is defined but the type of interface is not defined as OVSTunnel"); - }; - if ( exists(SELF['ovs_tunnel_opts']) && (!exists(SELF['type']) || SELF['type'] != 'OVSTunnel')) { - error("ovs_tunnel_opts is defined but the type of interface is not defined as OVSTunnel"); - }; - if ( exists(SELF['ovs_patch_peer']) && (!exists(SELF['type']) || SELF['type'] != 'OVSPatchPort')) { - error("ovs_patch_peer is defined but the type of interface is not defined as OVSPatchPort"); - }; - if ( exists(SELF['bond_ifaces']) ) { - if ( (!exists(SELF['type']) || SELF['type'] != 'OVSBond') ) { - error("bond_ifaces is defined but the type of interface is not defined as OVSBond"); - }; - foreach (i; iface; SELF['bond_ifaces']) { - if ( !exists("/system/network/interfaces/" + iface) ) { - error("The " + iface + " interface is used by bond_ifaces, but does not exist"); - }; - }; - }; - if (exists(SELF['ip']) && exists(SELF['netmask'])) { - if (exists(SELF['gateway']) && ! ip_in_network(SELF['gateway'], SELF['ip'], SELF['netmask'])) { - error('networkinterface has gateway %s not reachable from ip %s with netmask %s', - SELF['gateway'], SELF['ip'], SELF['netmask']); - }; - if (exists(SELF['broadcast']) && ! ip_in_network(SELF['broadcast'], SELF['ip'], SELF['netmask'])) { - error('networkinterface has broadcast %s not reachable from ip %s with netmask %s', - SELF['broadcast'], SELF['ip'], SELF['netmask']); - }; - }; - if (exists(SELF['plugin']) && exists(SELF['plugin']['vxlan']) && ! exists(SELF['physdev'])) { - error('vxlan plugin requires physdev'); - }; - - foreach (i; name; list('my_inner_ipaddr', 'my_inner_prefix', 'my_outer_ipaddr', 'peer_outer_ipaddr')) { - if ( exists(SELF[name]) && (!exists(SELF['type']) || SELF['type'] != 'IPIP')) { - error("%s is defined but the type of interface is not defined as IPIP", name); - }; - }; - - if ( exists(SELF['type']) && SELF['type'] == 'IPIP' ) { - foreach (i; name; list('my_inner_ipaddr', 'my_inner_prefix', 'my_outer_ipaddr')) { - if (!exists(SELF[name])) { - error("Type IPIP but %s is not defined.", name); - }; - }; - }; - - true; -}; - - -@documentation{ - router -} -type structure_router = string[]; - -@documentation{ - IPv6 global settings -} -type structure_ipv6 = { - "enabled" ? boolean - "default_gateway" ? type_ip - "gatewaydev" ? valid_interface # sets IPV6_DEFAULTDEV -}; - -@documentation{ - Host network configuration - - These values are used to generate /etc/sysconfig/network - when using ncm-network (unless specified otherwise). -} -type structure_network = { - "domainname" : type_fqdn - "hostname" : type_shorthostname - "realhostname" ? string with is_shorthostname(SELF) || is_fqdn(SELF) - "default_gateway" ? type_ip - @{When default_gateway is not set, the component will try to guess the default - gateway using the first configured gateway set on an interface. - The default is true for backward compatible behaviour.} - "guess_default_gateway" ? boolean - "gatewaydev" ? valid_interface - @{Per interface network settings. - These values are used to generate the /etc/sysconfig/network-scripts/ifcfg- files - when using ncm-network.} - "interfaces" : structure_interface{} - "nameserver" ? type_ip[] - "nisdomain" ? string(1..64) with match(SELF, '^\S+$') - @{Setting nozeroconf to true stops an interface from being assigned an automatic address in the 169.254.0.0 subnet.} - "nozeroconf" ? boolean - @{The default behaviour for all interfaces wrt setting the MAC address (see interface set_hwaddr attribute). - The component default is false.} - "set_hwaddr" ? boolean - "nmcontrolled" ? boolean - "allow_nm" ? boolean - @{let NetworkManager manage the dns (only for nmstate)} - "nm_manage_dns" : boolean = false - @{let ncm-network cleanup inactive connections (only for nmstate)} - "nm_clean_inactive_conn" : boolean = true - "primary_ip" ? string - "routers" ? structure_router{} - "ipv6" ? structure_ipv6 - "manage_vips" : boolean = false - "vips" ? structure_vip{} - @{Manage custom routing table entries; key is the name; value is the id} - "routing_table" ? long(1..252){} with { - if (exists(SELF['main']) || exists(SELF['local']) || exists(SELF['default']) || exists(SELF['unspec'])) { - error("No reserved names in routing table"); - }; - true; - } -} with { - if (exists(SELF['default_gateway'])) { - reachable = false; - # is there any interface that can reach it? - foreach (name; data; SELF['interfaces']) { - if (exists(data['ip']) && exists(data['netmask']) && - ip_in_network(SELF['default_gateway'], data['ip'], data['netmask'])) { - reachable = true; - }; - }; - if (!reachable) { - error("No interface with ip/mask found to reach default gateway"); - }; - }; - true; -}; +include if (QUATTOR_TYPES_NETWORK_LEGACY) 'components/network/core-schema-legacy' + else 'components/network/types/network'; diff --git a/ncm-network/src/main/pan/components/network/schema.pan b/ncm-network/src/main/pan/components/network/schema.pan old mode 100755 new mode 100644 diff --git a/ncm-network/src/main/pan/components/network/types/network.pan b/ncm-network/src/main/pan/components/network/types/network.pan new file mode 100644 index 0000000000..9abd28c550 --- /dev/null +++ b/ncm-network/src/main/pan/components/network/types/network.pan @@ -0,0 +1,97 @@ +declaration template components/network/types/network; + +include 'pan/types'; +include 'quattor/functions/network'; + +final variable QUATTOR_TYPES_NETWORK_BACKEND ?= 'initscripts'; + +include format('components/network/types/network/backend/%s', QUATTOR_TYPES_NETWORK_BACKEND); + +include 'components/network/types/network/interface'; + +@documentation{ + Define vip interfaces for configuring loopback interface. Used with frr/zebra configuration +} +type network_vip = { + "interfaces" : valid_interface[] + "ip" : type_ip + "fqdn" ? type_fqdn + "netmask" ? type_ip + "broadcast" ? type_ip +}; + +@documentation{ + router +} +type network_router = string[]; + +@documentation{ + IPv6 global settings +} +type network_ipv6 = { + "enabled" ? boolean + "default_gateway" ? type_ip + "gatewaydev" ? valid_interface # sets IPV6_DEFAULTDEV +}; + +@documentation{ + Host network configuration + + These values are used to generate /etc/sysconfig/network + when using ncm-network (unless specified otherwise). +} +type structure_network = { + "domainname" : type_fqdn + "hostname" : type_shorthostname + "realhostname" ? string with is_shorthostname(SELF) || is_fqdn(SELF) + "default_gateway" ? type_ip + @{When default_gateway is not set, the component will try to guess the default + gateway using the first configured gateway set on an interface. + The default is true for backward compatible behaviour.} + "guess_default_gateway" ? boolean + "gatewaydev" ? valid_interface + @{Per interface network settings. + These values are used to generate the /etc/sysconfig/network-scripts/ifcfg- files + when using ncm-network.} + "interfaces" : network_interface{} + "nameserver" ? type_ip[] + "nisdomain" ? string(1..64) with match(SELF, '^\S+$') + @{Setting nozeroconf to true stops an interface from being assigned an automatic address in the 169.254.0.0 subnet.} + "nozeroconf" ? boolean + @{The default behaviour for all interfaces wrt setting the MAC address (see interface set_hwaddr attribute). + The component default is false.} + "set_hwaddr" ? boolean + "nmcontrolled" ? boolean + "allow_nm" ? boolean + @{let NetworkManager manage the dns (only for nmstate)} + "nm_manage_dns" : boolean = false + @{let ncm-network cleanup inactive connections (only for nmstate)} + "nm_clean_inactive_conn" : boolean = true + "primary_ip" ? string + "routers" ? network_router{} + "ipv6" ? network_ipv6 + "manage_vips" : boolean = false + "vips" ? network_vip{} + @{Manage custom routing table entries; key is the name; value is the id} + "routing_table" ? long(1..252){} with { + if (exists(SELF['main']) || exists(SELF['local']) || exists(SELF['default']) || exists(SELF['unspec'])) { + error("No reserved names in routing table"); + }; + true; + } +} with { + if (exists(SELF['default_gateway'])) { + reachable = false; + # is there any interface that can reach it? + foreach (name; data; SELF['interfaces']) { + if (exists(data['ip']) && exists(data['netmask']) && + ip_in_network(SELF['default_gateway'], data['ip'], data['netmask'])) { + reachable = true; + }; + }; + if (!reachable) { + error("No interface with ip/mask found to reach default gateway"); + }; + }; + true; +}; diff --git a/ncm-network/src/main/pan/components/network/types/network/backend/initscripts.pan b/ncm-network/src/main/pan/components/network/types/network/backend/initscripts.pan new file mode 100644 index 0000000000..489692dcd8 --- /dev/null +++ b/ncm-network/src/main/pan/components/network/types/network/backend/initscripts.pan @@ -0,0 +1,3 @@ +declaration template components/network/types/network/backend/initscripts; + +@{implement types specific for initscripts / network.pm} diff --git a/ncm-network/src/main/pan/components/network/types/network/backend/nmstate.pan b/ncm-network/src/main/pan/components/network/types/network/backend/nmstate.pan new file mode 100644 index 0000000000..a2d478bba7 --- /dev/null +++ b/ncm-network/src/main/pan/components/network/types/network/backend/nmstate.pan @@ -0,0 +1,3 @@ +declaration template components/network/types/network/backend/nmstate; + +@{implement types specific for nmstate / nmstate.pm} diff --git a/ncm-network/src/main/pan/components/network/types/network/ethtool.pan b/ncm-network/src/main/pan/components/network/types/network/ethtool.pan new file mode 100644 index 0000000000..ff090e1012 --- /dev/null +++ b/ncm-network/src/main/pan/components/network/types/network/ethtool.pan @@ -0,0 +1,68 @@ +declaration template components/network/types/network/ethtool; + +@documentation{ + interface ethtool offload +} +type network_ethtool_offload = { + "rx" ? string with match (SELF, '^(on|off)$') + "tx" ? string with match (SELF, '^(on|off)$') + @{Set the TCP segment offload parameter to "off" or "on"} + "tso" ? string with match (SELF, '^(on|off)$') + "gro" ? string with match (SELF, '^(on|off)$') + "gso" ? string with match (SELF, '^(on|off)$') +}; + +@documentation{ + Set the ethernet transmit or receive buffer ring counts. + See ethtool --show-ring for the values. +} +type network_ethtool_ring = { + "rx" ? long + "tx" ? long + "rx-mini" ? long + "rx-jumbo" ? long +}; + +@documentation{ + Set the number of channels. + See ethtool --show-channels for the values. +} +type network_ethtool_channels = { + "rx" ? long(0..) + "tx" ? long(0..) + "other" ? long(0..) + "combined" ? long(0..) +}; + +@documentation{ + ethtool wol p|u|m|b|a|g|s|d... + from the man page + Sets Wake-on-LAN options. Not all devices support this. The argument to this option is a string + of characters specifying which options to enable. + p Wake on phy activity + u Wake on unicast messages + m Wake on multicast messages + b Wake on broadcast messages + a Wake on ARP + g Wake on MagicPacket(tm) + s Enable SecureOn(tm) password for MagicPacket(tm) + d Disable (wake on nothing). This option clears all previous option +} +type network_ethtool_wol = string with match (SELF, '^(p|u|m|b|a|g|s|d)+$'); + +@documentation{ + ethtool +} +type network_ethtool = { + "wol" ? network_ethtool_wol + "autoneg" ? string with match (SELF, '^(on|off)$') + "duplex" ? string with match (SELF, '^(half|full)$') + "speed" ? long + "channels" ? network_ethtool_channels +}; + +type network_interface_ethtool = { + "offload" ? network_ethtool_offload + "ring" ? network_ethtool_ring + "ethtool" ? network_ethtool +}; diff --git a/ncm-network/src/main/pan/components/network/types/network/interface.pan b/ncm-network/src/main/pan/components/network/types/network/interface.pan new file mode 100644 index 0000000000..f28ce63224 --- /dev/null +++ b/ncm-network/src/main/pan/components/network/types/network/interface.pan @@ -0,0 +1,165 @@ +declaration template components/network/types/network/interface; + +@{Generate error if network backend is not supported. + First argument is the component backend (ncm-module). + Optional 2nd is extra message +} +function network_exclude_backend = { + module = value('/software/components/network/ncm-module', ''); + msg = if (ARGC < 2) '' else format(': %s', ARGV[1]); + if (module == ARGV[0]) { + error("Not supported in backend module %s%s", module, msg) + }; + true; +}; + +include 'components/network/types/network/ethtool'; +include 'components/network/types/network/route'; +include 'components/network/types/network/rule'; +include 'components/network/types/network/ovs'; +include 'components/network/types/network/tunnel'; + + +@documentation{ + Interface alias +} +type network_interface_alias = { + "ip" ? type_ip + "netmask" : type_ip + "broadcast" ? type_ip + "fqdn" ? type_fqdn +}; + +@documentation{ + Describes the bonding options for configuring channel bonding on EL5 and similar. +} +type network_bonding_options = { + "mode" : long(0..6) + "miimon" : long + "updelay" ? long + "downdelay" ? long + "primary" ? valid_interface + "lacp_rate" ? long(0..1) + "xmit_hash_policy" ? string with match (SELF, '^(0|1|2|layer(2|2\+3|3\+4))$') +} with { + if ( SELF['mode'] == 1 || SELF['mode'] == 5 || SELF['mode'] == 6 ) { + if ( ! exists(SELF["primary"]) ) { + error("Bonding configured but no primary is defined."); + }; + } else { + if ( exists(SELF["primary"]) ) { + error("Primary is defined but this is not allowed with this bonding mode."); + }; + }; + true; +}; + +@documentation{ + describes the bridging options + (the parameters for /sys/class/net/
/brport) +} +type network_bridging_options = { + "bpdu_guard" ? long + "flush" ? long + "hairpin_mode" ? long + "multicast_fast_leave" ? long + "multicast_router" ? long + "path_cost" ? long + "priority" ? long + "root_block" ? long +}; + +@documentation{ + network interface +} +type network_interface = { + "ip" ? type_ip + "gateway" ? type_ip + "netmask" ? type_ip + "broadcast" ? type_ip + "driver" ? string + "bootproto" ? string with match(SELF, '^(static|bootp|dhcp|none)$') + "onboot" ? boolean + "type" ? string with match(SELF, '^(Ethernet|Bridge|Tap|xDSL|IPIP|OVS(Bridge|Port|IntPort|Bond|Tunnel|PatchPort))$') + "device" ? string + "mtu" ? long + "master" ? string + "bonding_opts" ? network_bonding_options + @{Routes for this interface. + These values are used to generate the /etc/sysconfig/network-scripts/route[6]- files + as used by ifup-routes when using ncm-network. + This allows for mixed IPv4 and IPv6 configuration} + "route" ? network_route[] + @{Rules for this interface. + These values are used to generate the /etc/sysconfig/network-scripts/rule[6]- files + as used by ifup-routes when using ncm-network. + This allows for mixed IPv4 and IPv6 configuration} + "rule" ? network_rule[] + @{Aliases for this interface. + These values are used to generate the /etc/sysconfig/network-scripts/ifcfg-: files + as used by ifup-aliases when using ncm-network.} + "aliases" ? network_interface_alias{} + @{Explicitly set the MAC address. The MAC address is taken from /hardware/cards/nic//hwaddr.} + "set_hwaddr" ? boolean + + + @{Is a VLAN device. If the device name starts with vlan, this is always true.} + "vlan" ? boolean + @{If the device name starts with vlan, this has to be set. + It is set (but ignored by ifup) if it the device is not named vlan} + "physdev" ? valid_interface + + "fqdn" ? string + "network_environment" ? string + "network_type" ? string + "nmcontrolled" ? boolean + @{Set DEFROUTE, is the default for ipv6_defroute} + "defroute" ? boolean + + "bridge" ? valid_interface + "linkdelay" ? long # LINKDELAY + "stp" ? boolean # enable/disable stp on bridge (true: STP=on) + "delay" ? long # brctl setfd DELAY + "bridging_opts" ? network_bridging_options + + "bond_ifaces" ? string[] + + "ipv4_failure_fatal" ? boolean + "ipv6_autoconf" ? boolean + "ipv6_failure_fatal" ? boolean + "ipv6_mtu" ? long(1280..65536) + "ipv6_privacy" ? string with match(SELF, '^rfc3041$') + "ipv6_rtr" ? boolean + @{Set IPV6_DEFROUTE, defaults to defroute value} + "ipv6_defroute" ? boolean + "ipv6addr" ? type_network_name + "ipv6addr_secondaries" ? type_network_name[] + "ipv6init" ? boolean + + include network_interface_ethtool + include network_interface_ovs + include network_interface_tunnel +} with { + network_interface_ovs_validate(SELF); + network_interface_tunnel_validate(SELF); + + if ( exists(SELF['bond_ifaces']) ) { + foreach (i; iface; SELF['bond_ifaces']) { + if ( !exists("/system/network/interfaces/" + iface) ) { + error("The " + iface + " interface is used by bond_ifaces, but does not exist"); + }; + }; + }; + if (exists(SELF['ip']) && exists(SELF['netmask'])) { + if (exists(SELF['gateway']) && ! ip_in_network(SELF['gateway'], SELF['ip'], SELF['netmask'])) { + error('networkinterface has gateway %s not reachable from ip %s with netmask %s', + SELF['gateway'], SELF['ip'], SELF['netmask']); + }; + if (exists(SELF['broadcast']) && ! ip_in_network(SELF['broadcast'], SELF['ip'], SELF['netmask'])) { + error('networkinterface has broadcast %s not reachable from ip %s with netmask %s', + SELF['broadcast'], SELF['ip'], SELF['netmask']); + }; + }; + + true; +}; diff --git a/ncm-network/src/main/pan/components/network/types/network/ovs.pan b/ncm-network/src/main/pan/components/network/types/network/ovs.pan new file mode 100644 index 0000000000..73d284b69a --- /dev/null +++ b/ncm-network/src/main/pan/components/network/types/network/ovs.pan @@ -0,0 +1,35 @@ +declaration template components/network/types/network/ovs; + + +type network_interface_ovs = { + "ovs_bridge" ? valid_interface + "ovs_extra" ? string + "ovs_opts" ? string # See ovs-vswitchd.conf.db(5) for documentation + "ovs_patch_peer" ? string + "ovs_tunnel_opts" ? string # See ovs-vswitchd.conf.db(5) for documentation + "ovs_tunnel_type" ? string with match(SELF, '^(gre|vxlan)$') +}; + + +@{validate the network_interface ovs config. error on error} +function network_interface_ovs_validate = { + nwcfg = ARGV[0]; + if ( exists(SELF['ovs_bridge']) && exists(SELF['type']) && SELF['type'] == 'OVSBridge') { + error("An OVSBridge interface cannot have the ovs_bridge option defined"); + }; + if ( exists(SELF['ovs_tunnel_type']) && (!exists(SELF['type']) || SELF['type'] != 'OVSTunnel')) { + error("ovs_tunnel_bridge is defined but the type of interface is not defined as OVSTunnel"); + }; + if ( exists(SELF['ovs_tunnel_opts']) && (!exists(SELF['type']) || SELF['type'] != 'OVSTunnel')) { + error("ovs_tunnel_opts is defined but the type of interface is not defined as OVSTunnel"); + }; + if ( exists(SELF['ovs_patch_peer']) && (!exists(SELF['type']) || SELF['type'] != 'OVSPatchPort')) { + error("ovs_patch_peer is defined but the type of interface is not defined as OVSPatchPort"); + }; + if ( exists(SELF['bond_ifaces']) ) { + if ( (!exists(SELF['type']) || SELF['type'] != 'OVSBond') ) { + error("bond_ifaces is defined but the type of interface is not defined as OVSBond"); + }; + }; + true; +}; diff --git a/ncm-network/src/main/pan/components/network/types/network/route.pan b/ncm-network/src/main/pan/components/network/types/network/route.pan new file mode 100644 index 0000000000..40c06c549b --- /dev/null +++ b/ncm-network/src/main/pan/components/network/types/network/route.pan @@ -0,0 +1,55 @@ +declaration template components/network/types/network/route; + +type network_valid_routing_table = string with exists("/system/network/routing_table/" + SELF); + +@documentation{ + Add route (IPv4 of IPv6) + Presence of ':' in any of the values indicates this is IPv6 related. +} +type network_route = { + @{The ADDRESS in ADDRESS/PREFIX via GATEWAY} + "address" ? string with {SELF == 'default' || is_ip(SELF)} + @{The PREFIX in ADDRESS/PREFIX via GATEWAY} + "prefix" ? long + @{The GATEWAY in ADDRESS/PREFIX via GATEWAY} + "gateway" ? type_ip + @{alternative notation for prefix (cannot be combined with prefix)} + "netmask" ? type_ip + @{routing table} + "table" ? network_valid_routing_table + @{pretend that the nexthop is directly attached to this link} + "onlink" ? boolean + @{route add command options to use (cannot be combined with other options)} + "command" ? string with !match(SELF, '[;]') +} with { + if (exists(SELF['command'])) { + network_exclude_backend('nmstate', 'command routes'); + if (length(SELF) != 1) error("Cannot use command and any of the other attributes as route"); + } else { + if (!exists(SELF['address'])) + error("Address is mandatory for route (in absence of command)"); + if (exists(SELF['prefix']) && exists(SELF['netmask'])) + error("Use either prefix or netmask as route"); + }; + + if (exists(SELF['prefix'])) { + pref = SELF['prefix']; + ipv6 = false; + foreach (k; v; SELF) { + if (match(to_string(v), ':')) { + ipv6 = true; + }; + }; + if (ipv6) { + if (!is_ipv6_prefix_length(pref)) { + error("Prefix %s is not a valid IPv6 prefix", pref); + }; + } else { + if (!is_ipv4_prefix_length(pref)) { + error("Prefix %s is not a valid IPv4 prefix", pref); + }; + }; + }; + + true; +}; diff --git a/ncm-network/src/main/pan/components/network/types/network/rule.pan b/ncm-network/src/main/pan/components/network/types/network/rule.pan new file mode 100644 index 0000000000..b4a70e24b1 --- /dev/null +++ b/ncm-network/src/main/pan/components/network/types/network/rule.pan @@ -0,0 +1,35 @@ +declaration template components/network/types/network/rule; + +type network_ip_cmd_prefix = string with {is_ipv4_netmask_pair(SELF) || is_ipv6_network_block(SELF)}; + +@documentation{ + Add rule (IPv4 of IPv6) + Presence of ':' in any of the values indicates this is IPv6 related. +} +type network_rule = { + @{to selector} + "to" ? network_ip_cmd_prefix + @{from selector} + "from" ? network_ip_cmd_prefix + @{not action (false value means no not action; also the default when not is not defined)} + "not" ? boolean + @{routing table action} + "table" ? network_valid_routing_table + @{priority, The priority of the rule over the others. Required by Network Manager when setting routing rules.} + "priority" ? long(0..0xffffffff) + @{rule add options to use (cannot be combined with other options)} + "command" ? string with !match(SELF, '[;]') +} with { + if (exists(SELF['command'])) { + network_exclude_backend('nmstate', 'command rules'); + if (length(SELF) != 1) error("Cannot use command and any of the other attributes as rule"); + } else { + if (!exists(SELF['to']) && !exists(SELF['from'])) { + error("Rule requires selector to or from (or use command)"); + }; + if (!exists(SELF['table'])) { + error("Rule requires action table (or use command)"); + }; + }; + true; +}; diff --git a/ncm-network/src/main/pan/components/network/types/network/tunnel.pan b/ncm-network/src/main/pan/components/network/types/network/tunnel.pan new file mode 100644 index 0000000000..c0def8d35d --- /dev/null +++ b/ncm-network/src/main/pan/components/network/types/network/tunnel.pan @@ -0,0 +1,75 @@ +declaration template components/network/types/network/tunnel; + +@documentation{ + interface plugin for vxlan support via initscripts-vxlan +} +type network_interface_plugin_vxlan = { + @{VXLAN Network Identifier (or VXLAN Segment ID); derived from devicename vxlan[0-9] if not defined} + 'vni' ? long(0..16777216) + @{multicast ip to join} + 'group' ? type_ip + @{destination IP address to use in outgoing packets} + 'remote' ? type_ip + @{source IP address to use in outgoing packets} + 'local' ? type_ip + @{UDP destination port} + 'dstport' ? long(2..65535) + @{Group Policy extension} + 'gbp' ? boolean +} with { + if (exists(SELF['group']) && exists(SELF['remote'])) { + error('Cannot define both group and remote for vxlan'); + }; + if (!exists(SELF['group']) && !exists(SELF['remote'])) { + error('Must define either group or remote for vxlan'); + }; + true; +}; + +# moved here, because this probably no nmstate equivalent to plugins +# vxlan doesn't need one in any case +@documentation{ + interface plugin via custom ifup/down[-pre]-local hooks +} +type network_interface_plugin = { + @{VXLAN support via initscripts-vxlan} + "vxlan" ? network_interface_plugin_vxlan +}; + + +type network_interface_tunnel = { + @{tunnel IP} + "my_inner_ipaddr" ? type_ip + @{tunnel IP netmask prefix} + "my_inner_prefix" ? long(0..32) + @{primary local IP address} + "my_outer_ipaddr" ? type_ip + @{remote peer primary IP address} + "peer_outer_ipaddr" ? type_ip + + "plugin" ? network_interface_plugin +}; + + +@{validate the network_interface tunnel config. error on error} +function network_interface_tunnel_validate = { + nwcfg = ARGV[0]; + if (exists(nwcfg['plugin']) && exists(nwcfg['plugin']['vxlan']) && ! exists(nwcfg['physdev'])) { + error('vxlan plugin requires physdev'); + }; + + foreach (i; name; list('my_inner_ipaddr', 'my_inner_prefix', 'my_outer_ipaddr', 'peer_outer_ipaddr')) { + if ( exists(nwcfg[name]) && (!exists(nwcfg['type']) || nwcfg['type'] != 'IPIP')) { + error("%s is defined but the type of interface is not defined as IPIP", name); + }; + }; + + if ( exists(nwcfg['type']) && nwcfg['type'] == 'IPIP' ) { + foreach (i; name; list('my_inner_ipaddr', 'my_inner_prefix', 'my_outer_ipaddr')) { + if (!exists(nwcfg[name])) { + error("Type IPIP but %s is not defined.", name); + }; + }; + }; + true; +}; diff --git a/ncm-network/src/test/resources/simple_base_profile.pan b/ncm-network/src/test/resources/simple_base_profile.pan index db54952de2..f740e07d7c 100644 --- a/ncm-network/src/test/resources/simple_base_profile.pan +++ b/ncm-network/src/test/resources/simple_base_profile.pan @@ -1,5 +1,9 @@ template simple_base_profile; +# uncomment to test any schema changes +#variable QUATTOR_TYPES_NETWORK_LEGACY = false; +#variable QUATTOR_TYPES_NETWORK_BACKEND = 'nmstate'; + function pkg_repl = { null; }; include 'components/network/config'; '/software/components/network/dependencies' = null; From a146d4d290555136e1d662e6773fc2919a096255 Mon Sep 17 00:00:00 2001 From: stdweird Date: Tue, 19 Mar 2024 12:20:00 +0100 Subject: [PATCH 2/6] minor type cleanup --- .../components/network/types/network/ethtool.pan | 14 +++++++------- .../components/network/types/network/interface.pan | 14 ++++++++++---- .../pan/components/network/types/network/ovs.pan | 2 +- 3 files changed, 18 insertions(+), 12 deletions(-) diff --git a/ncm-network/src/main/pan/components/network/types/network/ethtool.pan b/ncm-network/src/main/pan/components/network/types/network/ethtool.pan index ff090e1012..4ebfc110d7 100644 --- a/ncm-network/src/main/pan/components/network/types/network/ethtool.pan +++ b/ncm-network/src/main/pan/components/network/types/network/ethtool.pan @@ -4,12 +4,12 @@ declaration template components/network/types/network/ethtool; interface ethtool offload } type network_ethtool_offload = { - "rx" ? string with match (SELF, '^(on|off)$') - "tx" ? string with match (SELF, '^(on|off)$') + "rx" ? choice('on', 'off') + "tx" ? choice('on', 'off') @{Set the TCP segment offload parameter to "off" or "on"} - "tso" ? string with match (SELF, '^(on|off)$') - "gro" ? string with match (SELF, '^(on|off)$') - "gso" ? string with match (SELF, '^(on|off)$') + "tso" ? choice('on', 'off') + "gro" ? choice('on', 'off') + "gso" ? choice('on', 'off') }; @documentation{ @@ -55,8 +55,8 @@ type network_ethtool_wol = string with match (SELF, '^(p|u|m|b|a|g|s|d)+$'); } type network_ethtool = { "wol" ? network_ethtool_wol - "autoneg" ? string with match (SELF, '^(on|off)$') - "duplex" ? string with match (SELF, '^(half|full)$') + "autoneg" ? choice('on', 'off') + "duplex" ? choice('half', 'full') "speed" ? long "channels" ? network_ethtool_channels }; diff --git a/ncm-network/src/main/pan/components/network/types/network/interface.pan b/ncm-network/src/main/pan/components/network/types/network/interface.pan index f28ce63224..ae9ef307ae 100644 --- a/ncm-network/src/main/pan/components/network/types/network/interface.pan +++ b/ncm-network/src/main/pan/components/network/types/network/interface.pan @@ -40,7 +40,7 @@ type network_bonding_options = { "downdelay" ? long "primary" ? valid_interface "lacp_rate" ? long(0..1) - "xmit_hash_policy" ? string with match (SELF, '^(0|1|2|layer(2|2\+3|3\+4))$') + "xmit_hash_policy" ? choice('0', '1', '2', 'layer2', 'layer2+3', 'layer3+4') } with { if ( SELF['mode'] == 1 || SELF['mode'] == 5 || SELF['mode'] == 6 ) { if ( ! exists(SELF["primary"]) ) { @@ -69,6 +69,12 @@ type network_bridging_options = { "root_block" ? long }; + +type network_interface_type = choice( + 'Ethernet', 'Bridge', 'Tap', 'xDSL', 'IPIP', + 'OVSBridge', 'OVSPort', 'OVSIntPort', 'OVSBond, 'OVSTunnel', 'OVSPatchPort', + ); + @documentation{ network interface } @@ -78,9 +84,9 @@ type network_interface = { "netmask" ? type_ip "broadcast" ? type_ip "driver" ? string - "bootproto" ? string with match(SELF, '^(static|bootp|dhcp|none)$') + "bootproto" ? choice('static', 'bootp', 'dhcp', 'none') "onboot" ? boolean - "type" ? string with match(SELF, '^(Ethernet|Bridge|Tap|xDSL|IPIP|OVS(Bridge|Port|IntPort|Bond|Tunnel|PatchPort))$') + "type" ? network_interfce_type "device" ? string "mtu" ? long "master" ? string @@ -128,7 +134,7 @@ type network_interface = { "ipv6_autoconf" ? boolean "ipv6_failure_fatal" ? boolean "ipv6_mtu" ? long(1280..65536) - "ipv6_privacy" ? string with match(SELF, '^rfc3041$') + "ipv6_privacy" ? choice('rfc3041') "ipv6_rtr" ? boolean @{Set IPV6_DEFROUTE, defaults to defroute value} "ipv6_defroute" ? boolean diff --git a/ncm-network/src/main/pan/components/network/types/network/ovs.pan b/ncm-network/src/main/pan/components/network/types/network/ovs.pan index 73d284b69a..7ed759b877 100644 --- a/ncm-network/src/main/pan/components/network/types/network/ovs.pan +++ b/ncm-network/src/main/pan/components/network/types/network/ovs.pan @@ -7,7 +7,7 @@ type network_interface_ovs = { "ovs_opts" ? string # See ovs-vswitchd.conf.db(5) for documentation "ovs_patch_peer" ? string "ovs_tunnel_opts" ? string # See ovs-vswitchd.conf.db(5) for documentation - "ovs_tunnel_type" ? string with match(SELF, '^(gre|vxlan)$') + "ovs_tunnel_type" ? choice('gre', 'vxlan') }; From 804d5943740c1ea58d002f0ff9f7304f9df72c4d Mon Sep 17 00:00:00 2001 From: stdweird Date: Tue, 19 Mar 2024 12:35:28 +0100 Subject: [PATCH 3/6] ncm-network: map infiniband type to ethernet type in initscripts network --- ncm-network/src/main/perl/network.pm | 1 + 1 file changed, 1 insertion(+) diff --git a/ncm-network/src/main/perl/network.pm b/ncm-network/src/main/perl/network.pm index a2ad136ef4..55ada87f0b 100755 --- a/ncm-network/src/main/perl/network.pm +++ b/ncm-network/src/main/perl/network.pm @@ -1004,6 +1004,7 @@ sub make_ifcfg &$makeline('device', def => $ifacename); + $iface->{type} = 'Ethernet' if ($iface->{type} || '') eq 'Infiniband'; &$makeline('type', def => 'Ethernet'); if ( ($iface->{type} || '') =~ m/^OVS/) { From 30902b52063b326c1cf5878873dc80656c4787ef Mon Sep 17 00:00:00 2001 From: stdweird Date: Tue, 19 Mar 2024 12:39:15 +0100 Subject: [PATCH 4/6] ncm-network: add Infiniband type to schema --- .../main/pan/components/network/types/network/interface.pan | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/ncm-network/src/main/pan/components/network/types/network/interface.pan b/ncm-network/src/main/pan/components/network/types/network/interface.pan index ae9ef307ae..1db7af1edc 100644 --- a/ncm-network/src/main/pan/components/network/types/network/interface.pan +++ b/ncm-network/src/main/pan/components/network/types/network/interface.pan @@ -71,8 +71,8 @@ type network_bridging_options = { type network_interface_type = choice( - 'Ethernet', 'Bridge', 'Tap', 'xDSL', 'IPIP', - 'OVSBridge', 'OVSPort', 'OVSIntPort', 'OVSBond, 'OVSTunnel', 'OVSPatchPort', + 'Ethernet', 'Bridge', 'Tap', 'xDSL', 'IPIP', 'Infiniband', + 'OVSBridge', 'OVSPort', 'OVSIntPort', 'OVSBond', 'OVSTunnel', 'OVSPatchPort' ); @documentation{ @@ -86,7 +86,7 @@ type network_interface = { "driver" ? string "bootproto" ? choice('static', 'bootp', 'dhcp', 'none') "onboot" ? boolean - "type" ? network_interfce_type + "type" ? network_interface_type "device" ? string "mtu" ? long "master" ? string From c35a1dc9df2ca20b6d1564e13165fbdc649ab95d Mon Sep 17 00:00:00 2001 From: stdweird Date: Tue, 19 Mar 2024 12:50:50 +0100 Subject: [PATCH 5/6] ncm-network: nmstate: empty arrayref is not false --- ncm-network/src/main/perl/nmstate.pm | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/ncm-network/src/main/perl/nmstate.pm b/ncm-network/src/main/perl/nmstate.pm index 18f67052b2..c23eb35cef 100644 --- a/ncm-network/src/main/perl/nmstate.pm +++ b/ncm-network/src/main/perl/nmstate.pm @@ -122,7 +122,7 @@ sub make_nm_ip_rule $thisrule{'ip-to'} = $rule->{to} if $rule->{to}; $thisrule{'ip-from'} = $rule->{from} if $rule->{from}; push (@rule_entry, \%thisrule); - + # Add a default absent rule to match table defined. This will clear any existing rules for this table, instead of merging. if ($rule->{table}) { $rule_entry_absent{'state'} = "absent"; @@ -320,6 +320,7 @@ sub find_vlan_id { } return $vlanid; } + # Check if given ip belongs to a network sub ip_in_network { my ($self, $check_ip, $ip, $netmask) = @_; @@ -370,7 +371,7 @@ sub generate_nmstate_config $ifaceconfig->{type} = "vlan"; $ifaceconfig->{vlan}->{'base-iface'} = $iface->{physdev}; $ifaceconfig->{vlan}->{'id'} = $vlan_id; - } elsif ($bonded_eth) { + } elsif (@$bonded_eth) { # if bond device $ifaceconfig->{type} = "bond"; $ifaceconfig->{'link-aggregation'} = $iface->{link_aggregation}; @@ -579,7 +580,7 @@ sub nmstate_apply if (@ifaces) { $self->info("Applying changes using $NMSTATECTL ", join(', ', @ifaces)); - my @cmds; + my @cmds; foreach my $iface (@ifaces) { # apply config using nmstatectl my $ymlfile = $self->iface_filename($iface); @@ -711,7 +712,7 @@ sub Configure $idx++; } }; - + my $dev2mac = $self->make_dev2mac(); # We now have a map with files and values. From 659a1790b8745abe1ca0a143de4f9a9f8855e51f Mon Sep 17 00:00:00 2001 From: stdweird Date: Tue, 19 Mar 2024 14:46:49 +0100 Subject: [PATCH 6/6] ncm-network: nmstate: support ib interfaces --- ncm-network/src/main/perl/nmstate.pm | 18 +++++- ncm-network/src/test/perl/nmstate_advance.t | 55 +++++++++++++++++++ .../src/test/resources/nmstate_advance.pan | 17 +++++- 3 files changed, 87 insertions(+), 3 deletions(-) diff --git a/ncm-network/src/main/perl/nmstate.pm b/ncm-network/src/main/perl/nmstate.pm index c23eb35cef..4b52bf4023 100644 --- a/ncm-network/src/main/perl/nmstate.pm +++ b/ncm-network/src/main/perl/nmstate.pm @@ -355,7 +355,22 @@ sub generate_nmstate_config # this will be empty if the interface isnt a bond interface. # we can use this to determine if this interface is bond interface. my $bonded_eth = get_bonded_eth($self, $name, $net->{interfaces}); - if ($is_eth) { + + my $vlan_id = $self->find_vlan_id($name, $iface->{device}); + + if (lc($iface->{type} || '') eq 'infiniband') { + $ifaceconfig->{type} = "infiniband"; + my $ib = {}; + my $pkey = $vlan_id || 65535; + if ($vlan_id) { + my $ibdev = $name; + $ibdev =~ s/\.\d+$//; + $ib->{'base-iface'} = $ibdev; + }; + $ib->{pkey} = "0x" . sprintf("%04x", $pkey); + $ib->{mode} = 'datagram'; # TODO: add connected mode, but who still uses that + $ifaceconfig->{infiniband} = $ib; + } elsif ($is_eth) { $ifaceconfig->{type} = "ethernet"; if ($is_partof_bond) { # no ipv4 address for bonded eth, plus in nmstate bonded eth is controlled by controller. no config is required. @@ -363,7 +378,6 @@ sub generate_nmstate_config $ifaceconfig->{state} = "up"; } } elsif ($is_vlan_eth) { - my $vlan_id = $self->find_vlan_id($name, $iface->{device}); # if vlan_id is empty, error if (! $vlan_id) { $self->error("Could not find vlan id for vlan device $name"); diff --git a/ncm-network/src/test/perl/nmstate_advance.t b/ncm-network/src/test/perl/nmstate_advance.t index af1eddcfd2..8b30e414aa 100644 --- a/ncm-network/src/test/perl/nmstate_advance.t +++ b/ncm-network/src/test/perl/nmstate_advance.t @@ -167,6 +167,53 @@ routes: EOF +Readonly my $IB_YML => < < <