示例#1
0
 def add(self, kind, ifname, reuse=False, **kwarg):
     '''
     Create new network interface
     '''
     with self.ipdb.exclusive:
         # check for existing interface
         if ifname in self:
             if (self[ifname]['ipdb_scope'] == 'shadow') or reuse:
                 device = self[ifname]
                 kwarg['kind'] = kind
                 device.load_dict(kwarg)
                 if self[ifname]['ipdb_scope'] == 'shadow':
                     with device._direct_state:
                         device['ipdb_scope'] = 'create'
             else:
                 raise CreateException("interface %s exists" %
                                       ifname)
         else:
             device = self[ifname] = Interface(ipdb=self.ipdb,
                                               mode='snapshot')
             device.update(kwarg)
             if isinstance(kwarg.get('link', None), Interface):
                 device['link'] = kwarg['link']['index']
             if isinstance(kwarg.get('vxlan_link', None), Interface):
                 device['vxlan_link'] = kwarg['vxlan_link']['index']
             device['kind'] = kind
             device['index'] = kwarg.get('index', 0)
             device['ifname'] = ifname
             device['ipdb_scope'] = 'create'
             device._mode = self.ipdb.mode
         device.begin()
     return device
示例#2
0
    def commit(self, tid=None, transaction=None, rollback=False, newif=False):
        '''
        Commit transaction. In the case of exception all
        changes applied during commit will be reverted.
        '''
        error = None
        added = None
        removed = None
        drop = True
        if tid:
            transaction = self._transactions[tid]
        else:
            if transaction:
                drop = False
            else:
                transaction = self.last()
        if transaction.partial:
            transaction.errors = []

        wd = None
        with self._write_lock:
            # if the interface does not exist, create it first ;)
            if self['ipdb_scope'] != 'system':
                request = IPLinkRequest(self.filter('common'))

                # create watchdog
                wd = self.ipdb.watchdog(ifname=self['ifname'])

                newif = True
                try:
                    # 8<----------------------------------------------------
                    # ACHTUNG: hack for old platforms
                    if request.get('address', None) == '00:00:00:00:00:00':
                        del request['address']
                        del request['broadcast']
                    # 8<----------------------------------------------------
                    try:
                        self.nl.link('add', **request)
                    except NetlinkError as x:
                        # File exists
                        if x.code == errno.EEXIST:
                            # A bit special case, could be one of two cases:
                            #
                            # 1. A race condition between two different IPDB
                            #    processes
                            # 2. An attempt to create dummy0, gre0, bond0 when
                            #    the corrseponding module is not loaded. Being
                            #    loaded, the module creates a default interface
                            #    by itself, causing the request to fail
                            #
                            # The exception in that case can cause the DB
                            # inconsistence, since there can be queued not only
                            # the interface creation, but also IP address
                            # changes etc.
                            #
                            # So we ignore this particular exception and try to
                            # continue, as it is created by us.
                            pass

                        # Operation not supported
                        elif x.code == errno.EOPNOTSUPP and \
                                request.get('index', 0) != 0:
                            # ACHTUNG: hack for old platforms
                            request = IPLinkRequest({
                                'ifname': self['ifname'],
                                'kind': self['kind'],
                                'index': 0
                            })
                            self.nl.link('add', **request)
                        else:
                            raise
                except Exception as e:
                    # on failure, invalidate the interface and detach it
                    # from the parent
                    # 1. drop the IPRoute() link
                    self.nl = None
                    # 2. clean up ipdb
                    self.detach()
                    # 3. invalidate the interface
                    with self._direct_state:
                        for i in tuple(self.keys()):
                            del self[i]
                    # 4. the rest
                    self._mode = 'invalid'
                    self._exception = e
                    self._tb = traceback.format_exc()
                    # raise the exception
                    if transaction.partial:
                        transaction.errors.append(e)
                        raise PartialCommitException()
                    else:
                        raise

        if wd is not None:
            wd.wait()
            if self['index'] == 0:
                # Only the interface creation time issue on
                # old or compat platforms. The interface index
                # may be not known yet, but we can not continue
                # without it. It will be updated anyway, but
                # it is better to force the lookup.
                ix = self.nl.link_lookup(ifname=self['ifname'])
                if ix:
                    self['index'] = ix[0]
                else:
                    if transaction.partial:
                        transaction.errors.append(CreateException())
                        raise PartialCommitException()
                    else:
                        raise CreateException()

        # now we have our index and IP set and all other stuff
        snapshot = self.pick()

        # resolve all delayed ports
        def resolve_ports(transaction, ports, callback, self, drop):
            def error(x):
                return KeyError('can not resolve port %s' % x)

            for port in tuple(ports):
                ifindex = self._resolve_port(port)
                if ifindex is None:
                    if transaction.partial:
                        transaction.errors.append(error(port))
                    else:
                        if drop:
                            self.drop(transaction)
                        raise error(port)
                else:
                    ports.remove(port)
                    callback(ifindex, direct=True)

        resolve_ports(transaction, transaction._delay_add_port,
                      transaction.add_port, self, drop)
        resolve_ports(transaction, transaction._delay_del_port,
                      transaction.del_port, self, drop)

        try:
            removed = snapshot - transaction
            added = transaction - snapshot

            run = transaction._run
            nl = transaction.nl

            # 8<---------------------------------------------
            # Port vlans
            self['vlans'].set_target(transaction['vlans'])
            for i in removed['vlans']:
                # remove vlan from the port
                run(nl.link,
                    'vlan-del',
                    index=self['index'],
                    vlan_info=self['vlans'][i])

            for i in added['vlans']:
                # add vlan to the port
                run(nl.link,
                    'vlan-add',
                    index=self['index'],
                    vlan_info=transaction['vlans'][i])

            if (not transaction.partial) and \
                    (removed['vlans'] or added['vlans']):
                self['vlans'].target.wait(SYNC_TIMEOUT)
                if not self['vlans'].target.is_set():
                    raise CommitException('vlans target is not set')

            # 8<---------------------------------------------
            # Ports
            self['ports'].set_target(transaction['ports'])
            for i in removed['ports']:
                # detach port
                if i in self.ipdb.interfaces:
                    port = self.ipdb.interfaces[i]
                    port.set_target('master', None)
                    port.mirror_target('master', 'link')
                    run(nl.link, 'set', index=port['index'], master=0)
                else:
                    transaction.errors.append(KeyError(i))

            for i in added['ports']:
                # attach port
                if i in self.ipdb.interfaces:
                    port = self.ipdb.interfaces[i]
                    port.set_target('master', self['index'])
                    port.mirror_target('master', 'link')
                    run(nl.link,
                        'set',
                        index=port['index'],
                        master=self['index'])
                else:
                    transaction.errors.append(KeyError(i))

            if (not transaction.partial) and \
                    (removed['ports'] or added['ports']):
                for link in self.nl.get_links(*(removed['ports']
                                                | added['ports'])):
                    self.ipdb.device_put(link)
                self['ports'].target.wait(SYNC_TIMEOUT)
                if not self['ports'].target.is_set():
                    raise CommitException('ports target is not set')

                # wait for proper targets on ports
                for i in list(added['ports']) + list(removed['ports']):
                    port = self.ipdb.interfaces[i]
                    target = port._local_targets['master']
                    target.wait(SYNC_TIMEOUT)
                    del port._local_targets['master']
                    del port._local_targets['link']
                    if not target.is_set():
                        raise CommitException('master target failed')
                    if i in added['ports']:
                        if port.if_master != self['index']:
                            raise CommitException('master set failed')
                    else:
                        if port.if_master == self['index']:
                            raise CommitException('master unset failed')

            # 8<---------------------------------------------
            # Interface changes
            request = IPLinkRequest()
            for key in added:
                if (key in self._xfields['common']) and \
                        (key != 'kind'):
                    request[key] = added[key]
            request['index'] = self['index']

            # apply changes only if there is something to apply
            if any([
                    request[item] is not None for item in request
                    if item != 'index'
            ]):
                if request.get('address', None) == '00:00:00:00:00:00':
                    request.pop('address')
                    request.pop('broadcast', None)
                run(nl.link, 'set', **request)
                # hardcoded pause -- if the interface was moved
                # across network namespaces
                if not transaction.partial and 'net_ns_fd' in request:
                    while True:
                        # wait until the interface will disappear
                        # from the main network namespace
                        try:
                            for link in self.nl.get_links(self['index']):
                                self.ipdb.device_put(link)
                        except NetlinkError as e:
                            if e.code == errno.ENODEV:
                                break
                            raise
                        except Exception:
                            raise
                    time.sleep(0.1)

            # 8<---------------------------------------------
            # IP address changes
            #
            # There is one corner case: if the interface didn't
            # exist before commit(), the transaction may not
            # contain automatic IPv6 addresses.
            #
            # So fetch here possible addresses and use it to
            # extend the transaction
            target = self._commit_real_ip().union(set(transaction['ipaddr']))
            self['ipaddr'].set_target(target)

            # The promote_secondaries sysctl causes the kernel
            # to add secondary addresses back after the primary
            # address is removed.
            #
            # The library can not tell this from the result of
            # an external program.
            #
            # One simple way to work that around is to remove
            # secondaries first.
            rip = sorted(removed['ipaddr'],
                         key=lambda x: self['ipaddr'][x]['flags'],
                         reverse=True)
            # 8<--------------------------------------
            for i in rip:
                # Ignore link-local IPv6 addresses
                if i[0][:4] == 'fe80' and i[1] == 64:
                    continue
                # When you remove a primary IP addr, all subnetwork
                # can be removed. In this case you will fail, but
                # it is OK, no need to roll back
                try:
                    self.ipdb.update_addr(
                        run(nl.addr, 'delete', self['index'], i[0], i[1]),
                        'remove')
                except NetlinkError as x:
                    # bypass only errno 99, 'Cannot assign address'
                    if x.code != errno.EADDRNOTAVAIL:
                        raise
                except socket.error as x:
                    # bypass illegal IP requests
                    if isinstance(x.args[0], basestring) and \
                            x.args[0].startswith('illegal IP'):
                        continue
                    raise

            # 8<--------------------------------------
            target = added['ipaddr']
            for i in range(3):  # just to be sure
                self._commit_add_ip(target, transaction)
                if transaction.partial:
                    break
                real = self._commit_real_ip()
                if real >= set(transaction['ipaddr']):
                    break
                else:
                    target = set(transaction['ipaddr']) - real
            else:
                raise CommitException('ipaddr setup error', i)

            # 8<--------------------------------------
            if (not transaction.partial) and \
                    (removed['ipaddr'] or added['ipaddr']):
                # 8<--------------------------------------
                # bond and bridge interfaces do not send
                # IPv6 address updates, when are down
                #
                # beside of that, bridge interfaces are
                # down by default, so they never send
                # address updates from beginning
                #
                # so if we need, force address load
                #
                # FIXME: probably, we should handle other
                # types as well
                if self['kind'] in ('bond', 'bridge', 'veth'):
                    self.ipdb.update_addr(self.nl.get_addr(), 'add')
                # 8<--------------------------------------
                self['ipaddr'].target.wait(SYNC_TIMEOUT)
                if not self['ipaddr'].target.is_set():
                    raise CommitException('ipaddr target is not set')

            # 8<---------------------------------------------
            # Iterate callback chain
            for ch in self._commit_hooks:
                # An exception will rollback the transaction
                ch(self.dump(), snapshot.dump(), transaction.dump())

            # 8<---------------------------------------------
            # reload interface to hit targets
            if not transaction.partial:
                if transaction._targets:
                    try:
                        self.reload()
                    except NetlinkError as e:
                        if e.code == errno.ENODEV:  # No such device
                            if ('net_ns_fd' in added) or \
                                    ('net_ns_pid' in added):
                                # it means, that the device was moved
                                # to another netns; just give up
                                if drop:
                                    self.drop(transaction)
                                return self
                # wait for targets
                transaction._wait_all_targets()

            # 8<---------------------------------------------
            # Interface removal
            if (added.get('ipdb_scope') in ('shadow', 'remove')) or\
                    ((added.get('ipdb_scope') == 'create') and rollback):
                wd = self.ipdb.watchdog(action='RTM_DELLINK',
                                        ifname=self['ifname'])
                if added.get('ipdb_scope') in ('shadow', 'create'):
                    self.set_item('ipdb_scope', 'locked')
                self.nl.link('delete', **self)
                wd.wait()
                if added.get('ipdb_scope') == 'shadow':
                    self.set_item('ipdb_scope', 'shadow')
                if added['ipdb_scope'] == 'create':
                    self.load_dict(transaction)
                if drop:
                    self.drop(transaction)
                return self
            # 8<---------------------------------------------

        except Exception as e:
            error = e
            # something went wrong: roll the transaction back
            if not rollback:
                try:
                    self.commit(transaction=snapshot,
                                rollback=True,
                                newif=newif)
                except Exception as i_e:
                    error = RuntimeError()
                    error.cause = i_e
            else:
                # reload all the database -- it can take a long time,
                # but it is required since we have no idea, what is
                # the result of the failure
                links = self.nl.get_links()
                for link in links:
                    self.ipdb.device_put(link, skip_slaves=True)
                for link in links:
                    self.ipdb.update_slaves(link)
                links = self.nl.get_vlans()
                for link in links:
                    self.ipdb.update_dev(link)
                self.ipdb.update_addr(self.nl.get_addr())

            error.traceback = traceback.format_exc()
            for key in ('ipaddr', 'ports', 'vlans'):
                self[key].clear_target()

        # raise partial commit exceptions
        if transaction.partial and transaction.errors:
            error = PartialCommitException('partial commit error')

        # if it is not a rollback turn
        if drop and not rollback:
            # drop last transaction in any case
            self.drop(transaction)

        # raise exception for failed transaction
        if error is not None:
            error.transaction = transaction
            raise error

        time.sleep(config.commit_barrier)

        # drop all collected errors, if any
        self.errors = []
        return self
示例#3
0
    def commit(self,
               tid=None,
               transaction=None,
               commit_phase=1,
               commit_mask=0xff,
               newif=False):
        '''
        Commit transaction. In the case of exception all
        changes applied during commit will be reverted.
        '''

        if not commit_phase & commit_mask:
            return self

        def invalidate():
            # on failure, invalidate the interface and detach it
            # from the parent
            # 0. obtain lock on IPDB, to avoid deadlocks
            # ... all the DB updates will wait
            with self.ipdb.exclusive:
                # 1. drop the IPRoute() link
                self.nl = None
                # 2. clean up ipdb
                self.detach()
                # 3. invalidate the interface
                with self._direct_state:
                    for i in tuple(self.keys()):
                        del self[i]
                # 4. the rest
                self._mode = 'invalid'

        error = None
        added = None
        removed = None
        drop = True
        init = None

        if tid:
            transaction = self.global_tx[tid]
        else:
            if transaction:
                drop = False
            else:
                transaction = self.current_tx
        if transaction.partial:
            transaction.errors = []

        with self._write_lock:
            # if the interface does not exist, create it first ;)
            if self['ipdb_scope'] != 'system':

                newif = True
                self.set_target('ipdb_scope', 'system')
                try:
                    # 8<----------------------------------------------------
                    # ACHTUNG: hack for old platforms
                    if self['address'] == '00:00:00:00:00:00':
                        with self._direct_state:
                            self['address'] = None
                            self['broadcast'] = None
                    # 8<----------------------------------------------------
                    init = self.pick()
                    try:
                        self.nl.link('add', **self)
                    except NetlinkError as x:
                        # File exists
                        if x.code == errno.EEXIST:
                            # A bit special case, could be one of two cases:
                            #
                            # 1. A race condition between two different IPDB
                            #    processes
                            # 2. An attempt to create dummy0, gre0, bond0 when
                            #    the corrseponding module is not loaded. Being
                            #    loaded, the module creates a default interface
                            #    by itself, causing the request to fail
                            #
                            # The exception in that case can cause the DB
                            # inconsistence, since there can be queued not only
                            # the interface creation, but also IP address
                            # changes etc.
                            #
                            # So we ignore this particular exception and try to
                            # continue, as it is created by us.
                            pass

                        else:
                            raise
                except Exception as e:
                    if transaction.partial:
                        transaction.errors.append(e)
                        raise PartialCommitException()
                    else:
                        # If link('add', ...) raises an exception, no netlink
                        # broadcast will be sent, and the object is unmodified.
                        # After the exception forwarding, the object is ready
                        # to repeat the commit() call.
                        raise

        if transaction['ipdb_scope'] == 'create' and commit_phase > 1:
            if self['index']:
                wd = self.ipdb.watchdog(action='RTM_DELLINK',
                                        ifname=self['ifname'])
                with self._direct_state:
                    self['ipdb_scope'] = 'locked'
                self.nl.link('delete', index=self['index'])
                wd.wait()
            self.load_dict(transaction)
            return self

        elif newif:
            # Here we come only if a new interface is created
            #
            if commit_phase == 1 and not self.wait_target('ipdb_scope'):
                invalidate()
                raise CreateException()

            # Re-populate transaction.ipaddr to have a proper IP target
            #
            # The reason behind the code is that a new interface in the
            # "up" state will have automatic IPv6 addresses, that aren't
            # reflected in the transaction. This may cause a false IP
            # target mismatch and a commit failure.
            #
            # To avoid that, collect automatic addresses to the
            # transaction manually, since it is not yet properly linked.
            #
            for addr in self.ipdb.ipaddr[self['index']]:
                transaction['ipaddr'].add(addr)

        # now we have our index and IP set and all other stuff
        snapshot = self.pick()

        # resolve all delayed ports
        def resolve_ports(transaction, ports, callback, self, drop):
            def error(x):
                return KeyError('can not resolve port %s' % x)
            for port in tuple(ports):
                ifindex = self._resolve_port(port)
                if ifindex is None:
                    if transaction.partial:
                        transaction.errors.append(error(port))
                    else:
                        if drop:
                            self.drop(transaction.uid)
                        raise error(port)
                else:
                    ports.remove(port)
                    with transaction._direct_state:  # ????
                        callback(ifindex)
        resolve_ports(transaction,
                      transaction._delay_add_port,
                      transaction.add_port,
                      self, drop)
        resolve_ports(transaction,
                      transaction._delay_del_port,
                      transaction.del_port,
                      self, drop)

        try:
            removed, added = snapshot // transaction

            run = transaction._run
            nl = transaction.nl

            # 8<---------------------------------------------
            # Port vlans
            if removed['vlans'] or added['vlans']:

                if added['vlans']:
                    transaction['vlans'].add(1)
                self['vlans'].set_target(transaction['vlans'])

                for i in removed['vlans']:
                    if i != 1:
                        # remove vlan from the port
                        run(nl.vlan_filter, 'del',
                            index=self['index'],
                            vlan_info=self['vlans'][i])

                for i in added['vlans']:
                    if i != 1:
                        # add vlan to the port
                        run(nl.vlan_filter, 'add',
                            index=self['index'],
                            vlan_info=transaction['vlans'][i])

                self['vlans'].target.wait(SYNC_TIMEOUT)
                if not self['vlans'].target.is_set():
                    raise CommitException('vlans target is not set')

            # 8<---------------------------------------------
            # Ports
            if removed['ports'] or added['ports']:
                self['ports'].set_target(transaction['ports'])

                for i in removed['ports']:
                    # detach port
                    if i in self.ipdb.interfaces:
                        (self.ipdb.interfaces[i]
                         .set_target('master', None)
                         .mirror_target('master', 'link'))
                        run(nl.link, 'set', index=i, master=0)
                    else:
                        transaction.errors.append(KeyError(i))

                for i in added['ports']:
                    # attach port
                    if i in self.ipdb.interfaces:
                        (self.ipdb.interfaces[i]
                         .set_target('master', self['index'])
                         .mirror_target('master', 'link'))
                        run(nl.link, 'set', index=i, master=self['index'])
                    else:
                        transaction.errors.append(KeyError(i))

                self['ports'].target.wait(SYNC_TIMEOUT)
                if not self['ports'].target.is_set():
                    raise CommitException('ports target is not set')

                # wait for proper targets on ports
                for i in list(added['ports']) + list(removed['ports']):
                    port = self.ipdb.interfaces[i]
                    target = port._local_targets['master']
                    target.wait(SYNC_TIMEOUT)
                    with port._write_lock:
                        del port._local_targets['master']
                        del port._local_targets['link']
                    if not target.is_set():
                        raise CommitException('master target failed')
                    if i in added['ports']:
                        if port.if_master != self['index']:
                            raise CommitException('master set failed')
                    else:
                        if port.if_master == self['index']:
                            raise CommitException('master unset failed')

            # 8<---------------------------------------------
            # Interface changes
            request = IPLinkRequest()
            for key in added:
                if (key == 'net_ns_fd') or \
                        (key not in self._virtual_fields) and \
                        (key != 'kind'):
                    request[key] = added[key]

            # apply changes only if there is something to apply
            if any([request[item] is not None for item in request]):
                request['index'] = self['index']
                request['kind'] = self['kind']
                if request.get('address', None) == '00:00:00:00:00:00':
                    request.pop('address')
                    request.pop('broadcast', None)
                if tuple(filter(lambda x: x[:3] == 'br_', request)):
                    request['family'] = socket.AF_BRIDGE
                    run(nl.link,
                        (RTM_NEWLINK, NLM_F_REQUEST | NLM_F_ACK),
                        **request)
                else:
                    run(nl.link, 'set', **request)
                # hardcoded pause -- if the interface was moved
                # across network namespaces
                if 'net_ns_fd' in request:
                    while True:
                        # wait until the interface will disappear
                        # from the main network namespace
                        try:
                            for link in self.nl.get_links(self['index']):
                                self.ipdb.interfaces._new(link)
                        except NetlinkError as e:
                            if e.code == errno.ENODEV:
                                break
                            raise
                        except Exception:
                            raise
                    time.sleep(0.1)
                if not transaction.partial:
                    transaction.wait_all_targets()

            # 8<---------------------------------------------
            # IP address changes
            for _ in range(3):
                ip2add = transaction['ipaddr'] - self['ipaddr']
                ip2remove = self['ipaddr'] - transaction['ipaddr']

                if not ip2add and not ip2remove:
                    break

                self['ipaddr'].set_target(transaction['ipaddr'])
                ###
                # Remove
                #
                # The promote_secondaries sysctl causes the kernel
                # to add secondary addresses back after the primary
                # address is removed.
                #
                # The library can not tell this from the result of
                # an external program.
                #
                # One simple way to work that around is to remove
                # secondaries first.
                rip = sorted(ip2remove,
                             key=lambda x: self['ipaddr'][x]['flags'],
                             reverse=True)
                # 8<--------------------------------------
                for i in rip:
                    # Ignore link-local IPv6 addresses
                    if i[0][:4] == 'fe80' and i[1] == 64:
                        continue
                    # When you remove a primary IP addr, all the
                    # subnetwork can be removed. In this case you
                    # will fail, but it is OK, no need to roll back
                    try:
                        run(nl.addr, 'delete', self['index'], i[0], i[1])
                    except NetlinkError as x:
                        # bypass only errno 99,
                        # 'Cannot assign address'
                        if x.code != errno.EADDRNOTAVAIL:
                            raise
                    except socket.error as x:
                        # bypass illegal IP requests
                        if isinstance(x.args[0], basestring) and \
                                x.args[0].startswith('illegal IP'):
                            continue
                        raise
                ###
                # Add addresses
                # 8<--------------------------------------
                for i in ip2add:
                    # Ignore link-local IPv6 addresses
                    if i[0][:4] == 'fe80' and i[1] == 64:
                        continue
                    # Try to fetch additional address attributes
                    try:
                        kwarg = dict([k for k
                                      in transaction['ipaddr'][i].items()
                                      if k[0] in ('broadcast',
                                                  'anycast',
                                                  'scope')])
                    except KeyError:
                        kwarg = None
                    # feed the address to the OS
                    run(nl.addr, 'add', self['index'], i[0], i[1],
                        **kwarg if kwarg else {})

                # 8<--------------------------------------
                # bond and bridge interfaces do not send
                # IPv6 address updates, when are down
                #
                # beside of that, bridge interfaces are
                # down by default, so they never send
                # address updates from beginning
                #
                # so if we need, force address load
                #
                # FIXME: probably, we should handle other
                # types as well
                if self['kind'] in ('bond', 'bridge', 'veth'):
                    for addr in self.nl.get_addr(family=socket.AF_INET6):
                        self.ipdb.ipaddr._new(addr)

                # 8<--------------------------------------
                self['ipaddr'].target.wait(SYNC_TIMEOUT)
                if self['ipaddr'].target.is_set():
                    break
            else:
                raise CommitException('ipaddr target is not set')

            # 8<---------------------------------------------
            # Iterate callback chain
            for ch in self._commit_hooks:
                # An exception will rollback the transaction
                ch(self.dump(), snapshot.dump(), transaction.dump())

            # 8<---------------------------------------------
            # Interface removal
            if (added.get('ipdb_scope') in ('shadow', 'remove')):
                wd = self.ipdb.watchdog(action='RTM_DELLINK',
                                        ifname=self['ifname'])
                if added.get('ipdb_scope') in ('shadow', 'create'):
                    with self._direct_state:
                        self['ipdb_scope'] = 'locked'
                self.nl.link('delete', index=self['index'])
                wd.wait()
                if added.get('ipdb_scope') == 'shadow':
                    with self._direct_state:
                        self['ipdb_scope'] = 'shadow'
                if added['ipdb_scope'] == 'create':
                    self.load_dict(transaction)
                if drop:
                    self.drop(transaction.uid)
                return self
            # 8<---------------------------------------------

        except Exception as e:
            error = e
            # something went wrong: roll the transaction back
            if commit_phase == 1:
                if newif:
                    drop = False
                try:
                    self.commit(transaction=init if newif else snapshot,
                                commit_phase=2,
                                commit_mask=commit_mask,
                                newif=newif)
                except Exception as i_e:
                    error = RuntimeError()
                    error.cause = i_e
            else:
                # reload all the database -- it can take a long time,
                # but it is required since we have no idea, what is
                # the result of the failure
                links = self.nl.get_links()
                for link in links:
                    self.ipdb.interfaces._new(link)
                links = self.nl.get_vlans()
                for link in links:
                    self.ipdb.interfaces._new(link)
                for addr in self.nl.get_addr():
                    self.ipdb.ipaddr._new(addr)

            error.traceback = traceback.format_exc()
            for key in ('ipaddr', 'ports', 'vlans'):
                self[key].clear_target()

        # raise partial commit exceptions
        if transaction.partial and transaction.errors:
            error = PartialCommitException('partial commit error')

        # if it is not a rollback turn
        if drop and commit_phase == 1:
            # drop last transaction in any case
            self.drop(transaction.uid)

        # raise exception for failed transaction
        if error is not None:
            error.transaction = transaction
            raise error

        time.sleep(config.commit_barrier)

        # drop all collected errors, if any
        self.errors = []
        return self
示例#4
0
文件: main.py 项目: annp1987/pyroute2
    def create(self, kind, ifname, reuse=False, **kwarg):
        '''
        Create an interface. Arguments 'kind' and 'ifname' are
        required.

            - kind — interface type, can be of:
                - bridge
                - bond
                - vlan
                - tun
                - dummy
                - veth
                - macvlan
                - macvtap
                - gre
                - team
            - ifname — interface name
            - reuse — if such interface exists, return it anyway

        Different interface kinds can require different
        arguments for creation.

        ► **veth**

        To properly create `veth` interface, one should specify
        `peer` also, since `veth` interfaces are created in pairs::

            with ip.create(ifname='v1p0', kind='veth', peer='v1p1') as i:
                i.add_ip('10.0.0.1/24')
                i.add_ip('10.0.0.2/24')

        The code above creates two interfaces, `v1p0` and `v1p1`, and
        adds two addresses to `v1p0`.

        ► **macvlan**

        Macvlan interfaces act like VLANs within OS. The macvlan driver
        provides an ability to add several MAC addresses on one interface,
        where every MAC address is reflected with a virtual interface in
        the system.

        In some setups macvlan interfaces can replace bridge interfaces,
        providing more simple and at the same time high-performance
        solution::

            ip.create(ifname='mvlan0',
                      kind='macvlan',
                      link=ip.interfaces.em1,
                      macvlan_mode='private').commit()

        Several macvlan modes are available: 'private', 'vepa', 'bridge',
        'passthru'. Ususally the default is 'vepa'.

        ► **macvtap**

        Almost the same as macvlan, but creates also a character tap device::

            ip.create(ifname='mvtap0',
                      kind='macvtap',
                      link=ip.interfaces.em1,
                      macvtap_mode='vepa').commit()

        Will create a device file `"/dev/tap%s" % ip.interfaces.mvtap0.index`

        ► **gre**

        Create GRE tunnel::

            with ip.create(ifname='grex',
                           kind='gre',
                           gre_local='172.16.0.1',
                           gre_remote='172.16.0.101',
                           gre_ttl=16) as i:
                i.add_ip('192.168.0.1/24')
                i.up()

        The keyed GRE requires explicit iflags/oflags specification::

            ip.create(ifname='grex',
                      kind='gre',
                      gre_local='172.16.0.1',
                      gre_remote='172.16.0.101',
                      gre_ttl=16,
                      gre_ikey=10,
                      gre_okey=10,
                      gre_iflags=32,
                      gre_oflags=32).commit()

        ► **vlan**

        VLAN interfaces require additional parameters, `vlan_id` and
        `link`, where `link` is a master interface to create VLAN on::

            ip.create(ifname='v100',
                      kind='vlan',
                      link=ip.interfaces.eth0,
                      vlan_id=100)

            ip.create(ifname='v100',
                      kind='vlan',
                      link=1,
                      vlan_id=100)

        The `link` parameter should be either integer, interface id, or
        an interface object. VLAN id must be integer.

        ► **vxlan**

        VXLAN interfaces are like VLAN ones, but require a bit more
        parameters::

            ip.create(ifname='vx101',
                      kind='vxlan',
                      vxlan_link=ip.interfaces.eth0,
                      vxlan_id=101,
                      vxlan_group='239.1.1.1',
                      vxlan_ttl=16)

        All possible vxlan parameters are listed in the module
        `pyroute2.netlink.rtnl.ifinfmsg:... vxlan_data`.

        ► **tuntap**

        Possible `tuntap` keywords:

            - `mode` — "tun" or "tap"
            - `uid` — integer
            - `gid` — integer
            - `ifr` — dict of tuntap flags (see tuntapmsg.py)
        '''
        with self.exclusive:
            # check for existing interface
            if ifname in self.interfaces:
                if (self.interfaces[ifname]['ipdb_scope'] == 'shadow') \
                        or reuse:
                    device = self.interfaces[ifname]
                    kwarg['kind'] = kind
                    device.load_dict(kwarg)
                    device.set_item('ipdb_scope', 'create')
                else:
                    raise CreateException("interface %s exists" % ifname)
            else:
                device = \
                    self.interfaces[ifname] = \
                    self.iclass(ipdb=self, mode='snapshot')
                device.update(kwarg)
                if isinstance(kwarg.get('link', None), Interface):
                    device['link'] = kwarg['link']['index']
                if isinstance(kwarg.get('vxlan_link', None), Interface):
                    device['vxlan_link'] = kwarg['vxlan_link']['index']
                device['kind'] = kind
                device['index'] = kwarg.get('index', 0)
                device['ifname'] = ifname
                device['ipdb_scope'] = 'create'
                device._mode = self.mode
            tid = device.begin()
        #
        # All the device methods are handled via `transactional.update()`
        # except of the very creation.
        #
        # Commit the changes in the 'direct' mode, since this call is not
        # decorated.
        if self.mode == 'direct':
            device.commit(tid)
        return device