Beispiel #1
0
    def load_netlink(self, msg):
        with self._direct_state:
            if self['ipdb_scope'] == 'locked':
                # do not touch locked interfaces
                return

            self['ipdb_scope'] = 'system'
            self.update(msg)

            # re-init metrics
            metrics = self.get('metrics', Metrics(parent=self))
            with metrics._direct_state:
                for metric in tuple(metrics.keys()):
                    del metrics[metric]
            self['metrics'] = metrics

            # merge key
            for (name, value) in msg['attrs']:
                norm = rtmsg.nla2name(name)
                # normalize RTAX
                if norm == 'metrics':
                    with self['metrics']._direct_state:
                        for (rtax, rtax_value) in value['attrs']:
                            rtax_norm = rtmsg.metrics.nla2name(rtax)
                            self['metrics'][rtax_norm] = rtax_value
                elif norm == 'multipath':
                    self['multipath'] = NextHopSet()
                    for v in value:
                        nh = {}
                        for name in [x[0] for x in rtmsg.nh.fields]:
                            nh[name] = v[name]
                        for (rta, rta_value) in v['attrs']:
                            rta_norm = rtmsg.nla2name(rta)
                            nh[rta_norm] = rta_value
                        self['multipath'].add(nh)
                else:
                    self[norm] = value

            if msg.get_attr('RTA_DST', None) is not None:
                dst = '%s/%s' % (msg.get_attr('RTA_DST'),
                                 msg['dst_len'])
            else:
                dst = 'default'
            self['dst'] = dst
            # finally, cleanup all not needed
            for item in self.cleanup:
                if item in self:
                    del self[item]

            self.sync()
Beispiel #2
0
    def load_netlink(self, msg):
        with self._direct_state:
            if self['ipdb_scope'] == 'locked':
                # do not touch locked interfaces
                return

            self['ipdb_scope'] = 'system'
            self.update(msg)

            # re-init metrics
            metrics = self.get('metrics', Metrics(parent=self))
            with metrics._direct_state:
                for metric in tuple(metrics.keys()):
                    del metrics[metric]
            self['metrics'] = metrics

            # merge key
            for (name, value) in msg['attrs']:
                norm = rtmsg.nla2name(name)
                # normalize RTAX
                if norm == 'metrics':
                    with self['metrics']._direct_state:
                        for (rtax, rtax_value) in value['attrs']:
                            rtax_norm = rtmsg.metrics.nla2name(rtax)
                            self['metrics'][rtax_norm] = rtax_value
                elif norm == 'multipath':
                    self['multipath'] = NextHopSet()
                    for v in value:
                        nh = {}
                        for name in [x[0] for x in rtmsg.nh.fields]:
                            nh[name] = v[name]
                        for (rta, rta_value) in v['attrs']:
                            rta_norm = rtmsg.nla2name(rta)
                            nh[rta_norm] = rta_value
                        self['multipath'].add(nh)
                else:
                    self[norm] = value

            if msg.get_attr('RTA_DST', None) is not None:
                dst = '%s/%s' % (msg.get_attr('RTA_DST'),
                                 msg['dst_len'])
            else:
                dst = 'default'
            self['dst'] = dst
            # finally, cleanup all not needed
            for item in self.cleanup:
                if item in self:
                    del self[item]

            self.sync()
    def load_netlink(self, msg):
        with self._direct_state:
            self._exists = True
            self.update(msg)
            # merge key
            for (name, value) in msg['attrs']:
                norm = rtmsg.nla2name(name)
                # normalize RTAX
                if norm == 'metrics':
                    ret = self.get(norm, Metrics(parent=self))
                    with ret._direct_state:
                        for (rtax, rtax_value) in value['attrs']:
                            rtax_norm = rtmsg.metrics.nla2name(rtax)
                            ret[rtax_norm] = rtax_value
                    self[norm] = ret
                else:
                    self[norm] = value

            if msg.get_attr('RTA_DST', None) is not None:
                dst = '%s/%s' % (msg.get_attr('RTA_DST'),
                                 msg['dst_len'])
            else:
                dst = 'default'
            self['dst'] = dst
            # finally, cleanup all not needed
            for item in self.cleanup:
                if item in self:
                    del self[item]

            self.sync()
Beispiel #4
0
    def __init__(self, ipdb, mode=None, parent=None, uid=None):
        Transactional.__init__(self, ipdb, mode, parent, uid)
        self._load_event = threading.Event()
        self._fields = [rtmsg.nla2name(i[0]) for i in rtmsg.nla_map]
        self._fields.append('flags')
        self._fields.append('src_len')
        self._fields.append('dst_len')
        self._fields.append('table')
        self._fields.append('removal')
        self._virtual_fields = ['ipdb_scope', 'ipdb_priority']

        def make_set_value(self, key):
            def set_value(value):
                self[key] = value
                return self
            return set_value
        for key in self._fields + self._virtual_fields:
            setattr(self, 'set_%s' % key, make_set_value(self, key))
        self._fields.extend(self._virtual_fields)
        self.cleanup = ('attrs',
                        'header',
                        'event')
        with self._direct_state:
            for i in self._fields:
                self[i] = None
            self['metrics'] = Metrics(parent=self)
            self['ipdb_priority'] = 0
Beispiel #5
0
    def load_netlink(self, msg):
        with self._direct_state:
            self._exists = True
            self.update(msg)
            # merge key
            for (name, value) in msg['attrs']:
                norm = rtmsg.nla2name(name)
                # normalize RTAX
                if norm == 'metrics':
                    ret = self.get(norm, Metrics(parent=self))
                    with ret._direct_state:
                        for (rtax, rtax_value) in value['attrs']:
                            rtax_norm = rtmsg.metrics.nla2name(rtax)
                            ret[rtax_norm] = rtax_value
                    self[norm] = ret
                else:
                    self[norm] = value

            if msg.get_attr('RTA_DST', None) is not None:
                dst = '%s/%s' % (msg.get_attr('RTA_DST'), msg['dst_len'])
            else:
                dst = 'default'
            self['dst'] = dst
            # finally, cleanup all not needed
            for item in self.cleanup:
                if item in self:
                    del self[item]

            self.sync()
Beispiel #6
0
class Route(RTNL_Object):

    table = 'routes'
    api = 'route'
    summary = '''
              SELECT
                  rt.f_target, rt.f_tflags, rt.f_RTA_TABLE, rt.f_RTA_DST,
                  rt.f_dst_len, rt.f_RTA_GATEWAY, nh.f_RTA_GATEWAY
              FROM
                  routes AS rt
              LEFT JOIN nh
              ON
                  rt.f_route_id = nh.f_route_id
                  AND rt.f_target = nh.f_target
              '''
    summary_header = ('target', 'flags', 'table', 'dst', 'dst_len', 'gateway',
                      'nexthop')
    dump = '''
           SELECT rs.f_target,rs.f_tflags,%s
           FROM routes AS rs
           LEFT JOIN nh AS nh
           ON rs.f_route_id = nh.f_route_id
               AND rs.f_target = nh.f_target
           ''' % ','.join(['%s' % x for x in _dump_rt + _dump_nh])
    dump_header = (['target', 'tflags'] +
                   [rtmsg.nla2name(x[5:]) for x in _dump_rt] +
                   ['nh_%s' % nh.nla2name(x[5:]) for x in _dump_nh])

    reverse_update = {
        'table':
        'routes',
        'name':
        'routes_f_tflags',
        'field':
        'f_tflags',
        'sql':
        '''
                          UPDATE interfaces
                          SET f_tflags = NEW.f_tflags
                          WHERE (f_index = NEW.f_RTA_OIF OR
                                 f_index = NEW.f_RTA_IIF) AND
                                 f_target = NEW.f_target;
                      '''
    }

    def __init__(self, view, key, ctxid=None):
        self.event_map = {rtmsg: "load_rtnlmsg"}
        super(Route, self).__init__(view, key, rtmsg, ctxid)

    def complete_key(self, key):
        if isinstance(key, dict):
            ret_key = key
        else:
            ret_key = {'target': 'localhost'}

        if isinstance(key, basestring):
            ret_key['RTA_DST'], ret_key['dst_len'] = key.split('/')

        return super(Route, self).complete_key(ret_key)
Beispiel #7
0
 def __init__(self, ipdb, mode=None, parent=None, uid=None):
     Transactional.__init__(self, ipdb, mode, parent, uid)
     self._exists = False
     self._load_event = threading.Event()
     self._fields = [rtmsg.nla2name(i[0]) for i in rtmsg.nla_map]
     self._fields.append('flags')
     self._fields.append('src_len')
     self._fields.append('dst_len')
     self._fields.append('table')
     self._fields.append('removal')
     self.cleanup = ('attrs', 'header', 'event')
     with self._direct_state:
         self['metrics'] = Metrics(parent=self)
Beispiel #8
0
 def __init__(self, ipdb, mode=None):
     Transactional.__init__(self, ipdb, mode)
     self._exists = False
     self._load_event = threading.Event()
     self._fields = [rtmsg.nla2name(i[0]) for i in rtmsg.nla_map]
     self._fields.append('flags')
     self._fields.append('src_len')
     self._fields.append('dst_len')
     self._fields.append('table')
     self._fields.append('removal')
     self.cleanup = ('attrs',
                     'header',
                     'event')
 def __init__(self, ipdb, mode=None, parent=None, uid=None):
     Transactional.__init__(self, ipdb, mode, parent, uid)
     self._exists = False
     self._load_event = threading.Event()
     self._fields = [rtmsg.nla2name(i[0]) for i in rtmsg.nla_map]
     self._fields.append('flags')
     self._fields.append('src_len')
     self._fields.append('dst_len')
     self._fields.append('table')
     self._fields.append('removal')
     self.cleanup = ('attrs',
                     'header',
                     'event')
     with self._direct_state:
         self['metrics'] = Metrics(parent=self)
Beispiel #10
0
 def dump(cls, view):
     req = '''
           SELECT main.f_target,main.f_tflags,%s
           FROM routes AS main
           LEFT JOIN nh AS nh
           ON main.f_route_id = nh.f_route_id
               AND main.f_target = nh.f_target
           ''' % ','.join(
         ['%s' % x for x in _dump_rt + _dump_nh + ['main.f_route_id']])
     header = (['target', 'tflags'] +
               [rtmsg.nla2name(x[7:]) for x in _dump_rt] +
               ['nh_%s' % nh.nla2name(x[5:])
                for x in _dump_nh] + ['metrics', 'encap'])
     yield header
     plch = view.ndb.schema.plch
     where, values = cls._dump_where(view)
     for record in view.ndb.schema.fetch(req + where, values):
         route_id = record[-1]
         record = list(record[:-1])
         #
         # fetch metrics
         metrics = tuple(
             view.ndb.schema.fetch(
                 '''
             SELECT * FROM metrics WHERE f_route_id = %s
         ''' % (plch, ), (route_id, )))
         if metrics:
             ret = {}
             names = view.ndb.schema.compiled['metrics']['norm_names']
             for k, v in zip(names, metrics[0]):
                 if v is not None and \
                         k not in ('target', 'route_id', 'tflags'):
                     ret[k] = v
             record.append(json.dumps(ret))
         else:
             record.append(None)
         #
         # fetch encap
         enc_mpls = tuple(
             view.ndb.schema.fetch(
                 '''
             SELECT * FROM enc_mpls WHERE f_route_id = %s
         ''' % (plch, ), (route_id, )))
         if enc_mpls:
             record.append(enc_mpls[0][2])
         else:
             record.append(None)
         yield record
Beispiel #11
0
    def load_netlink(self, msg):
        with self._direct_state:
            self._exists = True
            self.update(msg)
            # merge key
            for (name, value) in msg['attrs']:
                norm = rtmsg.nla2name(name)
                self[norm] = value
            if msg.get_attr('RTA_DST', None) is not None:
                dst = '%s/%s' % (msg.get_attr('RTA_DST'),
                                 msg['dst_len'])
            else:
                dst = 'default'
            self['dst'] = dst
            # finally, cleanup all not needed
            for item in self.cleanup:
                if item in self:
                    del self[item]

            self.sync()
Beispiel #12
0
class Route(RTNL_Object):

    table = 'routes'
    summary = '''
              SELECT
                  rt.f_target, rt.f_RTA_TABLE, rt.f_RTA_DST,
                  rt.f_dst_len, rt.f_RTA_GATEWAY, nh.f_RTA_GATEWAY
              FROM
                  routes AS rt
              LEFT JOIN nh
              ON
                  rt.f_route_id = nh.f_route_id
              '''
    summary_header = ('target', 'table', 'dst',
                      'dst_len', 'gateway', 'nexthop')
    dump = '''
           SELECT rs.f_target,%s
           FROM routes AS rs
           LEFT JOIN nh AS nh
           ON rs.f_route_id = nh.f_route_id
               AND rs.f_target = nh.f_target
           ''' % ','.join(['%s' % x for x in _dump_rt + _dump_nh])
    dump_header = (['target'] +
                   [rtmsg.nla2name(x[5:]) for x in _dump_rt] +
                   ['nh_%s' % nh.nla2name(x[5:]) for x in _dump_nh])

    def __init__(self, schema, key):
        self.event_map = {rtmsg: "load_rtnlmsg"}
        super(Route, self).__init__(schema, key, rtmsg)

    def complete_key(self, key):
        if isinstance(key, dict):
            ret_key = key
        else:
            ret_key = {'target': 'localhost'}

        if isinstance(key, basestring):
            ret_key['RTA_DST'], ret_key['dst_len'] = key.split('/')

        return super(Route, self).complete_key(ret_key)
Beispiel #13
0
    def load_netlink(self, msg):
        with self._direct_state:
            if self['ipdb_scope'] == 'locked':
                # do not touch locked interfaces
                return

            self['ipdb_scope'] = 'system'
            for (key, value) in msg.items():
                self[key] = value

            # cleanup multipath NH
            for nh in self['multipath']:
                self.del_nh(nh)

            # merge NLA
            for cell in msg['attrs']:
                #
                # Parse on demand
                #
                norm = rtmsg.nla2name(cell[0])
                if norm in self.cleanup:
                    continue
                value = cell[1]
                # normalize RTAX
                if norm == 'metrics':
                    with self['metrics']._direct_state:
                        for metric in tuple(self['metrics'].keys()):
                            del self['metrics'][metric]
                        for (rtax, rtax_value) in value['attrs']:
                            rtax_norm = rtmsg.metrics.nla2name(rtax)
                            self['metrics'][rtax_norm] = rtax_value
                elif norm == 'multipath':
                    for record in value:
                        nh = type(self)(ipdb=self.ipdb, parent=self)
                        nh.load_netlink(record)
                        with nh._direct_state:
                            del nh['dst']
                            del nh['ipdb_scope']
                            del nh['ipdb_priority']
                            del nh['multipath']
                            del nh['metrics']
                        self['multipath'].add(nh)
                elif norm == 'encap':
                    with self['encap']._direct_state:
                        ret = []
                        for l in value.get_attr('MPLS_IPTUNNEL_DST'):
                            ret.append(str(l['label']))
                        self['encap']['labels'] = '/'.join(ret)
                elif norm == 'via':
                    with self['via']._direct_state:
                        self['via'] = value
                elif norm == 'newdst':
                    self['newdst'] = [x['label'] for x in value]
                else:
                    self[norm] = value

            if msg.get('family', 0) == AF_MPLS:
                dst = msg.get_attr('RTA_DST')
                if dst:
                    dst = dst[0]['label']
            else:
                if msg.get_attr('RTA_DST'):
                    dst = '%s/%s' % (msg.get_attr('RTA_DST'),
                                     msg['dst_len'])
                else:
                    dst = 'default'
            self['dst'] = dst

            # fix RTA_ENCAP_TYPE if needed
            if msg.get_attr('RTA_ENCAP'):
                if self['encap_type'] is not None:
                    with self['encap']._direct_state:
                        self['encap']['type'] = self['encap_type']
                    self['encap_type'] = None
            # or drop encap, if there is no RTA_ENCAP in msg
            elif self['encap'] is not None:
                self['encap_type'] = None
                with self['encap']._direct_state:
                    self['encap'] = {}

            # drop metrics, if there is no RTA_METRICS in msg
            if not msg.get_attr('RTA_METRICS') and self['metrics'] is not None:
                with self['metrics']._direct_state:
                    self['metrics'] = {}

            # same for via
            if not msg.get_attr('RTA_VIA') and self['via'] is not None:
                with self['via']._direct_state:
                    self['via'] = {}

            # one hop -> multihop transition
            if not msg.get_attr('RTA_GATEWAY') and self['gateway'] is not None:
                self['gateway'] = None
            if 'oif' not in msg and \
                    not msg.get_attr('RTA_OIF') and \
                    self['oif'] is not None:
                self['oif'] = None

            # finally, cleanup all not needed
            for item in self.cleanup:
                if item in self:
                    del self[item]
Beispiel #14
0
class Route(RTNL_Object):

    table = 'routes'
    msg_class = rtmsg
    api = 'route'
    summary = '''
              SELECT
                  rt.f_target, rt.f_tflags, rt.f_RTA_TABLE, rt.f_RTA_DST,
                  rt.f_dst_len, rt.f_RTA_GATEWAY, nh.f_RTA_GATEWAY
              FROM
                  routes AS rt
              LEFT JOIN nh
              ON
                  rt.f_route_id = nh.f_route_id
                  AND rt.f_target = nh.f_target
              '''
    table_alias = 'rt'
    summary_header = ('target', 'tflags', 'table', 'dst',
                      'dst_len', 'gateway', 'nexthop')
    dump = '''
           SELECT rt.f_target,rt.f_tflags,%s
           FROM routes AS rt
           LEFT JOIN nh AS nh
           ON rt.f_route_id = nh.f_route_id
               AND rt.f_target = nh.f_target
           ''' % ','.join(['%s' % x for x in _dump_rt + _dump_nh])
    dump_header = (['target', 'tflags'] +
                   [rtmsg.nla2name(x[5:]) for x in _dump_rt] +
                   ['nh_%s' % nh.nla2name(x[5:]) for x in _dump_nh])

    reverse_update = {'table': 'routes',
                      'name': 'routes_f_tflags',
                      'field': 'f_tflags',
                      'sql': '''
                          UPDATE interfaces
                          SET f_tflags = NEW.f_tflags
                          WHERE (f_index = NEW.f_RTA_OIF OR
                                 f_index = NEW.f_RTA_IIF) AND
                                 f_target = NEW.f_target;
                      '''}

    def __init__(self, *argv, **kwarg):
        kwarg['iclass'] = rtmsg
        self.event_map = {rtmsg: "load_rtnlmsg"}
        super(Route, self).__init__(*argv, **kwarg)

    def complete_key(self, key):
        if isinstance(key, dict):
            ret_key = key
        else:
            ret_key = {'target': 'localhost'}

        if isinstance(key, basestring):
            ret_key['RTA_DST'], ret_key['dst_len'] = key.split('/')

        return super(Route, self).complete_key(ret_key)

    def __setitem__(self, key, value):
        if self.state == 'system' and key in self.knorm:
            self._replace = type(self)(self.view, self.key)
            self.state.set('replace')
        if key in ('net_ns_fd', 'net_ns_pid'):
            self.state.set('setns')
        if value != self.get(key, None):
            self.changed.add(key)
            dict.__setitem__(self, key, value)
Beispiel #15
0
class Route(RTNL_Object):

    table = 'routes'
    msg_class = rtmsg
    api = 'route'
    summary = '''
              SELECT
                  rt.f_target, rt.f_tflags, rt.f_RTA_TABLE, rt.f_RTA_DST,
                  rt.f_dst_len, rt.f_RTA_GATEWAY, nh.f_RTA_GATEWAY
              FROM
                  routes AS rt
              LEFT JOIN nh
              ON
                  rt.f_route_id = nh.f_route_id
                  AND rt.f_target = nh.f_target
              '''
    table_alias = 'rt'
    summary_header = ('target', 'tflags', 'table', 'dst', 'dst_len', 'gateway',
                      'nexthop')
    dump = '''
           SELECT rt.f_target,rt.f_tflags,%s
           FROM routes AS rt
           LEFT JOIN nh AS nh
           ON rt.f_route_id = nh.f_route_id
               AND rt.f_target = nh.f_target
           ''' % ','.join(['%s' % x for x in _dump_rt + _dump_nh])
    dump_header = (['target', 'tflags'] +
                   [rtmsg.nla2name(x[5:]) for x in _dump_rt] +
                   ['nh_%s' % nh.nla2name(x[5:]) for x in _dump_nh])

    reverse_update = {
        'table':
        'routes',
        'name':
        'routes_f_tflags',
        'field':
        'f_tflags',
        'sql':
        '''
                          UPDATE interfaces
                          SET f_tflags = NEW.f_tflags
                          WHERE (f_index = NEW.f_RTA_OIF OR
                                 f_index = NEW.f_RTA_IIF) AND
                                 f_target = NEW.f_target;
                      '''
    }

    _replace_on_key_change = True

    def __init__(self, *argv, **kwarg):
        kwarg['iclass'] = rtmsg
        self.event_map = {rtmsg: "load_rtnlmsg"}
        dict.__setitem__(self, 'multipath', [])
        super(Route, self).__init__(*argv, **kwarg)

    def complete_key(self, key):
        if isinstance(key, dict):
            ret_key = key
        else:
            ret_key = {'target': 'localhost'}

        if isinstance(key, basestring):
            ret_key['RTA_DST'], ret_key['dst_len'] = key.split('/')

        return super(Route, self).complete_key(ret_key)

    def make_req(self, prime):
        req = dict(prime)
        for key in self.changed:
            req[key] = self[key]
        if self['multipath']:
            req['multipath'] = self['multipath']
        return req

    def __setitem__(self, key, value):
        super(Route, self).__setitem__(key, value)
        if key == 'multipath':
            self.changed.remove(key)

    def apply(self, rollback=False):
        if (self.get('table') == 255) and \
                (self.get('family') == 10) and \
                (self.get('proto') == 2):
            # skip automatic ipv6 routes with proto kernel
            return self
        else:
            return super(Route, self).apply(rollback)

    def load_sql(self, *argv, **kwarg):
        super(Route, self).load_sql(*argv, **kwarg)
        if not self.load_event.is_set():
            return
        if 'nh_id' not in self and self.get('route_id') is not None:
            nhs = (self.schema.fetch(
                'SELECT * FROM nh WHERE f_route_id = %s' %
                (self.schema.plch, ), (self['route_id'], )))
            flush = False
            idx = 0
            for nexthop in tuple(self['multipath']):
                if not isinstance(nexthop, NextHop):
                    flush = True

                if not flush:
                    try:
                        spec = next(nhs)
                    except StopIteration:
                        flush = True
                    for key, value in zip(nexthop.names, spec):
                        if key in nexthop and value is None:
                            continue
                        else:
                            nexthop.load_value(key, value)
                if flush:
                    self['multipath'].pop(idx)
                    continue
                idx += 1

            for nexthop in nhs:
                key = {'route_id': self['route_id'], 'nh_id': nexthop[-1]}
                self['multipath'].append(NextHop(self.view, key))
Beispiel #16
0
class Route(RTNL_Object):

    table = 'routes'
    msg_class = rtmsg
    hidden_fields = ['route_id']
    api = 'route'
    summary = '''
              SELECT
                  rt.f_target, rt.f_tflags, rt.f_RTA_TABLE, rt.f_RTA_DST,
                  rt.f_dst_len, rt.f_RTA_GATEWAY, nh.f_RTA_GATEWAY
              FROM
                  routes AS rt
              LEFT JOIN nh
              ON
                  rt.f_route_id = nh.f_route_id
                  AND rt.f_target = nh.f_target
              '''
    table_alias = 'rt'
    summary_header = ('target', 'tflags', 'table', 'dst', 'dst_len', 'gateway',
                      'nexthop')
    dump = '''
           SELECT rt.f_target,rt.f_tflags,%s
           FROM routes AS rt
           LEFT JOIN nh AS nh
           ON rt.f_route_id = nh.f_route_id
               AND rt.f_target = nh.f_target
           ''' % ','.join(['%s' % x for x in _dump_rt + _dump_nh])
    dump_header = (['target', 'tflags'] +
                   [rtmsg.nla2name(x[5:]) for x in _dump_rt] +
                   ['nh_%s' % nh.nla2name(x[5:]) for x in _dump_nh])

    _replace_on_key_change = True

    def mark_tflags(self, mark):
        plch = (self.schema.plch, ) * 4
        self.schema.execute(
            '''
                            UPDATE interfaces SET
                                f_tflags = %s
                            WHERE
                                (f_index = %s OR f_index = %s)
                                AND f_target = %s
                            ''' % plch,
            (mark, self['iif'], self['oif'], self['target']))

    def __init__(self, *argv, **kwarg):
        kwarg['iclass'] = rtmsg
        self.event_map = {rtmsg: "load_rtnlmsg"}
        dict.__setitem__(self, 'multipath', [])
        dict.__setitem__(self, 'metrics', {})
        super(Route, self).__init__(*argv, **kwarg)

    def complete_key(self, key):
        ret_key = {}
        if isinstance(key, basestring):
            ret_key['dst'] = key
        elif isinstance(key, (Record, tuple, list)):
            return super(Route, self).complete_key(key)
        elif isinstance(key, dict):
            ret_key.update(key)
        else:
            raise TypeError('unsupported key type')

        if 'target' not in ret_key:
            ret_key['target'] = 'localhost'

        table = ret_key.get('table', ret_key.get('RTA_TABLE', 254))
        if 'table' not in ret_key:
            ret_key['table'] = table

        if isinstance(ret_key.get('dst_len'), basestring):
            ret_key['dst_len'] = int(ret_key['dst_len'])

        if isinstance(ret_key.get('dst'), basestring):
            if ret_key.get('dst') == 'default':
                ret_key['dst'] = ''
                ret_key['dst_len'] = 0
            elif '/' in ret_key['dst']:
                ret_key['dst'], ret_key['dst_len'] = ret_key['dst'].split('/')

        return super(Route, self).complete_key(ret_key)

    @property
    def clean(self):
        clean = True
        for s in (self['metrics'], ) + tuple(self['multipath']):
            if hasattr(s, 'changed'):
                clean &= len(s.changed) == 0
        return clean & super(Route, self).clean

    def make_req(self, prime):
        req = dict(prime)
        for key in self.changed:
            req[key] = self[key]
        if self['multipath']:
            req['multipath'] = self['multipath']
        if self['metrics']:
            req['metrics'] = self['metrics']
        if self.get('gateway'):
            req['gateway'] = self['gateway']
        return req

    def __setitem__(self, key, value):
        if key in ('dst', 'src') and '/' in value:
            net, net_len = value.split('/')
            if net in ('0', '0.0.0.0'):
                net = ''
            super(Route, self).__setitem__(key, net)
            super(Route, self).__setitem__('%s_len' % key, int(net_len))
        elif key == 'dst' and value == 'default':
            super(Route, self).__setitem__('dst', '')
            super(Route, self).__setitem__('dst_len', 0)
        elif key == 'route_id':
            raise ValueError('route_id is read only')
        elif key == 'multipath':
            super(Route, self).__setitem__('multipath', [])
            for mp in value:
                mp = dict(mp)
                if self.state == 'invalid':
                    mp['create'] = True
                obj = NextHop(self, self.view, mp)
                obj.state.set(self.state.get())
                self['multipath'].append(obj)
            if key in self.changed:
                self.changed.remove(key)
        elif key == 'metrics':
            value = dict(value)
            if self.state == 'invalid':
                value['create'] = True
            obj = Metrics(self, self.view, value)
            obj.state.set(self.state.get())
            super(Route, self).__setitem__('metrics', obj)
            if key in self.changed:
                self.changed.remove(key)
        else:
            super(Route, self).__setitem__(key, value)

    def apply(self, rollback=False):
        if (self.get('table') == 255) and \
                (self.get('family') == 10) and \
                (self.get('proto') == 2):
            # skip automatic ipv6 routes with proto kernel
            return self
        else:
            return super(Route, self).apply(rollback)

    def load_sql(self, *argv, **kwarg):
        super(Route, self).load_sql(*argv, **kwarg)
        if not self.load_event.is_set():
            return
        if 'nh_id' not in self and self.get('route_id') is not None:
            nhs = (self.schema.fetch(
                'SELECT * FROM nh WHERE f_route_id = %s' %
                (self.schema.plch, ), (self['route_id'], )))
            metrics = (self.schema.fetch(
                'SELECT * FROM metrics WHERE f_route_id = %s' %
                (self.schema.plch, ), (self['route_id'], )))

            if len(tuple(metrics)):
                self['metrics'] = Metrics(self, self.view,
                                          {'route_id': self['route_id']})
            flush = False
            idx = 0
            for nexthop in tuple(self['multipath']):
                if not isinstance(nexthop, NextHop):
                    flush = True

                if not flush:
                    try:
                        spec = next(nhs)
                    except StopIteration:
                        flush = True
                    for key, value in zip(nexthop.names, spec):
                        if key in nexthop and value is None:
                            continue
                        else:
                            nexthop.load_value(key, value)
                if flush:
                    self['multipath'].pop(idx)
                    continue
                idx += 1

            for nexthop in nhs:
                key = {'route_id': self['route_id'], 'nh_id': nexthop[-1]}
                self['multipath'].append(NextHop(self, self.view, key))
Beispiel #17
0
class Route(Transactional):
    '''
    Persistent transactional route object
    '''

    _fields = [rtmsg.nla2name(i[0]) for i in rtmsg.nla_map]
    _fields.append('flags')
    _fields.append('src_len')
    _fields.append('dst_len')
    _fields.append('table')
    _fields.append('removal')
    _virtual_fields = ['ipdb_scope', 'ipdb_priority']
    _fields.extend(_virtual_fields)
    _linked_sets = [
        'multipath',
    ]
    cleanup = ('attrs', 'header', 'event', 'cacheinfo')

    def __init__(self, ipdb, mode=None, parent=None, uid=None):
        Transactional.__init__(self, ipdb, mode, parent, uid)
        self._load_event = threading.Event()
        with self._direct_state:
            for i in self._fields:
                self[i] = None
            self['metrics'] = Metrics(parent=self)
            self['multipath'] = NextHopSet()
            self['ipdb_priority'] = 0

    def add_nh(self, prime):
        with self._write_lock:
            tx = self.get_tx()
            with tx._direct_state:
                tx['multipath'].add(prime)

    def del_nh(self, prime):
        with self._write_lock:
            tx = self.get_tx()
            with tx._direct_state:
                tx['multipath'].remove(prime)

    def load_netlink(self, msg):
        with self._direct_state:
            if self['ipdb_scope'] == 'locked':
                # do not touch locked interfaces
                return

            self['ipdb_scope'] = 'system'
            self.update(msg)

            # re-init metrics
            metrics = self.get('metrics', Metrics(parent=self))
            with metrics._direct_state:
                for metric in tuple(metrics.keys()):
                    del metrics[metric]
            self['metrics'] = metrics

            # merge key
            for (name, value) in msg['attrs']:
                norm = rtmsg.nla2name(name)
                # normalize RTAX
                if norm == 'metrics':
                    with self['metrics']._direct_state:
                        for (rtax, rtax_value) in value['attrs']:
                            rtax_norm = rtmsg.metrics.nla2name(rtax)
                            self['metrics'][rtax_norm] = rtax_value
                elif norm == 'multipath':
                    self['multipath'] = NextHopSet()
                    for v in value:
                        nh = {}
                        for name in [x[0] for x in rtmsg.nh.fields]:
                            nh[name] = v[name]
                        for (rta, rta_value) in v['attrs']:
                            rta_norm = rtmsg.nla2name(rta)
                            nh[rta_norm] = rta_value
                        self['multipath'].add(nh)
                else:
                    self[norm] = value

            if msg.get_attr('RTA_DST', None) is not None:
                dst = '%s/%s' % (msg.get_attr('RTA_DST'), msg['dst_len'])
            else:
                dst = 'default'
            self['dst'] = dst
            # finally, cleanup all not needed
            for item in self.cleanup:
                if item in self:
                    del self[item]

            self.sync()

    def sync(self):
        self._load_event.set()

    def reload(self):
        # do NOT call get_routes() here, it can cause race condition
        # self._load_event.wait()
        return self

    def commit(self, tid=None, transaction=None, rollback=False):
        self._load_event.clear()
        error = None
        drop = True

        if tid:
            transaction = self._transactions[tid]
        else:
            if transaction:
                drop = False
            else:
                transaction = self.last()

        # create a new route
        if self['ipdb_scope'] != 'system':
            try:
                self.ipdb.update_routes(
                    self.nl.route('add', **IPRouteRequest(transaction)))
            except Exception:
                self.nl = None
                self.ipdb.routes.remove(self)
                raise

        # work on existing route
        snapshot = self.pick()
        try:
            # route set
            request = IPRouteRequest(transaction - snapshot)
            if any([request[x] not in (None, {'attrs': []}) for x in request]):
                self.ipdb.update_routes(
                    self.nl.route('set', **IPRouteRequest(transaction)))

            # route removal
            if (transaction['ipdb_scope'] in ('shadow', 'remove')) or\
                    ((transaction['ipdb_scope'] == 'create') and rollback):
                if transaction['ipdb_scope'] == 'shadow':
                    self.set_item('ipdb_scope', 'locked')
                self.ipdb.update_routes(
                    self.nl.route('delete', **IPRouteRequest(snapshot)))
                if transaction['ipdb_scope'] == 'shadow':
                    self.set_item('ipdb_scope', 'shadow')

        except Exception as e:
            if not rollback:
                ret = self.commit(transaction=snapshot, rollback=True)
                if isinstance(ret, Exception):
                    error = ret
                else:
                    error = e
            else:
                if drop:
                    self.drop()
                x = RuntimeError()
                x.cause = e
                raise x

        if drop and not rollback:
            self.drop()

        if error is not None:
            error.transaction = transaction
            raise error

        if not rollback:
            with self._direct_state:
                self['multipath'] = transaction['multipath']
            self.reload()

        return self

    def remove(self):
        self['ipdb_scope'] = 'remove'
        return self

    def shadow(self):
        self['ipdb_scope'] = 'shadow'
        return self
Beispiel #18
0
    def load_netlink(self, msg):
        with self._direct_state:
            if self['ipdb_scope'] == 'locked':
                # do not touch locked interfaces
                return

            self['ipdb_scope'] = 'system'
            for (key, value) in msg.items():
                self[key] = value

            # cleanup multipath NH
            for nh in self['multipath']:
                self.del_nh(nh)

            # merge NLA
            for cell in msg['attrs']:
                #
                # Parse on demand
                #
                norm = rtmsg.nla2name(cell[0])
                if norm in self.cleanup:
                    continue
                value = cell[1]
                # normalize RTAX
                if norm == 'metrics':
                    with self['metrics']._direct_state:
                        for metric in tuple(self['metrics'].keys()):
                            del self['metrics'][metric]
                        for (rtax, rtax_value) in value['attrs']:
                            rtax_norm = rtmsg.metrics.nla2name(rtax)
                            self['metrics'][rtax_norm] = rtax_value
                elif norm == 'multipath':
                    for record in value:
                        nh = type(self)(ipdb=self.ipdb, parent=self)
                        nh.load_netlink(record)
                        with nh._direct_state:
                            del nh['dst']
                            del nh['ipdb_scope']
                            del nh['ipdb_priority']
                            del nh['multipath']
                            del nh['metrics']
                        self['multipath'].add(nh)
                elif norm == 'encap':
                    with self['encap']._direct_state:
                        ret = []
                        # FIXME: should support encap_types other than MPLS
                        try:
                            for l in value.get_attr('MPLS_IPTUNNEL_DST'):
                                ret.append(str(l['label']))
                            if ret:
                                self['encap']['labels'] = '/'.join(ret)
                        except AttributeError:
                            pass
                elif norm == 'via':
                    with self['via']._direct_state:
                        self['via'] = value
                elif norm == 'newdst':
                    self['newdst'] = [x['label'] for x in value]
                else:
                    self[norm] = value

            if msg.get('family', 0) == AF_MPLS:
                dst = msg.get_attr('RTA_DST')
                if dst:
                    dst = dst[0]['label']
            else:
                if msg.get_attr('RTA_DST'):
                    dst = '%s/%s' % (msg.get_attr('RTA_DST'), msg['dst_len'])
                else:
                    dst = 'default'
            self['dst'] = dst

            # fix RTA_ENCAP_TYPE if needed
            if msg.get_attr('RTA_ENCAP'):
                if self['encap_type'] is not None:
                    with self['encap']._direct_state:
                        self['encap']['type'] = self['encap_type']
                    self['encap_type'] = None
            # or drop encap, if there is no RTA_ENCAP in msg
            elif self['encap'] is not None:
                self['encap_type'] = None
                with self['encap']._direct_state:
                    self['encap'] = {}

            # drop metrics, if there is no RTA_METRICS in msg
            if not msg.get_attr('RTA_METRICS') and self['metrics'] is not None:
                with self['metrics']._direct_state:
                    self['metrics'] = {}

            # same for via
            if not msg.get_attr('RTA_VIA') and self['via'] is not None:
                with self['via']._direct_state:
                    self['via'] = {}

            # one hop -> multihop transition
            if not msg.get_attr('RTA_GATEWAY') and self['gateway'] is not None:
                self['gateway'] = None
            if 'oif' not in msg and \
                    not msg.get_attr('RTA_OIF') and \
                    self['oif'] is not None:
                self['oif'] = None

            # finally, cleanup all not needed
            for item in self.cleanup:
                if item in self:
                    del self[item]
Beispiel #19
0
class BaseRoute(Transactional):
    '''
    Persistent transactional route object
    '''

    _fields = [rtmsg.nla2name(i[0]) for i in rtmsg.nla_map]
    for key, _ in rtmsg.fields:
        _fields.append(key)
    _fields.append('removal')
    _virtual_fields = ['ipdb_scope', 'ipdb_priority']
    _fields.extend(_virtual_fields)
    _linked_sets = [
        'multipath',
    ]
    _nested = []
    _gctime = None
    cleanup = ('attrs', 'header', 'event', 'cacheinfo')
    _fields_cmp = {
        'src': _normalize_ipnet,
        'dst': _normalize_ipnet,
        'gateway': _normalize_ipaddr,
        'prefsrc': _normalize_ipaddr
    }

    def __init__(self, ipdb, mode=None, parent=None, uid=None):
        Transactional.__init__(self, ipdb, mode, parent, uid)
        with self._direct_state:
            self['ipdb_priority'] = 0

    @with_transaction
    def add_nh(self, prime):
        with self._write_lock:
            # if the multipath chain is empty, copy the current
            # nexthop as the first in the multipath
            if not self['multipath']:
                first = {}
                for key in ('oif', 'gateway', 'newdst'):
                    if self[key]:
                        first[key] = self[key]
                if first:
                    if self['family']:
                        first['family'] = self['family']
                    for key in ('encap', 'via', 'metrics'):
                        if self[key] and any(self[key].values()):
                            first[key] = self[key]
                            self[key] = None
                    self['multipath'].add(first)
                    # cleanup key fields
                    for key in ('oif', 'gateway', 'newdst'):
                        self[key] = None
            # add the prime as NH
            if self['family'] == AF_MPLS:
                prime['family'] = AF_MPLS
            self['multipath'].add(prime)

    @with_transaction
    def del_nh(self, prime):
        with self._write_lock:
            if not self['multipath']:
                raise KeyError('attempt to delete nexthop from '
                               'non-multipath route')
            nh = dict(prime)
            if self['family'] == AF_MPLS:
                nh['family'] = AF_MPLS
            self['multipath'].remove(nh)

    def load_netlink(self, msg):
        with self._direct_state:
            if self['ipdb_scope'] == 'locked':
                # do not touch locked interfaces
                return

            self['ipdb_scope'] = 'system'
            for (key, value) in msg.items():
                self[key] = value

            # cleanup multipath NH
            for nh in self['multipath']:
                self.del_nh(nh)

            # merge NLA
            for cell in msg['attrs']:
                #
                # Parse on demand
                #
                norm = rtmsg.nla2name(cell[0])
                if norm in self.cleanup:
                    continue
                value = cell[1]
                # normalize RTAX
                if norm == 'metrics':
                    with self['metrics']._direct_state:
                        for metric in tuple(self['metrics'].keys()):
                            del self['metrics'][metric]
                        for (rtax, rtax_value) in value['attrs']:
                            rtax_norm = rtmsg.metrics.nla2name(rtax)
                            self['metrics'][rtax_norm] = rtax_value
                elif norm == 'multipath':
                    for record in value:
                        nh = type(self)(ipdb=self.ipdb, parent=self)
                        nh.load_netlink(record)
                        with nh._direct_state:
                            del nh['dst']
                            del nh['ipdb_scope']
                            del nh['ipdb_priority']
                            del nh['multipath']
                            del nh['metrics']
                        self['multipath'].add(nh)
                elif norm == 'encap':
                    with self['encap']._direct_state:
                        ret = []
                        # FIXME: should support encap_types other than MPLS
                        try:
                            for l in value.get_attr('MPLS_IPTUNNEL_DST'):
                                ret.append(str(l['label']))
                            if ret:
                                self['encap']['labels'] = '/'.join(ret)
                        except AttributeError:
                            pass
                elif norm == 'via':
                    with self['via']._direct_state:
                        self['via'] = value
                elif norm == 'newdst':
                    self['newdst'] = [x['label'] for x in value]
                else:
                    self[norm] = value

            if msg.get('family', 0) == AF_MPLS:
                dst = msg.get_attr('RTA_DST')
                if dst:
                    dst = dst[0]['label']
            else:
                if msg.get_attr('RTA_DST'):
                    dst = '%s/%s' % (msg.get_attr('RTA_DST'), msg['dst_len'])
                else:
                    dst = 'default'
            self['dst'] = dst

            # fix RTA_ENCAP_TYPE if needed
            if msg.get_attr('RTA_ENCAP'):
                if self['encap_type'] is not None:
                    with self['encap']._direct_state:
                        self['encap']['type'] = self['encap_type']
                    self['encap_type'] = None
            # or drop encap, if there is no RTA_ENCAP in msg
            elif self['encap'] is not None:
                self['encap_type'] = None
                with self['encap']._direct_state:
                    self['encap'] = {}

            # drop metrics, if there is no RTA_METRICS in msg
            if not msg.get_attr('RTA_METRICS') and self['metrics'] is not None:
                with self['metrics']._direct_state:
                    self['metrics'] = {}

            # same for via
            if not msg.get_attr('RTA_VIA') and self['via'] is not None:
                with self['via']._direct_state:
                    self['via'] = {}

            # one hop -> multihop transition
            if not msg.get_attr('RTA_GATEWAY') and self['gateway'] is not None:
                self['gateway'] = None
            if 'oif' not in msg and \
                    not msg.get_attr('RTA_OIF') and \
                    self['oif'] is not None:
                self['oif'] = None

            # finally, cleanup all not needed
            for item in self.cleanup:
                if item in self:
                    del self[item]

    def commit(self,
               tid=None,
               transaction=None,
               commit_phase=1,
               commit_mask=0xff):

        if not commit_phase & commit_mask:
            return self

        error = None
        drop = self.ipdb.txdrop
        devop = 'set'
        cleanup = []
        # FIXME -- make a debug object
        debug = {'traceback': None, 'next_stage': None}
        notx = True

        if tid or transaction:
            notx = False

        if tid:
            transaction = self.global_tx[tid]
        else:
            transaction = transaction or self.current_tx

        # ignore global rollbacks on invalid routes
        if self['ipdb_scope'] == 'create' and commit_phase > 1:
            return

        # create a new route
        if self['ipdb_scope'] != 'system':
            devop = 'add'

        # work on an existing route
        snapshot = self.pick()
        added, removed = transaction // snapshot
        added.pop('ipdb_scope', None)
        removed.pop('ipdb_scope', None)

        try:
            # route set
            if self['family'] != AF_MPLS:
                cleanup = [
                    any(snapshot['metrics'].values())
                    and not any(added.get('metrics', {}).values()),
                    any(snapshot['encap'].values())
                    and not any(added.get('encap', {}).values())
                ]
            if any(added.values()) or \
                    any(cleanup) or \
                    removed.get('multipath', None) or \
                    devop == 'add':
                # prepare multipath target sync
                wlist = []
                if transaction['multipath']:
                    mplen = len(transaction['multipath'])
                    if mplen == 1:
                        # set up local targets
                        for nh in transaction['multipath']:
                            for key in ('oif', 'gateway', 'newdst'):
                                if nh.get(key, None):
                                    self.set_target(key, nh[key])
                                    wlist.append(key)
                        mpt = None
                    else:

                        def mpcheck(mpset):
                            return len(mpset) == mplen

                        mpt = self['multipath'].set_target(mpcheck, True)
                else:
                    mpt = None

                # prepare the anchor key to catch *possible* route update
                old_key = self.make_key(self)
                new_key = self.make_key(transaction)
                if old_key != new_key:
                    # assume we can not move routes between tables (yet ;)
                    if self['family'] == AF_MPLS:
                        route_index = self.ipdb.routes.tables['mpls'].idx
                    else:
                        route_index = (self.ipdb.routes.tables[self['table']
                                                               or 254].idx)
                    # re-link the route record
                    if new_key in route_index:
                        raise CommitException('route idx conflict')
                    else:
                        route_index[new_key] = {'key': new_key, 'route': self}
                    # wipe the old key, if needed
                    if old_key in route_index:
                        del route_index[old_key]
                self.nl.route(devop, **transaction)
                # delete old record, if required
                if (old_key != new_key) and (devop == 'set'):
                    self.nl.route('del', **dict(old_key._asdict()))
                transaction.wait_all_targets()
                for key in ('metrics', 'via'):
                    if transaction[key] and transaction[key]._targets:
                        transaction[key].wait_all_targets()
                if mpt is not None:
                    mpt.wait(SYNC_TIMEOUT)
                    if not mpt.is_set():
                        raise CommitException('multipath target is not set')
                    self['multipath'].clear_target(mpt)
                for key in wlist:
                    self.wait_target(key)
            # route removal
            if (transaction['ipdb_scope'] in ('shadow', 'remove')) or\
                    ((transaction['ipdb_scope'] == 'create') and
                     commit_phase == 2):
                if transaction['ipdb_scope'] == 'shadow':
                    with self._direct_state:
                        self['ipdb_scope'] = 'locked'
                # create watchdog
                wd = self.ipdb.watchdog('RTM_DELROUTE',
                                        **self.wd_key(snapshot))
                for route in self.nl.route('delete', **snapshot):
                    self.ipdb.routes.load_netlink(route)
                wd.wait()
                if transaction['ipdb_scope'] == 'shadow':
                    with self._direct_state:
                        self['ipdb_scope'] = 'shadow'

            # success, so it's safe to drop the transaction
            drop = True

        except Exception as e:

            error = e
            # prepare postmortem
            debug['traceback'] = traceback.format_exc()
            debug['error_stack'] = []
            debug['next_stage'] = None

            if commit_phase == 1:
                try:
                    self.commit(transaction=snapshot,
                                commit_phase=2,
                                commit_mask=commit_mask)
                except Exception as i_e:
                    debug['next_stage'] = i_e
                    error = RuntimeError()

        if drop and notx:
            self.drop(transaction.uid)

        if error is not None:
            error.debug = debug
            raise error

        self.ipdb.routes.gc()
        return self

    def remove(self):
        self['ipdb_scope'] = 'remove'
        return self

    def shadow(self):
        self['ipdb_scope'] = 'shadow'
        return self

    def detach(self):
        if self.get('family') == AF_MPLS:
            table = 'mpls'
        else:
            table = self.get('table', 254)
        del self.ipdb.routes.tables[table][self.make_key(self)]
Beispiel #20
0
    def load_netlink(self, msg):
        with self._direct_state:
            if self['ipdb_scope'] == 'locked':
                # do not touch locked interfaces
                return

            self['ipdb_scope'] = 'system'

            # IPv6 multipath via several devices (not networks) is a very
            # special case, since we get only the first hop notification. Ask
            # the kernel guys why. I've got no idea.
            #
            # So load all the rest
            flags = msg.get('header', {}).get('flags', 0)
            family = msg.get('family', 0)
            clean_mp = True
            table = msg.get_attr('RTA_TABLE') or msg.get('table')
            dst = msg.get_attr('RTA_DST')
            #
            # It MAY be a multipath hop
            #
            if family == AF_INET6 and not msg.get_attr('RTA_MULTIPATH'):
                #
                # It is a notification about the route created
                #
                if flags == NLM_F_CREATE:
                    #
                    # This routine can significantly slow down the IPDB
                    # instance, but I see no way around. Some are born
                    # to endless night.
                    #
                    clean_mp = False
                    msgs = self.nl.route('show',
                                         table=table,
                                         dst=dst,
                                         family=family)
                    for nhmsg in msgs:
                        nh = type(self)(ipdb=self.ipdb, parent=self)
                        nh.load_netlink(nhmsg)
                        with nh._direct_state:
                            del nh['dst']
                            del nh['ipdb_scope']
                            del nh['ipdb_priority']
                            del nh['multipath']
                            del nh['metrics']
                        self.add_nh(nh)
                #
                # it IS a multipath hop loaded during IPDB init
                #
                elif flags == NLM_F_MULTI and self.get('dst'):
                    nh = type(self)(ipdb=self.ipdb, parent=self)
                    nh.load_netlink(msg)
                    with nh._direct_state:
                        del nh['dst']
                        del nh['ipdb_scope']
                        del nh['ipdb_priority']
                        del nh['multipath']
                        del nh['metrics']
                    self.add_nh(nh)
                    return

            for (key, value) in msg.items():
                self[key] = value

            # cleanup multipath NH
            if clean_mp:
                for nh in self['multipath']:
                    self.del_nh(nh)

            for cell in msg['attrs']:
                #
                # Parse on demand
                #
                norm = rtmsg.nla2name(cell[0])
                if norm in self.cleanup:
                    continue
                value = cell[1]
                # normalize RTAX
                if norm == 'metrics':
                    with self['metrics']._direct_state:
                        for metric in tuple(self['metrics'].keys()):
                            del self['metrics'][metric]
                        for (rtax, rtax_value) in value['attrs']:
                            rtax_norm = rtmsg.metrics.nla2name(rtax)
                            self['metrics'][rtax_norm] = rtax_value
                elif norm == 'multipath':
                    for record in value:
                        nh = type(self)(ipdb=self.ipdb, parent=self)
                        nh.load_netlink(record)
                        with nh._direct_state:
                            del nh['dst']
                            del nh['ipdb_scope']
                            del nh['ipdb_priority']
                            del nh['multipath']
                            del nh['metrics']
                        self['multipath'].add(nh)
                elif norm == 'encap':
                    with self['encap']._direct_state:
                        # WIP: should support encap_types other than MPLS
                        if value.get_attr('MPLS_IPTUNNEL_DST'):
                            ret = []
                            for dst in value.get_attr('MPLS_IPTUNNEL_DST'):
                                ret.append(str(dst['label']))
                            if ret:
                                self['encap']['labels'] = '/'.join(ret)
                elif norm == 'via':
                    with self['via']._direct_state:
                        self['via'] = value
                elif norm == 'newdst':
                    self['newdst'] = [x['label'] for x in value]
                else:
                    self[norm] = value

            if msg.get('family', 0) == AF_MPLS:
                dst = msg.get_attr('RTA_DST')
                if dst:
                    dst = dst[0]['label']
            else:
                if msg.get_attr('RTA_DST'):
                    dst = '%s/%s' % (msg.get_attr('RTA_DST'),
                                     msg['dst_len'])
                else:
                    dst = 'default'
            self['dst'] = dst

            # fix RTA_ENCAP_TYPE if needed
            if msg.get_attr('RTA_ENCAP'):
                if self['encap_type'] is not None:
                    with self['encap']._direct_state:
                        self['encap']['type'] = self['encap_type']
                    self['encap_type'] = None
            # or drop encap, if there is no RTA_ENCAP in msg
            elif self['encap'] is not None:
                self['encap_type'] = None
                with self['encap']._direct_state:
                    self['encap'] = {}

            # drop metrics, if there is no RTA_METRICS in msg
            if not msg.get_attr('RTA_METRICS') and self['metrics'] is not None:
                with self['metrics']._direct_state:
                    self['metrics'] = {}

            # same for via
            if not msg.get_attr('RTA_VIA') and self['via'] is not None:
                with self['via']._direct_state:
                    self['via'] = {}

            # one hop -> multihop transition
            if not msg.get_attr('RTA_GATEWAY') and self['gateway'] is not None:
                self['gateway'] = None
            if 'oif' not in msg and \
                    not msg.get_attr('RTA_OIF') and \
                    self['oif'] is not None:
                self['oif'] = None

            # finally, cleanup all not needed
            for item in self.cleanup:
                if item in self:
                    del self[item]
Beispiel #21
0
    def load_netlink(self, msg):
        with self._direct_state:
            if self['ipdb_scope'] == 'locked':
                # do not touch locked interfaces
                return

            self['ipdb_scope'] = 'system'

            # IPv6 multipath via several devices (not networks) is a very
            # special case, since we get only the first hop notification. Ask
            # the kernel guys why. I've got no idea.
            #
            # So load all the rest
            flags = msg.get('header', {}).get('flags', 0)
            family = msg.get('family', 0)
            clean_mp = True
            table = msg.get_attr('RTA_TABLE') or msg.get('table')
            dst = msg.get_attr('RTA_DST')
            #
            # It MAY be a multipath hop
            #
            if family == AF_INET6 and not msg.get_attr('RTA_MULTIPATH'):
                #
                # It is a notification about the route created
                #
                if flags == NLM_F_CREATE:
                    #
                    # This routine can significantly slow down the IPDB
                    # instance, but I see no way around. Some are born
                    # to endless night.
                    #
                    clean_mp = False
                    msgs = self.nl.route('show',
                                         table=table,
                                         dst=dst,
                                         family=family)
                    for nhmsg in msgs:
                        nh = type(self)(ipdb=self.ipdb, parent=self)
                        nh.load_netlink(nhmsg)
                        with nh._direct_state:
                            del nh['dst']
                            del nh['ipdb_scope']
                            del nh['ipdb_priority']
                            del nh['multipath']
                            del nh['metrics']
                        self.add_nh(nh)
                #
                # it IS a multipath hop loaded during IPDB init
                #
                elif flags == NLM_F_MULTI and self.get('dst'):
                    nh = type(self)(ipdb=self.ipdb, parent=self)
                    nh.load_netlink(msg)
                    with nh._direct_state:
                        del nh['dst']
                        del nh['ipdb_scope']
                        del nh['ipdb_priority']
                        del nh['multipath']
                        del nh['metrics']
                    self.add_nh(nh)
                    return

            for (key, value) in msg.items():
                self[key] = value

            # cleanup multipath NH
            if clean_mp:
                for nh in self['multipath']:
                    self.del_nh(nh)

            for cell in msg['attrs']:
                #
                # Parse on demand
                #
                norm = rtmsg.nla2name(cell[0])
                if norm in self.cleanup:
                    continue
                value = cell[1]
                # normalize RTAX
                if norm == 'metrics':
                    with self['metrics']._direct_state:
                        for metric in tuple(self['metrics'].keys()):
                            del self['metrics'][metric]
                        for (rtax, rtax_value) in value['attrs']:
                            rtax_norm = rtmsg.metrics.nla2name(rtax)
                            self['metrics'][rtax_norm] = rtax_value
                elif norm == 'multipath':
                    for record in value:
                        nh = type(self)(ipdb=self.ipdb, parent=self)
                        nh.load_netlink(record)
                        with nh._direct_state:
                            del nh['dst']
                            del nh['ipdb_scope']
                            del nh['ipdb_priority']
                            del nh['multipath']
                            del nh['metrics']
                        self['multipath'].add(nh)
                elif norm == 'encap':
                    with self['encap']._direct_state:
                        ret = []
                        # FIXME: should support encap_types other than MPLS
                        try:
                            for l in value.get_attr('MPLS_IPTUNNEL_DST'):
                                ret.append(str(l['label']))
                            if ret:
                                self['encap']['labels'] = '/'.join(ret)
                        except AttributeError:
                            pass
                elif norm == 'via':
                    with self['via']._direct_state:
                        self['via'] = value
                elif norm == 'newdst':
                    self['newdst'] = [x['label'] for x in value]
                else:
                    self[norm] = value

            if msg.get('family', 0) == AF_MPLS:
                dst = msg.get_attr('RTA_DST')
                if dst:
                    dst = dst[0]['label']
            else:
                if msg.get_attr('RTA_DST'):
                    dst = '%s/%s' % (msg.get_attr('RTA_DST'),
                                     msg['dst_len'])
                else:
                    dst = 'default'
            self['dst'] = dst

            # fix RTA_ENCAP_TYPE if needed
            if msg.get_attr('RTA_ENCAP'):
                if self['encap_type'] is not None:
                    with self['encap']._direct_state:
                        self['encap']['type'] = self['encap_type']
                    self['encap_type'] = None
            # or drop encap, if there is no RTA_ENCAP in msg
            elif self['encap'] is not None:
                self['encap_type'] = None
                with self['encap']._direct_state:
                    self['encap'] = {}

            # drop metrics, if there is no RTA_METRICS in msg
            if not msg.get_attr('RTA_METRICS') and self['metrics'] is not None:
                with self['metrics']._direct_state:
                    self['metrics'] = {}

            # same for via
            if not msg.get_attr('RTA_VIA') and self['via'] is not None:
                with self['via']._direct_state:
                    self['via'] = {}

            # one hop -> multihop transition
            if not msg.get_attr('RTA_GATEWAY') and self['gateway'] is not None:
                self['gateway'] = None
            if 'oif' not in msg and \
                    not msg.get_attr('RTA_OIF') and \
                    self['oif'] is not None:
                self['oif'] = None

            # finally, cleanup all not needed
            for item in self.cleanup:
                if item in self:
                    del self[item]