]> git.proxmox.com Git - mirror_ifupdown2.git/blame - ifupdown2/ifupdown/networkinterfaces.py
networkinterfaces: support source-directory statement
[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
37c0543d 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]
62ddec8b 166 if sourced_file:
bb23b2f2 167 filenames = sorted(glob.glob(sourced_file))
2b5635d4 168 if not filenames:
3cdb1619
RP
169 if '*' not in sourced_file:
170 self._parse_warn(self._currentfile, lineno,
2b5635d4
RP
171 'cannot find source file %s' %sourced_file)
172 return 0
173 for f in filenames:
eab25b7c 174 self.read_file(f)
a6f80f0e 175 else:
9dce3561 176 self._parse_error(self._currentfile, lineno,
177 'unable to read source line')
a6f80f0e 178 return 0
179
0845cc57
PKS
180 def process_source_directory(self, lines, cur_idx, lineno):
181 self.logger.debug('processing source-directory line ..\'%s\'' % lines[cur_idx])
182 sourced_directory = re.split(self._ws_split_regex, lines[cur_idx], 2)[1]
183 if sourced_directory:
184 folders = glob.glob(sourced_directory)
185 for folder in folders:
186 filenames = [file for file in os.listdir(folder) if re.match(r'^[a-zA-Z0-9_-]+$', file)]
187
188 for f in filenames:
189 self.read_file(os.path.join(folder, f))
190 else:
191 self._parse_error(self._currentfile, lineno,
192 'unable to read source-directory line')
193 return 0
194
a6f80f0e 195 def process_auto(self, lines, cur_idx, lineno):
f102ef63 196 auto_ifaces = re.split(self._ws_split_regex, lines[cur_idx])[1:]
9dce3561 197 if not auto_ifaces:
d913472d 198 self._parse_error(self._currentfile, lineno,
9dce3561 199 'invalid auto line \'%s\''%lines[cur_idx])
200 return 0
679e6567
RP
201 for a in auto_ifaces:
202 if a == 'all':
203 self.auto_all = True
204 break
205 r = utils.parse_iface_range(a)
206 if r:
eba4da6e
RP
207 if len(r) == 3:
208 # eg swp1.[2-4], r = "swp1.", 2, 4)
209 for i in range(r[1], r[2]+1):
210 self.auto_ifaces.append('%s%d' %(r[0], i))
211 elif len(r) == 4:
212 for i in range(r[1], r[2]+1):
213 # eg swp[2-4].100, r = ("swp", 2, 4, ".100")
214 self.auto_ifaces.append('%s%d%s' %(r[0], i, r[3]))
679e6567 215 self.auto_ifaces.append(a)
a6f80f0e 216 return 0
217
14dc390d 218 def _add_to_iface_config(self, ifacename, iface_config, attrname,
219 attrval, lineno):
7a51240e 220 newattrname = attrname.replace("_", "-")
7949b8a5 221 try:
9e012f9e
RP
222 if not self.callbacks.get('validateifaceattr')(newattrname,
223 attrval):
7949b8a5 224 self._parse_error(self._currentfile, lineno,
14dc390d 225 'iface %s: unsupported keyword (%s)'
226 %(ifacename, attrname))
7949b8a5 227 return
3218f49d 228 except Exception:
7949b8a5 229 pass
7a51240e 230 attrvallist = iface_config.get(newattrname, [])
231 if newattrname in ['scope', 'netmask', 'broadcast', 'preferred-lifetime']:
be0b20f2 232 # For attributes that are related and that can have multiple
233 # entries, store them at the same index as their parent attribute.
234 # The example of such attributes is 'address' and its related
d486dd0d 235 # attributes. since the related attributes can be optional,
be0b20f2 236 # we add null string '' in places where they are optional.
237 # XXX: this introduces awareness of attribute names in
238 # this class which is a violation.
239
240 # get the index corresponding to the 'address'
241 addrlist = iface_config.get('address')
242 if addrlist:
243 # find the index of last address element
244 for i in range(0, len(addrlist) - len(attrvallist) -1):
245 attrvallist.append('')
246 attrvallist.append(attrval)
7a51240e 247 iface_config[newattrname] = attrvallist
be0b20f2 248 elif not attrvallist:
7a51240e 249 iface_config[newattrname] = [attrval]
be0b20f2 250 else:
7a51240e 251 iface_config[newattrname].append(attrval)
a6f80f0e 252
679e6567 253 def parse_iface(self, lines, cur_idx, lineno, ifaceobj):
a6f80f0e 254 lines_consumed = 0
579b3f25 255 line_idx = cur_idx
a6f80f0e 256
c0071225 257 iface_line = lines[cur_idx].strip(whitespaces)
f102ef63 258 iface_attrs = re.split(self._ws_split_regex, iface_line)
739f665b 259 ifacename = iface_attrs[1]
a6f80f0e 260
eba4da6e
RP
261 if (not utils.is_ifname_range(ifacename) and
262 utils.check_ifname_size_invalid(ifacename)):
9432c671
RP
263 self._parse_warn(self._currentfile, lineno,
264 '%s: interface name too long' %ifacename)
265
ef892ccc
RP
266 # in cases where mako is unable to render the template
267 # or incorrectly renders it due to user template
268 # errors, we maybe left with interface names with
269 # mako variables in them. There is no easy way to
270 # recognize and warn about these. In the below check
271 # we try to warn the user of such cases by looking for
272 # variable patterns ('$') in interface names.
273 if '$' in ifacename:
274 self._parse_warn(self._currentfile, lineno,
275 '%s: unexpected characters in interface name' %ifacename)
276
223ba5af
JF
277 if self.raw:
278 ifaceobj.raw_config.append(iface_line)
a6f80f0e 279 iface_config = collections.OrderedDict()
280 for line_idx in range(cur_idx + 1, len(lines)):
c0071225 281 l = lines[line_idx].strip(whitespaces)
a6f80f0e 282 if self.ignore_line(l) == 1:
223ba5af
JF
283 if self.raw:
284 ifaceobj.raw_config.append(l)
a6f80f0e 285 continue
f102ef63
RP
286 attrs = re.split(self._ws_split_regex, l, 1)
287 if self._is_keyword(attrs[0]):
a6f80f0e 288 line_idx -= 1
289 break
f102ef63 290 # if not a keyword, every line must have at least a key and value
eab25b7c 291 if len(attrs) < 2:
9dce3561 292 self._parse_error(self._currentfile, line_idx,
14dc390d 293 'iface %s: invalid syntax \'%s\'' %(ifacename, l))
eab25b7c 294 continue
223ba5af
JF
295 if self.raw:
296 ifaceobj.raw_config.append(l)
d08d5f54 297 attrname = attrs[0]
f102ef63
RP
298 # preprocess vars (XXX: only preprocesses $IFACE for now)
299 attrval = re.sub(r'\$IFACE', ifacename, attrs[1])
14dc390d 300 self._add_to_iface_config(ifacename, iface_config, attrname,
301 attrval, line_idx+1)
a6f80f0e 302 lines_consumed = line_idx - cur_idx
303
304 # Create iface object
37c0543d 305 if ifacename.find(':') != -1:
62ddec8b 306 ifaceobj.name = ifacename.split(':')[0]
37c0543d 307 else:
62ddec8b 308 ifaceobj.name = ifacename
37c0543d 309
62ddec8b 310 ifaceobj.config = iface_config
a6f80f0e 311 ifaceobj.generate_env()
d913472d 312
313 try:
004d1e65
JF
314 if iface_attrs[2]:
315 ifaceobj.addr_family.append(iface_attrs[2])
62ddec8b 316 ifaceobj.addr_method = iface_attrs[3]
d913472d 317 except IndexError:
318 # ignore
319 pass
320 self._validate_addr_family(ifaceobj, lineno)
a6f80f0e 321
679e6567 322 if self.auto_all or (ifaceobj.name in self.auto_ifaces):
62ddec8b 323 ifaceobj.auto = True
a6f80f0e 324
62ddec8b 325 classes = self.get_allow_classes_for_iface(ifaceobj.name)
fe0a57d3 326 if classes:
62ddec8b 327 [ifaceobj.set_class(c) for c in classes]
d486dd0d 328
a6f80f0e 329 return lines_consumed # Return next index
330
eba4da6e
RP
331 def _create_ifaceobj_clone(self, ifaceobj, newifaceobjname,
332 newifaceobjtype, newifaceobjflags):
333 ifaceobj_new = copy.deepcopy(ifaceobj)
334 ifaceobj_new.realname = '%s' %ifaceobj.name
335 ifaceobj_new.name = newifaceobjname
336 ifaceobj_new.type = newifaceobjtype
337 ifaceobj_new.flags = newifaceobjflags
338
339 return ifaceobj_new
340
679e6567
RP
341 def process_iface(self, lines, cur_idx, lineno):
342 ifaceobj = iface()
343 lines_consumed = self.parse_iface(lines, cur_idx, lineno, ifaceobj)
344
345 range_val = utils.parse_iface_range(ifaceobj.name)
346 if range_val:
eba4da6e
RP
347 if len(range_val) == 3:
348 for v in range(range_val[1], range_val[2]+1):
349 ifacename = '%s%d' %(range_val[0], v)
350 if utils.check_ifname_size_invalid(ifacename):
351 self._parse_warn(self._currentfile, lineno,
352 '%s: interface name too long' %ifacename)
353 flags = iface.IFACERANGE_ENTRY
354 if v == range_val[1]:
355 flags |= iface.IFACERANGE_START
356 ifaceobj_new = self._create_ifaceobj_clone(ifaceobj,
357 ifacename, ifaceobj.type, flags)
358 self.callbacks.get('iface_found')(ifaceobj_new)
359 elif len(range_val) == 4:
360 for v in range(range_val[1], range_val[2]+1):
361 ifacename = '%s%d%s' %(range_val[0], v, range_val[3])
362 if utils.check_ifname_size_invalid(ifacename):
363 self._parse_warn(self._currentfile, lineno,
364 '%s: interface name too long' %ifacename)
365 flags = iface.IFACERANGE_ENTRY
366 if v == range_val[1]:
367 flags |= iface.IFACERANGE_START
368 ifaceobj_new = self._create_ifaceobj_clone(ifaceobj,
369 ifacename, ifaceobj.type, flags)
370 self.callbacks.get('iface_found')(ifaceobj_new)
679e6567
RP
371 else:
372 self.callbacks.get('iface_found')(ifaceobj)
373
374 return lines_consumed # Return next index
a6f80f0e 375
84ca006f
RP
376 def process_vlan(self, lines, cur_idx, lineno):
377 ifaceobj = iface()
378 lines_consumed = self.parse_iface(lines, cur_idx, lineno, ifaceobj)
379
380 range_val = utils.parse_iface_range(ifaceobj.name)
381 if range_val:
eba4da6e
RP
382 if len(range_val) == 3:
383 for v in range(range_val[1], range_val[2]+1):
384 flags = iface.IFACERANGE_ENTRY
385 if v == range_val[1]:
386 flags |= iface.IFACERANGE_START
387 ifaceobj_new = self._create_ifaceobj_clone(ifaceobj,
388 '%s%d' %(range_val[0], v),
389 ifaceType.BRIDGE_VLAN, flags)
390 self.callbacks.get('iface_found')(ifaceobj_new)
391 elif len(range_val) == 4:
392 for v in range(range_val[1], range_val[2]+1):
393 flags = iface.IFACERANGE_ENTRY
394 if v == range_val[1]:
395 flags |= iface.IFACERANGE_START
396 ifaceobj_new = self._create_ifaceobj_clone(ifaceobj,
397 '%s%d%s' %(range_val[0], v, range_val[3]),
398 ifaceType.BRIDGE_VLAN,
399 flags)
400 self.callbacks.get('iface_found')(ifaceobj_new)
84ca006f
RP
401 else:
402 ifaceobj.type = ifaceType.BRIDGE_VLAN
403 self.callbacks.get('iface_found')(ifaceobj)
404
405 return lines_consumed # Return next index
406
0845cc57
PKS
407 network_elems = {
408 'source': process_source,
409 'source-directory': process_source_directory,
410 'allow': process_allow,
411 'auto': process_auto,
412 'iface': process_iface,
413 'vlan': process_vlan
414 }
a6f80f0e 415
9dce3561 416 def _is_keyword(self, str):
a6f80f0e 417 # The additional split here is for allow- keyword
3b01ed76 418 if (str in list(self.network_elems.keys()) or
3d58f6af 419 str.split('-')[0] == 'allow'):
a6f80f0e 420 return 1
a6f80f0e 421 return 0
422
0845cc57
PKS
423 def _get_keyword_func(self, str_):
424 tmp_str = str_.split('-')[0]
425 if tmp_str == "allow":
426 return self.network_elems.get(tmp_str)
427 else:
428 return self.network_elems.get(str_)
a6f80f0e 429
430 def get_allow_classes_for_iface(self, ifacename):
431 classes = []
3b01ed76 432 for class_name, ifacenames in list(self.allow_classes.items()):
a6f80f0e 433 if ifacename in ifacenames:
434 classes.append(class_name)
a6f80f0e 435 return classes
436
3dcc1d0e 437 def process_interfaces(self, filedata):
5b65654f
RP
438
439 # process line continuations
440 filedata = ' '.join(d.strip() for d in filedata.split('\\'))
441
a6f80f0e 442 line_idx = 0
443 lines_consumed = 0
62ddec8b 444 raw_config = filedata.split('\n')
c0071225 445 lines = [l.strip(whitespaces) for l in raw_config]
579b3f25 446 while (line_idx < len(lines)):
579b3f25 447 if self.ignore_line(lines[line_idx]):
448 line_idx += 1
449 continue
f102ef63 450 words = re.split(self._ws_split_regex, lines[line_idx])
c0071225
RP
451 if not words:
452 line_idx += 1
453 continue
579b3f25 454 # Check if first element is a supported keyword
9dce3561 455 if self._is_keyword(words[0]):
456 keyword_func = self._get_keyword_func(words[0])
d913472d 457 lines_consumed = keyword_func(self, lines, line_idx, line_idx+1)
579b3f25 458 line_idx += lines_consumed
459 else:
9dce3561 460 self._parse_error(self._currentfile, line_idx + 1,
461 'error processing line \'%s\'' %lines[line_idx])
579b3f25 462 line_idx += 1
579b3f25 463 return 0
a6f80f0e 464
3dcc1d0e 465 def read_filedata(self, filedata):
d40e96ee 466 self._currentfile_has_template = False
579b3f25 467 # run through template engine
e272efd9
JF
468 if filedata and '%' in filedata:
469 try:
800417ae
JF
470 if not self._template_engine:
471 self._template_engine = templateEngine(
1f5db4a8
MG
472 template_engine=self._template_engine_name,
473 template_enable=self._template_enable,
474 template_lookuppath=self._template_engine_path)
e272efd9
JF
475 rendered_filedata = self._template_engine.render(filedata)
476 if rendered_filedata is filedata:
477 self._currentfile_has_template = False
478 else:
479 self._currentfile_has_template = True
3b01ed76 480 except Exception as e:
e272efd9
JF
481 self._parse_error(self._currentfile, -1,
482 'failed to render template (%s). Continue without template rendering ...'
483 % str(e))
484 rendered_filedata = None
485 if rendered_filedata:
c44a7a36
JF
486
487 if isinstance(rendered_filedata, bytes):
488 # some template engine might return bytes but we want str
489 rendered_filedata = rendered_filedata.decode()
490
e272efd9
JF
491 self.process_interfaces(rendered_filedata)
492 return
493 self.process_interfaces(filedata)
3dcc1d0e 494
495 def read_file(self, filename, fileiobuf=None):
496 if fileiobuf:
497 self.read_filedata(fileiobuf)
498 return
499 self._filestack.append(filename)
500 self.logger.info('processing interfaces file %s' %filename)
38510743 501 try:
a193d8d1
JF
502 with open(filename) as f:
503 filedata = f.read()
3b01ed76 504 except Exception as e:
c46af1c9 505 self.logger.warning('error processing file %s (%s)',
38510743
RP
506 filename, str(e))
507 return
3dcc1d0e 508 self.read_filedata(filedata)
9dce3561 509 self._filestack.pop()
a6f80f0e 510
3dcc1d0e 511 def read_file_json(self, filename, fileiobuf=None):
512 if fileiobuf:
513 ifacedicts = json.loads(fileiobuf, encoding="utf-8")
514 #object_hook=ifaceJsonDecoder.json_object_hook)
515 elif filename:
516 self.logger.info('processing interfaces file %s' %filename)
a193d8d1
JF
517 with open(filename) as fp:
518 ifacedicts = json.load(fp)
3dcc1d0e 519 #object_hook=ifaceJsonDecoder.json_object_hook)
b6c1f551
ST
520
521 # we need to handle both lists and non lists formats (e.g. {{}})
522 if not isinstance(ifacedicts,list):
523 ifacedicts = [ifacedicts]
524
cfa06db6 525 errors = 0
3dcc1d0e 526 for ifacedict in ifacedicts:
527 ifaceobj = ifaceJsonDecoder.json_to_ifaceobj(ifacedict)
528 if ifaceobj:
529 self._validate_addr_family(ifaceobj)
cfa06db6
RP
530 if not self.callbacks.get('validateifaceobj')(ifaceobj):
531 errors += 1
3dcc1d0e 532 self.callbacks.get('iface_found')(ifaceobj)
cfa06db6 533 self.errors += errors
d486dd0d 534
3dcc1d0e 535 def load(self):
2c0ad8b3
RP
536 """ This member function loads the networkinterfaces file.
537
538 Assumes networkinterfaces parser object is initialized with the
539 parser arguments
540 """
c28fc55e 541 if not self.interfacesfile and not self.interfacesfileiobuf:
c46af1c9
JF
542 self.logger.warning('no terminal line stdin used or ')
543 self.logger.warning('no network interfaces file defined.')
cfa06db6 544 self.warns += 1
1e6d7bd7
ST
545 return
546
3dcc1d0e 547 if self.interfacesfileformat == 'json':
548 return self.read_file_json(self.interfacesfile,
549 self.interfacesfileiobuf)
550 return self.read_file(self.interfacesfile,
551 self.interfacesfileiobuf)