]> git.proxmox.com Git - mirror_ifupdown2.git/blob - pkg/iface.py
Doc updates + cleanup
[mirror_ifupdown2.git] / pkg / iface.py
1 #!/usr/bin/python
2 #
3 # Copyright 2013. Cumulus Networks, Inc.
4 # Author: Roopa Prabhu, roopa@cumulusnetworks.com
5 #
6 # iface --
7 # interface object
8 #
9
10 """ifupdown2 network interface object
11
12 It closely resembles the 'iface' object in /etc/network/interfaces
13 file. But can be extended to include any other network interface format
14 """
15
16 from collections import OrderedDict
17 import logging
18 import json
19
20 class ifaceStatus():
21 """Enumerates iface status """
22
23 UNKNOWN = 0x1
24 SUCCESS = 0x2
25 ERROR = 0x3
26 NOTFOUND = 0x4
27
28 @classmethod
29 def to_str(cls, state):
30 if state == cls.UNKNOWN:
31 return 'unknown'
32 elif state == cls.SUCCESS:
33 return 'success'
34 elif state == cls.ERROR:
35 return 'error'
36 elif state == cls.NOTFOUND:
37 return 'notfound'
38
39 @classmethod
40 def from_str(cls, state_str):
41 if state_str == 'unknown':
42 return cls.UNKNOWN
43 elif state_str == 'success':
44 return cls.SUCCESS
45 elif state_str == 'error':
46 return cls.ERROR
47
48 class ifaceState():
49 """Enumerates iface state """
50
51 UNKNOWN = 0x1
52 NEW = 0x2
53 PRE_UP = 0x3
54 UP = 0x4
55 POST_UP = 0x5
56 PRE_DOWN = 0x6
57 DOWN = 0x7
58 POST_DOWN = 0x8
59
60 # Pseudo states
61 QUERY_CHECKCURR = 0x9
62 QUERY_RUNNING = 0xa
63
64 @classmethod
65 def to_str(cls, state):
66 if state == cls.UNKNOWN:
67 return 'unknown'
68 elif state == cls.NEW:
69 return 'new'
70 elif state == cls.PRE_UP:
71 return 'pre-up'
72 elif state == cls.UP:
73 return 'up'
74 elif state == cls.POST_UP:
75 return 'post-up'
76 elif state == cls.PRE_DOWN:
77 return 'pre-down'
78 elif state == cls.DOWN:
79 return 'down'
80 elif state == cls.POST_DOWN:
81 return 'post-down'
82 elif state == cls.QUERY_CHECKCURR:
83 return 'query-checkcurr'
84 elif state == cls.QUERY_RUNNING:
85 return 'query-running'
86
87 @classmethod
88 def from_str(cls, state_str):
89 if state_str == 'unknown':
90 return cls.UNKNOWN
91 elif state_str == 'new':
92 return cls.NEW
93 elif state_str == 'pre-up':
94 return cls.PRE_UP
95 elif state_str == 'up':
96 return cls.UP
97 elif state_str == 'post-up':
98 return cls.POST_UP
99 elif state_str == 'pre-down':
100 return cls.PRE_DOWN
101 elif state_str == 'down':
102 return cls.DOWN
103 elif state_str == 'post-down':
104 return cls.POST_DOWN
105 elif state_str == 'query-checkcurr':
106 return cls.QUERY_CHECKCURR
107 elif state_str == 'query-running':
108 return cls.QUERY_RUNNING
109
110 class ifaceJsonEncoder(json.JSONEncoder):
111 def default(self, o):
112 retconfig = {}
113 if o.config:
114 retconfig = dict((k, (v[0] if len(v) == 1 else v))
115 for k,v in o.config.items())
116 return OrderedDict({'name' : o.name,
117 'addr_method' : o.addr_method,
118 'addr_family' : o.addr_family,
119 'auto' : o.auto,
120 'config' : retconfig})
121
122 class ifaceJsonDecoder():
123 @classmethod
124 def json_to_ifaceobj(cls, ifaceattrdict):
125 ifaceattrdict['config'] = OrderedDict([(k, (v if isinstance(v, list)
126 else [v]))
127 for k,v in ifaceattrdict.get('config',
128 OrderedDict()).items()])
129 return iface(attrsdict=ifaceattrdict)
130
131 class iface():
132 """ ifupdown2 interface object class
133
134 Attributes:
135 name Name of the interface
136 addr_family Address family eg, inet, inet6. Can be None to indicate both address families
137 addr_method Address method eg, static, manual or None for static
138 address method
139 config dictionary of config lines for this interface
140 state Configuration state of an interface as defined by
141 ifaceState
142 status Configuration status of an interface as defined by
143 ifaceStatus
144 flags Internal flags used by iface processing
145 priv_flags private flags owned by module using this class
146 refcnt reference count, indicating number of interfaces
147 dependent on this iface
148 lowerifaces list of interface names lower to this interface or
149 this interface depends on
150 upperifaces list of interface names upper to this interface or
151 the interfaces that depend on this interface
152 auto True if interface belongs to the auto class
153 classes List of classes the interface belongs to
154 env shell environment the interface needs during execution
155 raw_config raw interface config from file
156 """
157
158 # flag to indicate that the object was created from pickled state
159 _PICKLED = 0x1
160 HAS_SIBLINGS = 0x2
161
162 version = '0.1'
163
164 def __init__(self, attrsdict={}):
165 self._set_attrs_from_dict(attrsdict)
166 self._config_status = {}
167 """dict with config status of iface attributes"""
168 self.state = ifaceState.NEW
169 """iface state (of type ifaceState) """
170 self.status = ifaceStatus.UNKNOWN
171 """iface status (of type ifaceStatus) """
172 self.flags = 0x0
173 """iface flags """
174 self.priv_flags = 0x0
175 """iface priv flags. can be used by the external object manager """
176 self.refcnt = 0
177 """iface refcnt (incremented for each dependent this interface has) """
178 self.lowerifaces = None
179 """lower iface list (in other words: slaves of this interface """
180 self.upperifaces = None
181 """upper iface list (in other words: master of this interface """
182 self.classes = []
183 """interface classes this iface belongs to """
184 self.env = None
185 """environment variable dict required for this interface to run"""
186 self.raw_config = []
187 """interface config/attributes in raw format (eg: as it appeared in the interfaces file)"""
188 self.linkstate = None
189 """linkstate of the interface"""
190
191 def _set_attrs_from_dict(self, attrdict):
192 self.auto = attrdict.get('auto', False)
193 self.name = attrdict.get('name')
194 self.addr_family = attrdict.get('addr_family')
195 self.addr_method = attrdict.get('addr_method')
196 self.config = attrdict.get('config', OrderedDict())
197
198 def inc_refcnt(self):
199 """ increment refcnt of the interface. Usually used to indicate that
200 it has dependents """
201 self.refcnt += 1
202
203 def dec_refcnt(self):
204 """ decrement refcnt of the interface. Usually used to indicate that
205 it has lost its dependent """
206 self.refcnt -= 1
207
208 def is_config_present(self):
209 """ returns true if the interface has user provided config,
210 false otherwise """
211 addr_method = self.addr_method
212 if addr_method and addr_method in ['dhcp', 'dhcp6', 'loopback']:
213 return True
214 if not self.config:
215 return False
216 else:
217 return True
218
219 def set_class(self, classname):
220 """ appends class to the interfaces class list """
221 self.classes.append(classname)
222
223 def set_state_n_status(self, state, status):
224 """ sets state and status of an interface """
225 self.state = state
226 self.status = status
227
228 def set_flag(self, flag):
229 self.flags |= flag
230
231 def clear_flag(self, flag):
232 self.flags &= ~flag
233
234 def add_to_upperifaces(self, upperifacename):
235 """ add to the list of upperifaces """
236 if self.upperifaces:
237 if upperifacename not in self.upperifaces:
238 self.upperifaces.append(upperifacename)
239 else:
240 self.upperifaces = [upperifacename]
241
242 def get_attr_value(self, attr_name):
243 """ add to the list of upperifaces """
244 return self.config.get(attr_name)
245
246 def get_attr_value_first(self, attr_name):
247 """ get first value of the specified attr name """
248 attr_value_list = self.config.get(attr_name)
249 if attr_value_list:
250 return attr_value_list[0]
251 return None
252
253 def get_attr_value_n(self, attr_name, attr_index):
254 """ get n'th value of the specified attr name """
255 attr_value_list = self.config.get(attr_name)
256 if attr_value_list:
257 try:
258 return attr_value_list[attr_index]
259 except:
260 return None
261 return None
262
263 @property
264 def get_env(self):
265 """ get shell environment variables the interface must execute in """
266 if not self.env:
267 self.generate_env()
268 return self.env
269
270 def generate_env(self):
271 """ generate shell environment variables dict interface must execute
272 in. This is used to support legacy ifupdown scripts
273 """
274 env = {}
275 config = self.config
276 env['IFACE'] = self.name
277 for attr, attr_value in config.items():
278 attr_env_name = 'IF_%s' %attr.upper()
279 env[attr_env_name] = attr_value[0]
280 if env:
281 self.env = env
282
283 def update_config(self, attr_name, attr_value):
284 """ add attribute name and value to the interface config """
285 self.config.setdefault(attr_name, []).append(attr_value)
286
287 def update_config_dict(self, attrdict):
288 self.config.update(attrdict)
289
290 def update_config_with_status(self, attr_name, attr_value, attr_status=0):
291 """ add attribute name and value to the interface config and also
292 update the config_status dict with status of this attribute config """
293 if not attr_value:
294 attr_value = ''
295 self.config.setdefault(attr_name, []).append(attr_value)
296 self._config_status.setdefault(attr_name, []).append(attr_status)
297
298 # set global iface state
299 if attr_status:
300 self.status = ifaceStatus.ERROR
301 elif self.status != ifaceStatus.ERROR:
302 # Not already error, mark success
303 self.status = ifaceStatus.SUCCESS
304
305 def get_config_attr_status(self, attr_name, idx=0):
306 """ get status of a attribute config on this interface.
307
308 Looks at the iface _config_status dict"""
309 return self._config_status.get(attr_name, [])[idx]
310
311 def compare(self, dstiface):
312 """ compares iface object with iface object passed as argument
313
314 Returns True if object self is same as dstiface and False otherwise """
315
316 if self.name != dstiface.name: return False
317 if self.addr_family != dstiface.addr_family: return False
318 if self.addr_method != dstiface.addr_method: return False
319 if self.auto != dstiface.auto: return False
320 if self.classes != dstiface.classes: return False
321 if any(True for k in self.config if k not in dstiface.config):
322 return False
323 if any(True for k,v in self.config.items()
324 if v != dstiface.config.get(k)): return False
325 return True
326
327 def __getstate__(self):
328 odict = self.__dict__.copy()
329 del odict['state']
330 del odict['status']
331 del odict['lowerifaces']
332 del odict['upperifaces']
333 del odict['refcnt']
334 del odict['_config_status']
335 del odict['flags']
336 del odict['priv_flags']
337 del odict['raw_config']
338 del odict['linkstate']
339 del odict['env']
340 return odict
341
342 def __setstate__(self, dict):
343 self.__dict__.update(dict)
344 self._config_status = {}
345 self.state = ifaceState.NEW
346 self.status = ifaceStatus.UNKNOWN
347 self.refcnt = 0
348 self.flags = 0
349 self.lowerifaces = None
350 self.upperifaces = None
351 self.linkstate = None
352 self.env = None
353 self.priv_flags = 0
354 self.raw_config = []
355 self.flags |= self._PICKLED
356
357 def dump_raw(self, logger):
358 indent = ' '
359 if self.auto:
360 print 'auto %s' %self.name
361 print (self.raw_config[0])
362 for i in range(1, len(self.raw_config)):
363 print(indent + self.raw_config[i])
364
365 def dump(self, logger):
366 indent = '\t'
367 logger.info(self.name + ' : {')
368 logger.info(indent + 'family: %s' %self.addr_family)
369 logger.info(indent + 'method: %s' %self.addr_method)
370 logger.info(indent + 'flags: %x' %self.flags)
371 logger.info(indent + 'state: %s'
372 %ifaceState.to_str(self.state))
373 logger.info(indent + 'status: %s'
374 %ifaceStatus.to_str(self.status))
375 logger.info(indent + 'refcnt: %d' %self.refcnt)
376 d = self.lowerifaces
377 if d:
378 logger.info(indent + 'lowerdevs: %s' %str(d))
379 else:
380 logger.info(indent + 'lowerdevs: None')
381
382 logger.info(indent + 'config: ')
383 config = self.config
384 if config:
385 logger.info(indent + indent + str(config))
386 logger.info('}')
387
388 def dump_pretty(self, with_status=False,
389 successstr='success', errorstr='error'):
390 indent = '\t'
391 outbuf = ''
392 if self.auto:
393 outbuf += 'auto %s\n' %self.name
394 outbuf += 'iface %s' %self.name
395 if self.addr_family:
396 outbuf += ' %s' %self.addr_family
397 if self.addr_method:
398 outbuf += ' %s' %self.addr_method
399 if with_status:
400 if (self.status == ifaceStatus.NOTFOUND or
401 self.status == ifaceStatus.ERROR):
402 outbuf += ' (%s)' %errorstr
403 elif self.status == ifaceStatus.SUCCESS:
404 outbuf += ' (%s)' %successstr
405 if self.status == ifaceStatus.NOTFOUND:
406 if with_status:
407 outbuf = (outbuf.encode('utf8')
408 if isinstance(outbuf, unicode) else outbuf)
409 print outbuf + '\n'
410 return
411 outbuf += '\n'
412 config = self.config
413 if config:
414 for cname, cvaluelist in config.items():
415 idx = 0
416 for cv in cvaluelist:
417 if not cv: continue
418 if with_status:
419 s = self.get_config_attr_status(cname, idx)
420 if s:
421 outbuf += (indent + '%s %s (%s)\n'
422 %(cname, cv, errorstr))
423 elif s == 0:
424 outbuf += (indent + '%s %s (%s)\n'
425 %(cname, cv, successstr))
426 else:
427 outbuf += indent + '%s %s\n' %(cname, cv)
428 idx += 1
429 if with_status:
430 outbuf = (outbuf.encode('utf8')
431 if isinstance(outbuf, unicode) else outbuf)
432 print outbuf