def _adaptiveAggregation(V, n, yIntervals, weightF, param, freq): '''Apply adaptive aggregation algorithm to the given vocabulary. Algorithm 2 from paper. ''' # Initialize returned parameters finalVocabs = SortedDict() periodGroups = SortedDict() # Select weighting function f = _selectWeightingFunction(weightF, param) # Iterate over time frames for t in _arrangeIntervals(V, yIntervals, freq): mu_t = getRangeMiddle(t[0], t[-1]) V_prime = SortedDict({tx: V[tx] for tx in t}) score = defaultdict(float) for years_v, words_v in V_prime.iteritems(): mu_v = getRangeMiddle(years_v) fvt = f(mu_v, mu_t) for word, score_wv in words_v: score[word] += fvt * score_wv # Top n terms w sorted by score_w scoreList = [(k, v) for k, v in score.iteritems()] scoreList = sorted(scoreList, key=lambda pair: pair[1], reverse=True) topN = scoreList[:n] finalVocabs[str(int(mu_t))] = topN periodGroups[str(int(mu_t))] = t return finalVocabs, periodGroups
def signal_crosses(short_moving_averages, long_moving_averages): short_moving_averages = SortedDict(short_moving_averages) long_moving_averages = SortedDict(long_moving_averages) short_len = len(short_moving_averages.values()) long_len = len(long_moving_averages.values()) if(short_len != long_len): print "[Error] signal_crosses: inputs must be same size" return {} signal_crosses = {} last_diff_dir = 0 for date, short_average in short_moving_averages.iteritems(): long_average = long_moving_averages[date] diff = short_average - long_average if(last_diff_dir == 0): signal_crosses[date] = HOLD if(diff != 0): last_diff_dir = sign(diff) continue if(sign(diff) != last_diff_dir): signal_crosses[date] = BUY if last_diff_dir < 0 else SELL last_diff_dir = -last_diff_dir else: signal_crosses[date] = HOLD return SortedDict(signal_crosses)
def signal_crosses(short_moving_averages, long_moving_averages): short_moving_averages = SortedDict(short_moving_averages) long_moving_averages = SortedDict(long_moving_averages) short_len = len(short_moving_averages.values()) long_len = len(long_moving_averages.values()) if (short_len != long_len): print "[Error] signal_crosses: inputs must be same size" return {} signal_crosses = {} last_diff_dir = 0 for date, short_average in short_moving_averages.iteritems(): long_average = long_moving_averages[date] diff = short_average - long_average if (last_diff_dir == 0): signal_crosses[date] = HOLD if (diff != 0): last_diff_dir = sign(diff) continue if (sign(diff) != last_diff_dir): signal_crosses[date] = BUY if last_diff_dir < 0 else SELL last_diff_dir = -last_diff_dir else: signal_crosses[date] = HOLD return SortedDict(signal_crosses)
class TransactionRepository: def __init__(self): self.__accounts = SortedDict() def add_amount(self, account, amount): account = int(account) amount = float(amount) self.__accounts[account] = self.__accounts.get(account, 0) + float(amount) def get_account_amount(self, account): return self.__accounts[int(account)] def get_formatted_transactions(self): return self.__accounts.iteritems() def clear(self): self.__accounts.clear()
def simulation(prices, signal_crosses, budget): simulation = {} prices = SortedDict(prices) cash_on_hand = budget shares = 0 for date, price in prices.iteritems(): signal = signal_crosses[date] if(signal == SELL): shares += cash_on_hand / price cash_on_hand = 0 elif(signal == BUY): cash_on_hand += shares * price shares = 0 simulation[date] = { 'shares': shares, 'cash_on_hand': cash_on_hand } final_value = max(cash_on_hand, shares * prices.values()[-1]) earnings = final_value - budget return simulation, earnings
def simulation(prices, signal_crosses, budget): simulation = {} prices = SortedDict(prices) cash_on_hand = budget shares = 0 for date, price in prices.iteritems(): signal = signal_crosses[date] if (signal == SELL): shares += cash_on_hand / price cash_on_hand = 0 elif (signal == BUY): cash_on_hand += shares * price shares = 0 simulation[date] = {'shares': shares, 'cash_on_hand': cash_on_hand} final_value = max(cash_on_hand, shares * prices.values()[-1]) earnings = final_value - budget return simulation, earnings
def test_init(): sdict = SortedDict() sdict._check() sdict = SortedDict(load=17) sdict._check() sdict = SortedDict((val, -val) for val in range(10000)) sdict._check() assert all(key == -val for key, val in sdict.iteritems()) sdict.clear() sdict._check() assert len(sdict) == 0 sdict = SortedDict.fromkeys(range(1000), None) assert all(sdict[key] == None for key in range(1000))
class KeyedRegion(object): """ KeyedRegion keeps a mapping between stack offsets and all variables covering that offset. It assumes no variable in this region overlap with another variable in this region. Registers and function frames can all be viewed as a keyed region. """ def __init__(self, tree=None): self._storage = SortedDict() if tree is None else tree def _get_container(self, offset): try: base_offset = next( self._storage.irange(maximum=offset, reverse=True)) except StopIteration: return offset, None else: container = self._storage[base_offset] if container.includes(offset): return base_offset, container return offset, None def __contains__(self, offset): """ Test if there is at least one varaible covering the given offset. :param offset: :return: """ return self._get_container(offset)[1] is not None def __len__(self): return len(self._storage) def __iter__(self): return self._storage.itervalues() def __eq__(self, other): if set(self._storage.keys()) != set(other._storage.keys()): return False for k, v in self._storage.iteritems(): if v != other._storage[k]: return False return True def copy(self): if not self._storage: return KeyedRegion() kr = KeyedRegion() for key, ro in self._storage.iteritems(): kr._storage[key] = ro.copy() return kr def merge(self, other, make_phi_func=None): """ Merge another KeyedRegion into this KeyedRegion. :param KeyedRegion other: The other instance to merge with. :return: None """ # TODO: is the current solution not optimal enough? for _, item in other._storage.iteritems(): # type: RegionObject for loc_and_var in item.objects: self.__store(loc_and_var, overwrite=False, make_phi_func=make_phi_func) return self def dbg_repr(self): """ Get a debugging representation of this keyed region. :return: A string of debugging output. """ keys = self._storage.keys() offset_to_vars = {} for key in sorted(keys): ro = self._storage[key] variables = [obj.variable for obj in ro.objects] offset_to_vars[key] = variables s = [] for offset, variables in offset_to_vars.iteritems(): s.append("Offset %#x: %s" % (offset, variables)) return "\n".join(s) def add_variable(self, start, variable): """ Add a variable to this region at the given offset. :param int start: :param SimVariable variable: :return: None """ self._store(start, variable, overwrite=False) def set_variable(self, start, variable): """ Add a variable to this region at the given offset, and remove all other variables that are fully covered by this variable. :param int start: :param SimVariable variable: :return: None """ self._store(start, variable, overwrite=True) def get_base_addr(self, addr): """ Get the base offset (the key we are using to index variables covering the given offset) of a specific offset. :param int addr: :return: :rtype: int or None """ base_addr, container = self._get_container(addr) if container is None: return None else: return base_addr def get_variables_by_offset(self, start): """ Find variables covering the given region offset. :param int start: :return: A list of stack variables. :rtype: set """ base_addr, container = self._get_container(start) if container is None: return [] else: return container.variables # # Private methods # def _store(self, start, variable, overwrite=False): """ Store a variable into the storage. :param int start: The beginning address of the variable. :param variable: The variable to store. :param bool overwrite: Whether existing variables should be overwritten or not. :return: None """ loc_and_var = LocationAndVariable(start, variable) self.__store(loc_and_var, overwrite=overwrite) def __store(self, loc_and_var, overwrite=False, make_phi_func=None): """ Store a variable into the storage. :param LocationAndVariable loc_and_var: The descriptor describing start address and the variable. :param bool overwrite: Whether existing variables should be overwritten or not. :return: None """ start = loc_and_var.start variable = loc_and_var.variable variable_size = variable.size if variable.size is not None else 1 end = start + variable_size # region items in the middle overlapping_items = list(self._storage.irange(start, end - 1)) # is there a region item that begins before the start and overlaps with this variable? floor_key, floor_item = self._get_container(start) if floor_item is not None and floor_key not in overlapping_items: # insert it into the beginningq overlapping_items.insert(0, (floor_key, self._storage[floor_key])) # scan through the entire list of region items, split existing regions and insert new regions as needed to_update = {start: RegionObject(start, variable_size, {loc_and_var})} last_end = start for floor_key in overlapping_items: item = self._storage[floor_key] if item.start < start: # we need to break this item into two a, b = item.split(start) if overwrite: b.set_object(loc_and_var) else: self._add_object_or_make_phi(b, loc_and_var, make_phi_func=make_phi_func) to_update[a.start] = a to_update[b.start] = b last_end = b.end elif item.start > last_end: # there is a gap between the last item and the current item # fill in the gap new_item = RegionObject(last_end, item.start - last_end, {loc_and_var}) to_update[new_item.start] = new_item last_end = new_item.end elif item.end > end: # we need to split this item into two a, b = item.split(end) if overwrite: a.set_object(loc_and_var) else: self._add_object_or_make_phi(a, loc_and_var, make_phi_func=make_phi_func) to_update[a.start] = a to_update[b.start] = b last_end = b.end else: if overwrite: item.set_object(loc_and_var) else: self._add_object_or_make_phi(item, loc_and_var, make_phi_func=make_phi_func) to_update[loc_and_var.start] = item self._storage.update(to_update) def _is_overlapping(self, start, variable): if variable.size is not None: # make sure this variable does not overlap with any other variable end = start + variable.size try: prev_offset = next( self._storage.irange(maximum=end - 1, reverse=True)) except StopIteration: prev_offset = None if prev_offset is not None: if start <= prev_offset < end: return True prev_item = self._storage[prev_offset][0] prev_item_size = prev_item.size if prev_item.size is not None else 1 if start < prev_offset + prev_item_size < end: return True else: try: prev_offset = next( self._storage.irange(maximum=start, reverse=True)) except StopIteration: prev_offset = None if prev_offset is not None: prev_item = self._storage[prev_offset][0] prev_item_size = prev_item.size if prev_item.size is not None else 1 if prev_offset <= start < prev_offset + prev_item_size: return True return False def _add_object_or_make_phi(self, item, loc_and_var, make_phi_func=None): #pylint:disable=no-self-use if not make_phi_func or len({loc_and_var.variable} | item.variables) == 1: item.add_object(loc_and_var) else: # make a phi node item.set_object( LocationAndVariable( loc_and_var.start, make_phi_func(loc_and_var.variable, *item.variables)))
def test_iteritems(): mapping = [(val, pos) for pos, val in enumerate(string.ascii_lowercase)] temp = SortedDict(mapping) assert list(temp.iteritems()) == mapping
bywhat = int(input("Type in a number (1-2): ")) except ValueError: bywhat = int( input( "Menu choice must be entered as an integer. Type in a number (1-2): " )) if bywhat == 1: name = input("Name: ") if name in usernames: del usernames[name] else: print("Name not found.") elif bywhat == 2: usname = input("Username: "******"Username not found.") else: print("Invalid choice. Returning to main menu.") # view user name elif menu_choice == 4: print("Lookup User") name = input("Name: ") if name in usernames: print("Username is " + str(usernames[name])) else: print("User not found.")
class Model(object): ''' The model of a Stranbeest. The Model consists of a set of nodes, edges and boundary conditions. Each node has a unique name and a x and y position which may change whenever the simuation is incremented. Each node introduces two degrees of freedom. The edges are specified by the nodes they are connecting. The edges are the push/pull rods which connect the edges whith one another. An edges keeps the distances between two nodes constant and therefore constrains exactly one degree of freedom in the system. ''' def __init__(self): ''' Constructor ''' self._nodes = SortedDict() self._edges = defaultdict(set) def addNode(self,name,x,y): if not isinstance(name,str ): raise Exception("The 1st argument must be the node's name as str.") if not isinstance(x ,float): raise Exception("The 2nd argument must be the node's x position as float.") if not isinstance(y ,float): raise Exception("The 2nd argument must be the node's y position as float.") if name in self._nodes: raise Exception( 'There already exists a node by the name of "%(name)s"' % locals() ) self._nodes[name] = x,y self.__t = 0.0 for listener in self.onNodeAddListeners: listener(name,x,y) def addEdge(self,node1,node2): if node1 == node2: raise Exception('"node1" cannot be equal to "node2".') self._edges[node1].add(node2) self._edges[node2].add(node1) for listener in self.onEdgeAddListeners: listener( min(node1,node2), max(node1,node2) ) def pos(self,name): return self._nodes[name] def move(self,name,x,y): self._nodes[name] = x,y for listener in self.onNodeMoveListeners: listener(name,x,y) def state(self): return fromiter( chain.from_iterable( self._nodes.values() ), float ) def setState(self,state): for i,(x,y) in enumerate( zip(state[::2],state[1::2]) ): self.move(self._nodes.keys()[i],x,y) @property def t(self): return self.__t def increment(self,dt): v = self.v t0 = self.__t x0 = self.state() # https://en.wikipedia.org/wiki/Runge%E2%80%93Kutta_methods#The_Runge.E2.80.93Kutta_method k0 = v(x0, t0) k1 = v(x0+k0*(dt/2), t0+dt/2) k2 = v(x0+k1*(dt/2), t0+dt/2) k3 = v(x0+k2*(dt), t0+dt) self.setState( x0 + dt/6 * (k0+k1+k2+k3) ) self.__t += dt def v(self,x,t): lhs = zeros( 2*[len(x)] ) rhs = zeros( len(x) ) iRows = iter( range( len(x) ) ) for start,end in self.edges(): iStart = 2*self._nodes.index(start) iEnd = 2*self._nodes.index(end) iRow = next(iRows) dx = x[iEnd+0] - x[iStart+0] dy = x[iEnd+1] - x[iStart+1] lhs[iRow,iStart+0] = dx; lhs[iRow,iEnd+0] = -dx lhs[iRow,iStart+1] = dy; lhs[iRow,iEnd+1] = -dy rhs[iRow] = 0 for bc in self.bcs: bc.addEquations(x,t,iRows,lhs,rhs) return linalg.solve(lhs,rhs) def nodes(self): return self._nodes.iteritems() def edges(self): for node1,neighbors in self._edges.items(): for node2 in neighbors: if node1 < node2: yield node1,node2 bcs = [] onEdgeAddListeners = set() # <- FIXME should be a multiset onNodeAddListeners = set() # <- FIXME should be a multiset onNodeMoveListeners = set() # <- FIXME should be a multiset
class KeyedRegion(object): """ KeyedRegion keeps a mapping between stack offsets and all variables covering that offset. It assumes no variable in this region overlap with another variable in this region. Registers and function frames can all be viewed as a keyed region. """ def __init__(self, tree=None): self._storage = SortedDict() if tree is None else tree def _get_container(self, offset): try: base_offset = next(self._storage.irange(maximum=offset, reverse=True)) except StopIteration: return offset, None else: container = self._storage[base_offset] if container.includes(offset): return base_offset, container return offset, None def __contains__(self, offset): """ Test if there is at least one varaible covering the given offset. :param offset: :return: """ return self._get_container(offset)[1] is not None def __len__(self): return len(self._storage) def __iter__(self): return self._storage.itervalues() def __eq__(self, other): if set(self._storage.keys()) != set(other._storage.keys()): return False for k, v in self._storage.iteritems(): if v != other._storage[k]: return False return True def copy(self): if not self._storage: return KeyedRegion() kr = KeyedRegion() for key, ro in self._storage.iteritems(): kr._storage[key] = ro.copy() return kr def merge(self, other, make_phi_func=None): """ Merge another KeyedRegion into this KeyedRegion. :param KeyedRegion other: The other instance to merge with. :return: None """ # TODO: is the current solution not optimal enough? for _, item in other._storage.iteritems(): # type: RegionObject for loc_and_var in item.objects: self.__store(loc_and_var, overwrite=False, make_phi_func=make_phi_func) return self def dbg_repr(self): """ Get a debugging representation of this keyed region. :return: A string of debugging output. """ keys = self._storage.keys() offset_to_vars = { } for key in sorted(keys): ro = self._storage[key] variables = [ obj.variable for obj in ro.objects ] offset_to_vars[key] = variables s = [ ] for offset, variables in offset_to_vars.iteritems(): s.append("Offset %#x: %s" % (offset, variables)) return "\n".join(s) def add_variable(self, start, variable): """ Add a variable to this region at the given offset. :param int start: :param SimVariable variable: :return: None """ self._store(start, variable, overwrite=False) def set_variable(self, start, variable): """ Add a variable to this region at the given offset, and remove all other variables that are fully covered by this variable. :param int start: :param SimVariable variable: :return: None """ self._store(start, variable, overwrite=True) def get_base_addr(self, addr): """ Get the base offset (the key we are using to index variables covering the given offset) of a specific offset. :param int addr: :return: :rtype: int or None """ base_addr, container = self._get_container(addr) if container is None: return None else: return base_addr def get_variables_by_offset(self, start): """ Find variables covering the given region offset. :param int start: :return: A list of stack variables. :rtype: set """ base_addr, container = self._get_container(start) if container is None: return [] else: return container.variables # # Private methods # def _store(self, start, variable, overwrite=False): """ Store a variable into the storage. :param int start: The beginning address of the variable. :param variable: The variable to store. :param bool overwrite: Whether existing variables should be overwritten or not. :return: None """ loc_and_var = LocationAndVariable(start, variable) self.__store(loc_and_var, overwrite=overwrite) def __store(self, loc_and_var, overwrite=False, make_phi_func=None): """ Store a variable into the storage. :param LocationAndVariable loc_and_var: The descriptor describing start address and the variable. :param bool overwrite: Whether existing variables should be overwritten or not. :return: None """ start = loc_and_var.start variable = loc_and_var.variable variable_size = variable.size if variable.size is not None else 1 end = start + variable_size # region items in the middle overlapping_items = list(self._storage.irange(start, end-1)) # is there a region item that begins before the start and overlaps with this variable? floor_key, floor_item = self._get_container(start) if floor_item is not None and floor_key not in overlapping_items: # insert it into the beginningq overlapping_items.insert(0, (floor_key, self._storage[floor_key])) # scan through the entire list of region items, split existing regions and insert new regions as needed to_update = { start: RegionObject(start, variable_size, { loc_and_var }) } last_end = start for floor_key in overlapping_items: item = self._storage[floor_key] if item.start < start: # we need to break this item into two a, b = item.split(start) if overwrite: b.set_object(loc_and_var) else: self._add_object_or_make_phi(b, loc_and_var, make_phi_func=make_phi_func) to_update[a.start] = a to_update[b.start] = b last_end = b.end elif item.start > last_end: # there is a gap between the last item and the current item # fill in the gap new_item = RegionObject(last_end, item.start - last_end, { loc_and_var }) to_update[new_item.start] = new_item last_end = new_item.end elif item.end > end: # we need to split this item into two a, b = item.split(end) if overwrite: a.set_object(loc_and_var) else: self._add_object_or_make_phi(a, loc_and_var, make_phi_func=make_phi_func) to_update[a.start] = a to_update[b.start] = b last_end = b.end else: if overwrite: item.set_object(loc_and_var) else: self._add_object_or_make_phi(item, loc_and_var, make_phi_func=make_phi_func) to_update[loc_and_var.start] = item self._storage.update(to_update) def _is_overlapping(self, start, variable): if variable.size is not None: # make sure this variable does not overlap with any other variable end = start + variable.size try: prev_offset = next(self._storage.irange(maximum=end-1, reverse=True)) except StopIteration: prev_offset = None if prev_offset is not None: if start <= prev_offset < end: return True prev_item = self._storage[prev_offset][0] prev_item_size = prev_item.size if prev_item.size is not None else 1 if start < prev_offset + prev_item_size < end: return True else: try: prev_offset = next(self._storage.irange(maximum=start, reverse=True)) except StopIteration: prev_offset = None if prev_offset is not None: prev_item = self._storage[prev_offset][0] prev_item_size = prev_item.size if prev_item.size is not None else 1 if prev_offset <= start < prev_offset + prev_item_size: return True return False def _add_object_or_make_phi(self, item, loc_and_var, make_phi_func=None): #pylint:disable=no-self-use if not make_phi_func or len({loc_and_var.variable} | item.variables) == 1: item.add_object(loc_and_var) else: # make a phi node item.set_object(LocationAndVariable(loc_and_var.start, make_phi_func(loc_and_var.variable, *item.variables) ) )
class Book(object): '''| TODO | Keep track of the active orders. It is constructed by using unordered_map, map, and vector data-structures. | Unordered map is used to keep pointers to all active orders. In this implementation, it is used to check whether an order already | exists in the book. Sorted maps are used to represent the bid and ask depths of the book using the price as a key. For efficiency, | the price is represented as (scaled) uint64_t. The insert operation inserts the element at the correct place implementing the | price priority in the book. Each element of the maps is a price level (see Level.hpp). Note that the best bid is the last element, i.e., | the last price level, of the bid map; best ask is the first element (price level) of the ask map. |________''' def __init__(self): self.bid = SortedDict( neg) # Key is price as int, value is Level (in descending order) self.ask = SortedDict() # Key is price as int, value is Level # Uniqueness of the keys is guaranteed only for active orders, i.e., if an order is removed, another order with the same key can be added self.activeOrders = { } # Unordered map of active orders; Key is order Id, value is Order. Used for quick search of orders. # Otherwise, we need to iterate over the levels (bid and ask) and to check the orders for the orderId in question def isBidEmpty(self): return len(self.bid) == 0 def isAskEmpty(self): return len(self.ask) == 0 def isEmpty(self): return len(self.activeOrders) == 0 def isPresent(self, anOrderId): return anOrderId in self.activeOrders def addOrder(self, isBuy=None, orderId=None, price=None, qty=None, peakSize=None, order=None): '''| TODO | Creates and adds an order to the map of orders. In addition, pointer to the order is added to the proper map (bid/ask) and | vector (price level). The maps are ordered, therefore, inserting elements with price as keys, automatically builds a correct | depth of the book. | Note: best bid is the last element (price level) of the bid map; best ask is the first element (price level) of the ask map. |________''' # Already checked that an order with the same Id is not present in the book myOrder = Order(isBuy, orderId, price, qty, peakSize) if order == None else order self.activeOrders[myOrder.orderId] = myOrder # TODO: Where do we deal with int*100 price as keys? key_price = int(myOrder.price * 100) level = self.bid if myOrder.isBuy else self.ask if key_price not in level: level[key_price] = Level() level[key_price].addOrder(myOrder) def removeOrder(self, orderId): '''| TODO | Removes an active order from the book if present (return false if order not found). | In case of icebergs, removes both the visible and hidden parts. |________''' if orderId in self.activeOrders: isBuy = self.activeOrders[orderId].isBuy key_price = int(self.activeOrders[orderId].price * 100) level = self.bid if isBuy else self.ask level[key_price].remove(orderId) del self.activeOrders[orderId] return True return False def removeActiveOrder(self, orderId): '''| TODO |________''' if orderId in self.activeOrders: del self.activeOrders[orderId] return True return False def removeEmptyLevels(self): '''| TODO | If an incoming order executes and matches with all active orders of the best level | including visible and invisible part of the orders, the level is considered empty | and the matching continues with the next price level. After the execution, before | adding an order and processing a new incoming order, this function is used to remove | all empty levels. The book state is updated with new best (bid/ask) levels. |________''' for price in self.bid.keys(): if self.bid[price].isEmpty(): del self.bid[price] for price in self.ask.keys(): if self.ask[price].isEmpty(): del self.ask[price] def clear(self): self.activeOrders.clear() self.bid.clear() self.ask.clear() def show(self): '''| TODO | Called as a result of command 's' | Since the best price is listed first, and the maps used to store the levels are ordered, | this function outputs the bid levels by traversing the bid map in reverse (highest price first) |________''' if self.isEmpty(): print "Book --- EMPTY ---" else: if len(self.bid) == 0: print "Bid depth --- EMPTY ---" else: print "Bid depth (highest priority at top):" print "Price ", "Order Id ", "Quantity ", "Iceberg" # Highest price first for _, level in self.bid.iteritems(): level.show() print if len(self.ask) == 0: print "Ask depth --- EMPTY ---" else: print "Ask depth (highest priority at top):" print "Price ", "Order Id ", "Quantity ", "Iceberg" # Lowest price first for _, level in self.ask.iteritems(): level.show() print
class AccessList: MIN_SEQUENCE_NUM = 1 MAX_SEQUENCE_NUM = 2147483647 def __init__(self, name, seq_step=10): if len(name) > 64: raise ValueError("Name {} is too long.".format(name)) self._name = name self._seq_step = seq_step self._statements = SortedDict() self._fs_start = None self._fs_end = None self._changes = [] @property def title(self): return 'ipv4 access-list {}'.format(self._name) @property def name(self): return self._name def _remove_statement(self, seq): statement = self._statements.pop(seq, None) if statement: self._changes.append('no {}'.format(seq)) def apply_flowspec(self, fs_ace_list, fs_start_seq=None): self.remove_flowspec() if not fs_ace_list: return to_apply = [AccessListEntry.create_remark(FLOWSPEC_START_REMARK).rule] to_apply.extend(fs_ace_list) to_apply.append( AccessListEntry.create_remark(FLOWSPEC_END_REMARK).rule) if len(self._statements): last_seq, last_statement = self._statements.peekitem() next_free_seq = last_seq + self._seq_step else: last_statement = None next_free_seq = self._seq_step permit_all_statement = 'permit ipv4 any any' if last_statement == permit_all_statement: self._remove_statement(last_seq) next_free_seq = last_seq to_apply.append(permit_all_statement) if fs_start_seq is None or fs_start_seq < next_free_seq: fs_start_seq = next_free_seq after_apply_last_seq = fs_start_seq + len(to_apply) if after_apply_last_seq > self.MAX_SEQUENCE_NUM: raise IndexError( "Added sequence {} exceed maximum allowed {}".format( after_apply_last_seq, self.MAX_SEQUENCE_NUM)) cur_seq = fs_start_seq for statement in to_apply: self._add_statement(statement, cur_seq) cur_seq += 1 def _add_statement(self, statement, seq=None, save_change=True): self._statements.update({seq: statement}) if save_change: self._changes.append('{} {}'.format(seq, statement)) def add_statement(self, statement, seq=None): statement_pat = re.compile(r'(deny|remark|permit) .+') if not statement_pat.match(statement): raise ValueError('Wrong statement format: {}'.format(statement)) if seq is None: if self.is_empty(): seq = self._seq_step else: seq = self._statements.peekitem()[0] + self._seq_step seq = int(seq) if seq > self.MAX_SEQUENCE_NUM: raise IndexError( 'Sequence index is out of range: {}. Max: {}'.format( seq, self.MAX_SEQUENCE_NUM)) self._add_statement(statement, seq) def is_empty(self): return len(self._statements) == 0 def is_flowspec_applied(self): return bool(self._fs_start) @classmethod def from_config(cls, raw_config_list): if len(raw_config_list) <= 1: raise ValueError('Passed empty config list.') acl_name_pat = re.compile(r'ipv4 access-list ([^\s]{1,64})') acls = [] cur_acl = None for line in raw_config_list: acl_title = acl_name_pat.match(line) if acl_title: cur_acl = cls(acl_title.group(1)) elif line == '!' and cur_acl is not None: acls.append(cur_acl) cur_acl = None elif cur_acl is not None: seq, statement = line.split(' ', 1) seq = int(seq) if FLOWSPEC_START_REMARK in statement and cur_acl._fs_start is None: cur_acl._fs_start = seq elif FLOWSPEC_END_REMARK in statement: cur_acl._fs_end = seq cur_acl._add_statement(statement, seq, save_change=False) return acls def remove_flowspec(self): if self._fs_start is None: return None fs_iter = self._statements.irange(minimum=self._fs_start, maximum=self._fs_end) for seq in list(fs_iter): self._remove_statement(seq) # TODO: change this logic: need to find and move only 'permit any any' statement, # which is located after flowspec, if there is no such ace, then skip last_seq, last_statement = self._statements.peekitem() if last_seq > self._fs_start: self._remove_statement(last_seq) self._add_statement(last_statement, self._fs_start) self._fs_start = None self._fs_end = None def reset_changes(self): self._changes = [] def get_changes_config(self): if not self._changes: return None changes_config = '\n'.join(self._changes) changes_config = '\n'.join([self.title, changes_config]) return changes_config def get_config(self): if self.is_empty(): return None config = '\n'.join([ '{} {}'.format(seq, statement) for seq, statement in self._statements.iteritems() ]) config = '\n'.join([self.title, config]) return config