class SourceCalendar: """ SourceCalendar organizes state for a delegation used as a source for client reservations. A source calendar maintains a list of "outlays", client reservations that have been allocated from the source delegation. The outlay calendar is organized by real time. The calendar also maintains a list of incoming extension requests for reservations that have been satisfied from the underlying source delegations. """ def __init__(self, *, clock: ActorClock, source: ABCDelegation): """ Constructor @params clock: clock @params source: source delegation """ # Clock. self.clock = clock # The source delegation. self.source = source # Allocated reservations. self.outlays = ReservationHoldings() # Incoming extension requests. self.extending = ReservationList() def tick(self, *, cycle: int): ms = self.clock.cycle_end_in_millis(cycle=cycle) self.outlays.tick(time=ms) self.extending.tick(cycle=cycle)
def test_add_reservation(self): r_list = ReservationList() for i in range(10): for j in range(10): c = (i * 10) + j res = self.make_reservation(str(c)) r_list.add_reservation(reservation=res, cycle=i) self.assertEqual(c + 1, r_list.size())
def __init__(self, *, clock: ActorClock): """ Constructor @params clock: clock """ super().__init__(clock=clock) self.closing = ReservationList() self.redeeming = ReservationList()
def __init__(self, *, clock: ActorClock): """ Creates a new instance. @params clock : clock factory """ # List of incoming requests grouped by start cycle. super().__init__(clock=clock) self.requests = ReservationList() # List of reservations to be closed grouped by cycle. self.closing = ReservationList() # All currently active reservations. self.outlays = ReservationHoldings() self.lock = threading.Lock()
def __init__(self, *, clock: ActorClock): """ Constructor @params clock: clock factory """ super().__init__(clock=clock) # List of reservations grouped by closing time. self.closing = ReservationList() # Reservation requests grouped by start cycle. self.requests = ReservationList() # Source reservation calendars indexed by the source reservation identifier # <ReservationID, SourceCalendar> self.sources = {}
def __init__(self, *, clock: ActorClock, source: ABCDelegation): """ Constructor @params clock: clock @params source: source delegation """ # Clock. self.clock = clock # The source delegation. self.source = source # Allocated reservations. self.outlays = ReservationHoldings() # Incoming extension requests. self.extending = ReservationList()
def __init__(self, *, clock: ActorClock): """ Constructor @params clock: clock factory """ super().__init__(clock=clock) # Set of reservations representing the current demand. Callers are # responsible for removing serviced reservations self.demand = ReservationSet() # Set of reservations for which a request has been issued but no # confirmation has been received. Callers are responsible for removing # acknowledged reservations. self.pending = ReservationSet() # Set of reservations grouped by renewing time. self.renewing = ReservationList() # Set of active reservations. self.holdings = ReservationHoldings() self.lock = threading.Lock()
def test_remove_reservation(self): r_list = ReservationList() for i in range(10): for j in range(10): c = (i * 10) + j res = self.make_reservation(str(c)) r_list.add_reservation(reservation=res, cycle=i) self.assertEqual(c + 1, r_list.size()) self.assertEqual(c + 1, len(r_list.reservation_id_to_cycle)) for i in range(10): for j in range(10): c = (i * 10) + j res = self.make_reservation(str(c)) exist = self.check_exists(r_list, res, i) self.assertTrue(exist) r_list.remove_reservation(reservation=res) exist = self.check_exists(holdings=r_list, reservation=res, cycle=i) self.assertTrue(not exist)
def test_get_reservations(self): r_list = ReservationList() for i in range(10): for j in range(10): c = (i * 10) + j res = self.make_reservation(str(c)) r_list.add_reservation(reservation=res, cycle=i) self.assertEqual(c + 1, r_list.size()) for i in range(10): low = 10 * i high = 10 * (i + 1) rset = r_list.get_reservations(cycle=i) self.assertIsNotNone(rset) self.assertEqual(10, rset.size()) for r in rset.values(): value = int(str(r.get_reservation_id())) self.assertTrue(low <= value < high) rset = r_list.get_reservations(cycle=10) self.assertIsNotNone(rset) self.assertEqual(0, rset.size())
class AuthorityCalendar(BaseCalendar): """ An AuthorityCalendar is used to organized reservation information for an authority. It extends the functionality of BaseCalendar with a number of collections: - requests: a collection of client requests organized by the time to be serviced - closing: a collection of client reservations organized by closing time - outlays: a collection of active reservations (outlays) """ def __init__(self, *, clock: ActorClock): """ Creates a new instance. @params clock : clock factory """ # List of incoming requests grouped by start cycle. super().__init__(clock=clock) self.requests = ReservationList() # List of reservations to be closed grouped by cycle. self.closing = ReservationList() # All currently active reservations. self.outlays = ReservationHoldings() self.lock = threading.Lock() def __getstate__(self): state = self.__dict__.copy() del state['lock'] return state def __setstate__(self, state): self.__dict__.update(state) self.lock = threading.Lock() def remove(self, *, reservation: ABCReservationMixin): """ Removes the specified reservation from the calendar. @params reservation : reservation to remove """ if isinstance(reservation, ABCServerReservation): self.remove_request(reservation=reservation) self.remove_closing(reservation=reservation) if isinstance(reservation, ABCAuthorityReservation): self.remove_outlay(reservation=reservation) def remove_schedule_or_in_progress(self, *, reservation: ABCReservationMixin): """ Removes the specified reservations from all internal calendar data structures that represent operations to be scheduled in the future or operations that are currently in progress. Does not remove the reservation from the outlays list @params reservation: reservation to remove """ if isinstance(reservation, ABCServerReservation): self.remove_request(reservation=reservation) self.remove_closing(reservation=reservation) def get_requests(self, *, cycle: int) -> ReservationSet: """ Returns all client requests for the specified cycle. @params cycle: cycle @returns set of requests for the cycle """ try: self.lock.acquire() return self.requests.get_reservations(cycle=cycle) finally: self.lock.release() def add_request(self, *, reservation: ABCReservationMixin, cycle: int): """ Adds a new client request. @params reservation: reservation to add @params cycle: cycle """ try: self.lock.acquire() self.requests.add_reservation(reservation=reservation, cycle=cycle) finally: self.lock.release() def remove_request(self, *, reservation: ABCReservationMixin): """ Removes the specified reservation from the request list. @params reservation: reservation to remove """ try: self.lock.acquire() self.requests.remove_reservation(reservation=reservation) finally: self.lock.release() def get_closing(self, *, cycle: int) -> ReservationSet: """ Returns all reservations scheduled for closing at the specified cycle. @params cycle: cycle @returns set of reservations scheduled for closing at the cycle """ try: self.lock.acquire() result = self.closing.get_all_reservations(cycle=cycle) return result finally: self.lock.release() def add_closing(self, *, reservation: ABCReservationMixin, cycle: int): """ Adds a reservation to the closing list. @params reservation: reservation to add @params cycle: cycle """ try: self.lock.acquire() self.closing.add_reservation(reservation=reservation, cycle=cycle) finally: self.lock.release() def remove_closing(self, *, reservation: ABCReservationMixin): """ Removes the specified reservation from the closing list. @params reservation: reservation to remove """ try: self.lock.acquire() self.closing.remove_reservation(reservation=reservation) finally: self.lock.release() def add_outlay(self, *, reservation: ABCReservationMixin, start: datetime, end: datetime): """ Adds an allocated client reservation. @params reservation: reservation to add @params start: start time @params end: end time """ try: self.lock.acquire() self.outlays.add_reservation( reservation=reservation, start=ActorClock.to_milliseconds(when=start), end=ActorClock.to_milliseconds(when=end)) finally: self.lock.release() def remove_outlay(self, *, reservation: ABCReservationMixin): """ Removes a reservation from the outlays list. @params reservation: reservation to remove """ try: self.lock.acquire() self.outlays.remove_reservation(reservation=reservation) finally: self.lock.release() def get_outlays(self, *, d: datetime = None) -> ReservationSet: """ Returns the active client reservations. @returns set of all active client reservations """ try: self.lock.acquire() if d is None: return self.outlays.get_reservations() else: return self.outlays.get_reservations( time=ActorClock.to_milliseconds(when=d)) finally: self.lock.release() def tick(self, *, cycle: int): try: self.lock.acquire() super().tick(cycle=cycle) self.requests.tick(cycle=cycle) self.closing.tick(cycle=cycle) ms = self.clock.cycle_end_in_millis(cycle=cycle) self.outlays.tick(time=ms) finally: self.lock.release()
def test_tick(self): r_list = ReservationList() for i in range(10): for j in range(10): c = (i * 10) + j res = self.make_reservation(str(c)) r_list.add_reservation(reservation=res, cycle=i) self.assertEqual(c + 1, r_list.size()) self.assertEqual(c + 1, len(r_list.reservation_id_to_cycle)) for i in range(10): rset = r_list.get_reservations(cycle=i) self.assertIsNotNone(rset) self.assertEqual(10, rset.size()) expected_size = (10 - i) * 10 self.assertEqual(expected_size, r_list.size()) r_list.tick(cycle=i) rset = r_list.get_reservations(cycle=i) self.assertIsNotNone(rset) self.assertEqual(0, rset.size()) self.assertEqual(0, r_list.size()) self.assertEqual(0, len(r_list.reservation_id_to_cycle))
def test_create(self): r_list = ReservationList() self.assertEqual(r_list.size(), 0) self.assertIsNotNone(r_list.rset_wrapper_list) self.assertIsNotNone(r_list.cycle_to_rset) self.assertEqual(r_list.count, 0)
def check_exists(self, holdings: ReservationList, reservation: ABCReservationMixin, cycle: int): rset = holdings.get_reservations(cycle=cycle) return rset.contains(reservation=reservation)
class ClientCalendar(BaseCalendar): """ This a client-side calendar to be used by brokers or service managers. A client calendar maintains the following lists: - demand: a list of reservations representing demand for resources - pending: a list of reservations with pending operations - renewing: a list of reservations organized by the time they must be renewed (cycle) - holdings: a list of active/granted reservations associated with their lease term. This list should be maintained using real time, not cycles. The renewing and holding lists are automatically purged by the implementation as time advances. The demand and pending list, however, must be purged manually by the user of this class. """ def __init__(self, *, clock: ActorClock): """ Constructor @params clock: clock factory """ super().__init__(clock=clock) # Set of reservations representing the current demand. Callers are # responsible for removing serviced reservations self.demand = ReservationSet() # Set of reservations for which a request has been issued but no # confirmation has been received. Callers are responsible for removing # acknowledged reservations. self.pending = ReservationSet() # Set of reservations grouped by renewing time. self.renewing = ReservationList() # Set of active reservations. self.holdings = ReservationHoldings() self.lock = threading.Lock() def __getstate__(self): state = self.__dict__.copy() del state['lock'] return state def __setstate__(self, state): self.__dict__.update(state) self.lock = threading.Lock() def remove(self, *, reservation: ABCReservationMixin): """ Removes the specified reservation from all internal calendar data structures @params reservation: reservation to remove """ self.remove_demand(reservation=reservation) self.remove_pending(reservation=reservation) self.remove_renewing(reservation=reservation) self.remove_holdings(reservation=reservation) def remove_scheduled_or_in_progress(self, *, reservation: ABCReservationMixin): """ Removes the specified reservations from all internal calendar data structures that represent operations to be scheduled in the future or operations that are currently in progress. Does not remove the reservation from the holdings list. @params reservation : reservation to remove """ self.remove_demand(reservation=reservation) self.remove_pending(reservation=reservation) self.remove_renewing(reservation=reservation) def get_demand(self) -> ReservationSet: """ Returns the known demand. Can be for resources starting at different times. @returns the set of demanded reservations. """ try: self.lock.acquire() return self.demand.clone() finally: self.lock.release() def add_demand(self, *, reservation: ABCReservationMixin): """ Adds a reservation to the demand list. @params reservation: reservation to add """ try: self.lock.acquire() self.demand.add(reservation=reservation) finally: self.lock.release() def remove_demand(self, *, reservation: ABCReservationMixin): """ Removes a reservation to the demand list. @params reservation: reservation to remove """ try: self.lock.acquire() self.demand.remove(reservation=reservation) finally: self.lock.release() def get_pending(self) -> ReservationSet: """ Returns the known pending. Can be for resources starting at different times. @returns the set of pending reservations. """ try: self.lock.acquire() return self.pending.clone() finally: self.lock.release() def add_pending(self, *, reservation: ABCReservationMixin): """ Adds a reservation to the pending list. @params reservation: reservation to add """ try: self.lock.acquire() self.pending.add(reservation=reservation) finally: self.lock.release() def remove_pending(self, *, reservation: ABCReservationMixin): """ Removes a reservation to the pending list. @params reservation: reservation to remove """ try: self.lock.acquire() self.pending.remove(reservation=reservation) finally: self.lock.release() def get_renewing(self, *, cycle: int) -> ReservationSet: """ Returns the reservations that need to be renewed on the specified cycle. @params cycle : cycle number @returns reservation set with reservations to be renewed on the specified cycle """ try: self.lock.acquire() return self.renewing.get_all_reservations(cycle=cycle) finally: self.lock.release() def add_renewing(self, *, reservation: ABCReservationMixin, cycle: int): """ Adds a reservation to the renewing list at the given cycle. @params reservation : reservation to add @params cycle : cycle number """ try: self.lock.acquire() self.renewing.add_reservation(reservation=reservation, cycle=cycle) finally: self.lock.release() def remove_renewing(self, *, reservation: ABCReservationMixin): """ Removes the reservation from the renewing list. @params reservation : reservation to remove """ try: self.lock.acquire() self.renewing.remove_reservation(reservation=reservation) finally: self.lock.release() def get_holdings(self, *, d: datetime = None, type: ResourceType = None) -> ReservationSet: """ Returns the resources of the specified type held by the client that are active at the specified time instance. @params d : datetime instance. @params type : resource type @returns st of reservations of the specified type that are active at the specified time """ try: self.lock.acquire() when = None if d is not None: when = ActorClock.to_milliseconds(when=d) return self.holdings.get_reservations(time=when, rtype=type) finally: self.lock.release() def add_holdings(self, *, reservation: ABCReservationMixin, start: datetime, end: datetime): """ Adds a reservation to the holdings list. @params reservation : reservation to add @params start : start time @params end : end time """ try: self.lock.acquire() self.holdings.add_reservation( reservation=reservation, start=ActorClock.to_milliseconds(when=start), end=ActorClock.to_milliseconds(when=end)) finally: self.lock.release() def remove_holdings(self, *, reservation: ABCReservationMixin): """ Removes the reservation from the renewing list. @params reservation : reservation to remove """ try: self.lock.acquire() self.holdings.remove_reservation(reservation=reservation) finally: self.lock.release() def tick(self, *, cycle: int): try: self.lock.acquire() super().tick(cycle=cycle) self.renewing.tick(cycle=cycle) ms = self.clock.cycle_end_in_millis(cycle=cycle) self.holdings.tick(time=ms) finally: self.lock.release()
class ControllerCalendar(ClientCalendar): """ Controller calendar. In addition to the lists maintained by ClientCalendar, this class maintains the following lists: - closing: a list of reservations organized by the cycle they must be closed - redeeming: a list of reservations organized by the cycle they must be redeemed """ def __init__(self, *, clock: ActorClock): """ Constructor @params clock: clock """ super().__init__(clock=clock) self.closing = ReservationList() self.redeeming = ReservationList() def remove(self, *, reservation: ABCReservationMixin): """ Removes the reservation from the calendar. @params reservation: reservation to remove """ super().remove(reservation=reservation) self.remove_closing(reservation=reservation) self.remove_redeeming(reservation=reservation) def get_closing(self, *, cycle: int) -> ReservationSet: """ Returns all reservations that need to be closed up to and including the specified cycle. @params cycle : cycle @returns a set of reservations that must be closed on the specified cycle """ try: self.lock.acquire() return self.closing.get_all_reservations(cycle=cycle) finally: self.lock.release() def add_closing(self, *, reservation: ABCReservationMixin, cycle: int): """ Adds a reservation to be closed on the specified cycle. @params reservation : reservation to add @params cycle : cycle """ try: self.lock.acquire() self.closing.add_reservation(reservation=reservation, cycle=cycle) finally: self.lock.release() def remove_closing(self, *, reservation: ABCReservationMixin): """ Removes the given reservation from the closing list. @params reservation : reservation to remove """ try: self.lock.acquire() self.closing.remove_reservation(reservation=reservation) finally: self.lock.release() def get_redeeming(self, *, cycle: int) -> ReservationSet: """ Returns all reservations that need to be redeemed up to and including the specified cycle. @params cycle : cycle @returns a set of reservations that must be redeemed on the specified cycle """ try: self.lock.acquire() return self.redeeming.get_all_reservations(cycle=cycle) finally: self.lock.release() def add_redeeming(self, *, reservation: ABCReservationMixin, cycle: int): """ Adds a reservation to be redeeming on the specified cycle. @params reservation : reservation to add @params cycle : cycle """ try: self.lock.acquire() self.redeeming.add_reservation(reservation=reservation, cycle=cycle) finally: self.lock.release() def remove_redeeming(self, *, reservation: ABCReservationMixin): """ Removes the given reservation from the redeeming list. @params reservation : reservation to remove """ try: self.lock.acquire() self.redeeming.remove_reservation(reservation=reservation) finally: self.lock.release() def tick(self, *, cycle: int): super().tick(cycle=cycle) try: self.lock.acquire() self.closing.tick(cycle=cycle) self.redeeming.tick(cycle=cycle) finally: self.lock.release()
class BrokerCalendar(ClientCalendar): """ BrokerCalendar used to organize reservation information for a broker. It builds on the functionality provided by ClientCalendar and extends it with the following lists: - closing: list of reservations organized by closing time - requests: list of incoming requests - source calendars for each source reservation """ def __init__(self, *, clock: ActorClock): """ Constructor @params clock: clock factory """ super().__init__(clock=clock) # List of reservations grouped by closing time. self.closing = ReservationList() # Reservation requests grouped by start cycle. self.requests = ReservationList() # Source reservation calendars indexed by the source reservation identifier # <ReservationID, SourceCalendar> self.sources = {} def remove(self, *, reservation: ABCReservationMixin): super().remove(reservation=reservation) self.remove_closing(reservation=reservation) if isinstance(reservation, ABCBrokerReservation): self.remove_request(reservation=reservation) source = reservation.get_source() if source is not None: self.remove_request(reservation=reservation, source=source) self.remove_outlay(source=source, client=reservation) def remove_scheduled_or_in_progress(self, *, reservation: ABCReservationMixin): super().remove_scheduled_or_in_progress(reservation=reservation) self.remove_closing(reservation=reservation) if isinstance(reservation, ABCBrokerReservation): self.remove_request(reservation=reservation) source = reservation.get_source() if source is not None: self.remove_request(reservation=reservation, source=source) def get_requests(self, *, cycle: int) -> ReservationSet: """ Returns all client requests for the given cycle. @params cycle: cycle @returns set of reservations representing requests starting at the specified cycle """ try: self.lock.acquire() return self.requests.get_reservations(cycle=cycle) finally: self.lock.release() def get_all_requests(self, *, cycle: int) -> ReservationSet: """ Returns all client requests up the the given cycle. @params cycle: cycle @returns set of reservations representing requests with start time no later than cycle """ try: self.lock.acquire() return self.requests.get_all_reservations(cycle=cycle) finally: self.lock.release() def add_request(self, *, reservation: ABCReservationMixin, cycle: int, source: ABCDelegation = None): """ Adds a client request. @params reservation: client request @params cycle: start cycle @params source: source reservation """ try: self.lock.acquire() if source is None: self.requests.add_reservation(reservation=reservation, cycle=cycle) else: calendar = self.get_source_calendar(source=source) calendar.extending.add_reservation(reservation=reservation, cycle=cycle) finally: self.lock.release() def get_request(self, *, source: ABCDelegation, cycle: int) -> ReservationSet: """ Returns the extending requests for the given source reservation. @params source: source reservation @params cycle: cycle number @returns set of extending reservation requests for the given source at the specified cycle """ try: self.lock.acquire() calendar = self.get_source_calendar(source=source) return calendar.extending.get_reservations(cycle=cycle) finally: self.lock.release() def remove_request(self, *, reservation: ABCReservationMixin, source: ABCDelegation = None): """ Removes the specified reservation from the requests list. @params reservation: reservation to remove @params source: source reservation """ try: self.lock.acquire() if source is not None: calendar = self.get_source_calendar(source=source) calendar.extending.remove_reservation(reservation=reservation) else: self.requests.remove_reservation(reservation=reservation) finally: self.lock.release() def add_outlay(self, *, source: ABCDelegation, client: ABCReservationMixin, start: datetime, end: datetime): """ Adds an outlay reservation. @params source: source reservation @params client: reservation to add @params start: start time @params end: start time """ try: self.lock.acquire() calendar = self.get_source_calendar(source=source) calendar.outlays.add_reservation( reservation=client, start=ActorClock.to_milliseconds(when=start), end=ActorClock.to_milliseconds(when=end)) finally: self.lock.release() def remove_outlay(self, *, source: ABCDelegation, client: ABCReservationMixin): """ Removes an outlay reservation. @params source : source reservation @params client : client reservation to be removed """ try: self.lock.acquire() calendar = self.get_source_calendar(source=source) calendar.outlays.remove_reservation(reservation=client) finally: self.lock.release() def add_source(self, *, source: ABCDelegation): """ Adds a source reservation. Creates a placeholder if necessary and adds the reservation to the holdings list. @params source: source reservation """ term = None try: self.lock.acquire() self.get_source_calendar(source=source) term = source.get_term() finally: self.lock.release() self.add_holdings(reservation=source, start=term.get_new_start_time(), end=term.get_end_time()) def get_source_calendar(self, *, source: ABCDelegation) -> SourceCalendar: """ Returns the outlay calendar for the given source reservation. @params source : source reservation @returns source calendar """ calendar = self.sources.get(source.get_delegation_id()) if calendar is None: calendar = SourceCalendar(clock=self.clock, source=source) self.sources[source.get_delegation_id()] = calendar return calendar def remove_source_calendar(self, *, source: ABCDelegation): """ Removes any data structures associated with a source reservation. @params source : source reservation """ try: self.lock.acquire() if source.get_delegation_id() in self.sources: self.sources.pop(source.get_delegation_id()) finally: self.lock.release() def get_outlays(self, *, source: ABCDelegation, time: datetime = None) -> ReservationSet: """ Returns the client reservations satisfied from the given source reservation at the specified time. @params source : source reservation @params time: time instance @returns set of client reservations satisfied from the given source at the specified time. """ try: self.lock.acquire() calendar = self.get_source_calendar(source=source) if time is None: return calendar.outlays.get_reservations() else: return calendar.outlays.get_reservations( time=ActorClock.to_milliseconds(when=time)) finally: self.lock.release() def get_closing(self, *, cycle: int) -> ReservationSet: """ Returns all reservations that need to be closed on the specified cycle. @params cycle : cycle @returns a set of reservations to be closed on the specified cycle """ try: self.lock.acquire() return self.closing.get_all_reservations(cycle=cycle) finally: self.lock.release() def add_closing(self, *, reservation: ABCReservationMixin, cycle: int): """ Adds a reservation to be closed on the specified cycle @params reservation : reservation to close @params cycle : cycle """ try: self.lock.acquire() self.closing.add_reservation(reservation=reservation, cycle=cycle) finally: self.lock.release() def remove_closing(self, *, reservation: ABCReservationMixin): """ Removes the specified reservation from the list of closing reservations. @params reservation : reservation to remove """ try: self.lock.acquire() self.closing.remove_reservation(reservation=reservation) finally: self.lock.release() def tick(self, *, cycle: int): super().tick(cycle=cycle) try: self.lock.acquire() self.requests.tick(cycle=cycle) self.closing.tick(cycle=cycle) for calendar in self.sources.values(): calendar.tick(cycle=cycle) finally: self.lock.release()