Пример #1
0
class ReservationHoldings:
    """
    This class maintains a collection of reservations. Each reservation is
    associated with a validity interval. The class allows to answer intersection
    queries: what reservations are valid at a given time instance.

    As time goes by, the class can be purged from irrelevant reservation records
    by invoking tick(). Purging is strongly recommended as it reduces the cost of intersection queries.

    An attempt has been made to optimize the cost of using this data structure.
    Inserts are O(log(n)). Queries, however, may take between O(log(n)) and O(n).
    """
    def __init__(self):
        # List of reservation wrappers sorted by increasing end time.
        self.list = []
        # All reservations stored in this collection.
        self.reservation_set = ReservationSet()
        # Map of reservations to ReservationWrappers. Needed when removing a reservation.
        self.map = {}

    def add_reservation(self, *, reservation: ABCReservationMixin, start: int,
                        end: int):
        """
        Adds a reservation to the collection for the specified period of time.
        The interval is closed on both sides.
        @params reservation : reservation to add
        @params start : start time
        @params end : end time
        """
        # If this is an extended reservation, we may already have it in the
        # list (with potentially different start and end times). Remove the
        # previous entry if this is the case.
        my_start = start
        entry = None
        if reservation.get_reservation_id() in self.map:
            entry = self.map[reservation.get_reservation_id()]
            if entry is not None:
                assert (start - entry.end) <= 1
                my_start = entry.start
                self.remove_reservation(reservation=reservation)

        entry = ReservationWrapper(reservation=reservation,
                                   start=my_start,
                                   end=end)
        self.add_to_list(entry=entry)
        self.reservation_set.add(reservation=reservation)
        self.map[reservation.get_reservation_id()] = entry

    def add_to_list(self, *, entry: ReservationWrapper):
        """
        Adds the entry to the linked list. Maintains the list in sorted order.
        Cost: O(log(n)).
        @params entry : entry to add
        """
        bisect.insort_left(self.list, entry)

    def clear(self):
        """
        Clears the collection.
        """
        self.map.clear()
        self.list.clear()
        self.reservation_set.clear()

    def get_reservations(self,
                         *,
                         time: int = None,
                         rtype: ResourceType = None) -> ReservationSet:
        """
        Performs an intersection query: returns all reservations from the
        specified resource type present in the collection that are active at the
        specified time instance.
        @params time : time instance
        @params rtype : resource type
        @returns reservations set containing active reservations
        """
        if time is None and rtype is None:
            return self.reservation_set

        result = ReservationSet()
        key = ReservationWrapper(reservation=None, start=time, end=time)

        # Find the location of key in the list.
        index = binary_search(a=self.list, x=key)
        if index < 0:
            index = -index - 1

        # Scan the upper part of the list. We need to scan the whole list.
        i = index
        count = self.size()
        while i < count:
            entry = self.list[i]

            if rtype is None or rtype == entry.reservation.getType():
                if entry.start <= time <= entry.end:
                    result.add(reservation=entry.reservation)
            i += 1

        # Scan the lower part of the list until no further intersections are possible
        i = index - 1
        while i >= 0:
            entry = self.list[i]
            if entry.end < time:
                break

            if entry.start <= time and rtype is None or entry.reservation.getType(
            ) == rtype:
                result.add(reservation=entry.reservation)
            i -= 1

        return result

    def remove_from_list(self, *, entry: ReservationWrapper):
        """
        Removes a reservation from the collection.
        @params reservation : reservation to remove
        """
        index = binary_search(a=self.list, x=entry)

        if index >= 0:
            self.list.pop(index)

    def remove_reservation(self, *, reservation: ABCReservationMixin):
        """
        Removes a reservation from the collection.
        @params reservation : reservation to remove
        """
        if reservation.get_reservation_id() in self.map:
            entry = self.map[reservation.get_reservation_id()]
            self.map.pop(reservation.get_reservation_id())
            self.reservation_set.remove(reservation=reservation)
            self.remove_from_list(entry=entry)

    def size(self) -> int:
        """
        Returns the size of the collection.
        @returns size of the collection
        """
        return self.reservation_set.size()

    def tick(self, *, time: int):
        """
        Removes all reservations that have end time not after the given cycle.
        @params time : time
        """
        while True:
            if len(self.list) > 0:
                entry = self.list[0]
                if entry.end <= time:
                    self.list.remove(entry)
                    self.reservation_set.remove(reservation=entry.reservation)
                    self.map.pop(entry.reservation.get_reservation_id())
                else:
                    break
            else:
                break
Пример #2
0
    def check_set(self, rset: ReservationSet, check: ReservationSet):
        self.assertIsNotNone(check)
        self.assertEqual(rset.size(), check.size())

        for res in rset.values():
            self.assertTrue(check.contains(reservation=res))
Пример #3
0
    def transition_slice(
            self, *, operation: SliceOperation,
            reservations: ReservationSet) -> Tuple[bool, SliceState]:
        """
        Attempt to transition a slice to a new state
        @param operation slice operation
        @param reservations reservations
        @return Slice State
        @throws Exception in case of error
        """
        state_changed = False
        prev_state = self.state
        if self.state not in operation.valid_from_states:
            raise SliceException(
                f"Operation: {operation} cannot transition from state {self.state}"
            )

        if operation.command == SliceCommand.Create:
            self.state = SliceState.Configuring

        elif operation.command == SliceCommand.Modify:
            self.state = SliceState.Configuring

        elif operation.command == SliceCommand.Delete:
            if self.state != SliceState.Dead:
                self.state = SliceState.Closing

        elif operation.command == SliceCommand.Reevaluate:
            if reservations is None or reservations.size() == 0:
                return state_changed, self.state

            bins = StateBins()
            for r in reservations.values():
                bins.add(s=r.get_state())

            if self.state == SliceState.Nascent or self.state == SliceState.Configuring:
                if not bins.has_state_other_than(ReservationStates.Active,
                                                 ReservationStates.Closed):
                    self.state = SliceState.StableOK

                if (not bins.has_state_other_than(ReservationStates.Active, ReservationStates.Failed,
                                                  ReservationStates.Closed)) and \
                        bins.has_state(s=ReservationStates.Failed):
                    self.state = SliceState.StableError

                if not bins.has_state_other_than(ReservationStates.Closed,
                                                 ReservationStates.CloseWait,
                                                 ReservationStates.Failed):
                    self.state = SliceState.Closing

            elif self.state == SliceState.StableError or self.state == SliceState.StableOK:
                if not bins.has_state_other_than(ReservationStates.Closed,
                                                 ReservationStates.CloseWait,
                                                 ReservationStates.Failed):
                    self.state = SliceState.Dead

                if not bins.has_state_other_than(
                        ReservationStates.Closed, ReservationStates.CloseWait,
                        ReservationPendingStates.Closing,
                        ReservationStates.Failed):
                    self.state = SliceState.Closing

            elif self.state == SliceState.Closing and not bins.has_state_other_than(
                    ReservationStates.CloseWait, ReservationStates.Closed,
                    ReservationStates.Failed):
                self.state = SliceState.Dead
        if prev_state != self.state:
            state_changed = True

        return state_changed, self.state