Skip to content

Commit 63501c8

Browse files
committed
Handle IPv6
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: Benjamin Reis <[email protected]>
1 parent 171793d commit 63501c8

7 files changed

+97
-44
lines changed

XSConsoleData.py

Lines changed: 25 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1039,7 +1039,22 @@ def ReconfigureManagement(self, inPIF, inMode, inIP, inNetmask, inGateway, in
10391039
Auth.Inst().AssertAuthenticated()
10401040
try:
10411041
self.RequireSession()
1042-
self.session.xenapi.PIF.reconfigure_ip(inPIF['opaqueref'], inMode, inIP, inNetmask, inGateway, FirstValue(inDNS, ''))
1042+
if inPIF['primary_address_type'].lower() == 'ipv4':
1043+
self.session.xenapi.PIF.reconfigure_ip(inPIF['opaqueref'], inMode, inIP, inNetmask, inGateway, FirstValue(inDNS, ''))
1044+
if inPIF['ipv6_configuration_mode'].lower() == 'static':
1045+
# Update IPv6 DNS as well
1046+
self.session.xenapi.PIF.reconfigure_ipv6(
1047+
inPIF['opaqueref'], inPIF['ipv6_configuration_mode'], ','.join(inPIF['IPv6']), inPIF['ipv6_gateway'], FirstValue(inDNS, '')
1048+
)
1049+
else:
1050+
inIPv6 = '' if inIP == '0.0.0.0' else inIP + '/' + inNetmask
1051+
inGateway = '' if inGateway == '0.0.0.0' else inGateway
1052+
self.session.xenapi.PIF.reconfigure_ipv6(inPIF['opaqueref'], inMode, inIPv6, inGateway, FirstValue(inDNS, ''))
1053+
if inPIF['ip_configuration_mode'].lower() == 'static':
1054+
# Update IPv4 DNS as well
1055+
self.session.xenapi.PIF.reconfigure_ip(
1056+
inPIF['opaqueref'], inPIF['ip_configuration_mode'], inPIF['IP'], inPIF['netmask'], inPIF['gateway'], FirstValue(inDNS, '')
1057+
)
10431058
self.session.xenapi.host.management_reconfigure(inPIF['opaqueref'])
10441059
status, output = getstatusoutput('%s host-signal-networking-change' % (Config.Inst().XECLIPath()))
10451060
if status != 0:
@@ -1059,6 +1074,7 @@ def DisableManagement(self):
10591074
# Disable the PIF that the management interface was using
10601075
for pif in self.derived.managementpifs([]):
10611076
self.session.xenapi.PIF.reconfigure_ip(pif['opaqueref'], 'None','' ,'' ,'' ,'')
1077+
self.session.xenapi.PIF.reconfigure_ipv6(pif['opaqueref'], 'None','' ,'' ,'')
10621078
finally:
10631079
# Network reconfigured so this link is potentially no longer valid
10641080
self.session = Auth.Inst().CloseSession(self.session)
@@ -1099,7 +1115,12 @@ def ManagementNetmask(self, inDefault = None):
10991115
retVal = inDefault
11001116

11011117
for pif in self.derived.managementpifs([]):
1102-
retVal = pif['netmask']
1118+
ipv6 = pif['primary_address_type'].lower() == 'ipv6'
1119+
try:
1120+
# IPv6 are stored as an array of `<ipv6>/<prefix>`
1121+
retVal = pif['IPv6'][0].split('/')[1] if ipv6 else pif['netmask']
1122+
except IndexError:
1123+
return ''
11031124
if retVal:
11041125
break
11051126

@@ -1109,7 +1130,8 @@ def ManagementGateway(self, inDefault = None):
11091130
retVal = inDefault
11101131

11111132
for pif in self.derived.managementpifs([]):
1112-
retVal = pif['gateway']
1133+
ipv6 = pif['primary_address_type'].lower() == 'ipv6'
1134+
retVal = pif['ipv6_gateway'] if ipv6 else pif['gateway']
11131135
if retVal:
11141136
break
11151137

XSConsoleUtils.py

Lines changed: 19 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@
1313
# with this program; if not, write to the Free Software Foundation, Inc.,
1414
# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
1515

16-
import re, signal, string, subprocess, time, types
16+
import re, signal, socket, string, subprocess, time, types
1717
from pprint import pprint
1818

1919
from XSConsoleBases import *
@@ -190,28 +190,29 @@ def DateTimeToSecs(cls, inDateTime):
190190
return retVal
191191

192192
class IPUtils:
193+
@classmethod
194+
def ValidateIPFamily(cls, text, family):
195+
try:
196+
socket.inet_pton(family, text)
197+
return True
198+
except socket.error:
199+
return False
200+
201+
@classmethod
202+
def ValidateIPv4(cls, text):
203+
return cls.ValidateIPFamily(text, socket.AF_INET)
204+
205+
@classmethod
206+
def ValidateIPv6(cls, text):
207+
return cls.ValidateIPFamily(text, socket.AF_INET6)
208+
193209
@classmethod
194210
def ValidateIP(cls, text):
195-
rc = re.match("^(0|[1-9][0-9]*)\.(0|[1-9][0-9]*)\.(0|[1-9][0-9]*)\.(0|[1-9][0-9]*)$", text)
196-
if not rc: return False
197-
ints = list(map(int, rc.groups()))
198-
largest = 0
199-
for i in ints:
200-
if i > 255: return False
201-
largest = max(largest, i)
202-
if largest == 0: return False
203-
return True
211+
return cls.ValidateIPv4(text) or cls.ValidateIPv6(text)
204212

205213
@classmethod
206214
def ValidateNetmask(cls, text):
207-
rc = re.match("^(0|[1-9][0-9]*)\.(0|[1-9][0-9]*)\.(0|[1-9][0-9]*)\.(0|[1-9][0-9]*)$", text)
208-
if not rc:
209-
return False
210-
ints = list(map(int, rc.groups()))
211-
for i in ints:
212-
if i > 255:
213-
return False
214-
return True
215+
return cls.ValidateIPv4(text) or (int(text) > 4 and int(text) < 128)
215216

216217
@classmethod
217218
def AssertValidNetmask(cls, inIP):

plugins-base/XSFeatureDNS.py

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -179,7 +179,9 @@ def StatusUpdateHandler(cls, inPane):
179179
inPane.AddWrappedTextField(str(dns))
180180
inPane.NewLine()
181181
for pif in data.derived.managementpifs([]):
182-
if pif['ip_configuration_mode'].lower().startswith('static'):
182+
ipv6 = pif['primary_address_type'].lower() == 'ipv6'
183+
configuration_mode = pif['ipv6_configuration_mode'] if ipv6 else pif['ip_configuration_mode']
184+
if configuration_mode.lower().startswith('static'):
183185
inPane.AddKeyHelpField( { Lang("Enter") : Lang("Update DNS Servers") })
184186
break
185187
inPane.AddKeyHelpField( {
@@ -203,7 +205,9 @@ def Register(self):
203205
def ActivateHandler(cls):
204206
data = Data.Inst()
205207
for pif in data.derived.managementpifs([]):
206-
if pif['ip_configuration_mode'].lower().startswith('static'):
208+
ipv6 = pif['primary_address_type'].lower() == 'ipv6'
209+
configuration_mode = pif['ipv6_configuration_mode'] if ipv6 else pif['ip_configuration_mode']
210+
if configuration_mode.lower().startswith('static'):
207211
DialogueUtils.AuthenticatedOnly(lambda: Layout.Inst().PushDialogue(DNSDialogue()))
208212
return
209213

plugins-base/XSFeatureInterface.py

Lines changed: 27 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -53,11 +53,14 @@ def __init__(self):
5353

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

56-
self.modeMenu = Menu(self, None, Lang("Select IP Address Configuration Mode"), [
57-
ChoiceDef(Lang("DHCP"), lambda: self.HandleModeChoice('DHCP2') ),
58-
ChoiceDef(Lang("DHCP with Manually Assigned Hostname"), lambda: self.HandleModeChoice('DHCPMANUAL') ),
59-
ChoiceDef(Lang("Static"), lambda: self.HandleModeChoice('STATIC') )
60-
])
56+
mode_choicedefs = []
57+
if(currentPIF and currentPIF['primary_address_type'].lower() == 'ipv6'):
58+
mode_choicedefs.append(ChoiceDef(Lang("Autoconf"), lambda : self.HandleModeChoice("AUTOCONF") ))
59+
mode_choicedefs.append(ChoiceDef(Lang("DHCP"), lambda: self.HandleModeChoice('DHCP2') ))
60+
mode_choicedefs.append(ChoiceDef(Lang("DHCP with Manually Assigned Hostname"),
61+
lambda: self.HandleModeChoice('DHCPMANUAL') ))
62+
mode_choicedefs.append(ChoiceDef(Lang("Static"), lambda: self.HandleModeChoice('STATIC') ))
63+
self.modeMenu = Menu(self, None, Lang("Select IP Address Configuration Mode"), mode_choicedefs)
6164

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

8588
if currentPIF is not None:
86-
if 'ip_configuration_mode' in currentPIF: self.mode = currentPIF['ip_configuration_mode']
89+
ipv6 = currentPIF['primary_address_type'].lower() == 'ipv6'
90+
configuration_mode_key = 'ipv6_configuration_mode' if ipv6 else 'ip_configuration_mode'
91+
if configuration_mode_key in currentPIF:
92+
self.mode = currentPIF[configuration_mode_key]
8793
if self.mode.lower().startswith('static'):
88-
if 'IP' in currentPIF: self.IP = currentPIF['IP']
89-
if 'netmask' in currentPIF: self.netmask = currentPIF['netmask']
90-
if 'gateway' in currentPIF: self.gateway = currentPIF['gateway']
94+
if 'IP' in currentPIF:
95+
self.IP = currentPIF['IPv6'][0].split('/')[0] if ipv6 else currentPIF['IP']
96+
if 'netmask' in currentPIF:
97+
self.netmask = currentPIF['IPv6'][0].split('/')[1] if ipv6 else currentPIF['netmask']
98+
if 'gateway' in currentPIF:
99+
self.gateway = currentPIF['ipv6_gateway'] if ipv6 else currentPIF['gateway']
91100

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

172-
if self.mode != 'Static' and self.hostname == '':
181+
if self.mode == 'DHCP' and self.hostname == '':
173182
pane.AddStatusField(Lang("Hostname", 16), Lang("Assigned by DHCP"))
183+
elif self.mode == 'Autoconf' and self.hostname == '':
184+
pane.AddStatusField(Lang("Hostname", 16), Lang("Automatically assigned"))
174185
else:
175186
pane.AddStatusField(Lang("Hostname", 16), self.hostname)
176187

@@ -376,6 +387,9 @@ def HandleModeChoice(self, inChoice):
376387
self.hostname = Data.Inst().host.hostname('')
377388
self.mode = 'Static'
378389
self.ChangeState('STATICIP')
390+
elif inChoice == 'AUTOCONF':
391+
self.mode = 'Autoconf'
392+
self.ChangeState('PRECOMMIT')
379393

380394
def HandlePostDHCPChoice(self, inChoice):
381395
if inChoice == 'CONTINUE':
@@ -463,11 +477,13 @@ def StatusUpdateHandler(cls, inPane):
463477
inPane.AddWrappedTextField(Lang("<No interface configured>"))
464478
else:
465479
for pif in data.derived.managementpifs([]):
480+
ipv6 = pif['primary_address_type'].lower() == 'ipv6'
481+
configuration_mode = pif['ipv6_configuration_mode'] if ipv6 else pif['ip_configuration_mode']
466482
inPane.AddStatusField(Lang('Device', 16), pif['device'])
467483
if int(pif['VLAN']) >= 0:
468484
inPane.AddStatusField(Lang('VLAN', 16), pif['VLAN'])
469485
inPane.AddStatusField(Lang('MAC Address', 16), pif['MAC'])
470-
inPane.AddStatusField(Lang('DHCP/Static IP', 16), pif['ip_configuration_mode'])
486+
inPane.AddStatusField(Lang('DHCP/Static IP', 16), configuration_mode)
471487

472488
inPane.AddStatusField(Lang('IP address', 16), data.ManagementIP(''))
473489
inPane.AddStatusField(Lang('Netmask', 16), data.ManagementNetmask(''))

plugins-base/XSFeatureNetworkReset.py

Lines changed: 16 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -365,18 +365,23 @@ def Commit(self):
365365
inventory['CURRENT_INTERFACES'] = ''
366366
write_inventory(inventory)
367367

368+
ipv6 = self.IP.find(':') > -1
369+
368370
# Rewrite firstboot management.conf file, which will be picked it by xcp-networkd on restart (if used)
369371
f = open(management_conf, 'w')
370372
try:
371373
f.write("LABEL='" + self.device + "'\n")
372-
f.write("MODE='" + self.mode + "'\n")
374+
f.write(("MODEV6" if ipv6 else "MODE") + "='" + self.mode + "'\n")
373375
if self.vlan != '':
374376
f.write("VLAN='" + self.vlan + "'\n")
375377
if self.mode == 'static':
376-
f.write("IP='" + self.IP + "'\n")
377-
f.write("NETMASK='" + self.netmask + "'\n")
378+
if ipv6:
379+
f.write("IPv6='" + self.IP + "/" + self.netmask + "'\n")
380+
else:
381+
f.write("IP='" + self.IP + "'\n")
382+
f.write("NETMASK='" + self.netmask + "'\n")
378383
if self.gateway != '':
379-
f.write("GATEWAY='" + self.gateway + "'\n")
384+
f.write(("IPv6_GATEWAY" if ipv6 else "GATEWAY") + "='" + self.gateway + "'\n")
380385
if self.dns != '':
381386
f.write("DNS='" + self.dns + "'\n")
382387
finally:
@@ -386,14 +391,17 @@ def Commit(self):
386391
f = open(network_reset, 'w')
387392
try:
388393
f.write('DEVICE=' + self.device + '\n')
389-
f.write('MODE=' + self.mode + '\n')
394+
f.write(('MODE_V6' if ipv6 else 'MODE') + '=' + self.mode + '\n')
390395
if self.vlan != '':
391396
f.write('VLAN=' + self.vlan + '\n')
392397
if self.mode == 'static':
393-
f.write('IP=' + self.IP + '\n')
394-
f.write('NETMASK=' + self.netmask + '\n')
398+
if ipv6:
399+
f.write('IPV6=' + self.IP + '/' + self.netmask + '\n')
400+
else:
401+
f.write('IP=' + self.IP + '\n')
402+
f.write('NETMASK=' + self.netmask + '\n')
395403
if self.gateway != '':
396-
f.write('GATEWAY=' + self.gateway + '\n')
404+
f.write(('GATEWAY_V6' if ipv6 else 'GATEWAY') + '=' + self.gateway + '\n')
397405
if self.dns != '':
398406
f.write('DNS=' + self.dns + '\n')
399407
finally:

plugins-base/XSMenuLayout.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -65,11 +65,13 @@ def UpdateFieldsNETWORK(self, inPane):
6565
inPane.AddTitleField(Lang("Current Management Interface"))
6666

6767
for pif in data.derived.managementpifs([]):
68+
ipv6 = pif['primary_address_type'].lower() == 'ipv6'
69+
configuration_mode = pif['ipv6_configuration_mode'] if ipv6 else pif['ip_configuration_mode']
6870
inPane.AddStatusField(Lang('Device', 16), pif['device'])
6971
if int(pif['VLAN']) >= 0:
7072
inPane.AddStatusField(Lang('VLAN', 16), pif['VLAN'])
7173
inPane.AddStatusField(Lang('MAC Address', 16), pif['MAC'])
72-
inPane.AddStatusField(Lang('DHCP/Static IP', 16), pif['ip_configuration_mode'])
74+
inPane.AddStatusField(Lang('DHCP/Static IP', 16), configuration_mode)
7375

7476
inPane.AddStatusField(Lang('IP address', 16), data.ManagementIP(''))
7577
inPane.AddStatusField(Lang('Netmask', 16), data.ManagementNetmask(''))

tests/test_utils.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ def test_min(self):
1212
self.assertTrue(IPUtils.ValidateIP('0.0.0.1'))
1313

1414
def test_beyond_min(self):
15-
self.assertFalse(IPUtils.ValidateIP('0.0.0.0'))
15+
self.assertTrue(IPUtils.ValidateIP('0.0.0.0'))
1616

1717
def test_max(self):
1818
self.assertTrue(IPUtils.ValidateIP('255.255.255.255'))

0 commit comments

Comments
 (0)