]>
Commit | Line | Data |
---|---|---|
f82758bf RP |
1 | #!/usr/bin/python |
2 | # | |
3 | # Copyright 2014 Cumulus Networks, Inc. All rights reserved. | |
4 | # Author: Roopa Prabhu, roopa@cumulusnetworks.com | |
5 | # | |
0b762139 ST |
6 | import json |
7 | import ifupdown.policymanager as policymanager | |
f82758bf RP |
8 | |
9 | try: | |
10 | from ipaddr import IPNetwork | |
11 | from sets import Set | |
12 | from ifupdown.iface import * | |
0b762139 | 13 | from ifupdownaddons.utilsbase import * |
f82758bf RP |
14 | from ifupdownaddons.modulebase import moduleBase |
15 | from ifupdownaddons.iproute2 import iproute2 | |
16 | except ImportError, e: | |
17 | raise ImportError (str(e) + "- required module not found") | |
18 | ||
0b762139 | 19 | class ethtool(moduleBase,utilsBase): |
f82758bf RP |
20 | """ ifupdown2 addon module to configure ethtool attributes """ |
21 | ||
22 | _modinfo = {'mhelp' : 'ethtool configuration module for interfaces', | |
23 | 'attrs': { | |
24 | 'link-speed' : | |
25 | {'help' : 'set link speed', | |
0b762139 ST |
26 | 'example' : ['link-speed 1000'], |
27 | 'default' : 'varies by platform and port'}, | |
f82758bf RP |
28 | 'link-duplex' : |
29 | {'help': 'set link duplex', | |
30 | 'example' : ['link-duplex full'], | |
31 | 'validvals' : ['half', 'full'], | |
0b762139 | 32 | 'default' : 'full'}, |
f82758bf RP |
33 | 'link-autoneg' : |
34 | {'help': 'set autonegotiation', | |
35 | 'example' : ['link-autoneg on'], | |
36 | 'validvals' : ['on', 'off'], | |
0b762139 | 37 | 'default' : 'varies by platform and port'}}} |
f82758bf RP |
38 | |
39 | def __init__(self, *args, **kargs): | |
40 | moduleBase.__init__(self, *args, **kargs) | |
41 | self.ipcmd = None | |
42 | ||
0b762139 ST |
43 | def _post_up(self, ifaceobj, operation='post_up'): |
44 | """ | |
45 | _post_up and _pre_down will reset the layer 2 attributes to default policy | |
46 | settings. | |
47 | """ | |
f82758bf RP |
48 | if not self.ipcmd.link_exists(ifaceobj.name): |
49 | return | |
50 | cmd = '' | |
0b762139 ST |
51 | for attr in ['speed', 'duplex', 'autoneg']: |
52 | # attribute existed before but we must reset to default | |
53 | config_val = ifaceobj.get_attr_value_first('link-%s'%attr) | |
54 | default_val = policymanager.policymanager_api.get_iface_default( | |
55 | module_name='ethtool', | |
56 | ifname=ifaceobj.name, | |
57 | attr='link-%s'%attr) | |
58 | ||
59 | # check running values | |
60 | running_val = None | |
61 | if attr == 'autoneg': | |
62 | # we can only get autoneg from ethtool | |
63 | output = self.exec_commandl(['ethtool', ifaceobj.name]) | |
64 | running_val = self.get_autoneg(ethtool_output=output) | |
65 | else: | |
66 | running_val = self.read_file_oneline('/sys/class/net/%s/%s' % \ | |
67 | (ifaceobj.name, attr)) | |
68 | if config_val and config_val == running_val: | |
69 | # running value is what is configured, do nothing | |
70 | continue | |
71 | if not config_val and default_val and default_val == running_val: | |
72 | # nothing configured but the default is running | |
73 | continue | |
74 | # if we got this far, we need to change it | |
75 | if config_val and (config_val != running_val): | |
76 | # if the configured value is not set, set it | |
77 | cmd += ' %s %s' % (attr, config_val) | |
78 | elif default_val and (default_val != running_val): | |
79 | # or if it has a default not equal to running value, set it | |
80 | cmd += ' %s %s' % (attr, default_val) | |
81 | else: | |
82 | # no value set nor default, leave it alone | |
83 | pass | |
f82758bf | 84 | if cmd: |
0b762139 ST |
85 | self.logger.debug('ethtool %s: iface %s cmd is %s' % \ |
86 | (operation, ifaceobj.name, cmd)) | |
f82758bf | 87 | try: |
0b762139 ST |
88 | # we should only be calling ethtool if there |
89 | # is a speed set or we can find a default speed | |
90 | # because we should only be calling ethtool on swp ports | |
f82758bf RP |
91 | cmd = 'ethtool -s %s %s' %(ifaceobj.name, cmd) |
92 | self.exec_command(cmd) | |
93 | except Exception, e: | |
94 | ifaceobj.status = ifaceStatus.ERROR | |
95 | self.log_warn('%s: %s' %(ifaceobj.name, str(e))) | |
0b762139 ST |
96 | else: |
97 | pass | |
98 | ||
99 | def _pre_down(self, ifaceobj): | |
100 | pass #self._post_up(ifaceobj,operation="_pre_down") | |
f82758bf RP |
101 | |
102 | def _query_check(self, ifaceobj, ifaceobjcurr): | |
103 | """ | |
0b762139 ST |
104 | _query_check() needs to compare the configured (or running) |
105 | attribute with the running attribute. | |
106 | ||
107 | If there is nothing configured, we compare the default attribute with | |
108 | the running attribute and FAIL if they are different. | |
109 | This is because a reboot will lose their running attribute | |
110 | (the default will get set). | |
111 | """ | |
112 | for attr in ['speed', 'duplex', 'autoneg']: | |
113 | # autoneg comes from ethtool whereas speed and duplex from /sys/class | |
114 | if attr == 'autoneg': | |
115 | output = self.exec_commandl(['ethtool', ifaceobj.name]) | |
116 | running_attr = self.get_autoneg(ethtool_output=output) | |
117 | else: | |
118 | running_attr = self.read_file_oneline('/sys/class/net/%s/%s' % \ | |
119 | (ifaceobj.name, attr)) | |
120 | ||
121 | configured = ifaceobj.get_attr_value_first('link-%s'%attr) | |
122 | default = policymanager.policymanager_api.get_iface_default( | |
123 | module_name='ethtool', | |
124 | ifname=ifaceobj.name, | |
125 | attr='link-%s'%attr) | |
126 | ||
127 | # there is a case where there is no running config or | |
128 | # (there is no default and it is not configured). | |
129 | # In this case, we do nothing (e.g. eth0 has only a | |
130 | # default duplex, lo has nothing) | |
131 | if (not running_attr or (not configured and not default)): | |
132 | continue | |
133 | ||
134 | # we make sure we can get a running value first | |
135 | if (running_attr and configured and running_attr == configured): | |
136 | # PASS since running is what is configured | |
137 | ifaceobjcurr.update_config_with_status('link-%s'%attr, | |
138 | running_attr, 0) | |
139 | elif (running_attr and configured and running_attr != configured): | |
140 | # We show a FAIL since it is not the configured or default | |
141 | ifaceobjcurr.update_config_with_status('link-%s'%attr, | |
142 | running_attr, 1) | |
143 | elif (running_attr and default and running_attr == default): | |
144 | # PASS since running is default | |
145 | ifaceobjcurr.update_config_with_status('link-%s'%attr, | |
146 | running_attr, 0) | |
147 | elif (default or configured): | |
148 | # We show a FAIL since it is not the configured or default | |
149 | ifaceobjcurr.update_config_with_status('link-%s'%attr, | |
150 | running_attr, 1) | |
151 | return | |
152 | ||
153 | def get_autoneg(self,ethtool_output=None): | |
154 | """ | |
155 | get_autoneg simply calls the ethtool command and parses out | |
156 | the autoneg value. | |
157 | """ | |
158 | ethtool_attrs = ethtool_output.split() | |
159 | if ('Auto-negotiation:' in ethtool_attrs): | |
160 | return(ethtool_attrs[ethtool_attrs.index('Auto-negotiation:')+1]) | |
161 | else: | |
162 | return(None) | |
163 | ||
164 | def _query_running(self, ifaceobj, ifaceobj_getfunc=None): | |
165 | """ | |
166 | _query_running looks at the speed and duplex from /sys/class | |
167 | and retreives autoneg from ethtool. We do not report autoneg | |
168 | if speed is not available because this usually means the link is | |
169 | down and the autoneg value is not reliable when the link is down. | |
170 | """ | |
171 | # do not bother showing swp ifaces that are not up for the speed | |
172 | # duplex and autoneg are not reliable. | |
173 | if not self.ipcmd.is_link_up(ifaceobj.name): | |
f82758bf | 174 | return |
0b762139 ST |
175 | for attr in ['speed', 'duplex', 'autoneg']: |
176 | # autoneg comes from ethtool whereas speed and duplex from /sys/class | |
177 | running_attr = None | |
178 | try: | |
179 | if attr == 'autoneg': | |
180 | output=self.exec_commandl(['ethtool', ifaceobj.name]) | |
181 | running_attr = self.get_autoneg(ethtool_output=output) | |
f82758bf | 182 | else: |
0b762139 ST |
183 | running_attr = self.read_file_oneline('/sys/class/net/%s/%s' % \ |
184 | (ifaceobj.name, attr)) | |
185 | except: | |
186 | # for nonexistent interfaces, we get an error (rc = 256 or 19200) | |
187 | pass | |
188 | ||
189 | # show it | |
190 | if (running_attr): | |
191 | ifaceobj.update_config('link-%s'%attr, running_attr) | |
f82758bf | 192 | |
f82758bf RP |
193 | return |
194 | ||
0b762139 ST |
195 | _run_ops = {'pre-down' : _pre_down, |
196 | 'post-up' : _post_up, | |
197 | 'query-checkcurr' : _query_check, | |
198 | 'query-running' : _query_running } | |
f82758bf RP |
199 | |
200 | def get_ops(self): | |
201 | """ returns list of ops supported by this module """ | |
202 | return self._run_ops.keys() | |
203 | ||
204 | def _init_command_handlers(self): | |
205 | if not self.ipcmd: | |
206 | self.ipcmd = iproute2(**self.get_flags()) | |
207 | ||
208 | def run(self, ifaceobj, operation, query_ifaceobj=None, **extra_args): | |
209 | """ run ethtool configuration on the interface object passed as | |
210 | argument | |
211 | ||
212 | Args: | |
213 | **ifaceobj** (object): iface object | |
214 | ||
215 | **operation** (str): any of 'post-up', 'query-checkcurr', | |
216 | 'query-running' | |
217 | Kwargs: | |
218 | **query_ifaceobj** (object): query check ifaceobject. This is only | |
219 | valid when op is 'query-checkcurr'. It is an object same as | |
220 | ifaceobj, but contains running attribute values and its config | |
221 | status. The modules can use it to return queried running state | |
222 | of interfaces. status is success if the running state is same | |
223 | as user required state in ifaceobj. error otherwise. | |
224 | """ | |
225 | op_handler = self._run_ops.get(operation) | |
226 | if not op_handler: | |
227 | return | |
228 | self._init_command_handlers() | |
0b762139 ST |
229 | |
230 | # check to make sure we are only checking/setting interfaces with | |
231 | # no lower interfaces. No bridges, no vlans, loopbacks. | |
232 | if ifaceobj.lowerifaces != None or \ | |
233 | self.ipcmd.link_isloopback(ifaceobj.name) or \ | |
234 | self.ipcmd.is_vlan_device_by_name(ifaceobj.name): | |
235 | return | |
236 | ||
f82758bf RP |
237 | if operation == 'query-checkcurr': |
238 | op_handler(self, ifaceobj, query_ifaceobj) | |
239 | else: | |
240 | op_handler(self, ifaceobj) |