]> git.proxmox.com Git - mirror_ifupdown2.git/blame - ifupdown2/ifupdown/networkinterfaces.py
networkinterfaces: support relative paths in source/source-directory statements
[mirror_ifupdown2.git] / ifupdown2 / ifupdown / networkinterfaces.py
CommitLineData
35681c06 1#!/usr/bin/env python3
3e8ee54f 2#
d486dd0d 3# Copyright 2014-2017 Cumulus Networks, Inc. All rights reserved.
3e8ee54f 4# Author: Roopa Prabhu, roopa@cumulusnetworks.com
5#
6# networkInterfaces --
7# ifupdown network interfaces file parser
8#
a6f80f0e 9
0845cc57 10import collections
679e6567 11import copy
d486dd0d
JF
12import glob
13import logging
0845cc57
PKS
14import os
15import re
d486dd0d
JF
16
17try:
18 from ifupdown2.ifupdown.iface import *
19 from ifupdown2.ifupdown.utils import utils
20 from ifupdown2.ifupdown.template import templateEngine
bd441a51 21except (ImportError, ModuleNotFoundError):
d486dd0d
JF
22 from ifupdown.iface import *
23 from ifupdown.utils import utils
24 from ifupdown.template import templateEngine
25
a6f80f0e 26
c0071225
RP
27whitespaces = '\n\t\r '
28
a6f80f0e 29class networkInterfaces():
2c0ad8b3 30 """ debian ifupdown /etc/network/interfaces file parser """
a6f80f0e 31
77054f7f
SA
32 _addrfams = {'inet' : ['static', 'manual', 'loopback', 'dhcp', 'dhcp6', 'ppp', 'tunnel'],
33 'inet6' : ['static', 'manual', 'loopback', 'dhcp', 'dhcp6', 'ppp', 'tunnel']}
223ba5af 34 # tunnel is part of the address family for backward compatibility but is not required.
d913472d 35
14dc390d 36 def __init__(self, interfacesfile='/etc/network/interfaces',
3dcc1d0e 37 interfacesfileiobuf=None, interfacesfileformat='native',
f27710fe 38 template_enable='0', template_engine=None,
223ba5af 39 template_lookuppath=None, raw=False):
2c0ad8b3
RP
40 """This member function initializes the networkinterfaces parser object.
41
42 Kwargs:
904908bc
RP
43 **interfacesfile** (str): path to the interfaces file (default is /etc/network/interfaces)
44
45 **interfacesfileiobuf** (object): interfaces file io stream
46
47 **interfacesfileformat** (str): format of interfaces file (choices are 'native' and 'json'. 'native' being the default)
48
49 **template_engine** (str): template engine name
50
51 **template_lookuppath** (str): template lookup path
2c0ad8b3
RP
52
53 Raises:
54 AttributeError, KeyError """
55
d486dd0d
JF
56 self.auto_ifaces = []
57 self.callbacks = {}
58 self.auto_all = False
223ba5af 59 self.raw = raw
a6f80f0e 60 self.logger = logging.getLogger('ifupdown.' +
61 self.__class__.__name__)
d08d5f54 62 self.callbacks = {'iface_found' : None,
3dcc1d0e 63 'validateifaceattr' : None,
64 'validateifaceobj' : None}
a6f80f0e 65 self.allow_classes = {}
14dc390d 66 self.interfacesfile = interfacesfile
3dcc1d0e 67 self.interfacesfileiobuf = interfacesfileiobuf
68 self.interfacesfileformat = interfacesfileformat
14dc390d 69 self._filestack = [self.interfacesfile]
e272efd9
JF
70
71 self._template_engine = None
1f5db4a8 72 self._template_enable = template_enable
e272efd9
JF
73 self._template_engine_name = template_engine
74 self._template_engine_path = template_lookuppath
75
d40e96ee 76 self._currentfile_has_template = False
f102ef63 77 self._ws_split_regex = re.compile(r'[\s\t]\s*')
a6f80f0e 78
cfa06db6
RP
79 self.errors = 0
80 self.warns = 0
81
9dce3561 82 @property
83 def _currentfile(self):
84 try:
85 return self._filestack[-1]
3218f49d 86 except Exception:
14dc390d 87 return self.interfacesfile
9dce3561 88
89 def _parse_error(self, filename, lineno, msg):
d40e96ee 90 if lineno == -1 or self._currentfile_has_template:
9dce3561 91 self.logger.error('%s: %s' %(filename, msg))
92 else:
93 self.logger.error('%s: line%d: %s' %(filename, lineno, msg))
cfa06db6 94 self.errors += 1
a6f80f0e 95
aa5751ba
RP
96 def _parse_warn(self, filename, lineno, msg):
97 if lineno == -1 or self._currentfile_has_template:
c46af1c9 98 self.logger.warning('%s: %s' %(filename, msg))
aa5751ba 99 else:
c46af1c9 100 self.logger.warning('%s: line%d: %s' %(filename, lineno, msg))
cfa06db6 101 self.warns += 1
aa5751ba 102
3dcc1d0e 103 def _validate_addr_family(self, ifaceobj, lineno=-1):
004d1e65
JF
104 for family in ifaceobj.addr_family:
105 if not self._addrfams.get(family):
d913472d 106 self._parse_error(self._currentfile, lineno,
004d1e65
JF
107 'iface %s: unsupported address family \'%s\''
108 % (ifaceobj.name, family))
109 ifaceobj.addr_family = []
d913472d 110 ifaceobj.addr_method = None
111 return
112 if ifaceobj.addr_method:
004d1e65 113 if ifaceobj.addr_method not in self._addrfams.get(family):
d913472d 114 self._parse_error(self._currentfile, lineno,
004d1e65
JF
115 'iface %s: unsupported '
116 'address method \'%s\''
117 % (ifaceobj.name, ifaceobj.addr_method))
d913472d 118 else:
119 ifaceobj.addr_method = 'static'
120
a6f80f0e 121 def subscribe(self, callback_name, callback_func):
904908bc 122 """This member function registers callback functions.
2c0ad8b3
RP
123
124 Args:
904908bc
RP
125 **callback_name** (str): callback function name (supported names: 'iface_found', 'validateifaceattr', 'validateifaceobj')
126
127 **callback_func** (function pointer): callback function pointer
2c0ad8b3
RP
128
129 Warns on error
130 """
131
3b01ed76
JF
132 if callback_name not in list(self.callbacks.keys()):
133 print('warning: invalid callback ' + callback_name)
a6f80f0e 134 return -1
135
136 self.callbacks[callback_name] = callback_func
137
a6f80f0e 138 def ignore_line(self, line):
c0071225 139 l = line.strip(whitespaces)
fe0a57d3 140 if not l or l[0] == '#':
a6f80f0e 141 return 1
a6f80f0e 142 return 0
143
144 def process_allow(self, lines, cur_idx, lineno):
3e8ee54f 145 allow_line = lines[cur_idx]
a6f80f0e 146
f102ef63 147 words = re.split(self._ws_split_regex, allow_line)
a6f80f0e 148 if len(words) <= 1:
be0b20f2 149 raise Exception('invalid allow line \'%s\' at line %d'
150 %(allow_line, lineno))
a6f80f0e 151
152 allow_class = words[0].split('-')[1]
153 ifacenames = words[1:]
154
62ddec8b 155 if self.allow_classes.get(allow_class):
a6f80f0e 156 for i in ifacenames:
157 self.allow_classes[allow_class].append(i)
158 else:
159 self.allow_classes[allow_class] = ifacenames
a6f80f0e 160 return 0
161
a6f80f0e 162 def process_source(self, lines, cur_idx, lineno):
163 # Support regex
9a810db1 164 self.logger.debug('processing sourced line ..\'%s\'' % lines[cur_idx])
f102ef63 165 sourced_file = re.split(self._ws_split_regex, lines[cur_idx], 2)[1]
9a810db1 166
62ddec8b 167 if sourced_file:
9a810db1
PKS
168 if not os.path.isabs(sourced_file):
169 sourced_file = os.path.join(os.path.dirname(self._currentfile), sourced_file)
170
bb23b2f2 171 filenames = sorted(glob.glob(sourced_file))
2b5635d4 172 if not filenames:
3cdb1619
RP
173 if '*' not in sourced_file:
174 self._parse_warn(self._currentfile, lineno,
2b5635d4
RP
175 'cannot find source file %s' %sourced_file)
176 return 0
177 for f in filenames:
eab25b7c 178 self.read_file(f)
a6f80f0e 179 else:
9dce3561 180 self._parse_error(self._currentfile, lineno,
181 'unable to read source line')
a6f80f0e 182 return 0
183
0845cc57
PKS
184 def process_source_directory(self, lines, cur_idx, lineno):
185 self.logger.debug('processing source-directory line ..\'%s\'' % lines[cur_idx])
186 sourced_directory = re.split(self._ws_split_regex, lines[cur_idx], 2)[1]
9a810db1 187
0845cc57 188 if sourced_directory:
9a810db1
PKS
189 if not os.path.isabs(sourced_directory):
190 sourced_directory = os.path.join(os.path.dirname(self._currentfile), sourced_directory)
191
0845cc57
PKS
192 folders = glob.glob(sourced_directory)
193 for folder in folders:
194 filenames = [file for file in os.listdir(folder) if re.match(r'^[a-zA-Z0-9_-]+$', file)]
195
196 for f in filenames:
197 self.read_file(os.path.join(folder, f))
198 else:
199 self._parse_error(self._currentfile, lineno,
200 'unable to read source-directory line')
201 return 0
202
a6f80f0e 203 def process_auto(self, lines, cur_idx, lineno):
f102ef63 204 auto_ifaces = re.split(self._ws_split_regex, lines[cur_idx])[1:]
9dce3561 205 if not auto_ifaces:
d913472d 206 self._parse_error(self._currentfile, lineno,
9dce3561 207 'invalid auto line \'%s\''%lines[cur_idx])
208 return 0
679e6567
RP
209 for a in auto_ifaces:
210 if a == 'all':
211 self.auto_all = True
212 break
213 r = utils.parse_iface_range(a)
214 if r:
eba4da6e
RP
215 if len(r) == 3:
216 # eg swp1.[2-4], r = "swp1.", 2, 4)
217 for i in range(r[1], r[2]+1):
218 self.auto_ifaces.append('%s%d' %(r[0], i))
219 elif len(r) == 4:
220 for i in range(r[1], r[2]+1):
221 # eg swp[2-4].100, r = ("swp", 2, 4, ".100")
222 self.auto_ifaces.append('%s%d%s' %(r[0], i, r[3]))
679e6567 223 self.auto_ifaces.append(a)
a6f80f0e 224 return 0
225
14dc390d 226 def _add_to_iface_config(self, ifacename, iface_config, attrname,
227 attrval, lineno):
7a51240e 228 newattrname = attrname.replace("_", "-")
7949b8a5 229 try:
9e012f9e
RP
230 if not self.callbacks.get('validateifaceattr')(newattrname,
231 attrval):
7949b8a5 232 self._parse_error(self._currentfile, lineno,
14dc390d 233 'iface %s: unsupported keyword (%s)'
234 %(ifacename, attrname))
7949b8a5 235 return
3218f49d 236 except Exception:
7949b8a5 237 pass
7a51240e 238 attrvallist = iface_config.get(newattrname, [])
239 if newattrname in ['scope', 'netmask', 'broadcast', 'preferred-lifetime']:
be0b20f2 240 # For attributes that are related and that can have multiple
241 # entries, store them at the same index as their parent attribute.
242 # The example of such attributes is 'address' and its related
d486dd0d 243 # attributes. since the related attributes can be optional,
be0b20f2 244 # we add null string '' in places where they are optional.
245 # XXX: this introduces awareness of attribute names in
246 # this class which is a violation.
247
248 # get the index corresponding to the 'address'
249 addrlist = iface_config.get('address')
250 if addrlist:
251 # find the index of last address element
252 for i in range(0, len(addrlist) - len(attrvallist) -1):
253 attrvallist.append('')
254 attrvallist.append(attrval)
7a51240e 255 iface_config[newattrname] = attrvallist
be0b20f2 256 elif not attrvallist:
7a51240e 257 iface_config[newattrname] = [attrval]
be0b20f2 258 else:
7a51240e 259 iface_config[newattrname].append(attrval)
a6f80f0e 260
679e6567 261 def parse_iface(self, lines, cur_idx, lineno, ifaceobj):
a6f80f0e 262 lines_consumed = 0
579b3f25 263 line_idx = cur_idx
a6f80f0e 264
c0071225 265 iface_line = lines[cur_idx].strip(whitespaces)
f102ef63 266 iface_attrs = re.split(self._ws_split_regex, iface_line)
739f665b 267 ifacename = iface_attrs[1]
a6f80f0e 268
eba4da6e
RP
269 if (not utils.is_ifname_range(ifacename) and
270 utils.check_ifname_size_invalid(ifacename)):
9432c671
RP
271 self._parse_warn(self._currentfile, lineno,
272 '%s: interface name too long' %ifacename)
273
ef892ccc
RP
274 # in cases where mako is unable to render the template
275 # or incorrectly renders it due to user template
276 # errors, we maybe left with interface names with
277 # mako variables in them. There is no easy way to
278 # recognize and warn about these. In the below check
279 # we try to warn the user of such cases by looking for
280 # variable patterns ('$') in interface names.
281 if '$' in ifacename:
282 self._parse_warn(self._currentfile, lineno,
283 '%s: unexpected characters in interface name' %ifacename)
284
223ba5af
JF
285 if self.raw:
286 ifaceobj.raw_config.append(iface_line)
a6f80f0e 287 iface_config = collections.OrderedDict()
288 for line_idx in range(cur_idx + 1, len(lines)):
c0071225 289 l = lines[line_idx].strip(whitespaces)
a6f80f0e 290 if self.ignore_line(l) == 1:
223ba5af
JF
291 if self.raw:
292 ifaceobj.raw_config.append(l)
a6f80f0e 293 continue
f102ef63
RP
294 attrs = re.split(self._ws_split_regex, l, 1)
295 if self._is_keyword(attrs[0]):
a6f80f0e 296 line_idx -= 1
297 break
f102ef63 298 # if not a keyword, every line must have at least a key and value
eab25b7c 299 if len(attrs) < 2:
9dce3561 300 self._parse_error(self._currentfile, line_idx,
14dc390d 301 'iface %s: invalid syntax \'%s\'' %(ifacename, l))
eab25b7c 302 continue
223ba5af
JF
303 if self.raw:
304 ifaceobj.raw_config.append(l)
d08d5f54 305 attrname = attrs[0]
f102ef63
RP
306 # preprocess vars (XXX: only preprocesses $IFACE for now)
307 attrval = re.sub(r'\$IFACE', ifacename, attrs[1])
14dc390d 308 self._add_to_iface_config(ifacename, iface_config, attrname,
309 attrval, line_idx+1)
a6f80f0e 310 lines_consumed = line_idx - cur_idx
311
312 # Create iface object
37c0543d 313 if ifacename.find(':') != -1:
62ddec8b 314 ifaceobj.name = ifacename.split(':')[0]
37c0543d 315 else:
62ddec8b 316 ifaceobj.name = ifacename
37c0543d 317
62ddec8b 318 ifaceobj.config = iface_config
a6f80f0e 319 ifaceobj.generate_env()
d913472d 320
321 try:
004d1e65
JF
322 if iface_attrs[2]:
323 ifaceobj.addr_family.append(iface_attrs[2])
62ddec8b 324 ifaceobj.addr_method = iface_attrs[3]
d913472d 325 except IndexError:
326 # ignore
327 pass
328 self._validate_addr_family(ifaceobj, lineno)
a6f80f0e 329
679e6567 330 if self.auto_all or (ifaceobj.name in self.auto_ifaces):
62ddec8b 331 ifaceobj.auto = True
a6f80f0e 332
62ddec8b 333 classes = self.get_allow_classes_for_iface(ifaceobj.name)
fe0a57d3 334 if classes:
62ddec8b 335 [ifaceobj.set_class(c) for c in classes]
d486dd0d 336
a6f80f0e 337 return lines_consumed # Return next index
338
eba4da6e
RP
339 def _create_ifaceobj_clone(self, ifaceobj, newifaceobjname,
340 newifaceobjtype, newifaceobjflags):
341 ifaceobj_new = copy.deepcopy(ifaceobj)
342 ifaceobj_new.realname = '%s' %ifaceobj.name
343 ifaceobj_new.name = newifaceobjname
344 ifaceobj_new.type = newifaceobjtype
345 ifaceobj_new.flags = newifaceobjflags
346
347 return ifaceobj_new
348
679e6567
RP
349 def process_iface(self, lines, cur_idx, lineno):
350 ifaceobj = iface()
351 lines_consumed = self.parse_iface(lines, cur_idx, lineno, ifaceobj)
352
353 range_val = utils.parse_iface_range(ifaceobj.name)
354 if range_val:
eba4da6e
RP
355 if len(range_val) == 3:
356 for v in range(range_val[1], range_val[2]+1):
357 ifacename = '%s%d' %(range_val[0], v)
358 if utils.check_ifname_size_invalid(ifacename):
359 self._parse_warn(self._currentfile, lineno,
360 '%s: interface name too long' %ifacename)
361 flags = iface.IFACERANGE_ENTRY
362 if v == range_val[1]:
363 flags |= iface.IFACERANGE_START
364 ifaceobj_new = self._create_ifaceobj_clone(ifaceobj,
365 ifacename, ifaceobj.type, flags)
366 self.callbacks.get('iface_found')(ifaceobj_new)
367 elif len(range_val) == 4:
368 for v in range(range_val[1], range_val[2]+1):
369 ifacename = '%s%d%s' %(range_val[0], v, range_val[3])
370 if utils.check_ifname_size_invalid(ifacename):
371 self._parse_warn(self._currentfile, lineno,
372 '%s: interface name too long' %ifacename)
373 flags = iface.IFACERANGE_ENTRY
374 if v == range_val[1]:
375 flags |= iface.IFACERANGE_START
376 ifaceobj_new = self._create_ifaceobj_clone(ifaceobj,
377 ifacename, ifaceobj.type, flags)
378 self.callbacks.get('iface_found')(ifaceobj_new)
679e6567
RP
379 else:
380 self.callbacks.get('iface_found')(ifaceobj)
381
382 return lines_consumed # Return next index
a6f80f0e 383
84ca006f
RP
384 def process_vlan(self, lines, cur_idx, lineno):
385 ifaceobj = iface()
386 lines_consumed = self.parse_iface(lines, cur_idx, lineno, ifaceobj)
387
388 range_val = utils.parse_iface_range(ifaceobj.name)
389 if range_val:
eba4da6e
RP
390 if len(range_val) == 3:
391 for v in range(range_val[1], range_val[2]+1):
392 flags = iface.IFACERANGE_ENTRY
393 if v == range_val[1]:
394 flags |= iface.IFACERANGE_START
395 ifaceobj_new = self._create_ifaceobj_clone(ifaceobj,
396 '%s%d' %(range_val[0], v),
397 ifaceType.BRIDGE_VLAN, flags)
398 self.callbacks.get('iface_found')(ifaceobj_new)
399 elif len(range_val) == 4:
400 for v in range(range_val[1], range_val[2]+1):
401 flags = iface.IFACERANGE_ENTRY
402 if v == range_val[1]:
403 flags |= iface.IFACERANGE_START
404 ifaceobj_new = self._create_ifaceobj_clone(ifaceobj,
405 '%s%d%s' %(range_val[0], v, range_val[3]),
406 ifaceType.BRIDGE_VLAN,
407 flags)
408 self.callbacks.get('iface_found')(ifaceobj_new)
84ca006f
RP
409 else:
410 ifaceobj.type = ifaceType.BRIDGE_VLAN
411 self.callbacks.get('iface_found')(ifaceobj)
412
413 return lines_consumed # Return next index
414
0845cc57
PKS
415 network_elems = {
416 'source': process_source,
417 'source-directory': process_source_directory,
418 'allow': process_allow,
419 'auto': process_auto,
420 'iface': process_iface,
421 'vlan': process_vlan
422 }
a6f80f0e 423
9dce3561 424 def _is_keyword(self, str):
a6f80f0e 425 # The additional split here is for allow- keyword
3b01ed76 426 if (str in list(self.network_elems.keys()) or
3d58f6af 427 str.split('-')[0] == 'allow'):
a6f80f0e 428 return 1
a6f80f0e 429 return 0
430
0845cc57
PKS
431 def _get_keyword_func(self, str_):
432 tmp_str = str_.split('-')[0]
433 if tmp_str == "allow":
434 return self.network_elems.get(tmp_str)
435 else:
436 return self.network_elems.get(str_)
a6f80f0e 437
438 def get_allow_classes_for_iface(self, ifacename):
439 classes = []
3b01ed76 440 for class_name, ifacenames in list(self.allow_classes.items()):
a6f80f0e 441 if ifacename in ifacenames:
442 classes.append(class_name)
a6f80f0e 443 return classes
444
3dcc1d0e 445 def process_interfaces(self, filedata):
5b65654f
RP
446
447 # process line continuations
448 filedata = ' '.join(d.strip() for d in filedata.split('\\'))
449
a6f80f0e 450 line_idx = 0
451 lines_consumed = 0
62ddec8b 452 raw_config = filedata.split('\n')
c0071225 453 lines = [l.strip(whitespaces) for l in raw_config]
579b3f25 454 while (line_idx < len(lines)):
579b3f25 455 if self.ignore_line(lines[line_idx]):
456 line_idx += 1
457 continue
f102ef63 458 words = re.split(self._ws_split_regex, lines[line_idx])
c0071225
RP
459 if not words:
460 line_idx += 1
461 continue
579b3f25 462 # Check if first element is a supported keyword
9dce3561 463 if self._is_keyword(words[0]):
464 keyword_func = self._get_keyword_func(words[0])
d913472d 465 lines_consumed = keyword_func(self, lines, line_idx, line_idx+1)
579b3f25 466 line_idx += lines_consumed
467 else:
9dce3561 468 self._parse_error(self._currentfile, line_idx + 1,
469 'error processing line \'%s\'' %lines[line_idx])
579b3f25 470 line_idx += 1
579b3f25 471 return 0
a6f80f0e 472
3dcc1d0e 473 def read_filedata(self, filedata):
d40e96ee 474 self._currentfile_has_template = False
579b3f25 475 # run through template engine
e272efd9
JF
476 if filedata and '%' in filedata:
477 try:
800417ae
JF
478 if not self._template_engine:
479 self._template_engine = templateEngine(
1f5db4a8
MG
480 template_engine=self._template_engine_name,
481 template_enable=self._template_enable,
482 template_lookuppath=self._template_engine_path)
e272efd9
JF
483 rendered_filedata = self._template_engine.render(filedata)
484 if rendered_filedata is filedata:
485 self._currentfile_has_template = False
486 else:
487 self._currentfile_has_template = True
3b01ed76 488 except Exception as e:
e272efd9
JF
489 self._parse_error(self._currentfile, -1,
490 'failed to render template (%s). Continue without template rendering ...'
491 % str(e))
492 rendered_filedata = None
493 if rendered_filedata:
c44a7a36
JF
494
495 if isinstance(rendered_filedata, bytes):
496 # some template engine might return bytes but we want str
497 rendered_filedata = rendered_filedata.decode()
498
e272efd9
JF
499 self.process_interfaces(rendered_filedata)
500 return
501 self.process_interfaces(filedata)
3dcc1d0e 502
503 def read_file(self, filename, fileiobuf=None):
504 if fileiobuf:
505 self.read_filedata(fileiobuf)
506 return
507 self._filestack.append(filename)
508 self.logger.info('processing interfaces file %s' %filename)
38510743 509 try:
a193d8d1
JF
510 with open(filename) as f:
511 filedata = f.read()
3b01ed76 512 except Exception as e:
c46af1c9 513 self.logger.warning('error processing file %s (%s)',
38510743
RP
514 filename, str(e))
515 return
3dcc1d0e 516 self.read_filedata(filedata)
9dce3561 517 self._filestack.pop()
a6f80f0e 518
3dcc1d0e 519 def read_file_json(self, filename, fileiobuf=None):
520 if fileiobuf:
521 ifacedicts = json.loads(fileiobuf, encoding="utf-8")
522 #object_hook=ifaceJsonDecoder.json_object_hook)
523 elif filename:
524 self.logger.info('processing interfaces file %s' %filename)
a193d8d1
JF
525 with open(filename) as fp:
526 ifacedicts = json.load(fp)
3dcc1d0e 527 #object_hook=ifaceJsonDecoder.json_object_hook)
b6c1f551
ST
528
529 # we need to handle both lists and non lists formats (e.g. {{}})
530 if not isinstance(ifacedicts,list):
531 ifacedicts = [ifacedicts]
532
cfa06db6 533 errors = 0
3dcc1d0e 534 for ifacedict in ifacedicts:
535 ifaceobj = ifaceJsonDecoder.json_to_ifaceobj(ifacedict)
536 if ifaceobj:
537 self._validate_addr_family(ifaceobj)
cfa06db6
RP
538 if not self.callbacks.get('validateifaceobj')(ifaceobj):
539 errors += 1
3dcc1d0e 540 self.callbacks.get('iface_found')(ifaceobj)
cfa06db6 541 self.errors += errors
d486dd0d 542
3dcc1d0e 543 def load(self):
2c0ad8b3
RP
544 """ This member function loads the networkinterfaces file.
545
546 Assumes networkinterfaces parser object is initialized with the
547 parser arguments
548 """
c28fc55e 549 if not self.interfacesfile and not self.interfacesfileiobuf:
c46af1c9
JF
550 self.logger.warning('no terminal line stdin used or ')
551 self.logger.warning('no network interfaces file defined.')
cfa06db6 552 self.warns += 1
1e6d7bd7
ST
553 return
554
3dcc1d0e 555 if self.interfacesfileformat == 'json':
556 return self.read_file_json(self.interfacesfile,
557 self.interfacesfileiobuf)
558 return self.read_file(self.interfacesfile,
559 self.interfacesfileiobuf)