]>
Commit | Line | Data |
---|---|---|
15ef32ea RP |
1 | #!/usr/bin/python |
2 | # | |
d486dd0d | 3 | # Copyright 2014-2017 Cumulus Networks, Inc. All rights reserved. |
15ef32ea RP |
4 | # Author: Roopa Prabhu, roopa@cumulusnetworks.com |
5 | # | |
6 | ||
d486dd0d JF |
7 | import re |
8 | import time | |
9 | ||
15ef32ea | 10 | try: |
d486dd0d JF |
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: | |
6d359159 | 21 | import ifupdown.policymanager as policymanager |
fc5e1735 | 22 | import ifupdown.ifupdownflags as ifupdownflags |
d486dd0d JF |
23 | |
24 | from ifupdown.iface import * | |
20fd3a06 | 25 | from ifupdown.utils import utils |
d486dd0d JF |
26 | |
27 | from ifupdownaddons.dhclient import dhclient | |
28 | from ifupdownaddons.LinkUtils import LinkUtils | |
29 | from ifupdownaddons.modulebase import moduleBase | |
30 | ||
15ef32ea RP |
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 | ||
22b49c28 JF |
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 | ||
15ef32ea | 48 | def _up(self, ifaceobj): |
9e1b366e | 49 | # if dhclient is already running do not stop and start it |
de4104b0 JF |
50 | dhclient4_running = self.dhclientcmd.is_running(ifaceobj.name) |
51 | dhclient6_running = self.dhclientcmd.is_running6(ifaceobj.name) | |
96dddc0e JF |
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 | ||
15ef32ea | 59 | try: |
717cee31 | 60 | dhclient_cmd_prefix = None |
a0a8d7e0 SE |
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" | |
a252fb20 RD |
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 | ||
717cee31 | 72 | vrf = ifaceobj.get_attr_value_first('vrf') |
3cf287b8 RP |
73 | if (vrf and self.vrf_exec_cmd_prefix and |
74 | self.ipcmd.link_exists(vrf)): | |
717cee31 RP |
75 | dhclient_cmd_prefix = '%s %s' %(self.vrf_exec_cmd_prefix, vrf) |
76 | ||
004d1e65 | 77 | if 'inet' in ifaceobj.addr_family: |
de4104b0 JF |
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 | |
15ef32ea | 83 | try: |
de4104b0 JF |
84 | if not ifupdownflags.flags.PERFMODE: |
85 | self.dhclientcmd.stop(ifaceobj.name) | |
15ef32ea RP |
86 | except: |
87 | pass | |
de4104b0 JF |
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. | |
a252fb20 RD |
111 | if ll_wait_time: |
112 | timeout = ll_wait_time | |
113 | time.sleep(1) | |
114 | else: | |
115 | timeout = ll_wait_time+1 | |
116 | ||
de4104b0 | 117 | while timeout: |
d486dd0d JF |
118 | addr_output = utils.exec_command('%s -6 addr show %s' |
119 | %(utils.ip_cmd, ifaceobj.name)) | |
de4104b0 JF |
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 | |
a252fb20 RD |
126 | timeout -= 1 |
127 | if timeout: | |
128 | time.sleep(1) | |
b62a7e73 | 129 | |
15ef32ea | 130 | except Exception, e: |
bf3eda91 | 131 | self.log_error(str(e), ifaceobj) |
15ef32ea | 132 | |
96dddc0e JF |
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): | |
717cee31 RP |
145 | dhclient_cmd_prefix = None |
146 | vrf = ifaceobj.get_attr_value_first('vrf') | |
3cf287b8 RP |
147 | if (vrf and self.vrf_exec_cmd_prefix and |
148 | self.ipcmd.link_exists(vrf)): | |
717cee31 | 149 | dhclient_cmd_prefix = '%s %s' %(self.vrf_exec_cmd_prefix, vrf) |
004d1e65 | 150 | if 'inet6' in ifaceobj.addr_family: |
20fd3a06 | 151 | self.dhclientcmd.release6(ifaceobj.name, dhclient_cmd_prefix) |
004d1e65 | 152 | if 'inet' in ifaceobj.addr_family: |
20fd3a06 | 153 | self.dhclientcmd.release(ifaceobj.name, dhclient_cmd_prefix) |
96dddc0e JF |
154 | |
155 | def _down(self, ifaceobj): | |
156 | self._dhcp_down(ifaceobj) | |
15ef32ea RP |
157 | self.ipcmd.link_down(ifaceobj.name) |
158 | ||
159 | def _query_check(self, ifaceobj, ifaceobjcurr): | |
004d1e65 JF |
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 | |
15ef32ea | 170 | ifaceobjcurr.addr_method = 'dhcp' |
004d1e65 JF |
171 | if dhcp_v6: |
172 | dhcp_running = True | |
173 | if 'inet6' not in ifaceobj.addr_family and not dhcp_v4: | |
174 | status = ifaceStatus.ERROR | |
15ef32ea | 175 | ifaceobjcurr.addr_method = 'dhcp' |
004d1e65 JF |
176 | ifaceobjcurr.addr_family = ifaceobj.addr_family |
177 | if not dhcp_running: | |
178 | ifaceobjcurr.addr_family = [] | |
179 | status = ifaceStatus.ERROR | |
180 | ifaceobjcurr.status = status | |
15ef32ea RP |
181 | |
182 | def _query_running(self, ifaceobjrunning): | |
183 | if not self.ipcmd.link_exists(ifaceobjrunning.name): | |
15ef32ea RP |
184 | return |
185 | if self.dhclientcmd.is_running(ifaceobjrunning.name): | |
004d1e65 | 186 | ifaceobjrunning.addr_family.append('inet') |
15ef32ea | 187 | ifaceobjrunning.addr_method = 'dhcp' |
004d1e65 JF |
188 | if self.dhclientcmd.is_running6(ifaceobjrunning.name): |
189 | ifaceobjrunning.addr_family.append('inet6') | |
15ef32ea RP |
190 | ifaceobjrunning.addr_method = 'dhcp6' |
191 | ||
192 | _run_ops = {'up' : _up, | |
193 | 'down' : _down, | |
20fd3a06 | 194 | 'pre-down' : _down, |
15ef32ea RP |
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: | |
d486dd0d | 204 | self.ipcmd = LinkUtils() |
15ef32ea | 205 | |
84ca006f | 206 | def run(self, ifaceobj, operation, query_ifaceobj=None, **extra_args): |
15ef32ea RP |
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 | |
d486dd0d | 228 | (ifaceobj.addr_method != 'dhcp' and |
15ef32ea RP |
229 | ifaceobj.addr_method != 'dhcp6')): |
230 | return | |
231 | except: | |
232 | return | |
22b49c28 JF |
233 | if not self.is_dhcp_allowed_on(ifaceobj, syntax_check=False): |
234 | return | |
15ef32ea RP |
235 | self._init_command_handlers() |
236 | if operation == 'query-checkcurr': | |
237 | op_handler(self, ifaceobj, query_ifaceobj) | |
238 | else: | |
239 | op_handler(self, ifaceobj) |