]> git.proxmox.com Git - mirror_ifupdown2.git/blob - ifupdown2/addons/dhcp.py
Policy to wait for IPv6 link local address to be available
[mirror_ifupdown2.git] / ifupdown2 / addons / dhcp.py
1 #!/usr/bin/python
2 #
3 # Copyright 2014-2017 Cumulus Networks, Inc. All rights reserved.
4 # Author: Roopa Prabhu, roopa@cumulusnetworks.com
5 #
6
7 import re
8 import time
9
10 try:
11 import ifupdown2.ifupdown.policymanager as policymanager
12 import ifupdown2.ifupdown.ifupdownflags as ifupdownflags
13
14 from ifupdown2.ifupdown.iface import *
15 from ifupdown2.ifupdown.utils import utils
16
17 from ifupdown2.ifupdownaddons.dhclient import dhclient
18 from ifupdown2.ifupdownaddons.LinkUtils import LinkUtils
19 from ifupdown2.ifupdownaddons.modulebase import moduleBase
20 except ImportError:
21 import ifupdown.policymanager as policymanager
22 import ifupdown.ifupdownflags as ifupdownflags
23
24 from ifupdown.iface import *
25 from ifupdown.utils import utils
26
27 from ifupdownaddons.dhclient import dhclient
28 from ifupdownaddons.LinkUtils import LinkUtils
29 from ifupdownaddons.modulebase import moduleBase
30
31
32 class dhcp(moduleBase):
33 """ ifupdown2 addon module to configure dhcp on interface """
34
35 def __init__(self, *args, **kargs):
36 moduleBase.__init__(self, *args, **kargs)
37 self.dhclientcmd = dhclient(**kargs)
38 self.ipcmd = None
39
40 def syntax_check(self, ifaceobj, ifaceobj_getfunc):
41 return self.is_dhcp_allowed_on(ifaceobj, syntax_check=True)
42
43 def is_dhcp_allowed_on(self, ifaceobj, syntax_check):
44 if ifaceobj.addr_method and 'dhcp' in ifaceobj.addr_method:
45 return utils.is_addr_ip_allowed_on(ifaceobj, syntax_check=True)
46 return True
47
48 def _up(self, ifaceobj):
49 # if dhclient is already running do not stop and start it
50 dhclient4_running = self.dhclientcmd.is_running(ifaceobj.name)
51 dhclient6_running = self.dhclientcmd.is_running6(ifaceobj.name)
52
53 # today if we have an interface with both inet and inet6, if we
54 # remove the inet or inet6 or both then execute ifreload, we need
55 # to release/kill the appropriate dhclient(4/6) if they are running
56 self._down_stale_dhcp_config(ifaceobj, 'inet', dhclient4_running)
57 self._down_stale_dhcp_config(ifaceobj, 'inet6', dhclient6_running)
58
59 try:
60 dhclient_cmd_prefix = None
61 dhcp_wait = policymanager.policymanager_api.get_attr_default(
62 module_name=self.__class__.__name__, attr='dhcp-wait')
63 wait = not str(dhcp_wait).lower() == "no"
64 inet6_ll_wait = policymanager.policymanager_api.get_iface_default(module_name=self.__class__.__name__, \
65 ifname=ifaceobj.name, attr='inet6-ll-wait')
66 try:
67 ll_wait_time = int(inet6_ll_wait)
68 except:
69 ll_wait_time = 10
70 pass
71
72 vrf = ifaceobj.get_attr_value_first('vrf')
73 if (vrf and self.vrf_exec_cmd_prefix and
74 self.ipcmd.link_exists(vrf)):
75 dhclient_cmd_prefix = '%s %s' %(self.vrf_exec_cmd_prefix, vrf)
76
77 if 'inet' in ifaceobj.addr_family:
78 if dhclient4_running:
79 self.logger.info('dhclient4 already running on %s. '
80 'Not restarting.' % ifaceobj.name)
81 else:
82 # First release any existing dhclient processes
83 try:
84 if not ifupdownflags.flags.PERFMODE:
85 self.dhclientcmd.stop(ifaceobj.name)
86 except:
87 pass
88 self.dhclientcmd.start(ifaceobj.name, wait=wait,
89 cmd_prefix=dhclient_cmd_prefix)
90 if 'inet6' in ifaceobj.addr_family:
91 if dhclient6_running:
92 self.logger.info('dhclient6 already running on %s. '
93 'Not restarting.' % ifaceobj.name)
94 else:
95 accept_ra = ifaceobj.get_attr_value_first('accept_ra')
96 if accept_ra:
97 # XXX: Validate value
98 self.sysctl_set('net.ipv6.conf.%s' %ifaceobj.name +
99 '.accept_ra', accept_ra)
100 autoconf = ifaceobj.get_attr_value_first('autoconf')
101 if autoconf:
102 # XXX: Validate value
103 self.sysctl_set('net.ipv6.conf.%s' %ifaceobj.name +
104 '.autoconf', autoconf)
105 try:
106 self.dhclientcmd.stop6(ifaceobj.name)
107 except:
108 pass
109 #add delay before starting IPv6 dhclient to
110 #make sure the configured interface/link is up.
111 if ll_wait_time:
112 timeout = ll_wait_time
113 time.sleep(1)
114 else:
115 timeout = ll_wait_time+1
116
117 while timeout:
118 addr_output = utils.exec_command('%s -6 addr show %s'
119 %(utils.ip_cmd, ifaceobj.name))
120 r = re.search('inet6 .* scope link', addr_output)
121 if r:
122 self.dhclientcmd.start6(ifaceobj.name,
123 wait=wait,
124 cmd_prefix=dhclient_cmd_prefix)
125 return
126 timeout -= 1
127 if timeout:
128 time.sleep(1)
129
130 except Exception, e:
131 self.log_error(str(e), ifaceobj)
132
133 def _down_stale_dhcp_config(self, ifaceobj, family, dhclientX_running):
134 addr_family = ifaceobj.addr_family
135 try:
136 if not family in ifaceobj.addr_family and dhclientX_running:
137 ifaceobj.addr_family = [family]
138 self._dhcp_down(ifaceobj)
139 except:
140 pass
141 finally:
142 ifaceobj.addr_family = addr_family
143
144 def _dhcp_down(self, ifaceobj):
145 dhclient_cmd_prefix = None
146 vrf = ifaceobj.get_attr_value_first('vrf')
147 if (vrf and self.vrf_exec_cmd_prefix and
148 self.ipcmd.link_exists(vrf)):
149 dhclient_cmd_prefix = '%s %s' %(self.vrf_exec_cmd_prefix, vrf)
150 if 'inet6' in ifaceobj.addr_family:
151 self.dhclientcmd.release6(ifaceobj.name, dhclient_cmd_prefix)
152 if 'inet' in ifaceobj.addr_family:
153 self.dhclientcmd.release(ifaceobj.name, dhclient_cmd_prefix)
154
155 def _down(self, ifaceobj):
156 self._dhcp_down(ifaceobj)
157 self.ipcmd.link_down(ifaceobj.name)
158
159 def _query_check(self, ifaceobj, ifaceobjcurr):
160 status = ifaceStatus.SUCCESS
161 dhcp_running = False
162
163 dhcp_v4 = self.dhclientcmd.is_running(ifaceobjcurr.name)
164 dhcp_v6 = self.dhclientcmd.is_running6(ifaceobjcurr.name)
165
166 if dhcp_v4:
167 dhcp_running = True
168 if 'inet' not in ifaceobj.addr_family and not dhcp_v6:
169 status = ifaceStatus.ERROR
170 ifaceobjcurr.addr_method = 'dhcp'
171 if dhcp_v6:
172 dhcp_running = True
173 if 'inet6' not in ifaceobj.addr_family and not dhcp_v4:
174 status = ifaceStatus.ERROR
175 ifaceobjcurr.addr_method = 'dhcp'
176 ifaceobjcurr.addr_family = ifaceobj.addr_family
177 if not dhcp_running:
178 ifaceobjcurr.addr_family = []
179 status = ifaceStatus.ERROR
180 ifaceobjcurr.status = status
181
182 def _query_running(self, ifaceobjrunning):
183 if not self.ipcmd.link_exists(ifaceobjrunning.name):
184 return
185 if self.dhclientcmd.is_running(ifaceobjrunning.name):
186 ifaceobjrunning.addr_family.append('inet')
187 ifaceobjrunning.addr_method = 'dhcp'
188 if self.dhclientcmd.is_running6(ifaceobjrunning.name):
189 ifaceobjrunning.addr_family.append('inet6')
190 ifaceobjrunning.addr_method = 'dhcp6'
191
192 _run_ops = {'up' : _up,
193 'down' : _down,
194 'pre-down' : _down,
195 'query-checkcurr' : _query_check,
196 'query-running' : _query_running }
197
198 def get_ops(self):
199 """ returns list of ops supported by this module """
200 return self._run_ops.keys()
201
202 def _init_command_handlers(self):
203 if not self.ipcmd:
204 self.ipcmd = LinkUtils()
205
206 def run(self, ifaceobj, operation, query_ifaceobj=None, **extra_args):
207 """ run dhcp configuration on the interface object passed as argument
208
209 Args:
210 **ifaceobj** (object): iface object
211
212 **operation** (str): any of 'up', 'down', 'query-checkcurr',
213 'query-running'
214
215 Kwargs:
216 **query_ifaceobj** (object): query check ifaceobject. This is only
217 valid when op is 'query-checkcurr'. It is an object same as
218 ifaceobj, but contains running attribute values and its config
219 status. The modules can use it to return queried running state
220 of interfaces. status is success if the running state is same
221 as user required state in ifaceobj. error otherwise.
222 """
223 op_handler = self._run_ops.get(operation)
224 if not op_handler:
225 return
226 try:
227 if (operation != 'query-running' and
228 (ifaceobj.addr_method != 'dhcp' and
229 ifaceobj.addr_method != 'dhcp6')):
230 return
231 except:
232 return
233 if not self.is_dhcp_allowed_on(ifaceobj, syntax_check=False):
234 return
235 self._init_command_handlers()
236 if operation == 'query-checkcurr':
237 op_handler(self, ifaceobj, query_ifaceobj)
238 else:
239 op_handler(self, ifaceobj)