~juju-qa/ubuntu/xenial/juju/2.0-rc2

« back to all changes in this revision

Viewing changes to src/github.com/juju/juju/provider/maas/bridgescript.go

  • Committer: Nicholas Skaggs
  • Date: 2016-09-30 14:39:30 UTC
  • mfrom: (1.8.1)
  • Revision ID: nicholas.skaggs@canonical.com-20160930143930-vwwhrefh6ftckccy
import upstream

Show diffs side-by-side

added added

removed removed

Lines of Context:
81
81
            options = []
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
90
91
        self.parent = None
91
92
 
92
93
    def __str__(self):
93
94
        return self.name
94
95
 
 
96
    def has_option(self, options):
 
97
        for o in self.options:
 
98
            words = o.split()
 
99
            ident = words[0]
 
100
            if ident in options:
 
101
                return True, words[1:]
 
102
        return False, []
 
103
 
95
104
    @classmethod
96
105
    def prune_options(cls, options, invalid_options):
97
106
        result = []
102
111
        return result
103
112
 
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()
220
229
            s.iface.has_auto_stanza = s.iface.name in physical_interfaces
221
230
 
222
231
        self._connect_aliases()
 
232
        self._bridged_interfaces = self._find_bridged_ifaces()
223
233
 
224
234
    def _parse_stanza(self, stanza_line, iterable):
225
235
        stanza_options = []
254
264
            if parent_name in ifaces:
255
265
                alias.iface.parent = ifaces[parent_name]
256
266
 
 
267
    def _find_bridged_ifaces(self):
 
268
        bridged_ifaces = {}
 
269
        for stanza in self._stanzas:
 
270
            if not stanza.is_logical_interface:
 
271
                continue
 
272
            if stanza.iface.is_bridged:
 
273
                bridged_ifaces[stanza.iface.name] = stanza.iface
 
274
        return bridged_ifaces
 
275
 
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]}
259
278
 
261
280
        for s in self._stanzas:
262
281
            yield s
263
282
 
 
283
    def _is_already_bridged(self, name, bridge_port):
 
284
        iface = self._bridged_interfaces.get(name, None)
 
285
        if iface:
 
286
            return bridge_port in iface.bridge_ports
 
287
        return False
 
288
 
 
289
    def bridge(self, interface_names_to_bridge, bridge_prefix, bridge_name):
 
290
        bridged_stanzas = []
 
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)
 
297
                else:
 
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)
 
303
                    else:
 
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
 
308
 
264
309
 
265
310
def uniq_append(dst, src):
266
311
    for x in src:
273
318
    """Convenience function to create a new "iface" stanza.
274
319
 
275
320
Maintains original options order but removes duplicates with the
276
 
exception of 'dns-*' options which are normlised as required by
 
321
exception of 'dns-*' options which are normalised as required by
277
322
resolvconf(8) and all the dns-* options are moved to the end.
278
323
 
279
324
    """
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")
367
412
    return parser
368
413
 
369
414
 
370
415
def main(args):
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")
373
 
        exit(1)
374
 
 
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")
377
 
        exit(1)
378
 
 
379
 
    stanzas = []
380
 
    config_parser = NetworkInterfaceParser(args.filename)
381
 
 
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.
389
 
 
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))
395
 
                stanzas.append(s)
396
 
            else:
397
 
                stanzas.extend(s.iface.bridge(args.bridge_prefix, args.bridge_name))
398
 
        elif not s.is_physical_interface:
399
 
            stanzas.append(s)
 
416
    interfaces = args.interfaces_to_bridge.split()
 
417
 
 
418
    if len(interfaces) == 0:
 
419
        sys.stderr.write("error: no interfaces specified\n")
 
420
        exit(1)
 
421
 
 
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))
 
424
        exit(1)
 
425
 
 
426
    parser = NetworkInterfaceParser(args.filename)
 
427
    stanzas = parser.bridge(interfaces, args.bridge_prefix, args.bridge_name)
400
428
 
401
429
    if not args.activate:
402
430
        print_stanzas(stanzas)