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