summaryrefslogtreecommitdiff
path: root/qpid/extras/dispatch/python/qpid/dispatch/router/mobile.py
blob: ce80f3878071aff92863072606d03b2fe8ee4ed8 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
#
# Licensed to the Apache Software Foundation (ASF) under one
# or more contributor license agreements.  See the NOTICE file
# distributed with this work for additional information
# regarding copyright ownership.  The ASF licenses this file
# to you under the Apache License, Version 2.0 (the
# "License"); you may not use this file except in compliance
# with the License.  You may obtain a copy of the License at
#
#   http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing,
# software distributed under the License is distributed on an
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
# KIND, either express or implied.  See the License for the
# specific language governing permissions and limitations
# under the License.
#

from data import MessageRA, MessageMAR, MessageMAU

try:
  from dispatch import *
except ImportError:
  from ..stubs import *

class MobileAddressEngine(object):
  """
  This module is responsible for maintaining an up-to-date list of mobile addresses in the domain.
  It runs the Mobile-Address protocol and generates an un-optimized routing table for mobile addresses.
  Note that this routing table maps from the mobile address to the remote router where that address
  is directly bound.
  """
  def __init__(self, container):
    self.container = container
    self.id = self.container.id
    self.area = self.container.area
    self.mobile_addr_max_age = self.container.config.mobile_addr_max_age
    self.mobile_seq = 0
    self.local_keys = []
    self.added_keys = []
    self.deleted_keys = []
    self.remote_lists = {}      # map router_id => (sequence, list of keys)
    self.remote_last_seen = {}  # map router_id => time of last seen advertizement/update
    self.remote_changed = False
    self.needed_mars = {}


  def tick(self, now):
    self._expire_remotes(now)
    self._send_mars()

    ##
    ## If local keys have changed, collect the changes and send a MAU with the diffs
    ## Note: it is important that the differential-MAU be sent before a RA is sent
    ##
    if len(self.added_keys) > 0 or len(self.deleted_keys) > 0:
      self.mobile_seq += 1
      self.container.send('_topo.%s.all' % self.area,
                          MessageMAU(None, self.id, self.area, self.mobile_seq, self.added_keys, self.deleted_keys))
      self.local_keys.extend(self.added_keys)
      for key in self.deleted_keys:
        self.local_keys.remove(key)
      self.added_keys = []
      self.deleted_keys = []
      self.container.mobile_sequence_changed(self.mobile_seq)

    ##
    ## If remotes have changed, start the process of updating local bindings
    ##
    if self.remote_changed:
      self.remote_changed = False
      self._update_remote_keys()


  def add_local_address(self, key):
    """
    """
    if self.local_keys.count(key) == 0:
      if self.added_keys.count(key) == 0:
        self.added_keys.append(key)
    else:
      if self.deleted_keys.count(key) > 0:
        self.deleted_keys.remove(key)


  def del_local_address(self, key):
    """
    """
    if self.local_keys.count(key) > 0:
      if self.deleted_keys.count(key) == 0:
        self.deleted_keys.append(key)
    else:
      if self.added_keys.count(key) > 0:
        self.added_keys.remove(key)


  def handle_ra(self, msg, now):
    if msg.id == self.id:
      return

    if msg.mobile_seq == 0:
      return

    if msg.id in self.remote_lists:
      _seq, _list = self.remote_lists[msg.id]
      self.remote_last_seen[msg.id] = now
      if _seq < msg.mobile_seq:
        self.needed_mars[(msg.id, msg.area, _seq)] = None
    else:
      self.needed_mars[(msg.id, msg.area, 0)] = None


  def handle_mau(self, msg, now):
    ##
    ## If the MAU is differential, we can only use it if its sequence is exactly one greater
    ## than our stored sequence.  If not, we will ignore the content and schedule a MAR.
    ##
    ## If the MAU is absolute, we can use it in all cases.
    ##
    if msg.id == self.id:
      return

    if msg.exist_list:
      ##
      ## Absolute MAU
      ##
      if msg.id in self.remote_lists:
        _seq, _list = self.remote_lists[msg.id]
        if _seq >= msg.mobile_seq:  # ignore duplicates
          return
      self.remote_lists[msg.id] = (msg.mobile_seq, msg.exist_list)
      self.remote_last_seen[msg.id] = now
      self.remote_changed = True
    else:
      ##
      ## Differential MAU
      ##
      if msg.id in self.remote_lists:
        _seq, _list = self.remote_lists[msg.id]
        if _seq == msg.mobile_seq:  # ignore duplicates
          return
        self.remote_last_seen[msg.id] = now
        if _seq + 1 == msg.mobile_seq:
          ##
          ## This is one greater than our stored value, incorporate the deltas
          ##
          if msg.add_list and msg.add_list.__class__ == list:
            _list.extend(msg.add_list)
          if msg.del_list and msg.del_list.__class__ == list:
            for key in msg.del_list:
              _list.remove(key)
          self.remote_lists[msg.id] = (msg.mobile_seq, _list)
          self.remote_changed = True
        else:
          self.needed_mars[(msg.id, msg.area, _seq)] = None
      else:
        self.needed_mars[(msg.id, msg.area, 0)] = None


  def handle_mar(self, msg, now):
    if msg.id == self.id:
      return
    if msg.have_seq < self.mobile_seq:
      self.container.send('_topo.%s.%s' % (msg.area, msg.id),
                          MessageMAU(None, self.id, self.area, self.mobile_seq, None, None, self.local_keys))


  def _update_remote_keys(self):
    keys = {}
    for _id,(seq,key_list) in self.remote_lists.items():
      keys[_id] = key_list
    self.container.mobile_keys_changed(keys)


  def _expire_remotes(self, now):
    for _id, t in self.remote_last_seen.items():
      if now - t > self.mobile_addr_max_age:
        self.remote_lists.pop(_id)
        self.remote_last_seen.pop(_id)
        self.remote_changed = True


  def _send_mars(self):
    for _id, _area, _seq in self.needed_mars.keys():
      self.container.send('_topo.%s.%s' % (_area, _id), MessageMAR(None, self.id, self.area, _seq))
    self.needed_mars = {}