-
Notifications
You must be signed in to change notification settings - Fork 23
Handle IPv6 #4
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: master
Are you sure you want to change the base?
Handle IPv6 #4
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -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 * | ||
|
|
@@ -190,28 +190,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) | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The previous code accepted In the long term I'd rather adopt ipaddress now that we aren't held back by python 2 |
||
|
|
||
| @classmethod | ||
| def AssertValidNetmask(cls, inIP): | ||
|
|
@@ -228,7 +229,7 @@ def AssertValidIP(cls, inIP): | |
| @classmethod | ||
| def AssertValidHostname(cls, inName): | ||
| # Allow 0-9, A-Z, a-z and hyphen, but disallow hyphen at start and end | ||
| if not re.match(r'[0-9A-Za-z]([-0-9A-Za-z]{0,61}[0-9A-Za-z]|)$', inName): | ||
| if not (re.match(r'[0-9A-Za-z]([-0-9A-Za-z]{0,61}[0-9A-Za-z]|)$', inName) or cls.AssertValidIP(inName)): | ||
| raise Exception(Lang('Invalid hostname')) | ||
| return inName | ||
|
|
||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -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 and currentPIF['primary_address_type'].lower() == 'ipv6'): | ||
| mode_choicedefs.append(ChoiceDef(Lang("Autoconf"), lambda : self.HandleModeChoice("AUTOCONF") )) | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Current code assumes IP and DNS configuration are linked. This makes selecting both SLAAC and DHCPv6 at the same time for configuring IPs in an interface (and getting 2 IPs for the same interface), or SLAAC for IP and DHCP for DNS awkward. I think letting users choose how they want to configure IPs for the interface, and how they want to configure DNS independently would be easier for users to parse. This is even if we decide not support the dual-ipv6 setting. |
||
| 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') ), | ||
|
|
@@ -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' | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Is it possible to configure IPv6 if IPv4 is the primary address type like this? Doesn't seem compatible with having both types of addresses |
||
| 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: | ||
|
|
@@ -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) | ||
|
|
||
|
|
@@ -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': | ||
|
|
@@ -463,11 +477,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('')) | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -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") | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This will get hairy with dual-stack ip, it needs a rethinking when that is introduced so both ipv4 and ipv6 can be written independently. As it is, it will get difficult to read with 3 modes. |
||
| 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: | ||
|
|
@@ -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: | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -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')) | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Why is this test being changed?
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Because changing the validate method hasa changed how
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Doesn't other code depend on this property? |
||
|
|
||
| def test_max(self): | ||
| self.assertTrue(IPUtils.ValidateIP('255.255.255.255')) | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The expressions in the two ifs can raise exceptions when the keys are not defined, please add presence checks before checking the contents.