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