Skip to content

Commit

Permalink
Handle IPv6
Browse files Browse the repository at this point in the history
Display IPv6 PIF's fields when `primary_address_type` is `IPv6`
Reconfigure management interface with appropriate method
Support network reset in the IPv6 case

Signed-off-by: BenjiReis <benjamin.reis@vates.fr>
  • Loading branch information
benjamreis committed Feb 16, 2024
1 parent 0ef1e7e commit b990b4e
Show file tree
Hide file tree
Showing 7 changed files with 97 additions and 45 deletions.
28 changes: 25 additions & 3 deletions XSConsoleData.py
Original file line number Diff line number Diff line change
Expand Up @@ -929,7 +929,22 @@ def ReconfigureManagement(self, inPIF, inMode, inIP, inNetmask, inGateway, in
Auth.Inst().AssertAuthenticated()
try:
self.RequireSession()
self.session.xenapi.PIF.reconfigure_ip(inPIF['opaqueref'], inMode, inIP, inNetmask, inGateway, FirstValue(inDNS, ''))
if inPIF['primary_address_type'].lower() == 'ipv4':
self.session.xenapi.PIF.reconfigure_ip(inPIF['opaqueref'], inMode, inIP, inNetmask, inGateway, FirstValue(inDNS, ''))
if inPIF['ipv6_configuration_mode'].lower() == 'static':
# Update IPv6 DNS as well
self.session.xenapi.PIF.reconfigure_ipv6(
inPIF['opaqueref'], inPIF['ipv6_configuration_mode'], ','.join(inPIF['IPv6']), inPIF['ipv6_gateway'], FirstValue(inDNS, '')
)
else:
inIPv6 = '' if inIP == '0.0.0.0' else inIP + '/' + inNetmask
inGateway = '' if inGateway == '0.0.0.0' else inGateway
self.session.xenapi.PIF.reconfigure_ipv6(inPIF['opaqueref'], inMode, inIPv6, inGateway, FirstValue(inDNS, ''))
if inPIF['ip_configuration_mode'].lower() == 'static':
# Update IPv4 DNS as well
self.session.xenapi.PIF.reconfigure_ip(
inPIF['opaqueref'], inPIF['ip_configuration_mode'], inPIF['IP'], inPIF['netmask'], inPIF['gateway'], FirstValue(inDNS, '')
)
self.session.xenapi.host.management_reconfigure(inPIF['opaqueref'])
status, output = getstatusoutput('%s host-signal-networking-change' % (Config.Inst().XECLIPath()))
if status != 0:
Expand All @@ -949,6 +964,7 @@ def DisableManagement(self):
# Disable the PIF that the management interface was using
for pif in self.derived.managementpifs([]):
self.session.xenapi.PIF.reconfigure_ip(pif['opaqueref'], 'None','' ,'' ,'' ,'')
self.session.xenapi.PIF.reconfigure_ipv6(pif['opaqueref'], 'None','' ,'' ,'')
finally:
# Network reconfigured so this link is potentially no longer valid
self.session = Auth.Inst().CloseSession(self.session)
Expand Down Expand Up @@ -983,7 +999,12 @@ def ManagementNetmask(self, inDefault = None):
retVal = inDefault

for pif in self.derived.managementpifs([]):
retVal = pif['netmask']
ipv6 = pif['primary_address_type'].lower() == 'ipv6'
try:
# IPv6 are stored as an array of `<ipv6>/<prefix>`
retVal = pif['IPv6'][0].split('/')[1] if ipv6 else pif['netmask']
except IndexError:
return ''
if retVal:
break

Expand All @@ -993,7 +1014,8 @@ def ManagementGateway(self, inDefault = None):
retVal = inDefault

for pif in self.derived.managementpifs([]):
retVal = pif['gateway']
ipv6 = pif['primary_address_type'].lower() == 'ipv6'
retVal = pif['ipv6_gateway'] if ipv6 else pif['gateway']
if retVal:
break

Expand Down
38 changes: 19 additions & 19 deletions XSConsoleUtils.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
# with this program; if not, write to the Free Software Foundation, Inc.,
# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.

import re, signal, string, subprocess, time, types
import re, signal, socket, string, subprocess, time, types
from pprint import pprint

from XSConsoleBases import *
Expand Down Expand Up @@ -189,28 +189,29 @@ def DateTimeToSecs(cls, inDateTime):
return retVal

class IPUtils:
@classmethod
def ValidateIPFamily(cls, text, family):
try:
socket.inet_pton(family, text)
return True
except socket.error:
return False

@classmethod
def ValidateIPv4(cls, text):
return cls.ValidateIPFamily(text, socket.AF_INET)

@classmethod
def ValidateIPv6(cls, text):
return cls.ValidateIPFamily(text, socket.AF_INET6)

@classmethod
def ValidateIP(cls, text):
rc = re.match("^(0|[1-9][0-9]*)\.(0|[1-9][0-9]*)\.(0|[1-9][0-9]*)\.(0|[1-9][0-9]*)$", text)
if not rc: return False
ints = list(map(int, rc.groups()))
largest = 0
for i in ints:
if i > 255: return False
largest = max(largest, i)
if largest == 0: return False
return True
return cls.ValidateIPv4(text) or cls.ValidateIPv6(text)

@classmethod
def ValidateNetmask(cls, text):
rc = re.match("^(0|[1-9][0-9]*)\.(0|[1-9][0-9]*)\.(0|[1-9][0-9]*)\.(0|[1-9][0-9]*)$", text)
if not rc:
return False
ints = list(map(int, rc.groups()))
for i in ints:
if i > 255:
return False
return True
return cls.ValidateIPv4(text) or (int(text) > 4 and int(text) < 128)

@classmethod
def AssertValidNetmask(cls, inIP):
Expand Down Expand Up @@ -329,4 +330,3 @@ def SRSizeString(cls, inBytes):
@classmethod
def DiskSizeString(cls, inBytes):
return cls.BinarySizeString(inBytes)+' ('+cls.DecimalSizeString(inBytes)+')'

8 changes: 6 additions & 2 deletions plugins-base/XSFeatureDNS.py
Original file line number Diff line number Diff line change
Expand Up @@ -179,7 +179,9 @@ def StatusUpdateHandler(cls, inPane):
inPane.AddWrappedTextField(str(dns))
inPane.NewLine()
for pif in data.derived.managementpifs([]):
if pif['ip_configuration_mode'].lower().startswith('static'):
ipv6 = pif['primary_address_type'].lower() == 'ipv6'
configuration_mode = pif['ipv6_configuration_mode'] if ipv6 else pif['ip_configuration_mode']
if configuration_mode.lower().startswith('static'):
inPane.AddKeyHelpField( { Lang("Enter") : Lang("Update DNS Servers") })
break
inPane.AddKeyHelpField( {
Expand All @@ -203,7 +205,9 @@ def Register(self):
def ActivateHandler(cls):
data = Data.Inst()
for pif in data.derived.managementpifs([]):
if pif['ip_configuration_mode'].lower().startswith('static'):
ipv6 = pif['primary_address_type'].lower() == 'ipv6'
configuration_mode = pif['ipv6_configuration_mode'] if ipv6 else pif['ip_configuration_mode']
if configuration_mode.lower().startswith('static'):
DialogueUtils.AuthenticatedOnly(lambda: Layout.Inst().PushDialogue(DNSDialogue()))
return

Expand Down
38 changes: 27 additions & 11 deletions plugins-base/XSFeatureInterface.py
Original file line number Diff line number Diff line change
Expand Up @@ -53,11 +53,14 @@ def __init__(self):

self.nicMenu = Menu(self, None, "Configure Management Interface", choiceDefs)

self.modeMenu = Menu(self, None, Lang("Select IP Address Configuration Mode"), [
ChoiceDef(Lang("DHCP"), lambda: self.HandleModeChoice('DHCP2') ),
ChoiceDef(Lang("DHCP with Manually Assigned Hostname"), lambda: self.HandleModeChoice('DHCPMANUAL') ),
ChoiceDef(Lang("Static"), lambda: self.HandleModeChoice('STATIC') )
])
mode_choicedefs = []
if(currentPIF['primary_address_type'].lower() == 'ipv6'):
mode_choicedefs.append(ChoiceDef(Lang("Autoconf"), lambda : self.HandleModeChoice("AUTOCONF") ))
mode_choicedefs.append(ChoiceDef(Lang("DHCP"), lambda: self.HandleModeChoice('DHCP2') ))
mode_choicedefs.append(ChoiceDef(Lang("DHCP with Manually Assigned Hostname"),
lambda: self.HandleModeChoice('DHCPMANUAL') ))
mode_choicedefs.append(ChoiceDef(Lang("Static"), lambda: self.HandleModeChoice('STATIC') ))
self.modeMenu = Menu(self, None, Lang("Select IP Address Configuration Mode"), mode_choicedefs)

self.postDHCPMenu = Menu(self, None, Lang("Accept or Edit"), [
ChoiceDef(Lang("Continue With DHCP Enabled"), lambda: self.HandlePostDHCPChoice('CONTINUE') ),
Expand All @@ -83,11 +86,17 @@ def __init__(self):
self.hostname = data.host.hostname('')

if currentPIF is not None:
if 'ip_configuration_mode' in currentPIF: self.mode = currentPIF['ip_configuration_mode']
ipv6 = currentPIF['primary_address_type'].lower() == 'ipv6'
configuration_mode_key = 'ipv6_configuration_mode' if ipv6 else 'ip_configuration_mode'
if configuration_mode_key in currentPIF:
self.mode = currentPIF[configuration_mode_key]
if self.mode.lower().startswith('static'):
if 'IP' in currentPIF: self.IP = currentPIF['IP']
if 'netmask' in currentPIF: self.netmask = currentPIF['netmask']
if 'gateway' in currentPIF: self.gateway = currentPIF['gateway']
if 'IP' in currentPIF:
self.IP = currentPIF['IPv6'][0].split('/')[0] if ipv6 else currentPIF['IP']
if 'netmask' in currentPIF:
self.netmask = currentPIF['IPv6'][0].split('/')[1] if ipv6 else currentPIF['netmask']
if 'gateway' in currentPIF:
self.gateway = currentPIF['ipv6_gateway'] if ipv6 else currentPIF['gateway']

# Make the menu current choices point to our best guess of current choices
if self.nic is not None:
Expand Down Expand Up @@ -169,8 +178,10 @@ def UpdateFieldsPRECOMMIT(self):
pane.AddStatusField(Lang("Netmask", 16), self.netmask)
pane.AddStatusField(Lang("Gateway", 16), self.gateway)

if self.mode != 'Static' and self.hostname == '':
if self.mode == 'DHCP' and self.hostname == '':
pane.AddStatusField(Lang("Hostname", 16), Lang("Assigned by DHCP"))
elif self.mode == 'Autoconf' and self.hostname == '':
pane.AddStatusField(Lang("Hostname", 16), Lang("Automatically assigned"))
else:
pane.AddStatusField(Lang("Hostname", 16), self.hostname)

Expand Down Expand Up @@ -376,6 +387,9 @@ def HandleModeChoice(self, inChoice):
self.hostname = Data.Inst().host.hostname('')
self.mode = 'Static'
self.ChangeState('STATICIP')
elif inChoice == 'AUTOCONF':
self.mode = 'Autoconf'
self.ChangeState('PRECOMMIT')

def HandlePostDHCPChoice(self, inChoice):
if inChoice == 'CONTINUE':
Expand Down Expand Up @@ -459,11 +473,13 @@ def StatusUpdateHandler(cls, inPane):
inPane.AddWrappedTextField(Lang("<No interface configured>"))
else:
for pif in data.derived.managementpifs([]):
ipv6 = pif['primary_address_type'].lower() == 'ipv6'
configuration_mode = pif['ipv6_configuration_mode'] if ipv6 else pif['ip_configuration_mode']
inPane.AddStatusField(Lang('Device', 16), pif['device'])
if int(pif['VLAN']) >= 0:
inPane.AddStatusField(Lang('VLAN', 16), pif['VLAN'])
inPane.AddStatusField(Lang('MAC Address', 16), pif['MAC'])
inPane.AddStatusField(Lang('DHCP/Static IP', 16), pif['ip_configuration_mode'])
inPane.AddStatusField(Lang('DHCP/Static IP', 16), configuration_mode)

inPane.AddStatusField(Lang('IP address', 16), data.ManagementIP(''))
inPane.AddStatusField(Lang('Netmask', 16), data.ManagementNetmask(''))
Expand Down
24 changes: 16 additions & 8 deletions plugins-base/XSFeatureNetworkReset.py
Original file line number Diff line number Diff line change
Expand Up @@ -365,18 +365,23 @@ def Commit(self):
inventory['CURRENT_INTERFACES'] = ''
write_inventory(inventory)

ipv6 = self.IP.find(':') > -1

# Rewrite firstboot management.conf file, which will be picked it by xcp-networkd on restart (if used)
f = open(management_conf, 'w')
try:
f.write("LABEL='" + self.device + "'\n")
f.write("MODE='" + self.mode + "'\n")
f.write(("MODEV6" if ipv6 else "MODE") + "='" + self.mode + "'\n")
if self.vlan != '':
f.write("VLAN='" + self.vlan + "'\n")
if self.mode == 'static':
f.write("IP='" + self.IP + "'\n")
f.write("NETMASK='" + self.netmask + "'\n")
if ipv6:
f.write("IPv6='" + self.IP + "/" + self.netmask + "'\n")
else:
f.write("IP='" + self.IP + "'\n")
f.write("NETMASK='" + self.netmask + "'\n")
if self.gateway != '':
f.write("GATEWAY='" + self.gateway + "'\n")
f.write(("IPv6_GATEWAY" if ipv6 else "GATEWAY") + "='" + self.gateway + "'\n")
if self.dns != '':
f.write("DNS='" + self.dns + "'\n")
finally:
Expand All @@ -386,14 +391,17 @@ def Commit(self):
f = open(network_reset, 'w')
try:
f.write('DEVICE=' + self.device + '\n')
f.write('MODE=' + self.mode + '\n')
f.write(('MODE_V6' if ipv6 else 'MODE') + '=' + self.mode + '\n')
if self.vlan != '':
f.write('VLAN=' + self.vlan + '\n')
if self.mode == 'static':
f.write('IP=' + self.IP + '\n')
f.write('NETMASK=' + self.netmask + '\n')
if ipv6:
f.write('IPV6=' + self.IP + '/' + self.netmask + '\n')
else:
f.write('IP=' + self.IP + '\n')
f.write('NETMASK=' + self.netmask + '\n')
if self.gateway != '':
f.write('GATEWAY=' + self.gateway + '\n')
f.write(('GATEWAY_V6' if ipv6 else 'GATEWAY') + '=' + self.gateway + '\n')
if self.dns != '':
f.write('DNS=' + self.dns + '\n')
finally:
Expand Down
4 changes: 3 additions & 1 deletion plugins-base/XSMenuLayout.py
Original file line number Diff line number Diff line change
Expand Up @@ -68,11 +68,13 @@ def UpdateFieldsNETWORK(self, inPane):
ntpState = 'Disabled'

for pif in data.derived.managementpifs([]):
ipv6 = pif['primary_address_type'].lower() == 'ipv6'
configuration_mode = pif['ipv6_configuration_mode'] if ipv6 else pif['ip_configuration_mode']
inPane.AddStatusField(Lang('Device', 16), pif['device'])
if int(pif['VLAN']) >= 0:
inPane.AddStatusField(Lang('VLAN', 16), pif['VLAN'])
inPane.AddStatusField(Lang('MAC Address', 16), pif['MAC'])
inPane.AddStatusField(Lang('DHCP/Static IP', 16), pif['ip_configuration_mode'])
inPane.AddStatusField(Lang('DHCP/Static IP', 16), configuration_mode)

inPane.AddStatusField(Lang('IP address', 16), data.ManagementIP(''))
inPane.AddStatusField(Lang('Netmask', 16), data.ManagementNetmask(''))
Expand Down
2 changes: 1 addition & 1 deletion tests/test_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ def test_min(self):
self.assertTrue(IPUtils.ValidateIP('0.0.0.1'))

def test_beyond_min(self):
self.assertFalse(IPUtils.ValidateIP('0.0.0.0'))
self.assertTrue(IPUtils.ValidateIP('0.0.0.0'))

def test_max(self):
self.assertTrue(IPUtils.ValidateIP('255.255.255.255'))
Expand Down

0 comments on commit b990b4e

Please sign in to comment.