82
82
_, self.name, self.family, self.method = definition.split()
83
83
self.options = options
84
self.is_loopback = self.method == 'loopback'
84
85
self.is_bonded = [x for x in self.options if "bond-" in x]
86
self.has_bond_master_option, self.bond_master_options = self.has_option(['bond-master'])
85
87
self.is_alias = ":" in self.name
86
88
self.is_vlan = [x for x in self.options if x.startswith("vlan-raw-device")]
87
self.is_active = self.method == "dhcp" or self.method == "static"
88
self.is_bridged = [x for x in self.options if x.startswith("bridge_ports ")]
89
self.is_bridged, self.bridge_ports = self.has_option(['bridge_ports'])
89
90
self.has_auto_stanza = None
96
def has_option(self, options):
97
for o in self.options:
101
return True, words[1:]
96
105
def prune_options(cls, options, invalid_options):
104
113
# Returns an ordered set of stanzas to bridge this interface.
105
def bridge(self, prefix, bridge_name):
114
def _bridge(self, prefix, bridge_name):
106
115
if bridge_name is None:
107
116
bridge_name = prefix + self.name
108
117
# Note: the testing order here is significant.
109
if not self.is_active or self.is_bridged:
118
if self.is_loopback or self.is_bridged or self.has_bond_master_option:
110
119
return self._bridge_unchanged()
111
120
elif self.is_alias:
112
if self.parent and self.parent.iface and (not self.parent.iface.is_active or self.parent.iface.is_bridged):
121
if self.parent and self.parent.iface and self.parent.iface.is_bridged:
113
122
# if we didn't change the parent interface
114
123
# then we don't change the aliases neither.
115
124
return self._bridge_unchanged()
254
264
if parent_name in ifaces:
255
265
alias.iface.parent = ifaces[parent_name]
267
def _find_bridged_ifaces(self):
269
for stanza in self._stanzas:
270
if not stanza.is_logical_interface:
272
if stanza.iface.is_bridged:
273
bridged_ifaces[stanza.iface.name] = stanza.iface
274
return bridged_ifaces
257
276
def _physical_interfaces(self):
258
277
return {x.phy.name: x.phy for x in [y for y in self._stanzas if y.is_physical_interface]}
261
280
for s in self._stanzas:
283
def _is_already_bridged(self, name, bridge_port):
284
iface = self._bridged_interfaces.get(name, None)
286
return bridge_port in iface.bridge_ports
289
def bridge(self, interface_names_to_bridge, bridge_prefix, bridge_name):
291
for s in self.stanzas():
292
if s.is_logical_interface:
293
if s.iface.name not in interface_names_to_bridge:
294
if s.iface.has_auto_stanza:
295
bridged_stanzas.append(AutoStanza(s.iface.name))
296
bridged_stanzas.append(s)
298
existing_bridge_name = bridge_prefix + s.iface.name
299
if self._is_already_bridged(existing_bridge_name, s.iface.name):
300
if s.iface.has_auto_stanza:
301
bridged_stanzas.append(AutoStanza(s.iface.name))
302
bridged_stanzas.append(s)
304
bridged_stanzas.extend(s.iface._bridge(bridge_prefix, bridge_name))
305
elif not s.is_physical_interface:
306
bridged_stanzas.append(s)
307
return bridged_stanzas
265
310
def uniq_append(dst, src):
361
406
parser.add_argument('--bridge-prefix', help="bridge prefix", type=str, required=False, default='br-')
362
407
parser.add_argument('--one-time-backup', help='A one time backup of filename', action='store_true', default=True, required=False)
363
408
parser.add_argument('--activate', help='activate new configuration', action='store_true', default=False, required=False)
364
parser.add_argument('--interface-to-bridge', help="interface to bridge", type=str, required=False)
409
parser.add_argument('--interfaces-to-bridge', help="interfaces to bridge; space delimited", type=str, required=True)
365
410
parser.add_argument('--bridge-name', help="bridge name", type=str, required=False)
366
411
parser.add_argument('filename', help="interfaces(5) based filename")
371
if args.bridge_name and args.interface_to_bridge is None:
372
sys.stderr.write("error: --interface-to-bridge required when using --bridge-name\n")
375
if args.interface_to_bridge and args.bridge_name is None:
376
sys.stderr.write("error: --bridge-name required when using --interface-to-bridge\n")
380
config_parser = NetworkInterfaceParser(args.filename)
382
# Bridging requires modifying 'auto' and 'iface' stanzas only.
383
# Calling <iface>.bridge() will return a set of stanzas that cover
384
# both of those stanzas. The 'elif' clause catches all the other
385
# stanza types. The args.interface_to_bridge test is to bridge a
386
# single interface only, which is only used for juju < 2.0. And if
387
# that argument is specified then args.bridge_name takes
388
# precedence over any args.bridge_prefix.
390
for s in config_parser.stanzas():
391
if s.is_logical_interface:
392
if args.interface_to_bridge and args.interface_to_bridge != s.iface.name:
393
if s.iface.has_auto_stanza:
394
stanzas.append(AutoStanza(s.iface.name))
397
stanzas.extend(s.iface.bridge(args.bridge_prefix, args.bridge_name))
398
elif not s.is_physical_interface:
416
interfaces = args.interfaces_to_bridge.split()
418
if len(interfaces) == 0:
419
sys.stderr.write("error: no interfaces specified\n")
422
if args.bridge_name and len(interfaces) > 1:
423
sys.stderr.write("error: cannot use single bridge name '{}' against multiple interface names\n".format(args.bridge_name))
426
parser = NetworkInterfaceParser(args.filename)
427
stanzas = parser.bridge(interfaces, args.bridge_prefix, args.bridge_name)
401
429
if not args.activate:
402
430
print_stanzas(stanzas)